1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | // ============================================================================================= |
5 | // Code for tracking method inlinings in NGen and R2R images. |
6 | // The only information stored is "who" got inlined "where", no offsets or inlining depth tracking. |
7 | // (No good for debugger yet.) |
8 | // This information is later exposed to profilers and can be useful for ReJIT. |
9 | // Runtime inlining is not being tracked because profilers can deduce it via callbacks anyway. |
10 | // ============================================================================================= |
11 | #include "common.h" |
12 | #include "inlinetracking.h" |
13 | #include "ceeload.h" |
14 | |
15 | #ifndef DACCESS_COMPILE |
16 | |
17 | bool MethodInModule::operator <(const MethodInModule& other) const |
18 | { |
19 | STANDARD_VM_CONTRACT; |
20 | if (m_module == other.m_module) |
21 | { |
22 | return m_methodDef < other.m_methodDef; |
23 | } |
24 | else |
25 | { |
26 | // Since NGen images are supposed to be determenistic, |
27 | // we need stable sort order that isn't changing between different runs |
28 | // That's why we use names and GUIDs instead of just doing m_module < other.m_module |
29 | |
30 | // First we try to compare simple names (should be fast enough) |
31 | LPCUTF8 simpleName = m_module ? m_module->GetSimpleName() : "" ; |
32 | LPCUTF8 otherSimpleName = other.m_module ? other.m_module->GetSimpleName() : "" ; |
33 | int nameCmpResult = strcmp(simpleName, otherSimpleName); |
34 | |
35 | if (nameCmpResult == 0) |
36 | { |
37 | // Names are equal but module addresses aren't, it's suspicious |
38 | // falling back to module GUIDs |
39 | GUID thisGuid, otherGuid; |
40 | if (m_module == NULL) |
41 | { |
42 | memset(&thisGuid, 0, sizeof(GUID)); |
43 | } |
44 | else |
45 | { |
46 | m_module->GetFile()->GetMVID(&thisGuid); |
47 | } |
48 | |
49 | if (other.m_module == NULL) |
50 | { |
51 | memset(&otherGuid, 0, sizeof(GUID)); |
52 | } |
53 | else |
54 | { |
55 | other.m_module->GetFile()->GetMVID(&otherGuid); |
56 | } |
57 | |
58 | return memcmp(&thisGuid, &otherGuid, sizeof(GUID)) < 0; |
59 | } |
60 | else |
61 | { |
62 | return nameCmpResult < 0; |
63 | } |
64 | } |
65 | } |
66 | |
67 | bool MethodInModule::operator ==(const MethodInModule& other) const |
68 | { |
69 | LIMITED_METHOD_DAC_CONTRACT; |
70 | return m_methodDef == other.m_methodDef && |
71 | m_module == other.m_module; |
72 | } |
73 | |
74 | bool MethodInModule::operator !=(const MethodInModule& other) const |
75 | { |
76 | LIMITED_METHOD_DAC_CONTRACT; |
77 | return m_methodDef != other.m_methodDef || |
78 | m_module != other.m_module; |
79 | } |
80 | |
81 | |
82 | void InlineTrackingEntry::SortAndDeduplicate() |
83 | { |
84 | STANDARD_VM_CONTRACT; |
85 | |
86 | //Sort |
87 | MethodInModule *begin = &m_inliners[0]; |
88 | MethodInModule *end = begin + m_inliners.GetCount(); |
89 | util::sort(begin, end); |
90 | |
91 | //Deduplicate |
92 | MethodInModule *left = begin; |
93 | MethodInModule *right = left + 1; |
94 | while (right < end) |
95 | { |
96 | auto rvalue = *right; |
97 | if (*left != rvalue) |
98 | { |
99 | left++; |
100 | if (left != right) |
101 | { |
102 | *left = rvalue; |
103 | } |
104 | } |
105 | right++; |
106 | } |
107 | |
108 | //Shrink |
109 | int newCount = (int)(left - begin + 1); |
110 | m_inliners.SetCount(newCount); |
111 | } |
112 | |
113 | InlineTrackingEntry::InlineTrackingEntry(const InlineTrackingEntry& other) |
114 | :m_inlinee(other.m_inlinee) |
115 | { |
116 | STANDARD_VM_CONTRACT; |
117 | m_inliners.Set(other.m_inliners); |
118 | } |
119 | |
120 | InlineTrackingEntry & InlineTrackingEntry::operator = (const InlineTrackingEntry &other) |
121 | { |
122 | STANDARD_VM_CONTRACT; |
123 | m_inlinee = other.m_inlinee; |
124 | m_inliners.Set(other.m_inliners); |
125 | return *this; |
126 | } |
127 | |
128 | void InlineTrackingEntry::Add(PTR_MethodDesc inliner) |
129 | { |
130 | STANDARD_VM_CONTRACT; |
131 | |
132 | MethodInModule method(inliner->GetModule(), inliner->GetMemberDef()); |
133 | |
134 | // Going through last 10 inliners to check if a given inliner has recently been registered. |
135 | // It allows to filter out most duplicates without having to scan through hundreds of inliners |
136 | // for methods like Object.ctor or Monitor.Enter. |
137 | // We are OK to keep occasional duplicates in m_inliners, we'll get rid of them |
138 | // in SortAndDeduplicate() anyway. |
139 | int count = static_cast<int>(m_inliners.GetCount()); |
140 | int start = max(0, count - 10); |
141 | for (int i = count - 1; i >= start; i--) |
142 | { |
143 | if (m_inliners[i] == method) |
144 | return; |
145 | } |
146 | |
147 | //look like we see this inliner for the first time, add it to the collection |
148 | m_inliners.Append(method); |
149 | } |
150 | |
151 | InlineTrackingMap::InlineTrackingMap() |
152 | : m_mapCrst(CrstInlineTrackingMap) |
153 | { |
154 | STANDARD_VM_CONTRACT; |
155 | } |
156 | |
157 | void InlineTrackingMap::AddInlining(MethodDesc *inliner, MethodDesc *inlinee) |
158 | { |
159 | STANDARD_VM_CONTRACT; |
160 | _ASSERTE(inliner != NULL); |
161 | _ASSERTE(inlinee != NULL); |
162 | |
163 | MethodInModule inlineeMnM(inlinee->GetModule(), inlinee->GetMemberDef()); |
164 | |
165 | if (RidFromToken(inlineeMnM.m_methodDef) == 0 || RidFromToken(inliner->GetMemberDef()) == 0) |
166 | { |
167 | // Sometimes we do see methods that don't have valid tokens (stubs etc) |
168 | // we just ignore them. |
169 | return; |
170 | } |
171 | |
172 | CrstHolder lock(&m_mapCrst); |
173 | InlineTrackingEntry *existingEntry = const_cast<InlineTrackingEntry *>(LookupPtr(inlineeMnM)); |
174 | if (existingEntry) |
175 | { |
176 | // We saw this inlinee before, just add one more inliner |
177 | existingEntry->Add(inliner); |
178 | } |
179 | else |
180 | { |
181 | // We haven't seen this inlinee before, create a new record in the hashtable |
182 | // and add a first inliner to it. |
183 | InlineTrackingEntry newEntry; |
184 | newEntry.m_inlinee = inlineeMnM; |
185 | newEntry.Add(inliner); |
186 | Add(newEntry); |
187 | } |
188 | } |
189 | |
190 | #endif //!DACCESS_COMPILE |
191 | |
192 | void ZapInlineeRecord::InitForNGen(RID rid, LPCUTF8 simpleName) |
193 | { |
194 | LIMITED_METHOD_CONTRACT; |
195 | //XOR of up to first 24 bytes in module name |
196 | DWORD hash = 0; |
197 | for (int i = 0; simpleName[i] && i < 24; i++) |
198 | hash ^= (BYTE)simpleName[i]; |
199 | |
200 | // This key contains 24 bits of RID and 8 bits from module name. |
201 | // Since RID can't be longer than 24 bits, we can't have method RID collistions, |
202 | // that's why PersistentInlineTrackingMap::GetInliners only deals with module collisions. |
203 | m_key = (hash << 24) | rid; |
204 | } |
205 | |
206 | |
207 | COUNT_T PersistentInlineTrackingMapNGen::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T , MethodInModule inliners[], BOOL *incompleteData) |
208 | { |
209 | CONTRACTL |
210 | { |
211 | THROWS; |
212 | GC_NOTRIGGER; |
213 | MODE_ANY; |
214 | } |
215 | CONTRACTL_END; |
216 | |
217 | _ASSERTE(inlineeOwnerMod); |
218 | _ASSERTE(inliners); |
219 | |
220 | if (incompleteData) |
221 | { |
222 | *incompleteData = FALSE; |
223 | } |
224 | if (m_inlineeIndex == NULL || m_inlinersBuffer == NULL) |
225 | { |
226 | //No inlines saved in this image. |
227 | return 0; |
228 | } |
229 | |
230 | // Binary search to find all records matching (inlineeTkn/inlineeOwnerMod) |
231 | ZapInlineeRecord probeRecord; |
232 | probeRecord.InitForNGen(RidFromToken(inlineeTkn), inlineeOwnerMod->GetSimpleName()); |
233 | ZapInlineeRecord *begin = m_inlineeIndex; |
234 | ZapInlineeRecord *end = m_inlineeIndex + m_inlineeIndexSize; |
235 | ZapInlineeRecord *foundRecord = util::lower_bound(begin, end, probeRecord); |
236 | DWORD result = 0; |
237 | DWORD outputIndex = 0; |
238 | |
239 | // Go through all matching records |
240 | for (; foundRecord < end && *foundRecord == probeRecord; foundRecord++) |
241 | { |
242 | DWORD offset = foundRecord->m_offset; |
243 | NibbleReader stream(m_inlinersBuffer + offset, m_inlinersBufferSize - offset); |
244 | |
245 | DWORD inlineeModuleZapIndex = stream.ReadEncodedU32(); |
246 | Module *decodedInlineeModule = GetModuleByIndex(inlineeModuleZapIndex); |
247 | |
248 | // Check if this is just token/method name hash collision |
249 | if (decodedInlineeModule == inlineeOwnerMod) |
250 | { |
251 | // We found the token and the module we were looking for! |
252 | DWORD inlinerModuleZapIndex = stream.ReadEncodedU32(); //read inliner module, it is same for all inliners |
253 | Module *inlinerModule = GetModuleByIndex(inlinerModuleZapIndex); |
254 | |
255 | if (inlinerModule != NULL) |
256 | { |
257 | DWORD inlinersCount = stream.ReadEncodedU32(); |
258 | _ASSERTE(inlinersCount > 0); |
259 | |
260 | RID inlinerRid = 0; |
261 | // Reading inliner RIDs one by one, each RID is represented as an adjustment (diff) to the previous one. |
262 | // Adding inliners module and coping to the output buffer |
263 | for (DWORD i = 0; i < inlinersCount && outputIndex < inlinersSize; i++) |
264 | { |
265 | inlinerRid += stream.ReadEncodedU32(); |
266 | mdMethodDef inlinerTkn = TokenFromRid(inlinerRid, mdtMethodDef); |
267 | inliners[outputIndex++] = MethodInModule(inlinerModule, inlinerTkn); |
268 | } |
269 | result += inlinersCount; |
270 | } |
271 | else |
272 | { |
273 | // We can't find module for this inlineeModuleZapIndex, it means it hasn't been loaded yet |
274 | // (maybe it never will be), we just report it to the profiler. |
275 | // Profiler might want to try later when more modules are loaded. |
276 | if (incompleteData) |
277 | { |
278 | *incompleteData = TRUE; |
279 | } |
280 | } |
281 | } |
282 | } |
283 | |
284 | return result; |
285 | } |
286 | |
287 | |
288 | |
289 | Module *PersistentInlineTrackingMapNGen::GetModuleByIndex(DWORD index) |
290 | { |
291 | CONTRACTL |
292 | { |
293 | NOTHROW; |
294 | GC_NOTRIGGER; |
295 | MODE_ANY; |
296 | } |
297 | CONTRACTL_END; |
298 | |
299 | // This "black magic spell" has in fact nothing to do with GenericInstantiationCompare per se, but just sets a thread flag |
300 | // that later activates more thorough search inside Module::GetAssemblyIfLoaded, which is indirectly called from GetModuleFromIndexIfLoaded. |
301 | // This is useful when ngen image was compiler against a different assembly version than the one loaded now. |
302 | ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); |
303 | |
304 | return m_module->GetModuleFromIndexIfLoaded(index); |
305 | } |
306 | |
307 | |
308 | |
309 | #ifndef DACCESS_COMPILE |
310 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
311 | |
312 | // This is a shared serialization routine used for both NGEN and R2R formats. If image != NULL the NGEN format is generated, otherwise the R2R format |
313 | void SerializeInlineTrackingEntry(DataImage* image, SBuffer *inlinersBuffer, SArray<ZapInlineeRecord> *inlineeIndex, InlineTrackingEntry *entry) |
314 | { |
315 | STANDARD_VM_CONTRACT; |
316 | // This call removes duplicates from inliners and makes sure they are sorted by module |
317 | entry->SortAndDeduplicate(); |
318 | MethodInModule inlinee = entry->m_inlinee; |
319 | DWORD inlineeModuleZapIndex = 0; |
320 | if (image != NULL) |
321 | { |
322 | inlineeModuleZapIndex = image->GetModuleImportIndex(inlinee.m_module); |
323 | } |
324 | InlineSArray<MethodInModule, 3> &inliners = entry->m_inliners; |
325 | COUNT_T totalInlinersCount = inliners.GetCount(); |
326 | _ASSERTE(totalInlinersCount > 0); |
327 | |
328 | COUNT_T sameModuleCount; |
329 | // Going through all inliners and grouping them by their module, for each module we'll create |
330 | // an ZapInlineeRecord and encode inliners as bytes in inlinersBuffer. |
331 | for (COUNT_T thisModuleBegin = 0; thisModuleBegin < totalInlinersCount; thisModuleBegin += sameModuleCount) |
332 | { |
333 | Module *lastInlinerModule = inliners[thisModuleBegin].m_module; |
334 | DWORD lastInlinerModuleZapIndex = 0; |
335 | if (image != NULL) |
336 | { |
337 | lastInlinerModuleZapIndex = image->GetModuleImportIndex(lastInlinerModule); |
338 | } |
339 | |
340 | // Counting how many inliners belong to this module |
341 | sameModuleCount = 1; |
342 | while (thisModuleBegin + sameModuleCount < totalInlinersCount && |
343 | inliners[thisModuleBegin + sameModuleCount].m_module == lastInlinerModule) |
344 | { |
345 | sameModuleCount++; |
346 | } |
347 | |
348 | // Saving module indexes and number of inliners |
349 | NibbleWriter inlinersStream; |
350 | if (image != NULL) |
351 | { |
352 | inlinersStream.WriteEncodedU32(inlineeModuleZapIndex); |
353 | inlinersStream.WriteEncodedU32(lastInlinerModuleZapIndex); |
354 | } |
355 | inlinersStream.WriteEncodedU32(sameModuleCount); |
356 | |
357 | // Saving inliners RIDs, each new RID is represented as an adjustment (diff) to the previous one |
358 | RID prevMethodRid = 0; |
359 | for (COUNT_T i = thisModuleBegin; i < thisModuleBegin + sameModuleCount; i++) |
360 | { |
361 | RID methodRid = RidFromToken(inliners[i].m_methodDef); |
362 | _ASSERTE(methodRid >= prevMethodRid); |
363 | inlinersStream.WriteEncodedU32(methodRid - prevMethodRid); |
364 | prevMethodRid = methodRid; |
365 | } |
366 | inlinersStream.Flush(); |
367 | |
368 | // Copy output of NibbleWriter into a big buffer (inlinersBuffer) for inliners from the same module |
369 | // and create an InlineeRecord with correct offset |
370 | DWORD inlinersStreamSize; |
371 | const BYTE *inlinersStreamPtr = (const BYTE *)inlinersStream.GetBlob(&inlinersStreamSize); |
372 | ZapInlineeRecord record; |
373 | if (image != NULL) |
374 | { |
375 | record.InitForNGen(RidFromToken(inlinee.m_methodDef), inlinee.m_module->GetSimpleName()); |
376 | } |
377 | else |
378 | { |
379 | record.InitForR2R(RidFromToken(inlinee.m_methodDef)); |
380 | } |
381 | record.m_offset = inlinersBuffer->GetSize(); |
382 | inlinersBuffer->Insert(inlinersBuffer->End(), SBuffer(SBuffer::Immutable, inlinersStreamPtr, inlinersStreamSize)); |
383 | inlineeIndex->Append(record); |
384 | } |
385 | } |
386 | |
387 | bool compare_entry(const InlineTrackingEntry* first, const InlineTrackingEntry* second) |
388 | { |
389 | return first->m_inlinee < second->m_inlinee; |
390 | } |
391 | |
392 | // This is a shared serialization routine used for both NGEN and R2R formats. If image != NULL the NGEN format is generated, otherwise the R2R format |
393 | void SerializeTrackingMapBuffers(ZapHeap* heap, DataImage *image, SBuffer *inlinersBuffer, SArray<ZapInlineeRecord> *inlineeIndex, InlineTrackingMap* runtimeMap) |
394 | { |
395 | STANDARD_VM_CONTRACT; |
396 | _ASSERTE(runtimeMap != NULL); |
397 | |
398 | // Sort records from runtimeMap, because we need to make sure |
399 | // we save everything in deterministic order. Hashtable iteration is not deterministic. |
400 | COUNT_T runtimeMapCount = runtimeMap->GetCount(); |
401 | InlineTrackingEntry **inlinees = new (heap) InlineTrackingEntry *[runtimeMapCount]; |
402 | int index = 0; |
403 | for (auto iter = runtimeMap->Begin(), end = runtimeMap->End(); iter != end; ++iter) |
404 | { |
405 | inlinees[index++] = const_cast<InlineTrackingEntry *>(&*iter); |
406 | } |
407 | util::sort(inlinees, inlinees + runtimeMapCount, compare_entry); |
408 | |
409 | |
410 | // Iterate throught each inlinee record from the InlineTrackingMap |
411 | // and write corresponding records into inlineeIndex and inlinersBuffer |
412 | for (COUNT_T i = 0; i < runtimeMapCount; i++) |
413 | { |
414 | SerializeInlineTrackingEntry(image, inlinersBuffer, inlineeIndex, inlinees[i]); |
415 | } |
416 | } |
417 | |
418 | |
419 | |
420 | void PersistentInlineTrackingMapNGen::Save(DataImage *image, InlineTrackingMap* runtimeMap) |
421 | { |
422 | STANDARD_VM_CONTRACT; |
423 | _ASSERTE(image != NULL); |
424 | _ASSERTE(runtimeMap != NULL); |
425 | |
426 | SArray<ZapInlineeRecord> inlineeIndex; |
427 | SBuffer inlinersBuffer; |
428 | |
429 | SerializeTrackingMapBuffers(image->GetHeap(), image, &inlinersBuffer, &inlineeIndex, runtimeMap); |
430 | |
431 | m_inlineeIndexSize = inlineeIndex.GetCount(); |
432 | m_inlinersBufferSize = inlinersBuffer.GetSize(); |
433 | _ASSERTE((m_inlineeIndexSize == 0) == (m_inlinersBufferSize == 0)); |
434 | |
435 | if (m_inlineeIndexSize != 0 && m_inlinersBufferSize != 0) |
436 | { |
437 | // Copy everything to the class fields, we didn't use the class fields for addition |
438 | // because we want to make sure we don't waste memory for buffer's amortized growth |
439 | m_inlineeIndex = new (image->GetHeap()) ZapInlineeRecord[m_inlineeIndexSize]; |
440 | inlineeIndex.Copy(m_inlineeIndex, inlineeIndex.Begin(), m_inlineeIndexSize); |
441 | |
442 | m_inlinersBuffer = new (image->GetHeap()) BYTE[m_inlinersBufferSize]; |
443 | inlinersBuffer.Copy(m_inlinersBuffer, inlinersBuffer.Begin(), m_inlinersBufferSize); |
444 | |
445 | //Sort m_inlineeIndex so we can later use binary search |
446 | util::sort(m_inlineeIndex, m_inlineeIndex + m_inlineeIndexSize); |
447 | |
448 | //Making sure all this memory actually gets saved into NGEN image |
449 | image->StoreStructure(m_inlineeIndex, m_inlineeIndexSize * sizeof(m_inlineeIndex[0]), DataImage::ITEM_INLINING_DATA); |
450 | image->StoreStructure(m_inlinersBuffer, m_inlinersBufferSize, DataImage::ITEM_INLINING_DATA); |
451 | } |
452 | |
453 | image->StoreStructure(this, sizeof(*this), DataImage::ITEM_INLINING_DATA); |
454 | LOG((LF_ZAP, LL_INFO100000, |
455 | "PersistentInlineTrackingMap saved. InlineeIndexSize: %d bytes, InlinersBufferSize: %d bytes\n" , |
456 | m_inlineeIndexSize * sizeof(m_inlineeIndex[0]), m_inlinersBufferSize)); |
457 | } |
458 | |
459 | void PersistentInlineTrackingMapNGen::Fixup(DataImage *image) |
460 | { |
461 | STANDARD_VM_CONTRACT; |
462 | image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_module)); |
463 | image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_inlineeIndex)); |
464 | image->FixupPointerField(this, offsetof(PersistentInlineTrackingMapNGen, m_inlinersBuffer)); |
465 | } |
466 | |
467 | #endif //FEATURE_NATIVE_IMAGE_GENERATION |
468 | #endif //!DACCESS_COMPILE |
469 | |
470 | #ifdef FEATURE_READYTORUN |
471 | |
472 | struct |
473 | { |
474 | int ; |
475 | }; |
476 | |
477 | #ifndef DACCESS_COMPILE |
478 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
479 | |
480 | |
481 | |
482 | void PersistentInlineTrackingMapR2R::Save(ZapHeap* pHeap, SBuffer* pSaveTarget, InlineTrackingMap* runtimeMap) |
483 | { |
484 | STANDARD_VM_CONTRACT; |
485 | _ASSERTE(pSaveTarget != NULL); |
486 | _ASSERTE(runtimeMap != NULL); |
487 | |
488 | SArray<ZapInlineeRecord> inlineeIndex; |
489 | SBuffer inlinersBuffer; |
490 | |
491 | SerializeTrackingMapBuffers(pHeap, NULL, &inlinersBuffer, &inlineeIndex, runtimeMap); |
492 | |
493 | InliningHeader header; |
494 | header.SizeOfInlineeIndex = inlineeIndex.GetCount() * sizeof(ZapInlineeRecord); |
495 | |
496 | pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) &header, sizeof(header))); |
497 | DWORD unused = 0; |
498 | pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) inlineeIndex.GetElements(), header.SizeOfInlineeIndex)); |
499 | pSaveTarget->Insert(pSaveTarget->End(), SBuffer(SBuffer::Immutable, (const BYTE*) inlinersBuffer, inlinersBuffer.GetSize())); |
500 | |
501 | LOG((LF_ZAP, LL_INFO100000, |
502 | "PersistentInlineTrackingMap saved. InlineeIndexSize: %d bytes, InlinersBufferSize: %d bytes\n" , |
503 | header.SizeOfInlineeIndex, inlinersBuffer.GetSize())); |
504 | } |
505 | |
506 | #endif //FEATURE_NATIVE_IMAGE_GENERATION |
507 | |
508 | BOOL PersistentInlineTrackingMapR2R::TryLoad(Module* pModule, const BYTE* pBuffer, DWORD cbBuffer, |
509 | AllocMemTracker *pamTracker, PersistentInlineTrackingMapR2R** ppLoadedMap) |
510 | { |
511 | InliningHeader* pHeader = (InliningHeader*)pBuffer; |
512 | if (pHeader->SizeOfInlineeIndex > (int)(cbBuffer - sizeof(InliningHeader))) |
513 | { |
514 | //invalid serialized data, the index can't be larger the entire block |
515 | _ASSERTE(!"R2R image is invalid or there is a bug in the R2R parser" ); |
516 | return FALSE; |
517 | } |
518 | |
519 | //NOTE: Error checking on the format is very limited at this point. |
520 | //We trust the image format is valid and this initial check is a cheap |
521 | //verification that may help catch simple bugs. It does not secure against |
522 | //a deliberately maliciously formed binary. |
523 | |
524 | LoaderHeap *pHeap = pModule->GetLoaderAllocator()->GetHighFrequencyHeap(); |
525 | void * pMemory = pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(PersistentInlineTrackingMapR2R))); |
526 | PersistentInlineTrackingMapR2R* pMap = new (pMemory) PersistentInlineTrackingMapR2R(); |
527 | |
528 | pMap->m_module = pModule; |
529 | pMap->m_inlineeIndex = (PTR_ZapInlineeRecord)(pHeader + 1); |
530 | pMap->m_inlineeIndexSize = pHeader->SizeOfInlineeIndex / sizeof(ZapInlineeRecord); |
531 | pMap->m_inlinersBuffer = ((PTR_BYTE)(pHeader+1)) + pHeader->SizeOfInlineeIndex; |
532 | pMap->m_inlinersBufferSize = cbBuffer - sizeof(InliningHeader) - pMap->m_inlineeIndexSize; |
533 | *ppLoadedMap = pMap; |
534 | return TRUE; |
535 | } |
536 | |
537 | #endif //!DACCESS_COMPILE |
538 | |
539 | COUNT_T PersistentInlineTrackingMapR2R::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T , MethodInModule inliners[], BOOL *incompleteData) |
540 | { |
541 | CONTRACTL |
542 | { |
543 | THROWS; |
544 | GC_NOTRIGGER; |
545 | MODE_ANY; |
546 | } |
547 | CONTRACTL_END; |
548 | |
549 | _ASSERTE(inlineeOwnerMod); |
550 | _ASSERTE(inliners); |
551 | |
552 | if (incompleteData) |
553 | { |
554 | *incompleteData = FALSE; |
555 | } |
556 | if (m_inlineeIndex == NULL || m_inlinersBuffer == NULL) |
557 | { |
558 | //No inlines saved in this image. |
559 | return 0; |
560 | } |
561 | if(inlineeOwnerMod != m_module) |
562 | { |
563 | // no cross module inlining (yet?) |
564 | return 0; |
565 | } |
566 | |
567 | // Binary search to find all records matching (inlineeTkn) |
568 | ZapInlineeRecord probeRecord; |
569 | probeRecord.InitForR2R(RidFromToken(inlineeTkn)); |
570 | ZapInlineeRecord *begin = m_inlineeIndex; |
571 | ZapInlineeRecord *end = m_inlineeIndex + m_inlineeIndexSize; |
572 | ZapInlineeRecord *foundRecord = util::lower_bound(begin, end, probeRecord); |
573 | DWORD result = 0; |
574 | DWORD outputIndex = 0; |
575 | |
576 | // Go through all matching records |
577 | for (; foundRecord < end && *foundRecord == probeRecord; foundRecord++) |
578 | { |
579 | DWORD offset = foundRecord->m_offset; |
580 | NibbleReader stream(m_inlinersBuffer + offset, m_inlinersBufferSize - offset); |
581 | Module *inlinerModule = m_module; |
582 | |
583 | DWORD inlinersCount = stream.ReadEncodedU32(); |
584 | _ASSERTE(inlinersCount > 0); |
585 | |
586 | RID inlinerRid = 0; |
587 | // Reading inliner RIDs one by one, each RID is represented as an adjustment (diff) to the previous one. |
588 | // Adding inliners module and coping to the output buffer |
589 | for (DWORD i = 0; i < inlinersCount && outputIndex < inlinersSize; i++) |
590 | { |
591 | inlinerRid += stream.ReadEncodedU32(); |
592 | mdMethodDef inlinerTkn = TokenFromRid(inlinerRid, mdtMethodDef); |
593 | inliners[outputIndex++] = MethodInModule(inlinerModule, inlinerTkn); |
594 | } |
595 | result += inlinersCount; |
596 | } |
597 | |
598 | return result; |
599 | } |
600 | |
601 | #endif //FEATURE_READYTORUN |