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 | // DebugInfoStore |
5 | |
6 | |
7 | |
8 | #include "common.h" |
9 | #include "debuginfostore.h" |
10 | #include "nibblestream.h" |
11 | |
12 | |
13 | #ifdef _DEBUG |
14 | // For debug builds only. |
15 | static bool Dbg_ShouldUseCookies() |
16 | { |
17 | SUPPORTS_DAC; |
18 | |
19 | // Normally we want this as false b/c it would bloat the image. |
20 | // But give us a hook to enable it in case we need it. |
21 | return false; |
22 | } |
23 | #endif |
24 | |
25 | //----------------------------------------------------------------------------- |
26 | // We have "Transfer" objects that sit on top of the streams. |
27 | // The objects look identical, but one serializes and the other deserializes. |
28 | // This lets the compression + restoration routines share all their compression |
29 | // logic and just swap out Transfer objects. |
30 | // |
31 | // It's not ideal that we have a lot of redundancy maintaining both Transfer |
32 | // objects, but at least the compiler can enforce that the Reader & Writer are |
33 | // in sync. It can't enforce that a 2 separate routines for Compression & |
34 | // restoration are in sync. |
35 | // |
36 | // We could have the TransferReader + Writer be polymorphic off a base class, |
37 | // but the virtual function calls will be extra overhead. May as well use |
38 | // templates and let the compiler resolve it all statically at compile time. |
39 | //----------------------------------------------------------------------------- |
40 | |
41 | |
42 | //----------------------------------------------------------------------------- |
43 | // Serialize to a NibbleWriter stream. |
44 | //----------------------------------------------------------------------------- |
45 | class TransferWriter |
46 | { |
47 | public: |
48 | TransferWriter(NibbleWriter & w) : m_w(w) |
49 | { |
50 | } |
51 | |
52 | // Write an raw U32 in nibble encoded form. |
53 | void DoEncodedU32(DWORD dw) { m_w.WriteEncodedU32(dw); } |
54 | |
55 | // Use to encode a monotonically increasing delta. |
56 | void DoEncodedDeltaU32(DWORD & dw, DWORD dwLast) |
57 | { |
58 | CONTRACTL |
59 | { |
60 | THROWS; |
61 | GC_NOTRIGGER; |
62 | MODE_ANY; |
63 | } |
64 | CONTRACTL_END; |
65 | _ASSERTE(dw >= dwLast); |
66 | DWORD dwDelta = dw - dwLast; |
67 | m_w.WriteEncodedU32(dwDelta); |
68 | } |
69 | |
70 | |
71 | // Some U32 may have a few sentinal negative values . |
72 | // We adjust it to be a real U32 and then encode that. |
73 | // dwAdjust should be the lower bound on the enum. |
74 | void DoEncodedAdjustedU32(DWORD dw, DWORD dwAdjust) |
75 | { |
76 | //_ASSERTE(dwAdjust < 0); // some negative lower bound. |
77 | m_w.WriteEncodedU32(dw - dwAdjust); |
78 | } |
79 | |
80 | // Typesafe versions of EncodeU32. |
81 | void DoEncodedSourceType(ICorDebugInfo::SourceTypes & dw) { m_w.WriteEncodedU32(dw); } |
82 | void DoEncodedVarLocType(ICorDebugInfo::VarLocType & dw) { m_w.WriteEncodedU32(dw); } |
83 | void DoEncodedUnsigned(unsigned & dw) { m_w.WriteEncodedU32(dw); } |
84 | |
85 | // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits. |
86 | void DoEncodedStackOffset(signed & dwOffset) |
87 | { |
88 | CONTRACTL |
89 | { |
90 | THROWS; |
91 | GC_NOTRIGGER; |
92 | MODE_ANY; |
93 | } |
94 | CONTRACTL_END; |
95 | #ifdef _TARGET_X86_ |
96 | _ASSERTE(dwOffset % sizeof(DWORD) == 0); // should be dword aligned. That'll save us 2 bits. |
97 | m_w.WriteEncodedI32(dwOffset / sizeof(DWORD)); |
98 | #else |
99 | // Non x86 platforms don't need it to be dword aligned. |
100 | m_w.WriteEncodedI32(dwOffset); |
101 | #endif |
102 | } |
103 | |
104 | void DoEncodedRegIdx(ICorDebugInfo::RegNum & reg) { m_w.WriteEncodedU32(reg); } |
105 | |
106 | // For debugging purposes, inject cookies into the Compression. |
107 | void DoCookie(BYTE b) { |
108 | #ifdef _DEBUG |
109 | if (Dbg_ShouldUseCookies()) |
110 | { |
111 | m_w.WriteNibble(b); |
112 | } |
113 | #endif |
114 | } |
115 | |
116 | protected: |
117 | NibbleWriter & m_w; |
118 | |
119 | }; |
120 | |
121 | //----------------------------------------------------------------------------- |
122 | // Deserializer that sits on top of a NibbleReader |
123 | // This class interface matches TransferWriter exactly. See that for details. |
124 | //----------------------------------------------------------------------------- |
125 | class TransferReader |
126 | { |
127 | public: |
128 | TransferReader(NibbleReader & r) : m_r(r) |
129 | { |
130 | SUPPORTS_DAC; |
131 | } |
132 | |
133 | void DoEncodedU32(DWORD & dw) |
134 | { |
135 | SUPPORTS_DAC; |
136 | dw = m_r.ReadEncodedU32(); |
137 | } |
138 | |
139 | // Use to decode a monotonically increasing delta. |
140 | // dwLast was the last value; we update it to the current value on output. |
141 | void DoEncodedDeltaU32(DWORD & dw, DWORD dwLast) |
142 | { |
143 | SUPPORTS_DAC; |
144 | DWORD dwDelta = m_r.ReadEncodedU32(); |
145 | dw = dwLast + dwDelta; |
146 | } |
147 | |
148 | void DoEncodedAdjustedU32(DWORD & dw, DWORD dwAdjust) |
149 | { |
150 | SUPPORTS_DAC; |
151 | //_ASSERTE(dwAdjust < 0); |
152 | dw = m_r.ReadEncodedU32() + dwAdjust; |
153 | } |
154 | |
155 | void DoEncodedSourceType(ICorDebugInfo::SourceTypes & dw) |
156 | { |
157 | SUPPORTS_DAC; |
158 | dw = (ICorDebugInfo::SourceTypes) m_r.ReadEncodedU32(); |
159 | } |
160 | |
161 | void DoEncodedVarLocType(ICorDebugInfo::VarLocType & dw) |
162 | { |
163 | SUPPORTS_DAC; |
164 | dw = (ICorDebugInfo::VarLocType) m_r.ReadEncodedU32(); |
165 | } |
166 | |
167 | void DoEncodedUnsigned(unsigned & dw) |
168 | { |
169 | SUPPORTS_DAC; |
170 | dw = (unsigned) m_r.ReadEncodedU32(); |
171 | } |
172 | |
173 | |
174 | // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits. |
175 | void DoEncodedStackOffset(signed & dwOffset) |
176 | { |
177 | SUPPORTS_DAC; |
178 | #ifdef _TARGET_X86_ |
179 | dwOffset = m_r.ReadEncodedI32() * sizeof(DWORD); |
180 | #else |
181 | // Non x86 platforms don't need it to be dword aligned. |
182 | dwOffset = m_r.ReadEncodedI32(); |
183 | #endif |
184 | } |
185 | |
186 | void DoEncodedRegIdx(ICorDebugInfo::RegNum & reg) |
187 | { |
188 | SUPPORTS_DAC; |
189 | reg = (ICorDebugInfo::RegNum) m_r.ReadEncodedU32(); |
190 | } |
191 | |
192 | // For debugging purposes, inject cookies into the Compression. |
193 | void DoCookie(BYTE b) |
194 | { |
195 | SUPPORTS_DAC; |
196 | |
197 | #ifdef _DEBUG |
198 | if (Dbg_ShouldUseCookies()) |
199 | { |
200 | BYTE b2 = m_r.ReadNibble(); |
201 | _ASSERTE(b == b2); |
202 | } |
203 | #endif |
204 | } |
205 | |
206 | |
207 | protected: |
208 | NibbleReader & m_r; |
209 | }; |
210 | |
211 | |
212 | #if defined(_DEBUG) && !defined(DACCESS_COMPILE) |
213 | // Perf tracking |
214 | static int g_CDI_TotalMethods = 0; |
215 | static int g_CDI_bMethodTotalUncompress = 0; |
216 | static int g_CDI_bMethodTotalCompress = 0; |
217 | |
218 | static int g_CDI_bVarsTotalUncompress = 0; |
219 | static int g_CDI_bVarsTotalCompress = 0; |
220 | #endif |
221 | |
222 | //----------------------------------------------------------------------------- |
223 | // Serialize Bounds info. |
224 | //----------------------------------------------------------------------------- |
225 | template <class T> |
226 | void DoBounds( |
227 | T trans, // transfer object. |
228 | ULONG32 cMap, |
229 | ICorDebugInfo::OffsetMapping *pMap |
230 | ) |
231 | { |
232 | CONTRACTL |
233 | { |
234 | THROWS; |
235 | GC_NOTRIGGER; |
236 | MODE_ANY; |
237 | SUPPORTS_DAC; |
238 | } |
239 | CONTRACTL_END; |
240 | |
241 | |
242 | // Bounds info contains (Native Offset, IL Offset, flags) |
243 | // - Sorted by native offset (so use a delta encoding for that). |
244 | // - IL offsets aren't sorted, but they should be close to each other (so a signed delta encoding) |
245 | // They may also include a sentinel value from MappingTypes. |
246 | // - flags is 3 indepedent bits. |
247 | |
248 | // Loop through and transfer each Entry in the Mapping. |
249 | DWORD dwLastNativeOffset = 0; |
250 | for(DWORD i = 0; i < cMap; i++) |
251 | { |
252 | ICorDebugInfo::OffsetMapping * pBound = &pMap[i]; |
253 | |
254 | trans.DoEncodedDeltaU32(pBound->nativeOffset, dwLastNativeOffset); |
255 | dwLastNativeOffset = pBound->nativeOffset; |
256 | |
257 | |
258 | trans.DoEncodedAdjustedU32(pBound->ilOffset, (DWORD) ICorDebugInfo::MAX_MAPPING_VALUE); |
259 | |
260 | trans.DoEncodedSourceType(pBound->source); |
261 | |
262 | trans.DoCookie(0xA); |
263 | } |
264 | } |
265 | |
266 | |
267 | |
268 | // Helper to write a compressed Native Var Info |
269 | template<class T> |
270 | void DoNativeVarInfo( |
271 | T trans, |
272 | ICorDebugInfo::NativeVarInfo * pVar |
273 | ) |
274 | { |
275 | CONTRACTL |
276 | { |
277 | THROWS; |
278 | GC_NOTRIGGER; |
279 | MODE_ANY; |
280 | SUPPORTS_DAC; |
281 | } |
282 | CONTRACTL_END; |
283 | |
284 | |
285 | // Each Varinfo has a: |
286 | // - native start +End offset. We can use a delta for the end offset. |
287 | // - Il variable number. These are usually small. |
288 | // - VarLoc information. This is a tagged variant. |
289 | // The entries aren't sorted in any particular order. |
290 | trans.DoCookie(0xB); |
291 | trans.DoEncodedU32(pVar->startOffset); |
292 | |
293 | |
294 | trans.DoEncodedDeltaU32(pVar->endOffset, pVar->startOffset); |
295 | |
296 | // record var number. |
297 | trans.DoEncodedAdjustedU32(pVar->varNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); |
298 | |
299 | |
300 | // Now write the VarLoc... This is a variant like structure and so we'll get different |
301 | // compressioned depending on what we've got. |
302 | trans.DoEncodedVarLocType(pVar->loc.vlType); |
303 | |
304 | switch(pVar->loc.vlType) |
305 | { |
306 | case ICorDebugInfo::VLT_REG: |
307 | case ICorDebugInfo::VLT_REG_FP: // fall through |
308 | case ICorDebugInfo::VLT_REG_BYREF: // fall through |
309 | trans.DoEncodedRegIdx(pVar->loc.vlReg.vlrReg); |
310 | break; |
311 | |
312 | case ICorDebugInfo::VLT_STK: |
313 | case ICorDebugInfo::VLT_STK_BYREF: // fall through |
314 | trans.DoEncodedRegIdx(pVar->loc.vlStk.vlsBaseReg); |
315 | trans.DoEncodedStackOffset(pVar->loc.vlStk.vlsOffset); |
316 | break; |
317 | |
318 | case ICorDebugInfo::VLT_REG_REG: |
319 | trans.DoEncodedRegIdx(pVar->loc.vlRegReg.vlrrReg1); |
320 | trans.DoEncodedRegIdx(pVar->loc.vlRegReg.vlrrReg2); |
321 | break; |
322 | |
323 | case ICorDebugInfo::VLT_REG_STK: |
324 | trans.DoEncodedRegIdx(pVar->loc.vlRegStk.vlrsReg); |
325 | trans.DoEncodedRegIdx(pVar->loc.vlRegStk.vlrsStk.vlrssBaseReg); |
326 | trans.DoEncodedStackOffset(pVar->loc.vlRegStk.vlrsStk.vlrssOffset); |
327 | break; |
328 | |
329 | case ICorDebugInfo::VLT_STK_REG: |
330 | trans.DoEncodedStackOffset(pVar->loc.vlStkReg.vlsrStk.vlsrsOffset); |
331 | trans.DoEncodedRegIdx(pVar->loc.vlStkReg.vlsrStk.vlsrsBaseReg); |
332 | trans.DoEncodedRegIdx(pVar->loc.vlStkReg.vlsrReg); |
333 | break; |
334 | |
335 | case ICorDebugInfo::VLT_STK2: |
336 | trans.DoEncodedRegIdx(pVar->loc.vlStk2.vls2BaseReg); |
337 | trans.DoEncodedStackOffset(pVar->loc.vlStk2.vls2Offset); |
338 | break; |
339 | |
340 | case ICorDebugInfo::VLT_FPSTK: |
341 | trans.DoEncodedUnsigned(pVar->loc.vlFPstk.vlfReg); |
342 | break; |
343 | |
344 | case ICorDebugInfo::VLT_FIXED_VA: |
345 | trans.DoEncodedUnsigned(pVar->loc.vlFixedVarArg.vlfvOffset); |
346 | break; |
347 | |
348 | default: |
349 | _ASSERTE(!"Unknown varloc type!" ); |
350 | break; |
351 | } |
352 | |
353 | |
354 | trans.DoCookie(0xC); |
355 | } |
356 | |
357 | |
358 | #ifndef DACCESS_COMPILE |
359 | |
360 | void CompressDebugInfo::CompressBoundaries( |
361 | IN ULONG32 cMap, |
362 | IN ICorDebugInfo::OffsetMapping *pMap, |
363 | IN OUT NibbleWriter *pWriter |
364 | ) |
365 | { |
366 | CONTRACTL |
367 | { |
368 | THROWS; |
369 | GC_NOTRIGGER; |
370 | MODE_ANY; |
371 | } |
372 | CONTRACTL_END; |
373 | |
374 | _ASSERTE(pWriter != NULL); |
375 | _ASSERTE((pMap == NULL) == (cMap == 0)); |
376 | |
377 | if (cMap != 0) |
378 | { |
379 | pWriter->WriteEncodedU32(cMap); |
380 | |
381 | TransferWriter t(*pWriter); |
382 | DoBounds(t, cMap, pMap); |
383 | |
384 | pWriter->Flush(); |
385 | } |
386 | |
387 | #ifdef _DEBUG |
388 | DWORD cbBlob; |
389 | PVOID pBlob = pWriter->GetBlob(&cbBlob); |
390 | |
391 | // Track perf #s for compression... |
392 | g_CDI_TotalMethods++; |
393 | g_CDI_bMethodTotalUncompress += sizeof(ICorDebugInfo::OffsetMapping) * cMap; |
394 | g_CDI_bMethodTotalCompress += (int) cbBlob; |
395 | #endif // _DEBUG |
396 | } |
397 | |
398 | |
399 | void CompressDebugInfo::CompressVars( |
400 | IN ULONG32 cVars, |
401 | IN ICorDebugInfo::NativeVarInfo *vars, |
402 | IN OUT NibbleWriter *pWriter |
403 | ) |
404 | { |
405 | CONTRACTL |
406 | { |
407 | THROWS; |
408 | GC_NOTRIGGER; |
409 | MODE_ANY; |
410 | } |
411 | CONTRACTL_END; |
412 | |
413 | _ASSERTE(pWriter != NULL); |
414 | _ASSERTE((cVars == 0) == (vars == NULL)); |
415 | |
416 | if (cVars != 0) |
417 | { |
418 | pWriter->WriteEncodedU32(cVars); |
419 | |
420 | TransferWriter t(*pWriter); |
421 | for(ULONG32 i = 0; i < cVars; i ++) |
422 | { |
423 | DoNativeVarInfo(t, &vars[i]); |
424 | } |
425 | |
426 | pWriter->Flush(); |
427 | } |
428 | |
429 | #ifdef _DEBUG |
430 | DWORD cbBlob; |
431 | PVOID pBlob = pWriter->GetBlob(&cbBlob); |
432 | |
433 | g_CDI_bVarsTotalUncompress += cVars * sizeof(ICorDebugInfo::NativeVarInfo); |
434 | g_CDI_bVarsTotalCompress += (int) cbBlob; |
435 | #endif |
436 | } |
437 | |
438 | PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( |
439 | IN ICorDebugInfo::OffsetMapping * pOffsetMapping, |
440 | IN ULONG iOffsetMapping, |
441 | IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, |
442 | IN ULONG iNativeVarInfo, |
443 | IN OUT SBuffer * pDebugInfoBuffer, |
444 | IN LoaderHeap * pLoaderHeap |
445 | ) |
446 | { |
447 | CONTRACTL { |
448 | THROWS; // compression routines throw |
449 | PRECONDITION((iOffsetMapping == 0) == (pOffsetMapping == NULL)); |
450 | PRECONDITION((iNativeVarInfo == 0) == (pNativeVarInfo == NULL)); |
451 | PRECONDITION((pDebugInfoBuffer != NULL) ^ (pLoaderHeap != NULL)); |
452 | } CONTRACTL_END; |
453 | |
454 | // Actually do the compression. These will throw on oom. |
455 | NibbleWriter boundsBuffer; |
456 | DWORD cbBounds = 0; |
457 | PVOID pBounds = NULL; |
458 | if (iOffsetMapping > 0) |
459 | { |
460 | CompressDebugInfo::CompressBoundaries(iOffsetMapping, pOffsetMapping, &boundsBuffer); |
461 | pBounds = boundsBuffer.GetBlob(&cbBounds); |
462 | } |
463 | |
464 | NibbleWriter varsBuffer; |
465 | DWORD cbVars = 0; |
466 | PVOID pVars = NULL; |
467 | if (iNativeVarInfo > 0) |
468 | { |
469 | CompressDebugInfo::CompressVars(iNativeVarInfo, pNativeVarInfo, &varsBuffer); |
470 | pVars = varsBuffer.GetBlob(&cbVars); |
471 | } |
472 | |
473 | // Now write it all out to the buffer in a compact fashion. |
474 | NibbleWriter w; |
475 | w.WriteEncodedU32(cbBounds); |
476 | w.WriteEncodedU32(cbVars); |
477 | w.Flush(); |
478 | |
479 | DWORD cbHeader; |
480 | PVOID pHeader = w.GetBlob(&cbHeader); |
481 | |
482 | S_UINT32 cbFinalSize = S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars); |
483 | if (cbFinalSize.IsOverflow()) |
484 | ThrowHR(COR_E_OVERFLOW); |
485 | |
486 | BYTE *ptrStart = NULL; |
487 | if (pLoaderHeap != NULL) |
488 | { |
489 | ptrStart = (BYTE *)(void *)pLoaderHeap->AllocMem(S_SIZE_T(cbFinalSize.Value())); |
490 | } |
491 | else |
492 | { |
493 | // Create a conservatively large buffer to hold all the data. |
494 | ptrStart = pDebugInfoBuffer->OpenRawBuffer(cbFinalSize.Value()); |
495 | } |
496 | _ASSERTE(ptrStart != NULL); // throws on oom. |
497 | |
498 | BYTE *ptr = ptrStart; |
499 | |
500 | memcpy(ptr, pHeader, cbHeader); |
501 | ptr += cbHeader; |
502 | |
503 | memcpy(ptr, pBounds, cbBounds); |
504 | ptr += cbBounds; |
505 | |
506 | memcpy(ptr, pVars, cbVars); |
507 | ptr += cbVars; |
508 | |
509 | if (pLoaderHeap != NULL) |
510 | { |
511 | return ptrStart; |
512 | } |
513 | else |
514 | { |
515 | pDebugInfoBuffer->CloseRawBuffer(cbFinalSize.Value()); |
516 | return NULL; |
517 | } |
518 | } |
519 | |
520 | #endif // DACCESS_COMPILE |
521 | |
522 | //----------------------------------------------------------------------------- |
523 | // Compression routines |
524 | // DAC only needs to run the uncompression routines. |
525 | //----------------------------------------------------------------------------- |
526 | |
527 | //----------------------------------------------------------------------------- |
528 | // Uncompression (restore) routines |
529 | //----------------------------------------------------------------------------- |
530 | |
531 | // Uncompress data supplied by Compress functions. |
532 | void CompressDebugInfo::RestoreBoundariesAndVars( |
533 | IN FP_IDS_NEW fpNew, IN void * pNewData, |
534 | IN PTR_BYTE pDebugInfo, |
535 | OUT ULONG32 * pcMap, // number of entries in ppMap |
536 | OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array |
537 | OUT ULONG32 *pcVars, |
538 | OUT ICorDebugInfo::NativeVarInfo **ppVars |
539 | ) |
540 | { |
541 | CONTRACTL |
542 | { |
543 | THROWS; // reading from nibble stream may throw on invalid data. |
544 | GC_NOTRIGGER; |
545 | MODE_ANY; |
546 | SUPPORTS_DAC; |
547 | } |
548 | CONTRACTL_END; |
549 | |
550 | if (pcMap != NULL) *pcMap = 0; |
551 | if (ppMap != NULL) *ppMap = NULL; |
552 | if (pcVars != NULL) *pcVars = 0; |
553 | if (ppVars != NULL) *ppVars = NULL; |
554 | |
555 | NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */); |
556 | |
557 | ULONG cbBounds = r.ReadEncodedU32(); |
558 | ULONG cbVars = r.ReadEncodedU32(); |
559 | |
560 | PTR_BYTE addrBounds = pDebugInfo + r.GetNextByteIndex(); |
561 | PTR_BYTE addrVars = addrBounds + cbBounds; |
562 | |
563 | if ((pcMap != NULL || ppMap != NULL) && (cbBounds != 0)) |
564 | { |
565 | NibbleReader r(addrBounds, cbBounds); |
566 | TransferReader t(r); |
567 | |
568 | UINT32 cNumEntries = r.ReadEncodedU32(); |
569 | _ASSERTE(cNumEntries > 0); |
570 | |
571 | if (pcMap != NULL) |
572 | *pcMap = cNumEntries; |
573 | |
574 | if (ppMap != NULL) |
575 | { |
576 | ICorDebugInfo::OffsetMapping * pMap = reinterpret_cast<ICorDebugInfo::OffsetMapping *> |
577 | (fpNew(pNewData, cNumEntries * sizeof(ICorDebugInfo::OffsetMapping))); |
578 | if (pMap == NULL) |
579 | { |
580 | ThrowOutOfMemory(); |
581 | } |
582 | *ppMap = pMap; |
583 | |
584 | // Main decompression routine. |
585 | DoBounds(t, cNumEntries, pMap); |
586 | } |
587 | } |
588 | |
589 | if ((pcVars != NULL || ppVars != NULL) && (cbVars != 0)) |
590 | { |
591 | NibbleReader r(addrVars, cbVars); |
592 | TransferReader t(r); |
593 | |
594 | UINT32 cNumEntries = r.ReadEncodedU32(); |
595 | _ASSERTE(cNumEntries > 0); |
596 | |
597 | if (pcVars != NULL) |
598 | *pcVars = cNumEntries; |
599 | |
600 | if (ppVars != NULL) |
601 | { |
602 | ICorDebugInfo::NativeVarInfo * pVars = reinterpret_cast<ICorDebugInfo::NativeVarInfo *> |
603 | (fpNew(pNewData, cNumEntries * sizeof(ICorDebugInfo::NativeVarInfo))); |
604 | if (pVars == NULL) |
605 | { |
606 | ThrowOutOfMemory(); |
607 | } |
608 | *ppVars = pVars; |
609 | |
610 | for(UINT32 i = 0; i < cNumEntries; i++) |
611 | { |
612 | DoNativeVarInfo(t, &pVars[i]); |
613 | } |
614 | } |
615 | } |
616 | } |
617 | |
618 | #ifdef DACCESS_COMPILE |
619 | void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo) |
620 | { |
621 | CONTRACTL |
622 | { |
623 | NOTHROW; |
624 | GC_NOTRIGGER; |
625 | SUPPORTS_DAC; |
626 | } |
627 | CONTRACTL_END; |
628 | |
629 | NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */); |
630 | |
631 | ULONG cbBounds = r.ReadEncodedU32(); |
632 | ULONG cbVars = r.ReadEncodedU32(); |
633 | |
634 | DacEnumMemoryRegion(dac_cast<TADDR>(pDebugInfo), r.GetNextByteIndex() + cbBounds + cbVars); |
635 | } |
636 | #endif // DACCESS_COMPILE |
637 | |
638 | // Init given a starting address from the start of code. |
639 | void DebugInfoRequest::InitFromStartingAddr(MethodDesc * pMD, PCODE addrCode) |
640 | { |
641 | CONTRACTL |
642 | { |
643 | NOTHROW; |
644 | GC_NOTRIGGER; |
645 | MODE_ANY; |
646 | SUPPORTS_DAC; |
647 | } |
648 | CONTRACTL_END; |
649 | |
650 | _ASSERTE(pMD != NULL); |
651 | _ASSERTE(addrCode != NULL); |
652 | |
653 | this->m_pMD = pMD; |
654 | this->m_addrStart = addrCode; |
655 | } |
656 | |
657 | |
658 | //----------------------------------------------------------------------------- |
659 | // Impl for DebugInfoManager's IDebugInfoStore |
660 | //----------------------------------------------------------------------------- |
661 | BOOL DebugInfoManager::GetBoundariesAndVars( |
662 | const DebugInfoRequest & request, |
663 | IN FP_IDS_NEW fpNew, IN void * pNewData, |
664 | OUT ULONG32 * pcMap, |
665 | OUT ICorDebugInfo::OffsetMapping ** ppMap, |
666 | OUT ULONG32 * pcVars, |
667 | OUT ICorDebugInfo::NativeVarInfo ** ppVars) |
668 | { |
669 | CONTRACTL |
670 | { |
671 | THROWS; |
672 | WRAPPER(GC_TRIGGERS); // depends on fpNew |
673 | SUPPORTS_DAC; |
674 | } |
675 | CONTRACTL_END; |
676 | |
677 | IJitManager* pJitMan = ExecutionManager::FindJitMan(request.GetStartAddress()); |
678 | if (pJitMan == NULL) |
679 | { |
680 | return FALSE; // no info available. |
681 | } |
682 | |
683 | return pJitMan->GetBoundariesAndVars(request, fpNew, pNewData, pcMap, ppMap, pcVars, ppVars); |
684 | } |
685 | |
686 | #ifdef DACCESS_COMPILE |
687 | void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD) |
688 | { |
689 | CONTRACTL |
690 | { |
691 | NOTHROW; |
692 | GC_NOTRIGGER; |
693 | SUPPORTS_DAC; |
694 | } |
695 | CONTRACTL_END; |
696 | |
697 | PCODE addrCode = pMD->GetNativeCode(); |
698 | if (addrCode == NULL) |
699 | { |
700 | return; |
701 | } |
702 | |
703 | IJitManager* pJitMan = ExecutionManager::FindJitMan(addrCode); |
704 | if (pJitMan == NULL) |
705 | { |
706 | return; // no info available. |
707 | } |
708 | |
709 | pJitMan->EnumMemoryRegionsForMethodDebugInfo(flags, pMD); |
710 | } |
711 | #endif |
712 | |