| 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 |