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 | // ==++== |
6 | // |
7 | |
8 | // |
9 | // ==--== |
10 | #ifndef __util_h__ |
11 | #define __util_h__ |
12 | |
13 | #define LIMITED_METHOD_CONTRACT |
14 | |
15 | // So we can use the PAL_TRY_NAKED family of macros without dependencies on utilcode. |
16 | inline void RestoreSOToleranceState() {} |
17 | |
18 | #include <cor.h> |
19 | #include <corsym.h> |
20 | #include <clrdata.h> |
21 | #include <palclr.h> |
22 | #include <metahost.h> |
23 | #include <new> |
24 | |
25 | #if !defined(FEATURE_PAL) |
26 | #include <dia2.h> |
27 | #endif |
28 | |
29 | #ifdef STRIKE |
30 | #if defined(_MSC_VER) |
31 | #pragma warning(disable:4200) |
32 | #pragma warning(default:4200) |
33 | #endif |
34 | #include "data.h" |
35 | #endif //STRIKE |
36 | |
37 | #include "cordebug.h" |
38 | #include "static_assert.h" |
39 | |
40 | typedef LPCSTR LPCUTF8; |
41 | typedef LPSTR LPUTF8; |
42 | |
43 | DECLARE_HANDLE(OBJECTHANDLE); |
44 | |
45 | struct IMDInternalImport; |
46 | |
47 | #if defined(_TARGET_WIN64_) |
48 | #define WIN64_8SPACES "" |
49 | #define WIN86_8SPACES " " |
50 | #define POINTERSIZE "16" |
51 | #define POINTERSIZE_HEX 16 |
52 | #define POINTERSIZE_BYTES 8 |
53 | #define POINTERSIZE_TYPE "I64" |
54 | #else |
55 | #define WIN64_8SPACES " " |
56 | #define WIN86_8SPACES "" |
57 | #define POINTERSIZE "8" |
58 | #define POINTERSIZE_HEX 8 |
59 | #define POINTERSIZE_BYTES 4 |
60 | #define POINTERSIZE_TYPE "I32" |
61 | #endif |
62 | |
63 | #ifndef TARGET_POINTER_SIZE |
64 | #define TARGET_POINTER_SIZE POINTERSIZE_BYTES |
65 | #endif // TARGET_POINTER_SIZE |
66 | |
67 | #if defined(_MSC_VER) |
68 | #pragma warning(disable:4510 4512 4610) |
69 | #endif |
70 | |
71 | #ifndef _ASSERTE |
72 | #ifdef _DEBUG |
73 | #define _ASSERTE(expr) \ |
74 | do { if (!(expr) ) { ExtErr("_ASSERTE fired:\n\t%s\n", #expr); if (IsDebuggerPresent()) DebugBreak(); } } while (0) |
75 | #else |
76 | #define _ASSERTE(x) |
77 | #endif |
78 | #endif // ASSERTE |
79 | |
80 | #ifdef _DEBUG |
81 | #define ASSERT_CHECK(expr, msg, reason) \ |
82 | do { if (!(expr) ) { ExtOut(reason); ExtOut(msg); ExtOut(#expr); DebugBreak(); } } while (0) |
83 | #endif |
84 | |
85 | // The native symbol reader dll name |
86 | #if defined(_AMD64_) |
87 | #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.amd64.dll") |
88 | #elif defined(_X86_) |
89 | #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.x86.dll") |
90 | #elif defined(_ARM_) |
91 | #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm.dll") |
92 | #elif defined(_ARM64_) |
93 | // Use diasymreader until the package has an arm64 version - issue #7360 |
94 | //#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm64.dll") |
95 | #define NATIVE_SYMBOL_READER_DLL W("diasymreader.dll") |
96 | #endif |
97 | |
98 | // PREFIX macros - Begin |
99 | |
100 | // SOS does not have support for Contracts. Therefore we needed to duplicate |
101 | // some of the PREFIX infrastructure from inc\check.h in here. |
102 | |
103 | // Issue - PREFast_:510 v4.51 does not support __assume(0) |
104 | #if (defined(_MSC_VER) && !defined(_PREFAST_)) || defined(_PREFIX_) |
105 | #if defined(_AMD64_) |
106 | // Empty methods that consist of UNREACHABLE() result in a zero-sized declspec(noreturn) method |
107 | // which causes the pdb file to make the next method declspec(noreturn) as well, thus breaking BBT |
108 | // Remove when we get a VC compiler that fixes VSW 449170 |
109 | # define __UNREACHABLE() DebugBreak(); __assume(0); |
110 | #else |
111 | # define __UNREACHABLE() __assume(0) |
112 | #endif |
113 | #else |
114 | #define __UNREACHABLE() do { } while(true) |
115 | #endif |
116 | |
117 | |
118 | #if defined(_PREFAST_) || defined(_PREFIX_) |
119 | #define COMPILER_ASSUME_MSG(_condition, _message) if (!(_condition)) __UNREACHABLE(); |
120 | #else |
121 | |
122 | #if defined(DACCESS_COMPILE) |
123 | #define COMPILER_ASSUME_MSG(_condition, _message) do { } while (0) |
124 | #else |
125 | |
126 | #if defined(_DEBUG) |
127 | #define COMPILER_ASSUME_MSG(_condition, _message) \ |
128 | ASSERT_CHECK(_condition, _message, "Compiler optimization assumption invalid") |
129 | #else |
130 | #define COMPILER_ASSUME_MSG(_condition, _message) __assume(_condition) |
131 | #endif // _DEBUG |
132 | |
133 | #endif // DACCESS_COMPILE |
134 | |
135 | #endif // _PREFAST_ || _PREFIX_ |
136 | |
137 | #define PREFIX_ASSUME(_condition) \ |
138 | COMPILER_ASSUME_MSG(_condition, "") |
139 | |
140 | // PREFIX macros - End |
141 | |
142 | class MethodTable; |
143 | |
144 | #define MD_NOT_YET_LOADED ((DWORD_PTR)-1) |
145 | /* |
146 | * HANDLES |
147 | * |
148 | * The default type of handle is a strong handle. |
149 | * |
150 | */ |
151 | #define HNDTYPE_DEFAULT HNDTYPE_STRONG |
152 | #define HNDTYPE_WEAK_DEFAULT HNDTYPE_WEAK_LONG |
153 | #define HNDTYPE_WEAK_SHORT (0) |
154 | #define HNDTYPE_WEAK_LONG (1) |
155 | #define HNDTYPE_STRONG (2) |
156 | #define HNDTYPE_PINNED (3) |
157 | #define HNDTYPE_VARIABLE (4) |
158 | #define HNDTYPE_REFCOUNTED (5) |
159 | #define HNDTYPE_DEPENDENT (6) |
160 | #define HNDTYPE_ASYNCPINNED (7) |
161 | #define HNDTYPE_SIZEDREF (8) |
162 | #define HNDTYPE_WEAK_WINRT (9) |
163 | |
164 | // Anything above this we consider abnormal and stop processing heap information |
165 | const int nMaxHeapSegmentCount = 1000; |
166 | |
167 | class BaseObject |
168 | { |
169 | MethodTable *m_pMethTab; |
170 | }; |
171 | |
172 | |
173 | const BYTE gElementTypeInfo[] = { |
174 | #define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) s, |
175 | #include "cortypeinfo.h" |
176 | #undef TYPEINFO |
177 | }; |
178 | |
179 | typedef struct tagLockEntry |
180 | { |
181 | tagLockEntry *pNext; // next entry |
182 | tagLockEntry *pPrev; // prev entry |
183 | DWORD dwULockID; |
184 | DWORD dwLLockID; // owning lock |
185 | WORD wReaderLevel; // reader nesting level |
186 | } LockEntry; |
187 | |
188 | #define MAX_CLASSNAME_LENGTH 1024 |
189 | |
190 | enum EEFLAVOR {UNKNOWNEE, MSCOREE, MSCORWKS, MSCOREND}; |
191 | |
192 | #include "sospriv.h" |
193 | extern IXCLRDataProcess *g_clrData; |
194 | extern ISOSDacInterface *g_sos; |
195 | |
196 | #include "dacprivate.h" |
197 | |
198 | interface ICorDebugProcess; |
199 | extern ICorDebugProcess * g_pCorDebugProcess; |
200 | |
201 | // This class is templated for easy modification. We may need to update the CachedString |
202 | // or related classes to use WCHAR instead of char in the future. |
203 | template <class T, int count, int size> |
204 | class StaticData |
205 | { |
206 | public: |
207 | StaticData() |
208 | { |
209 | for (int i = 0; i < count; ++i) |
210 | InUse[i] = false; |
211 | } |
212 | |
213 | // Whether the individual data pointers in the cache are in use. |
214 | bool InUse[count]; |
215 | |
216 | // The actual data itself. |
217 | T Data[count][size]; |
218 | |
219 | // The number of arrays in the cache. |
220 | static const int Count; |
221 | |
222 | // The size of each individual array. |
223 | static const int Size; |
224 | }; |
225 | |
226 | class CachedString |
227 | { |
228 | public: |
229 | CachedString(); |
230 | CachedString(const CachedString &str); |
231 | ~CachedString(); |
232 | |
233 | const CachedString &operator=(const CachedString &str); |
234 | |
235 | // Returns the capacity of this string. |
236 | size_t GetStrLen() const |
237 | { |
238 | return mSize; |
239 | } |
240 | |
241 | // Returns a mutable character pointer. Be sure not to write past the |
242 | // length of this string. |
243 | inline operator char *() |
244 | { |
245 | return mPtr; |
246 | } |
247 | |
248 | // Returns a const char representation of this string. |
249 | inline operator const char *() const |
250 | { |
251 | return GetPtr(); |
252 | } |
253 | |
254 | // To ensure no AV's, any time a constant pointer is requested, we will |
255 | // return an empty string "" if we hit an OOM. This will only happen |
256 | // if we hit an OOM and do not check for it before using the string. |
257 | // If you request a non-const char pointer out of this class, it may be |
258 | // null (see operator char *). |
259 | inline const char *GetPtr() const |
260 | { |
261 | if (!mPtr || IsOOM()) |
262 | return "" ; |
263 | |
264 | return mPtr; |
265 | } |
266 | |
267 | // Returns true if we ran out of memory trying to allocate the string |
268 | // or the refcount. |
269 | bool IsOOM() const |
270 | { |
271 | return mIndex == -2; |
272 | } |
273 | |
274 | // allocate a string of the specified size. this will Clear() any |
275 | // previously allocated string. call IsOOM() to check for failure. |
276 | void Allocate(int size); |
277 | |
278 | private: |
279 | // Copies rhs into this string. |
280 | void Copy(const CachedString &rhs); |
281 | |
282 | // Clears this string, releasing any underlying memory. |
283 | void Clear(); |
284 | |
285 | // Creates a new string. |
286 | void Create(); |
287 | |
288 | // Sets an out of memory state. |
289 | void SetOOM(); |
290 | |
291 | private: |
292 | char *mPtr; |
293 | |
294 | // The reference count. This may be null if there is only one copy |
295 | // of this string. |
296 | mutable unsigned int *mRefCount; |
297 | |
298 | // mIndex contains the index of the cached pointer we are using, or: |
299 | // ~0 - poison value we initialize it to for debugging purposes |
300 | // -1 - mPtr points to a pointer we have new'ed |
301 | // -2 - We hit an oom trying to allocate either mCount or mPtr |
302 | int mIndex; |
303 | |
304 | // contains the size of current string |
305 | int mSize; |
306 | |
307 | private: |
308 | static StaticData<char, 4, 1024> cache; |
309 | }; |
310 | |
311 | // Things in this namespace should not be directly accessed/called outside of |
312 | // the output-related functions. |
313 | namespace Output |
314 | { |
315 | extern unsigned int g_bSuppressOutput; |
316 | extern unsigned int g_Indent; |
317 | extern unsigned int g_DMLEnable; |
318 | extern bool g_bDbgOutput; |
319 | extern bool g_bDMLExposed; |
320 | |
321 | inline bool IsOutputSuppressed() |
322 | { return g_bSuppressOutput > 0; } |
323 | |
324 | inline void ResetIndent() |
325 | { g_Indent = 0; } |
326 | |
327 | inline void SetDebugOutputEnabled(bool enabled) |
328 | { g_bDbgOutput = enabled; } |
329 | |
330 | inline bool IsDebugOutputEnabled() |
331 | { return g_bDbgOutput; } |
332 | |
333 | inline void SetDMLExposed(bool exposed) |
334 | { g_bDMLExposed = exposed; } |
335 | |
336 | inline bool IsDMLExposed() |
337 | { return g_bDMLExposed; } |
338 | |
339 | enum FormatType |
340 | { |
341 | DML_None, |
342 | DML_MethodTable, |
343 | DML_MethodDesc, |
344 | DML_EEClass, |
345 | DML_Module, |
346 | DML_IP, |
347 | DML_Object, |
348 | DML_Domain, |
349 | DML_Assembly, |
350 | DML_ThreadID, |
351 | DML_ValueClass, |
352 | DML_DumpHeapMT, |
353 | DML_ListNearObj, |
354 | DML_ThreadState, |
355 | DML_PrintException, |
356 | DML_RCWrapper, |
357 | DML_CCWrapper, |
358 | DML_ManagedVar, |
359 | DML_Async, |
360 | }; |
361 | |
362 | /**********************************************************************\ |
363 | * This function builds a DML string for a ValueClass. If DML is * |
364 | * enabled, this function returns a DML string based on the format * |
365 | * type. Otherwise this returns a string containing only the hex value * |
366 | * of addr. * |
367 | * * |
368 | * Params: * |
369 | * mt - the method table of the ValueClass * |
370 | * addr - the address of the ValueClass * |
371 | * type - the format type to use to output this object * |
372 | * fill - whether or not to pad the hex value with zeros * |
373 | * * |
374 | \**********************************************************************/ |
375 | CachedString BuildVCValue(CLRDATA_ADDRESS mt, CLRDATA_ADDRESS addr, FormatType type, bool fill = true); |
376 | |
377 | |
378 | /**********************************************************************\ |
379 | * This function builds a DML string for an object. If DML is enabled, * |
380 | * this function returns a DML string based on the format type. * |
381 | * Otherwise this returns a string containing only the hex value of * |
382 | * addr. * |
383 | * * |
384 | * Params: * |
385 | * addr - the address of the object * |
386 | * type - the format type to use to output this object * |
387 | * fill - whether or not to pad the hex value with zeros * |
388 | * * |
389 | \**********************************************************************/ |
390 | CachedString BuildHexValue(CLRDATA_ADDRESS addr, FormatType type, bool fill = true); |
391 | |
392 | /**********************************************************************\ |
393 | * This function builds a DML string for an managed variable name. * |
394 | * If DML is enabled, this function returns a DML string that will * |
395 | * enable the expansion of that managed variable using the !ClrStack * |
396 | * command to display the variable's fields, otherwise it will just * |
397 | * return the variable's name as a string. |
398 | * * |
399 | * Params: * |
400 | * expansionName - the current variable expansion string * |
401 | * frame - the frame that contains the variable of interest * |
402 | * simpleName - simple name of the managed variable * |
403 | * * |
404 | \**********************************************************************/ |
405 | CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, __in_z LPCWSTR simpleName, FormatType type); |
406 | CachedString BuildManagedVarValue(__in_z LPCWSTR expansionName, ULONG frame, int indexInArray, FormatType type); //used for array indices (simpleName = "[<indexInArray>]") |
407 | } |
408 | |
409 | class NoOutputHolder |
410 | { |
411 | public: |
412 | NoOutputHolder(BOOL bSuppress = TRUE); |
413 | ~NoOutputHolder(); |
414 | |
415 | private: |
416 | BOOL mSuppress; |
417 | }; |
418 | |
419 | class EnableDMLHolder |
420 | { |
421 | public: |
422 | EnableDMLHolder(BOOL enable); |
423 | ~EnableDMLHolder(); |
424 | |
425 | private: |
426 | BOOL mEnable; |
427 | }; |
428 | |
429 | size_t CountHexCharacters(CLRDATA_ADDRESS val); |
430 | |
431 | // Normal output. |
432 | void DMLOut(PCSTR format, ...); /* Prints out DML strings. */ |
433 | void IfDMLOut(PCSTR format, ...); /* Prints given DML string ONLY if DML is enabled; prints nothing otherwise. */ |
434 | void ExtOut(PCSTR Format, ...); /* Prints out to ExtOut (no DML). */ |
435 | void ExtWarn(PCSTR Format, ...); /* Prints out to ExtWarn (no DML). */ |
436 | void ExtErr(PCSTR Format, ...); /* Prints out to ExtErr (no DML). */ |
437 | void ExtDbgOut(PCSTR Format, ...); /* Prints out to ExtOut in a checked build (no DML). */ |
438 | void WhitespaceOut(int count); /* Prints out "count" number of spaces in the output. */ |
439 | |
440 | // Change indent for ExtOut |
441 | inline void IncrementIndent() { Output::g_Indent++; } |
442 | inline void DecrementIndent() { if (Output::g_Indent > 0) Output::g_Indent--; } |
443 | inline void ExtOutIndent() { WhitespaceOut(Output::g_Indent << 2); } |
444 | |
445 | // DML Generation Methods |
446 | #define DMLListNearObj(addr) Output::BuildHexValue(addr, Output::DML_ListNearObj).GetPtr() |
447 | #define DMLDumpHeapMT(addr) Output::BuildHexValue(addr, Output::DML_DumpHeapMT).GetPtr() |
448 | #define DMLMethodTable(addr) Output::BuildHexValue(addr, Output::DML_MethodTable).GetPtr() |
449 | #define DMLMethodDesc(addr) Output::BuildHexValue(addr, Output::DML_MethodDesc).GetPtr() |
450 | #define DMLClass(addr) Output::BuildHexValue(addr, Output::DML_EEClass).GetPtr() |
451 | #define DMLModule(addr) Output::BuildHexValue(addr, Output::DML_Module).GetPtr() |
452 | #define DMLIP(ip) Output::BuildHexValue(ip, Output::DML_IP).GetPtr() |
453 | #define DMLObject(addr) Output::BuildHexValue(addr, Output::DML_Object).GetPtr() |
454 | #define DMLDomain(addr) Output::BuildHexValue(addr, Output::DML_Domain).GetPtr() |
455 | #define DMLAssembly(addr) Output::BuildHexValue(addr, Output::DML_Assembly).GetPtr() |
456 | #define DMLThreadID(id) Output::BuildHexValue(id, Output::DML_ThreadID, false).GetPtr() |
457 | #define DMLValueClass(mt, addr) Output::BuildVCValue(mt, addr, Output::DML_ValueClass).GetPtr() |
458 | #define DMLRCWrapper(addr) Output::BuildHexValue(addr, Output::DML_RCWrapper).GetPtr() |
459 | #define DMLCCWrapper(addr) Output::BuildHexValue(addr, Output::DML_CCWrapper).GetPtr() |
460 | #define DMLManagedVar(expansionName,frame,simpleName) Output::BuildManagedVarValue(expansionName, frame, simpleName, Output::DML_ManagedVar).GetPtr() |
461 | #define DMLAsync(addr) Output::BuildHexValue(addr, Output::DML_Async).GetPtr() |
462 | |
463 | bool IsDMLEnabled(); |
464 | |
465 | |
466 | #ifndef SOS_Assert |
467 | #define SOS_Assert(x) |
468 | #endif |
469 | |
470 | void ConvertToLower(__out_ecount(len) char *buffer, size_t len); |
471 | |
472 | extern const char * const DMLFormats[]; |
473 | int GetHex(CLRDATA_ADDRESS addr, __out_ecount(len) char *out, size_t len, bool fill); |
474 | |
475 | // A simple string class for mutable strings. We cannot use STL, so this is a stand in replacement |
476 | // for std::string (though it doesn't use the same interface). |
477 | template <class T, size_t (__cdecl *LEN)(const T *), errno_t (__cdecl *COPY)(T *, size_t, const T * _Src)> |
478 | class BaseString |
479 | { |
480 | public: |
481 | BaseString() |
482 | : mStr(0), mSize(0), mLength(0) |
483 | { |
484 | const size_t size = 64; |
485 | |
486 | mStr = new T[size]; |
487 | mSize = size; |
488 | mStr[0] = 0; |
489 | } |
490 | |
491 | BaseString(const T *str) |
492 | : mStr(0), mSize(0), mLength(0) |
493 | { |
494 | CopyFrom(str, LEN(str)); |
495 | } |
496 | |
497 | BaseString(const BaseString<T, LEN, COPY> &rhs) |
498 | : mStr(0), mSize(0), mLength(0) |
499 | { |
500 | *this = rhs; |
501 | } |
502 | |
503 | ~BaseString() |
504 | { |
505 | Clear(); |
506 | } |
507 | |
508 | const BaseString<T, LEN, COPY> &operator=(const BaseString<T, LEN, COPY> &rhs) |
509 | { |
510 | Clear(); |
511 | CopyFrom(rhs.mStr, rhs.mLength); |
512 | return *this; |
513 | } |
514 | |
515 | const BaseString<T, LEN, COPY> &operator=(const T *str) |
516 | { |
517 | Clear(); |
518 | CopyFrom(str, LEN(str)); |
519 | return *this; |
520 | } |
521 | |
522 | const BaseString<T, LEN, COPY> &operator +=(const T *str) |
523 | { |
524 | size_t len = LEN(str); |
525 | CopyFrom(str, len); |
526 | return *this; |
527 | } |
528 | |
529 | const BaseString<T, LEN, COPY> &operator +=(const BaseString<T, LEN, COPY> &str) |
530 | { |
531 | CopyFrom(str.mStr, str.mLength); |
532 | return *this; |
533 | } |
534 | |
535 | BaseString<T, LEN, COPY> operator+(const T *str) const |
536 | { |
537 | return BaseString<T, LEN, COPY>(mStr, mLength, str, LEN(str)); |
538 | } |
539 | |
540 | BaseString<T, LEN, COPY> operator+(const BaseString<T, LEN, COPY> &str) const |
541 | { |
542 | return BaseString<T, LEN, COPY>(mStr, mLength, str.mStr, str.mLength); |
543 | } |
544 | |
545 | operator const T *() const |
546 | { |
547 | return mStr; |
548 | } |
549 | |
550 | const T *c_str() const |
551 | { |
552 | return mStr; |
553 | } |
554 | |
555 | size_t GetLength() const |
556 | { |
557 | return mLength; |
558 | } |
559 | |
560 | private: |
561 | BaseString(const T * str1, size_t len1, const T * str2, size_t len2) |
562 | : mStr(0), mSize(0), mLength(0) |
563 | { |
564 | const size_t size = len1 + len2 + 1 + ((len1 + len2) >> 1); |
565 | mStr = new T[size]; |
566 | mSize = size; |
567 | |
568 | CopyFrom(str1, len1); |
569 | CopyFrom(str2, len2); |
570 | } |
571 | |
572 | void Clear() |
573 | { |
574 | mLength = 0; |
575 | mSize = 0; |
576 | if (mStr) |
577 | { |
578 | delete [] mStr; |
579 | mStr = 0; |
580 | } |
581 | } |
582 | |
583 | void CopyFrom(const T *str, size_t len) |
584 | { |
585 | if (mLength + len + 1 >= mSize) |
586 | Resize(mLength + len + 1); |
587 | |
588 | COPY(mStr+mLength, mSize-mLength, str); |
589 | mLength += len; |
590 | } |
591 | |
592 | void Resize(size_t size) |
593 | { |
594 | /* We always resize at least one half bigger than we need. When CopyFrom requests a resize |
595 | * it asks for the exact size that's needed to concatenate strings. However in practice |
596 | * it's common to add multiple strings together in a row, e.g.: |
597 | * String foo = "One " + "Two " + "Three " + "Four " + "\n"; |
598 | * Ensuring the size of the string is bigger than we need, and that the minimum size is 64, |
599 | * we will cut down on a lot of needless resizes at the cost of a few bytes wasted in some |
600 | * cases. |
601 | */ |
602 | size += size >> 1; |
603 | if (size < 64) |
604 | size = 64; |
605 | |
606 | T *newStr = new T[size]; |
607 | |
608 | if (mStr) |
609 | { |
610 | COPY(newStr, size, mStr); |
611 | delete [] mStr; |
612 | } |
613 | else |
614 | { |
615 | newStr[0] = 0; |
616 | } |
617 | |
618 | mStr = newStr; |
619 | mSize = size; |
620 | } |
621 | private: |
622 | T *mStr; |
623 | size_t mSize, mLength; |
624 | }; |
625 | |
626 | typedef BaseString<char, strlen, strcpy_s> String; |
627 | typedef BaseString<WCHAR, _wcslen, wcscpy_s> WString; |
628 | |
629 | |
630 | template<class T> |
631 | void Flatten(__out_ecount(len) T *data, unsigned int len) |
632 | { |
633 | for (unsigned int i = 0; i < len; ++i) |
634 | if (data[i] < 32 || (data[i] > 126 && data[i] <= 255)) |
635 | data[i] = '.'; |
636 | data[len] = 0; |
637 | } |
638 | |
639 | void Flatten(__out_ecount(len) char *data, unsigned int len); |
640 | |
641 | /* Formats for the Format class. We support the following formats: |
642 | * Pointer - Same as %p. |
643 | * Hex - Same as %x (same as %p, but does not output preceding zeros. |
644 | * PrefixHex - Same as %x, but prepends 0x. |
645 | * Decimal - Same as %d. |
646 | * Strings and wide strings don't use this. |
647 | */ |
648 | class Formats |
649 | { |
650 | public: |
651 | enum Format |
652 | { |
653 | Default, |
654 | Pointer, |
655 | Hex, |
656 | PrefixHex, |
657 | Decimal, |
658 | }; |
659 | }; |
660 | |
661 | enum Alignment |
662 | { |
663 | AlignLeft, |
664 | AlignRight |
665 | }; |
666 | |
667 | namespace Output |
668 | { |
669 | /* Defines how a value will be printed. This class understands how to format |
670 | * and print values according to the format and DML settings provided. |
671 | * The raw templated class handles the pointer/integer case. Support for |
672 | * character arrays and wide character arrays are handled by template |
673 | * specializations. |
674 | * |
675 | * Note that this class is not used directly. Instead use the typedefs and |
676 | * macros which define the type of data you are outputing (for example ObjectPtr, |
677 | * MethodTablePtr, etc). |
678 | */ |
679 | template <class T> |
680 | class Format |
681 | { |
682 | public: |
683 | Format(T value) |
684 | : mValue(value), mFormat(Formats::Default), mDml(Output::DML_None) |
685 | { |
686 | } |
687 | |
688 | Format(T value, Formats::Format format, Output::FormatType dmlType) |
689 | : mValue(value), mFormat(format), mDml(dmlType) |
690 | { |
691 | } |
692 | |
693 | Format(const Format<T> &rhs) |
694 | : mValue(rhs.mValue), mFormat(rhs.mFormat), mDml(rhs.mDml) |
695 | { |
696 | } |
697 | |
698 | /* Prints out the value according to the Format and DML settings provided. |
699 | */ |
700 | void Output() const |
701 | { |
702 | if (IsDMLEnabled() && mDml != Output::DML_None) |
703 | { |
704 | const int len = GetDMLWidth(mDml); |
705 | char *buffer = (char*)alloca(len); |
706 | |
707 | BuildDML(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml); |
708 | DMLOut(buffer); |
709 | } |
710 | else |
711 | { |
712 | if (mFormat == Formats::Default || mFormat == Formats::Pointer) |
713 | { |
714 | ExtOut("%p" , SOS_PTR(mValue)); |
715 | } |
716 | else |
717 | { |
718 | const char *format = NULL; |
719 | if (mFormat == Formats::PrefixHex) |
720 | { |
721 | format = "0x%x" ; |
722 | } |
723 | else if (mFormat == Formats::Hex) |
724 | { |
725 | format = "%x" ; |
726 | } |
727 | else if (mFormat == Formats::Decimal) |
728 | { |
729 | format = "%d" ; |
730 | } |
731 | |
732 | ExtOut(format, (__int32)mValue); |
733 | } |
734 | |
735 | } |
736 | } |
737 | |
738 | /* Prints out the value based on a specified width and alignment. |
739 | * Params: |
740 | * align - Whether the output should be left or right justified. |
741 | * width - The output width to fill. |
742 | * Note: |
743 | * This function guarantees that exactly width will be printed out (so if width is 24, |
744 | * exactly 24 characters will be printed), even if the output wouldn't normally fit |
745 | * in the space provided. This function makes no guarantees as to what part of the |
746 | * data will be printed in the case that width isn't wide enough. |
747 | */ |
748 | void OutputColumn(Alignment align, int width) const |
749 | { |
750 | bool leftAlign = align == AlignLeft; |
751 | if (IsDMLEnabled() && mDml != Output::DML_None) |
752 | { |
753 | const int len = GetDMLColWidth(mDml, width); |
754 | char *buffer = (char*)alloca(len); |
755 | |
756 | BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, leftAlign, width); |
757 | DMLOut(buffer); |
758 | } |
759 | else |
760 | { |
761 | int precision = GetPrecision(); |
762 | if (mFormat == Formats::Default || mFormat == Formats::Pointer) |
763 | { |
764 | if (precision > width) |
765 | precision = width; |
766 | |
767 | ExtOut(leftAlign ? "%-*.*p" : "%*.*p" , width, precision, SOS_PTR(mValue)); |
768 | } |
769 | else |
770 | { |
771 | const char *format = NULL; |
772 | if (mFormat == Formats::PrefixHex) |
773 | { |
774 | format = leftAlign ? "0x%-*.*x" : "0x%*.*x" ; |
775 | width -= 2; |
776 | } |
777 | else if (mFormat == Formats::Hex) |
778 | { |
779 | format = leftAlign ? "%-*.*x" : "%*.*x" ; |
780 | } |
781 | else if (mFormat == Formats::Decimal) |
782 | { |
783 | format = leftAlign ? "%-*.*d" : "%*.*d" ; |
784 | } |
785 | |
786 | if (precision > width) |
787 | precision = width; |
788 | |
789 | ExtOut(format, width, precision, (__int32)mValue); |
790 | } |
791 | } |
792 | } |
793 | |
794 | /* Converts this object into a Wide char string. This allows you to write the following code: |
795 | * WString foo = L"bar " + ObjectPtr(obj); |
796 | * Where ObjectPtr is a subclass/typedef of this Format class. |
797 | */ |
798 | operator WString() const |
799 | { |
800 | String str = *this; |
801 | const char *cstr = (const char *)str; |
802 | |
803 | int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0); |
804 | WCHAR *buffer = (WCHAR *)alloca(len*sizeof(WCHAR)); |
805 | |
806 | MultiByteToWideChar(CP_ACP, 0, cstr, -1, buffer, len); |
807 | |
808 | return WString(buffer); |
809 | } |
810 | |
811 | /* Converts this object into a String object. This allows you to write the following code: |
812 | * String foo = "bar " + ObjectPtr(obj); |
813 | * Where ObjectPtr is a subclass/typedef of this Format class. |
814 | */ |
815 | operator String() const |
816 | { |
817 | if (IsDMLEnabled() && mDml != Output::DML_None) |
818 | { |
819 | const int len = GetDMLColWidth(mDml, 0); |
820 | char *buffer = (char*)alloca(len); |
821 | |
822 | BuildDMLCol(buffer, len, (CLRDATA_ADDRESS)mValue, mFormat, mDml, false, 0); |
823 | return buffer; |
824 | } |
825 | else |
826 | { |
827 | char buffer[64]; |
828 | if (mFormat == Formats::Default || mFormat == Formats::Pointer) |
829 | { |
830 | sprintf_s(buffer, _countof(buffer), "%p" , (int *)(SIZE_T)mValue); |
831 | ConvertToLower(buffer, _countof(buffer)); |
832 | } |
833 | else |
834 | { |
835 | const char *format = NULL; |
836 | if (mFormat == Formats::PrefixHex) |
837 | format = "0x%x" ; |
838 | else if (mFormat == Formats::Hex) |
839 | format = "%x" ; |
840 | else if (mFormat == Formats::Decimal) |
841 | format = "%d" ; |
842 | |
843 | sprintf_s(buffer, _countof(buffer), format, (__int32)mValue); |
844 | ConvertToLower(buffer, _countof(buffer)); |
845 | } |
846 | |
847 | return buffer; |
848 | } |
849 | } |
850 | |
851 | private: |
852 | int GetPrecision() const |
853 | { |
854 | if (mFormat == Formats::Hex || mFormat == Formats::PrefixHex) |
855 | { |
856 | ULONGLONG val = mValue; |
857 | int count = 0; |
858 | while (val) |
859 | { |
860 | val >>= 4; |
861 | count++; |
862 | } |
863 | |
864 | if (count == 0) |
865 | count = 1; |
866 | |
867 | return count; |
868 | } |
869 | |
870 | else if (mFormat == Formats::Decimal) |
871 | { |
872 | T val = mValue; |
873 | int count = (val > 0) ? 0 : 1; |
874 | while (val) |
875 | { |
876 | val /= 10; |
877 | count++; |
878 | } |
879 | |
880 | return count; |
881 | } |
882 | |
883 | // mFormat == Formats::Pointer |
884 | return sizeof(int*)*2; |
885 | } |
886 | |
887 | static inline void BuildDML(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType) |
888 | { |
889 | BuildDMLCol(result, len, value, format, dmlType, true, 0); |
890 | } |
891 | |
892 | static int GetDMLWidth(Output::FormatType dmlType) |
893 | { |
894 | return GetDMLColWidth(dmlType, 0); |
895 | } |
896 | |
897 | static void BuildDMLCol(__out_ecount(len) char *result, int len, CLRDATA_ADDRESS value, Formats::Format format, Output::FormatType dmlType, bool leftAlign, int width) |
898 | { |
899 | char hex[64]; |
900 | int count = GetHex(value, hex, _countof(hex), format != Formats::Hex); |
901 | int i = 0; |
902 | |
903 | if (!leftAlign) |
904 | { |
905 | for (; i < width - count; ++i) |
906 | result[i] = ' '; |
907 | |
908 | result[i] = 0; |
909 | } |
910 | |
911 | int written = sprintf_s(result+i, len - i, DMLFormats[dmlType], hex, hex); |
912 | |
913 | SOS_Assert(written != -1); |
914 | if (written != -1) |
915 | { |
916 | for (i = i + written; i < width; ++i) |
917 | result[i] = ' '; |
918 | |
919 | result[i] = 0; |
920 | } |
921 | } |
922 | |
923 | static int GetDMLColWidth(Output::FormatType dmlType, int width) |
924 | { |
925 | return 1 + 4*sizeof(int*) + (int)strlen(DMLFormats[dmlType]) + width; |
926 | } |
927 | |
928 | private: |
929 | T mValue; |
930 | Formats::Format mFormat; |
931 | Output::FormatType mDml; |
932 | }; |
933 | |
934 | /* Format class used for strings. |
935 | */ |
936 | template <> |
937 | class Format<const char *> |
938 | { |
939 | public: |
940 | Format(const char *value) |
941 | : mValue(value) |
942 | { |
943 | } |
944 | |
945 | Format(const Format<const char *> &rhs) |
946 | : mValue(rhs.mValue) |
947 | { |
948 | } |
949 | |
950 | void Output() const |
951 | { |
952 | if (IsDMLEnabled()) |
953 | DMLOut("%s" , mValue); |
954 | else |
955 | ExtOut("%s" , mValue); |
956 | } |
957 | |
958 | void OutputColumn(Alignment align, int width) const |
959 | { |
960 | int precision = (int)strlen(mValue); |
961 | |
962 | if (precision > width) |
963 | precision = width; |
964 | |
965 | const char *format = align == AlignLeft ? "%-*.*s" : "%*.*s" ; |
966 | |
967 | if (IsDMLEnabled()) |
968 | DMLOut(format, width, precision, mValue); |
969 | else |
970 | ExtOut(format, width, precision, mValue); |
971 | } |
972 | |
973 | private: |
974 | const char *mValue; |
975 | }; |
976 | |
977 | /* Format class for wide char strings. |
978 | */ |
979 | template <> |
980 | class Format<const WCHAR *> |
981 | { |
982 | public: |
983 | Format(const WCHAR *value) |
984 | : mValue(value) |
985 | { |
986 | } |
987 | |
988 | Format(const Format<const WCHAR *> &rhs) |
989 | : mValue(rhs.mValue) |
990 | { |
991 | } |
992 | |
993 | void Output() const |
994 | { |
995 | if (IsDMLEnabled()) |
996 | DMLOut("%S" , mValue); |
997 | else |
998 | ExtOut("%S" , mValue); |
999 | } |
1000 | |
1001 | void OutputColumn(Alignment align, int width) const |
1002 | { |
1003 | int precision = (int)_wcslen(mValue); |
1004 | if (precision > width) |
1005 | precision = width; |
1006 | |
1007 | const char *format = align == AlignLeft ? "%-*.*S" : "%*.*S" ; |
1008 | |
1009 | if (IsDMLEnabled()) |
1010 | DMLOut(format, width, precision, mValue); |
1011 | else |
1012 | ExtOut(format, width, precision, mValue); |
1013 | } |
1014 | |
1015 | private: |
1016 | const WCHAR *mValue; |
1017 | }; |
1018 | |
1019 | |
1020 | template <class T> |
1021 | void InternalPrint(const T &t) |
1022 | { |
1023 | Format<T>(t).Output(); |
1024 | } |
1025 | |
1026 | template <class T> |
1027 | void InternalPrint(const Format<T> &t) |
1028 | { |
1029 | t.Output(); |
1030 | } |
1031 | |
1032 | inline void InternalPrint(const char t[]) |
1033 | { |
1034 | Format<const char *>(t).Output(); |
1035 | } |
1036 | } |
1037 | |
1038 | #define DefineFormatClass(name, format, dml) \ |
1039 | template <class T> \ |
1040 | Output::Format<T> name(T value) \ |
1041 | { return Output::Format<T>(value, format, dml); } |
1042 | |
1043 | DefineFormatClass(EEClassPtr, Formats::Pointer, Output::DML_EEClass); |
1044 | DefineFormatClass(ObjectPtr, Formats::Pointer, Output::DML_Object); |
1045 | DefineFormatClass(ExceptionPtr, Formats::Pointer, Output::DML_PrintException); |
1046 | DefineFormatClass(ModulePtr, Formats::Pointer, Output::DML_Module); |
1047 | DefineFormatClass(MethodDescPtr, Formats::Pointer, Output::DML_MethodDesc); |
1048 | DefineFormatClass(AppDomainPtr, Formats::Pointer, Output::DML_Domain); |
1049 | DefineFormatClass(ThreadState, Formats::Hex, Output::DML_ThreadState); |
1050 | DefineFormatClass(ThreadID, Formats::Hex, Output::DML_ThreadID); |
1051 | DefineFormatClass(RCWrapper, Formats::Pointer, Output::DML_RCWrapper); |
1052 | DefineFormatClass(CCWrapper, Formats::Pointer, Output::DML_CCWrapper); |
1053 | DefineFormatClass(InstructionPtr, Formats::Pointer, Output::DML_IP); |
1054 | DefineFormatClass(NativePtr, Formats::Pointer, Output::DML_None); |
1055 | |
1056 | DefineFormatClass(Decimal, Formats::Decimal, Output::DML_None); |
1057 | DefineFormatClass(Pointer, Formats::Pointer, Output::DML_None); |
1058 | DefineFormatClass(PrefixHex, Formats::PrefixHex, Output::DML_None); |
1059 | DefineFormatClass(Hex, Formats::Hex, Output::DML_None); |
1060 | |
1061 | #undef DefineFormatClass |
1062 | |
1063 | template <class T0> |
1064 | void Print(const T0 &val0) |
1065 | { |
1066 | Output::InternalPrint(val0); |
1067 | } |
1068 | |
1069 | template <class T0, class T1> |
1070 | void Print(const T0 &val0, const T1 &val1) |
1071 | { |
1072 | Output::InternalPrint(val0); |
1073 | Output::InternalPrint(val1); |
1074 | } |
1075 | |
1076 | template <class T0> |
1077 | void PrintLn(const T0 &val0) |
1078 | { |
1079 | Output::InternalPrint(val0); |
1080 | ExtOut("\n" ); |
1081 | } |
1082 | |
1083 | template <class T0, class T1> |
1084 | void PrintLn(const T0 &val0, const T1 &val1) |
1085 | { |
1086 | Output::InternalPrint(val0); |
1087 | Output::InternalPrint(val1); |
1088 | ExtOut("\n" ); |
1089 | } |
1090 | |
1091 | template <class T0, class T1, class T2> |
1092 | void PrintLn(const T0 &val0, const T1 &val1, const T2 &val2) |
1093 | { |
1094 | Output::InternalPrint(val0); |
1095 | Output::InternalPrint(val1); |
1096 | Output::InternalPrint(val2); |
1097 | ExtOut("\n" ); |
1098 | } |
1099 | |
1100 | |
1101 | /* This class handles the formatting for output which is in a table format. To use this class you define |
1102 | * how the table is formatted by setting the number of columns in the table, the default column width, |
1103 | * the default column alignment, the indentation (whitespace) for the table, and the amount of padding |
1104 | * (whitespace) between each column. Once this has been setup, you output rows at a time or individual |
1105 | * columns to build the output instead of manually tabbing out space. |
1106 | * Also note that this class was built to work with the Format class. When outputing data, use the |
1107 | * predefined output types to specify the format (such as ObjectPtr, MethodDescPtr, Decimal, etc). This |
1108 | * tells the TableOutput class how to display the data, and where applicable, it automatically generates |
1109 | * the appropriate DML output. See the DefineFormatClass macro. |
1110 | */ |
1111 | class TableOutput |
1112 | { |
1113 | public: |
1114 | |
1115 | TableOutput() |
1116 | : mColumns(0), mDefaultWidth(0), mIndent(0), mPadding(0), mCurrCol(0), mDefaultAlign(AlignLeft), |
1117 | mWidths(0), mAlignments(0) |
1118 | { |
1119 | } |
1120 | /* Constructor. |
1121 | * Params: |
1122 | * numColumns - the number of columns the table has |
1123 | * defaultColumnWidth - the default width of each column |
1124 | * alignmentDefault - whether columns are by default left aligned or right aligned |
1125 | * indent - the amount of whitespace to prefix at the start of the row (in characters) |
1126 | * padding - the amount of whitespace to place between each column (in characters) |
1127 | */ |
1128 | TableOutput(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1) |
1129 | : mColumns(numColumns), mDefaultWidth(defaultColumnWidth), mIndent(indent), mPadding(padding), mCurrCol(0), mDefaultAlign(alignmentDefault), |
1130 | mWidths(0), mAlignments(0) |
1131 | { |
1132 | } |
1133 | |
1134 | ~TableOutput() |
1135 | { |
1136 | Clear(); |
1137 | } |
1138 | |
1139 | /* See the documentation for the constructor. |
1140 | */ |
1141 | void ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault = AlignLeft, int indent = 0, int padding = 1); |
1142 | |
1143 | /* Sets the amount of whitespace to prefix at the start of the row (in characters). |
1144 | */ |
1145 | void SetIndent(int indent) |
1146 | { |
1147 | SOS_Assert(indent >= 0); |
1148 | |
1149 | mIndent = indent; |
1150 | } |
1151 | |
1152 | /* Sets the exact widths for the the given columns. |
1153 | * Params: |
1154 | * columns - the number of columns you are providing the width for, starting at the first column |
1155 | * ... - an int32 for each column (given by the number of columns in the first parameter). |
1156 | * Example: |
1157 | * If you have 5 columns in the table, you can set their widths like so: |
1158 | * tableOutput.SetWidths(5, 2, 3, 5, 7, 13); |
1159 | * Note: |
1160 | * It's fine to pass a value for "columns" less than the number of columns in the table. This |
1161 | * is useful when you set the default column width to be correct for most of the table, and need |
1162 | * to make a minor adjustment to a few. |
1163 | */ |
1164 | void SetWidths(int columns, ...); |
1165 | |
1166 | /* Individually sets a column to the given width. |
1167 | * Params: |
1168 | * col - the column to set, 0 indexed |
1169 | * width - the width of the column (note this must be non-negative) |
1170 | */ |
1171 | void SetColWidth(int col, int width); |
1172 | |
1173 | /* Individually sets the column alignment. |
1174 | * Params: |
1175 | * col - the column to set, 0 indexed |
1176 | * align - the new alignment (left or right) for the column |
1177 | */ |
1178 | void SetColAlignment(int col, Alignment align); |
1179 | |
1180 | |
1181 | /* The WriteRow family of functions allows you to write an entire row of the table at once. |
1182 | * The common use case for the TableOutput class is to individually output each column after |
1183 | * calculating what the value should contain. However, this would be tedious if you already |
1184 | * knew the contents of the entire row which usually happenes when you are printing out the |
1185 | * header for the table. To use this, simply pass each column as an individual parameter, |
1186 | * for example: |
1187 | * tableOutput.WriteRow("First Column", "Second Column", Decimal(3), PrefixHex(4), "Fifth Column"); |
1188 | */ |
1189 | template <class T0, class T1> |
1190 | void WriteRow(T0 t0, T1 t1) |
1191 | { |
1192 | WriteColumn(0, t0); |
1193 | WriteColumn(1, t1); |
1194 | } |
1195 | |
1196 | template <class T0, class T1, class T2> |
1197 | void WriteRow(T0 t0, T1 t1, T2 t2) |
1198 | { |
1199 | WriteColumn(0, t0); |
1200 | WriteColumn(1, t1); |
1201 | WriteColumn(2, t2); |
1202 | } |
1203 | |
1204 | |
1205 | template <class T0, class T1, class T2, class T3> |
1206 | void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3) |
1207 | { |
1208 | WriteColumn(0, t0); |
1209 | WriteColumn(1, t1); |
1210 | WriteColumn(2, t2); |
1211 | WriteColumn(3, t3); |
1212 | } |
1213 | |
1214 | |
1215 | template <class T0, class T1, class T2, class T3, class T4> |
1216 | void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4) |
1217 | { |
1218 | WriteColumn(0, t0); |
1219 | WriteColumn(1, t1); |
1220 | WriteColumn(2, t2); |
1221 | WriteColumn(3, t3); |
1222 | WriteColumn(4, t4); |
1223 | } |
1224 | |
1225 | template <class T0, class T1, class T2, class T3, class T4, class T5> |
1226 | void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) |
1227 | { |
1228 | WriteColumn(0, t0); |
1229 | WriteColumn(1, t1); |
1230 | WriteColumn(2, t2); |
1231 | WriteColumn(3, t3); |
1232 | WriteColumn(4, t4); |
1233 | WriteColumn(5, t5); |
1234 | } |
1235 | |
1236 | template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9> |
1237 | void WriteRow(T0 t0, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) |
1238 | { |
1239 | WriteColumn(0, t0); |
1240 | WriteColumn(1, t1); |
1241 | WriteColumn(2, t2); |
1242 | WriteColumn(3, t3); |
1243 | WriteColumn(4, t4); |
1244 | WriteColumn(5, t5); |
1245 | WriteColumn(6, t6); |
1246 | WriteColumn(7, t7); |
1247 | WriteColumn(8, t8); |
1248 | WriteColumn(9, t9); |
1249 | } |
1250 | |
1251 | /* The WriteColumn family of functions is used to output individual columns in the table. |
1252 | * The intent is that the bulk of the table will be generated in a loop like so: |
1253 | * while (condition) { |
1254 | * int value1 = CalculateFirstColumn(); |
1255 | * table.WriteColumn(0, value1); |
1256 | * |
1257 | * String value2 = CalculateSecondColumn(); |
1258 | * table.WriteColumn(1, value2); |
1259 | * } |
1260 | * Params: |
1261 | * col - the column to write, 0 indexed |
1262 | * t - the value to write |
1263 | * Note: |
1264 | * You should generally use the specific instances of the Format class to generate output. |
1265 | * For example, use the "Decimal", "Pointer", "ObjectPtr", etc. When passing data to this |
1266 | * function. This tells the Table class how to display the value. |
1267 | */ |
1268 | template <class T> |
1269 | void WriteColumn(int col, const Output::Format<T> &t) |
1270 | { |
1271 | SOS_Assert(col >= 0); |
1272 | SOS_Assert(col < mColumns); |
1273 | |
1274 | if (col != mCurrCol) |
1275 | OutputBlankColumns(col); |
1276 | |
1277 | if (col == 0) |
1278 | OutputIndent(); |
1279 | |
1280 | bool lastCol = col == mColumns - 1; |
1281 | |
1282 | if (!lastCol) |
1283 | t.OutputColumn(GetColAlign(col), GetColumnWidth(col)); |
1284 | else |
1285 | t.Output(); |
1286 | |
1287 | ExtOut(lastCol ? "\n" : GetWhitespace(mPadding)); |
1288 | |
1289 | if (lastCol) |
1290 | mCurrCol = 0; |
1291 | else |
1292 | mCurrCol = col+1; |
1293 | } |
1294 | |
1295 | template <class T> |
1296 | void WriteColumn(int col, T t) |
1297 | { |
1298 | WriteColumn(col, Output::Format<T>(t)); |
1299 | } |
1300 | |
1301 | void WriteColumn(int col, const String &str) |
1302 | { |
1303 | WriteColumn(col, Output::Format<const char *>(str)); |
1304 | } |
1305 | |
1306 | void WriteColumn(int col, const WString &str) |
1307 | { |
1308 | WriteColumn(col, Output::Format<const WCHAR *>(str)); |
1309 | } |
1310 | |
1311 | void WriteColumn(int col, __in_z WCHAR *str) |
1312 | { |
1313 | WriteColumn(col, Output::Format<const WCHAR *>(str)); |
1314 | } |
1315 | |
1316 | void WriteColumn(int col, const WCHAR *str) |
1317 | { |
1318 | WriteColumn(col, Output::Format<const WCHAR *>(str)); |
1319 | } |
1320 | |
1321 | inline void WriteColumn(int col, __in_z char *str) |
1322 | { |
1323 | WriteColumn(col, Output::Format<const char *>(str)); |
1324 | } |
1325 | |
1326 | /* Writes a column using a printf style format. You cannot use the Format class with |
1327 | * this function to specify how the output should look, use printf style formatting |
1328 | * with the appropriate parameters instead. |
1329 | */ |
1330 | void WriteColumnFormat(int col, const char *fmt, ...) |
1331 | { |
1332 | SOS_Assert(strstr(fmt, "%S" ) == NULL); |
1333 | |
1334 | char result[128]; |
1335 | |
1336 | va_list list; |
1337 | va_start(list, fmt); |
1338 | vsprintf_s(result, _countof(result), fmt, list); |
1339 | va_end(list); |
1340 | |
1341 | WriteColumn(col, result); |
1342 | } |
1343 | |
1344 | void WriteColumnFormat(int col, const WCHAR *fmt, ...) |
1345 | { |
1346 | WCHAR result[128]; |
1347 | |
1348 | va_list list; |
1349 | va_start(list, fmt); |
1350 | vswprintf_s(result, _countof(result), fmt, list); |
1351 | va_end(list); |
1352 | |
1353 | WriteColumn(col, result); |
1354 | } |
1355 | |
1356 | /* This function is a shortcut for writing the next column. (That is, the one after the |
1357 | * one you just wrote.) |
1358 | */ |
1359 | template <class T> |
1360 | void WriteColumn(T t) |
1361 | { |
1362 | WriteColumn(mCurrCol, t); |
1363 | } |
1364 | |
1365 | private: |
1366 | void Clear(); |
1367 | void AllocWidths(); |
1368 | int GetColumnWidth(int col); |
1369 | Alignment GetColAlign(int col); |
1370 | const char *GetWhitespace(int amount); |
1371 | void OutputBlankColumns(int col); |
1372 | void OutputIndent(); |
1373 | |
1374 | private: |
1375 | int mColumns, mDefaultWidth, mIndent, mPadding, mCurrCol; |
1376 | Alignment mDefaultAlign; |
1377 | int *mWidths; |
1378 | Alignment *mAlignments; |
1379 | }; |
1380 | |
1381 | HRESULT GetMethodDefinitionsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, IXCLRDataMethodDefinition **ppMethodDefinitions, int numMethods, int *numMethodsNeeded); |
1382 | HRESULT GetMethodDescsFromName(DWORD_PTR ModulePtr, IXCLRDataModule* mod, const char* name, DWORD_PTR **pOut, int *numMethodDescs); |
1383 | |
1384 | HRESULT FileNameForModule (DacpModuleData *pModule, __out_ecount (MAX_LONGPATH) WCHAR *fileName); |
1385 | HRESULT FileNameForModule (DWORD_PTR pModuleAddr, __out_ecount (MAX_LONGPATH) WCHAR *fileName); |
1386 | void IP2MethodDesc (DWORD_PTR IP, DWORD_PTR &methodDesc, JITTypes &jitType, |
1387 | DWORD_PTR &gcinfoAddr); |
1388 | const char *ElementTypeName (unsigned type); |
1389 | void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodTableFieldData *pMTFD, |
1390 | DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE); |
1391 | int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE); |
1392 | int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE, DacpFieldDescData* pDacpFieldDescData=NULL); |
1393 | int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData); |
1394 | |
1395 | BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb); |
1396 | void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, |
1397 | bool bClassName=true); |
1398 | void NameForToken_s(DWORD_PTR ModuleAddr, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, |
1399 | bool bClassName=true); |
1400 | HRESULT NameForToken_s(mdTypeDef mb, IMetaDataImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, |
1401 | bool bClassName); |
1402 | HRESULT NameForTokenNew_s(mdTypeDef mb, IMDInternalImport *pImport, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName, |
1403 | bool bClassName); |
1404 | |
1405 | void vmmap(); |
1406 | void vmstat(); |
1407 | |
1408 | #ifndef FEATURE_PAL |
1409 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
1410 | // Support for managed stack tracing |
1411 | // |
1412 | |
1413 | DWORD_PTR GetDebuggerJitInfo(DWORD_PTR md); |
1414 | |
1415 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
1416 | #endif // FEATURE_PAL |
1417 | |
1418 | template <typename SCALAR> |
1419 | inline |
1420 | int bitidx(SCALAR bitflag) |
1421 | { |
1422 | for (int idx = 0; idx < static_cast<int>(sizeof(bitflag))*8; ++idx) |
1423 | { |
1424 | if (bitflag & (1 << idx)) |
1425 | { |
1426 | _ASSERTE((bitflag & (~(1 << idx))) == 0); |
1427 | return idx; |
1428 | } |
1429 | } |
1430 | return -1; |
1431 | } |
1432 | |
1433 | HRESULT |
1434 | DllsName( |
1435 | ULONG_PTR addrContaining, |
1436 | __out_ecount (MAX_LONGPATH) WCHAR *dllName |
1437 | ); |
1438 | |
1439 | inline |
1440 | BOOL IsElementValueType (CorElementType cet) |
1441 | { |
1442 | return (cet >= ELEMENT_TYPE_BOOLEAN && cet <= ELEMENT_TYPE_R8) |
1443 | || cet == ELEMENT_TYPE_VALUETYPE || cet == ELEMENT_TYPE_I || cet == ELEMENT_TYPE_U; |
1444 | } |
1445 | |
1446 | |
1447 | #define safemove(dst, src) \ |
1448 | SafeReadMemory (TO_TADDR(src), &(dst), sizeof(dst), NULL) |
1449 | |
1450 | extern "C" PDEBUG_DATA_SPACES g_ExtData; |
1451 | |
1452 | #include <arrayholder.h> |
1453 | |
1454 | // This class acts a smart pointer which calls the Release method on any object |
1455 | // you place in it when the ToRelease class falls out of scope. You may use it |
1456 | // just like you would a standard pointer to a COM object (including if (foo), |
1457 | // if (!foo), if (foo == 0), etc) except for two caveats: |
1458 | // 1. This class never calls AddRef and it always calls Release when it |
1459 | // goes out of scope. |
1460 | // 2. You should never use & to try to get a pointer to a pointer unless |
1461 | // you call Release first, or you will leak whatever this object contains |
1462 | // prior to updating its internal pointer. |
1463 | template<class T> |
1464 | class ToRelease |
1465 | { |
1466 | public: |
1467 | ToRelease() |
1468 | : m_ptr(NULL) |
1469 | {} |
1470 | |
1471 | ToRelease(T* ptr) |
1472 | : m_ptr(ptr) |
1473 | {} |
1474 | |
1475 | ~ToRelease() |
1476 | { |
1477 | Release(); |
1478 | } |
1479 | |
1480 | void operator=(T *ptr) |
1481 | { |
1482 | Release(); |
1483 | |
1484 | m_ptr = ptr; |
1485 | } |
1486 | |
1487 | T* operator->() |
1488 | { |
1489 | return m_ptr; |
1490 | } |
1491 | |
1492 | operator T*() |
1493 | { |
1494 | return m_ptr; |
1495 | } |
1496 | |
1497 | T** operator&() |
1498 | { |
1499 | return &m_ptr; |
1500 | } |
1501 | |
1502 | T* GetPtr() const |
1503 | { |
1504 | return m_ptr; |
1505 | } |
1506 | |
1507 | T* Detach() |
1508 | { |
1509 | T* pT = m_ptr; |
1510 | m_ptr = NULL; |
1511 | return pT; |
1512 | } |
1513 | |
1514 | void Release() |
1515 | { |
1516 | if (m_ptr != NULL) |
1517 | { |
1518 | m_ptr->Release(); |
1519 | m_ptr = NULL; |
1520 | } |
1521 | } |
1522 | |
1523 | private: |
1524 | T* m_ptr; |
1525 | }; |
1526 | |
1527 | struct ModuleInfo |
1528 | { |
1529 | ULONG64 baseAddr; |
1530 | ULONG64 size; |
1531 | BOOL hasPdb; |
1532 | }; |
1533 | extern ModuleInfo moduleInfo[]; |
1534 | |
1535 | BOOL InitializeHeapData(); |
1536 | BOOL IsServerBuild (); |
1537 | UINT GetMaxGeneration(); |
1538 | UINT GetGcHeapCount(); |
1539 | BOOL GetGcStructuresValid(); |
1540 | |
1541 | ULONG GetILSize(DWORD_PTR ilAddr); // REturns 0 if error occurs |
1542 | HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr); |
1543 | void DecodeIL(IMetaDataImport *pImport, BYTE *buffer, ULONG bufSize); |
1544 | void DecodeDynamicIL(BYTE *data, ULONG Size, DacpObjectData& tokenArray); |
1545 | |
1546 | BOOL IsRetailBuild (size_t base); |
1547 | EEFLAVOR GetEEFlavor (); |
1548 | HRESULT InitCorDebugInterface(); |
1549 | VOID UninitCorDebugInterface(); |
1550 | #ifndef FEATURE_PAL |
1551 | BOOL GetEEVersion(VS_FIXEDFILEINFO *pFileInfo); |
1552 | BOOL GetSOSVersion(VS_FIXEDFILEINFO *pFileInfo); |
1553 | #endif |
1554 | |
1555 | BOOL IsDumpFile (); |
1556 | |
1557 | // IsMiniDumpFile will return true if 1) we are in |
1558 | // a small format minidump, and g_InMinidumpSafeMode is true. |
1559 | extern BOOL g_InMinidumpSafeMode; |
1560 | |
1561 | BOOL IsMiniDumpFile(); |
1562 | void ReportOOM(); |
1563 | |
1564 | BOOL SafeReadMemory (TADDR offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead); |
1565 | #if !defined(_TARGET_WIN64_) && !defined(_ARM64_) |
1566 | // on 64-bit platforms TADDR and CLRDATA_ADDRESS are identical |
1567 | inline BOOL SafeReadMemory (CLRDATA_ADDRESS offset, PVOID lpBuffer, ULONG cb, PULONG lpcbBytesRead) |
1568 | { return SafeReadMemory(TO_TADDR(offset), lpBuffer, cb, lpcbBytesRead); } |
1569 | #endif |
1570 | |
1571 | BOOL NameForMD_s (DWORD_PTR pMD, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName); |
1572 | BOOL NameForMT_s (DWORD_PTR MTAddr, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName); |
1573 | |
1574 | WCHAR *CreateMethodTableName(TADDR mt, TADDR cmt = NULL); |
1575 | |
1576 | void isRetAddr(DWORD_PTR retAddr, DWORD_PTR* whereCalled); |
1577 | DWORD_PTR GetValueFromExpression (___in __in_z const char *const str); |
1578 | |
1579 | enum ModuleHeapType |
1580 | { |
1581 | ModuleHeapType_ThunkHeap, |
1582 | ModuleHeapType_LookupTableHeap |
1583 | }; |
1584 | |
1585 | HRESULT PrintDomainHeapInfo(const char *name, CLRDATA_ADDRESS adPtr, DWORD_PTR *size, DWORD_PTR *wasted = 0); |
1586 | DWORD_PTR PrintModuleHeapInfo(DWORD_PTR *moduleList, int count, ModuleHeapType type, DWORD_PTR *wasted = 0); |
1587 | void PrintHeapSize(DWORD_PTR total, DWORD_PTR wasted); |
1588 | void DomainInfo(DacpAppDomainData *pDomain); |
1589 | void AssemblyInfo(DacpAssemblyData *pAssembly); |
1590 | DWORD_PTR LoaderHeapInfo(CLRDATA_ADDRESS pLoaderHeapAddr, DWORD_PTR *wasted = 0); |
1591 | DWORD_PTR JitHeapInfo(); |
1592 | DWORD_PTR VSDHeapInfo(CLRDATA_ADDRESS appDomain, DWORD_PTR *wasted = 0); |
1593 | |
1594 | DWORD GetNumComponents(TADDR obj); |
1595 | |
1596 | struct GenUsageStat |
1597 | { |
1598 | size_t allocd; |
1599 | size_t freed; |
1600 | size_t unrooted; |
1601 | }; |
1602 | |
1603 | struct HeapUsageStat |
1604 | { |
1605 | GenUsageStat genUsage[4]; // gen0, 1, 2, LOH |
1606 | }; |
1607 | |
1608 | extern DacpUsefulGlobalsData g_special_usefulGlobals; |
1609 | BOOL GCHeapUsageStats(const DacpGcHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage); |
1610 | |
1611 | class HeapStat |
1612 | { |
1613 | protected: |
1614 | struct Node |
1615 | { |
1616 | DWORD_PTR data; |
1617 | DWORD count; |
1618 | size_t totalSize; |
1619 | Node* left; |
1620 | Node* right; |
1621 | Node () |
1622 | : data(0), count(0), totalSize(0), left(NULL), right(NULL) |
1623 | { |
1624 | } |
1625 | }; |
1626 | BOOL bHasStrings; |
1627 | Node *head; |
1628 | BOOL fLinear; |
1629 | public: |
1630 | HeapStat () |
1631 | : bHasStrings(FALSE), head(NULL), fLinear(FALSE) |
1632 | {} |
1633 | ~HeapStat() |
1634 | { |
1635 | Delete(); |
1636 | } |
1637 | // TODO: Change the aSize argument to size_t when we start supporting |
1638 | // TODO: object sizes above 4GB |
1639 | void Add (DWORD_PTR aData, DWORD aSize); |
1640 | void Sort (); |
1641 | void Print (const char* label = NULL); |
1642 | void Delete (); |
1643 | void HasStrings(BOOL abHasStrings) |
1644 | { |
1645 | bHasStrings = abHasStrings; |
1646 | } |
1647 | private: |
1648 | int CompareData(DWORD_PTR n1, DWORD_PTR n2); |
1649 | void SortAdd (Node *&root, Node *entry); |
1650 | void LinearAdd (Node *&root, Node *entry); |
1651 | void ReverseLeftMost (Node *root); |
1652 | void Linearize(); |
1653 | }; |
1654 | |
1655 | class CGCDesc; |
1656 | |
1657 | // The information MethodTableCache returns. |
1658 | struct MethodTableInfo |
1659 | { |
1660 | bool IsInitialized() { return BaseSize != 0; } |
1661 | |
1662 | DWORD BaseSize; // Caching BaseSize and ComponentSize for a MethodTable |
1663 | DWORD ComponentSize; // here has HUGE perf benefits in heap traversals. |
1664 | BOOL bContainsPointers; |
1665 | BOOL bCollectible; |
1666 | DWORD_PTR* GCInfoBuffer; // Start of memory of GC info |
1667 | CGCDesc* GCInfo; // Just past GC info (which is how it is stored) |
1668 | bool ArrayOfVC; |
1669 | TADDR LoaderAllocatorObjectHandle; |
1670 | }; |
1671 | |
1672 | class MethodTableCache |
1673 | { |
1674 | protected: |
1675 | |
1676 | struct Node |
1677 | { |
1678 | DWORD_PTR data; // This is the key (the method table pointer) |
1679 | MethodTableInfo info; // The info associated with this MethodTable |
1680 | Node* left; |
1681 | Node* right; |
1682 | Node (DWORD_PTR aData) : data(aData), left(NULL), right(NULL) |
1683 | { |
1684 | info.BaseSize = 0; |
1685 | info.ComponentSize = 0; |
1686 | info.bContainsPointers = false; |
1687 | info.bCollectible = false; |
1688 | info.GCInfo = NULL; |
1689 | info.ArrayOfVC = false; |
1690 | info.GCInfoBuffer = NULL; |
1691 | info.LoaderAllocatorObjectHandle = NULL; |
1692 | } |
1693 | }; |
1694 | Node *head; |
1695 | public: |
1696 | MethodTableCache () |
1697 | : head(NULL) |
1698 | {} |
1699 | ~MethodTableCache() { Clear(); } |
1700 | |
1701 | // Always succeeds, if it is not present it adds an empty Info struct and returns that |
1702 | // Thus you must call 'IsInitialized' on the returned value before using it |
1703 | MethodTableInfo* Lookup(DWORD_PTR aData); |
1704 | |
1705 | void Clear (); |
1706 | private: |
1707 | int CompareData(DWORD_PTR n1, DWORD_PTR n2); |
1708 | void ReverseLeftMost (Node *root); |
1709 | }; |
1710 | |
1711 | extern MethodTableCache g_special_mtCache; |
1712 | |
1713 | struct DumpArrayFlags |
1714 | { |
1715 | DWORD_PTR startIndex; |
1716 | DWORD_PTR Length; |
1717 | BOOL bDetail; |
1718 | LPSTR strObject; |
1719 | BOOL bNoFieldsForElement; |
1720 | |
1721 | DumpArrayFlags () |
1722 | : startIndex(0), Length((DWORD_PTR)-1), bDetail(FALSE), strObject (0), bNoFieldsForElement(FALSE) |
1723 | {} |
1724 | ~DumpArrayFlags () |
1725 | { |
1726 | if (strObject) |
1727 | delete [] strObject; |
1728 | } |
1729 | }; //DumpArrayFlags |
1730 | |
1731 | |
1732 | |
1733 | // ----------------------------------------------------------------------- |
1734 | |
1735 | #define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000 |
1736 | #define BIT_SBLK_FINALIZER_RUN 0x40000000 |
1737 | #define BIT_SBLK_SPIN_LOCK 0x10000000 |
1738 | #define SBLK_MASK_LOCK_THREADID 0x000003FF // special value of 0 + 1023 thread ids |
1739 | #define SBLK_MASK_LOCK_RECLEVEL 0x0000FC00 // 64 recursion levels |
1740 | #define SBLK_APPDOMAIN_SHIFT 16 // shift right this much to get appdomain index |
1741 | #define SBLK_MASK_APPDOMAININDEX 0x000007FF // 2048 appdomain indices |
1742 | #define SBLK_RECLEVEL_SHIFT 10 // shift right this much to get recursion level |
1743 | #define BIT_SBLK_IS_HASHCODE 0x04000000 |
1744 | #define MASK_HASHCODE ((1<<HASHCODE_BITS)-1) |
1745 | #define SYNCBLOCKINDEX_BITS 26 |
1746 | #define MASK_SYNCBLOCKINDEX ((1<<SYNCBLOCKINDEX_BITS)-1) |
1747 | |
1748 | HRESULT GetMTOfObject(TADDR obj, TADDR *mt); |
1749 | |
1750 | struct needed_alloc_context |
1751 | { |
1752 | BYTE* alloc_ptr; // starting point for next allocation |
1753 | BYTE* alloc_limit; // ending point for allocation region/quantum |
1754 | }; |
1755 | |
1756 | struct AllocInfo |
1757 | { |
1758 | needed_alloc_context *array; |
1759 | int num; // number of allocation contexts in array |
1760 | |
1761 | AllocInfo() |
1762 | : array(NULL) |
1763 | , num(0) |
1764 | {} |
1765 | void Init() |
1766 | { |
1767 | extern void GetAllocContextPtrs(AllocInfo *pallocInfo); |
1768 | GetAllocContextPtrs(this); |
1769 | } |
1770 | ~AllocInfo() |
1771 | { |
1772 | if (array != NULL) |
1773 | delete[] array; |
1774 | } |
1775 | }; |
1776 | |
1777 | struct GCHandleStatistics |
1778 | { |
1779 | HeapStat hs; |
1780 | |
1781 | DWORD strongHandleCount; |
1782 | DWORD pinnedHandleCount; |
1783 | DWORD asyncPinnedHandleCount; |
1784 | DWORD refCntHandleCount; |
1785 | DWORD weakLongHandleCount; |
1786 | DWORD weakShortHandleCount; |
1787 | DWORD variableCount; |
1788 | DWORD sizedRefCount; |
1789 | DWORD dependentCount; |
1790 | DWORD weakWinRTHandleCount; |
1791 | DWORD unknownHandleCount; |
1792 | GCHandleStatistics() |
1793 | : strongHandleCount(0), pinnedHandleCount(0), asyncPinnedHandleCount(0), refCntHandleCount(0), |
1794 | weakLongHandleCount(0), weakShortHandleCount(0), variableCount(0), sizedRefCount(0), |
1795 | dependentCount(0), weakWinRTHandleCount(0), unknownHandleCount(0) |
1796 | {} |
1797 | ~GCHandleStatistics() |
1798 | { |
1799 | hs.Delete(); |
1800 | } |
1801 | }; |
1802 | |
1803 | struct SegmentLookup |
1804 | { |
1805 | DacpHeapSegmentData *m_segments; |
1806 | int m_iSegmentsSize; |
1807 | int m_iSegmentCount; |
1808 | |
1809 | SegmentLookup(); |
1810 | ~SegmentLookup(); |
1811 | |
1812 | void Clear(); |
1813 | BOOL AddSegment(DacpHeapSegmentData *pData); |
1814 | CLRDATA_ADDRESS GetHeap(CLRDATA_ADDRESS object, BOOL& bFound); |
1815 | }; |
1816 | |
1817 | class GCHeapSnapshot |
1818 | { |
1819 | private: |
1820 | BOOL m_isBuilt; |
1821 | DacpGcHeapDetails *m_heapDetails; |
1822 | DacpGcHeapData m_gcheap; |
1823 | SegmentLookup m_segments; |
1824 | |
1825 | BOOL AddSegments(DacpGcHeapDetails& details); |
1826 | public: |
1827 | GCHeapSnapshot(); |
1828 | |
1829 | BOOL Build(); |
1830 | void Clear(); |
1831 | BOOL IsBuilt() { return m_isBuilt; } |
1832 | |
1833 | DacpGcHeapData *GetHeapData() { return &m_gcheap; } |
1834 | |
1835 | int GetHeapCount() { return m_gcheap.HeapCount; } |
1836 | |
1837 | DacpGcHeapDetails *GetHeap(CLRDATA_ADDRESS objectPointer); |
1838 | int GetGeneration(CLRDATA_ADDRESS objectPointer); |
1839 | |
1840 | |
1841 | }; |
1842 | extern GCHeapSnapshot g_snapshot; |
1843 | |
1844 | BOOL IsSameModuleName (const char *str1, const char *str2); |
1845 | BOOL IsModule (DWORD_PTR moduleAddr); |
1846 | BOOL IsMethodDesc (DWORD_PTR value); |
1847 | BOOL IsMethodTable (DWORD_PTR value); |
1848 | BOOL IsStringObject (size_t obj); |
1849 | BOOL IsObjectArray (DWORD_PTR objPointer); |
1850 | BOOL IsObjectArray (DacpObjectData *pData); |
1851 | BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString); |
1852 | BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD); |
1853 | |
1854 | /* Returns a list of all modules in the process. |
1855 | * Params: |
1856 | * name - The name of the module you would like. If mName is NULL the all modules are returned. |
1857 | * numModules - The number of modules in the array returned. |
1858 | * Returns: |
1859 | * An array of modules whose length is *numModules, NULL if an error occurred. Note that if this |
1860 | * function succeeds but finds no modules matching the name given, this function returns a valid |
1861 | * array, but *numModules will equal 0. |
1862 | * Note: |
1863 | * You must clean up the return value of this array by calling delete [] on it, or using the |
1864 | * ArrayHolder class. |
1865 | */ |
1866 | DWORD_PTR *ModuleFromName(__in_opt LPSTR name, int *numModules); |
1867 | void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name); |
1868 | void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL); |
1869 | |
1870 | |
1871 | typedef void (*VISITGCHEAPFUNC)(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token); |
1872 | BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify=true); |
1873 | |
1874 | ///////////////////////////////////////////////////////////////////////////////////////////////////////// |
1875 | |
1876 | struct strobjInfo |
1877 | { |
1878 | size_t methodTable; |
1879 | DWORD m_StringLength; |
1880 | }; |
1881 | |
1882 | // Just to make figuring out which fill pointer element matches a generation |
1883 | // a bit less confusing. This gen_segment function is ported from gc.cpp. |
1884 | inline unsigned int gen_segment (int gen) |
1885 | { |
1886 | return (DAC_NUMBERGENERATIONS - gen - 1); |
1887 | } |
1888 | |
1889 | inline CLRDATA_ADDRESS SegQueue(DacpGcHeapDetails& heapDetails, int seg) |
1890 | { |
1891 | return heapDetails.finalization_fill_pointers[seg - 1]; |
1892 | } |
1893 | |
1894 | inline CLRDATA_ADDRESS SegQueueLimit(DacpGcHeapDetails& heapDetails, int seg) |
1895 | { |
1896 | return heapDetails.finalization_fill_pointers[seg]; |
1897 | } |
1898 | |
1899 | #define FinalizerListSeg (DAC_NUMBERGENERATIONS+1) |
1900 | #define CriticalFinalizerListSeg (DAC_NUMBERGENERATIONS) |
1901 | |
1902 | void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort); |
1903 | |
1904 | CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr); |
1905 | CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr); |
1906 | void GCHeapInfo(const DacpGcHeapDetails &heap, DWORD_PTR &total_size); |
1907 | BOOL GCObjInHeap(TADDR taddrObj, const DacpGcHeapDetails &heap, |
1908 | TADDR_SEGINFO& trngSeg, int& gen, TADDR_RANGE& allocCtx, BOOL &bLarge); |
1909 | |
1910 | BOOL VerifyObject(const DacpGcHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, |
1911 | BOOL bVerifyMember); |
1912 | BOOL VerifyObject(const DacpGcHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, |
1913 | BOOL bVerifyMember); |
1914 | |
1915 | BOOL IsMTForFreeObj(DWORD_PTR pMT); |
1916 | void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields); |
1917 | |
1918 | |
1919 | enum ARGTYPE {COBOOL,COSIZE_T,COHEX,COSTRING}; |
1920 | struct CMDOption |
1921 | { |
1922 | const char* name; |
1923 | void *vptr; |
1924 | ARGTYPE type; |
1925 | BOOL hasValue; |
1926 | BOOL hasSeen; |
1927 | }; |
1928 | struct CMDValue |
1929 | { |
1930 | void *vptr; |
1931 | ARGTYPE type; |
1932 | }; |
1933 | BOOL GetCMDOption(const char *string, CMDOption *option, size_t nOption, |
1934 | CMDValue *arg, size_t maxArg, size_t *nArg); |
1935 | |
1936 | void DumpMDInfo(DWORD_PTR dwStartAddr, CLRDATA_ADDRESS dwRequestedIP = 0, BOOL fStackTraceFormat = FALSE); |
1937 | void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, BOOL fStackTraceFormat); |
1938 | void GetDomainList(DWORD_PTR *&domainList, int &numDomain); |
1939 | HRESULT GetThreadList(DWORD_PTR **threadList, int *numThread); |
1940 | CLRDATA_ADDRESS GetCurrentManagedThread(); // returns current managed thread if any |
1941 | void GetAllocContextPtrs(AllocInfo *pallocInfo); |
1942 | |
1943 | void ReloadSymbolWithLineInfo(); |
1944 | |
1945 | size_t FunctionType (size_t EIP); |
1946 | |
1947 | size_t Align (size_t nbytes); |
1948 | // Aligns large objects |
1949 | size_t AlignLarge (size_t nbytes); |
1950 | |
1951 | ULONG OSPageSize (); |
1952 | size_t NextOSPageAddress (size_t addr); |
1953 | |
1954 | // This version of objectsize reduces the lookup of methodtables in the DAC. |
1955 | // It uses g_special_mtCache for it's work. |
1956 | BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, |
1957 | DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers); |
1958 | |
1959 | BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle); |
1960 | |
1961 | // ObjSize now uses the methodtable cache for its work too. |
1962 | size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE); |
1963 | size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE); |
1964 | |
1965 | void CharArrayContent(TADDR pos, ULONG num, bool widechar); |
1966 | void StringObjectContent (size_t obj, BOOL fLiteral=FALSE, const int length=-1); // length=-1: dump everything in the string object. |
1967 | |
1968 | UINT FindAllPinnedAndStrong (DWORD_PTR handlearray[],UINT arraySize); |
1969 | void PrintNotReachableInRange(TADDR rngStart, TADDR rngEnd, BOOL bExcludeReadyForFinalization, |
1970 | HeapStat* stat, BOOL bShort); |
1971 | |
1972 | const char *EHTypeName(EHClauseType et); |
1973 | |
1974 | struct StringHolder |
1975 | { |
1976 | LPSTR data; |
1977 | StringHolder() : data(NULL) { } |
1978 | ~StringHolder() { if(data) delete [] data; } |
1979 | }; |
1980 | |
1981 | |
1982 | ULONG DebuggeeType(); |
1983 | |
1984 | inline BOOL IsKernelDebugger () |
1985 | { |
1986 | return DebuggeeType() == DEBUG_CLASS_KERNEL; |
1987 | } |
1988 | |
1989 | void ResetGlobals(void); |
1990 | HRESULT LoadClrDebugDll(void); |
1991 | extern "C" void UnloadClrDebugDll(void); |
1992 | |
1993 | extern IMetaDataImport* MDImportForModule (DacpModuleData *pModule); |
1994 | extern IMetaDataImport* MDImportForModule (DWORD_PTR pModule); |
1995 | |
1996 | //***************************************************************************** |
1997 | // |
1998 | // **** CQuickBytes |
1999 | // This helper class is useful for cases where 90% of the time you allocate 512 |
2000 | // or less bytes for a data structure. This class contains a 512 byte buffer. |
2001 | // Alloc() will return a pointer to this buffer if your allocation is small |
2002 | // enough, otherwise it asks the heap for a larger buffer which is freed for |
2003 | // you. No mutex locking is required for the small allocation case, making the |
2004 | // code run faster, less heap fragmentation, etc... Each instance will allocate |
2005 | // 520 bytes, so use accordinly. |
2006 | // |
2007 | //***************************************************************************** |
2008 | template <DWORD SIZE, DWORD INCREMENT> |
2009 | class CQuickBytesBase |
2010 | { |
2011 | public: |
2012 | CQuickBytesBase() : |
2013 | pbBuff(0), |
2014 | iSize(0), |
2015 | cbTotal(SIZE) |
2016 | { } |
2017 | |
2018 | void Destroy() |
2019 | { |
2020 | if (pbBuff) |
2021 | { |
2022 | delete[] (BYTE*)pbBuff; |
2023 | pbBuff = 0; |
2024 | } |
2025 | } |
2026 | |
2027 | void *Alloc(SIZE_T iItems) |
2028 | { |
2029 | iSize = iItems; |
2030 | if (iItems <= SIZE) |
2031 | { |
2032 | cbTotal = SIZE; |
2033 | return (&rgData[0]); |
2034 | } |
2035 | else |
2036 | { |
2037 | if (pbBuff) |
2038 | delete[] (BYTE*)pbBuff; |
2039 | pbBuff = new BYTE[iItems]; |
2040 | cbTotal = pbBuff ? iItems : 0; |
2041 | return (pbBuff); |
2042 | } |
2043 | } |
2044 | |
2045 | // This is for conformity to the CQuickBytesBase that is defined by the runtime so |
2046 | // that we can use it inside of some GC code that SOS seems to include as well. |
2047 | // |
2048 | // The plain vanilla "Alloc" version on this CQuickBytesBase doesn't throw either, |
2049 | // so we'll just forward the call. |
2050 | void *AllocNoThrow(SIZE_T iItems) |
2051 | { |
2052 | return Alloc(iItems); |
2053 | } |
2054 | |
2055 | HRESULT ReSize(SIZE_T iItems) |
2056 | { |
2057 | void *pbBuffNew; |
2058 | if (iItems <= cbTotal) |
2059 | { |
2060 | iSize = iItems; |
2061 | return NOERROR; |
2062 | } |
2063 | |
2064 | pbBuffNew = new BYTE[iItems + INCREMENT]; |
2065 | if (!pbBuffNew) |
2066 | return E_OUTOFMEMORY; |
2067 | if (pbBuff) |
2068 | { |
2069 | memcpy(pbBuffNew, pbBuff, cbTotal); |
2070 | delete[] (BYTE*)pbBuff; |
2071 | } |
2072 | else |
2073 | { |
2074 | _ASSERTE(cbTotal == SIZE); |
2075 | memcpy(pbBuffNew, rgData, SIZE); |
2076 | } |
2077 | cbTotal = iItems + INCREMENT; |
2078 | iSize = iItems; |
2079 | pbBuff = pbBuffNew; |
2080 | return NOERROR; |
2081 | |
2082 | } |
2083 | |
2084 | operator PVOID() |
2085 | { return ((pbBuff) ? pbBuff : &rgData[0]); } |
2086 | |
2087 | void *Ptr() |
2088 | { return ((pbBuff) ? pbBuff : &rgData[0]); } |
2089 | |
2090 | SIZE_T Size() |
2091 | { return (iSize); } |
2092 | |
2093 | SIZE_T MaxSize() |
2094 | { return (cbTotal); } |
2095 | |
2096 | void *pbBuff; |
2097 | SIZE_T iSize; // number of bytes used |
2098 | SIZE_T cbTotal; // total bytes allocated in the buffer |
2099 | // use UINT64 to enforce the alignment of the memory |
2100 | UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)]; |
2101 | }; |
2102 | |
2103 | #define CQUICKBYTES_BASE_SIZE 512 |
2104 | #define CQUICKBYTES_INCREMENTAL_SIZE 128 |
2105 | |
2106 | class CQuickBytesNoDtor : public CQuickBytesBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE> |
2107 | { |
2108 | }; |
2109 | |
2110 | class CQuickBytes : public CQuickBytesNoDtor |
2111 | { |
2112 | public: |
2113 | CQuickBytes() { } |
2114 | |
2115 | ~CQuickBytes() |
2116 | { |
2117 | Destroy(); |
2118 | } |
2119 | }; |
2120 | |
2121 | template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE> |
2122 | class CQuickBytesNoDtorSpecifySize : public CQuickBytesBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE> |
2123 | { |
2124 | }; |
2125 | |
2126 | template <DWORD CQUICKBYTES_BASE_SPECIFY_SIZE> |
2127 | class CQuickBytesSpecifySize : public CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE> |
2128 | { |
2129 | public: |
2130 | CQuickBytesSpecifySize() { } |
2131 | |
2132 | ~CQuickBytesSpecifySize() |
2133 | { |
2134 | CQuickBytesNoDtorSpecifySize<CQUICKBYTES_BASE_SPECIFY_SIZE>::Destroy(); |
2135 | } |
2136 | }; |
2137 | |
2138 | |
2139 | #define STRING_SIZE 10 |
2140 | class CQuickString : public CQuickBytesBase<STRING_SIZE, STRING_SIZE> |
2141 | { |
2142 | public: |
2143 | CQuickString() { } |
2144 | |
2145 | ~CQuickString() |
2146 | { |
2147 | Destroy(); |
2148 | } |
2149 | |
2150 | void *Alloc(SIZE_T iItems) |
2151 | { |
2152 | return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Alloc(iItems*sizeof(WCHAR)); |
2153 | } |
2154 | |
2155 | HRESULT ReSize(SIZE_T iItems) |
2156 | { |
2157 | return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::ReSize(iItems * sizeof(WCHAR)); |
2158 | } |
2159 | |
2160 | SIZE_T Size() |
2161 | { |
2162 | return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::Size() / sizeof(WCHAR); |
2163 | } |
2164 | |
2165 | SIZE_T MaxSize() |
2166 | { |
2167 | return CQuickBytesBase<STRING_SIZE, STRING_SIZE>::MaxSize() / sizeof(WCHAR); |
2168 | } |
2169 | |
2170 | WCHAR* String() |
2171 | { |
2172 | return (WCHAR*) Ptr(); |
2173 | } |
2174 | |
2175 | }; |
2176 | |
2177 | enum GetSignatureStringResults |
2178 | { |
2179 | GSS_SUCCESS, |
2180 | GSS_ERROR, |
2181 | GSS_INSUFFICIENT_DATA, |
2182 | }; |
2183 | |
2184 | GetSignatureStringResults GetMethodSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString); |
2185 | GetSignatureStringResults GetSignatureString (PCCOR_SIGNATURE pbSigBlob, ULONG ulSigBlob, DWORD_PTR dwModuleAddr, CQuickBytes *sigString); |
2186 | void GetMethodName(mdMethodDef methodDef, IMetaDataImport * pImport, CQuickBytes *fullName); |
2187 | |
2188 | #ifndef _TARGET_WIN64_ |
2189 | #define itoa_s_ptr _itoa_s |
2190 | #define itow_s_ptr _itow_s |
2191 | #else |
2192 | #define itoa_s_ptr _i64toa_s |
2193 | #define itow_s_ptr _i64tow_s |
2194 | #endif |
2195 | |
2196 | #ifdef FEATURE_PAL |
2197 | extern "C" |
2198 | int _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); |
2199 | extern "C" |
2200 | int _ui64toa_s( unsigned __int64 inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); |
2201 | #endif // FEATURE_PAL |
2202 | |
2203 | struct MemRange |
2204 | { |
2205 | MemRange (ULONG64 s = NULL, size_t l = 0, MemRange * n = NULL) |
2206 | : start(s), len (l), next (n) |
2207 | {} |
2208 | |
2209 | bool InRange (ULONG64 addr) |
2210 | { |
2211 | return addr >= start && addr < start + len; |
2212 | } |
2213 | |
2214 | ULONG64 start; |
2215 | size_t len; |
2216 | MemRange * next; |
2217 | }; //struct MemRange |
2218 | |
2219 | #ifndef FEATURE_PAL |
2220 | |
2221 | class StressLogMem |
2222 | { |
2223 | private: |
2224 | // use a linked list for now, could be optimazied later |
2225 | MemRange * list; |
2226 | |
2227 | void AddRange (ULONG64 s, size_t l) |
2228 | { |
2229 | list = new MemRange (s, l, list); |
2230 | } |
2231 | |
2232 | public: |
2233 | StressLogMem () : list (NULL) |
2234 | {} |
2235 | ~StressLogMem (); |
2236 | bool Init (ULONG64 stressLogAddr, IDebugDataSpaces* memCallBack); |
2237 | bool IsInStressLog (ULONG64 addr); |
2238 | }; //class StressLogMem |
2239 | |
2240 | // An adapter class that DIA consumes so that it can read PE data from the an image |
2241 | // This implementation gets the backing data from the image loaded in debuggee memory |
2242 | // that has been layed out identical to the disk format (ie not seperated by section) |
2243 | class PEOffsetMemoryReader : IDiaReadExeAtOffsetCallback |
2244 | { |
2245 | public: |
2246 | PEOffsetMemoryReader(TADDR moduleBaseAddress); |
2247 | |
2248 | // IUnknown implementation |
2249 | HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface); |
2250 | ULONG __stdcall AddRef(); |
2251 | ULONG __stdcall Release(); |
2252 | |
2253 | // IDiaReadExeAtOffsetCallback implementation |
2254 | HRESULT __stdcall ReadExecutableAt(DWORDLONG fileOffset, DWORD cbData, DWORD* pcbData, BYTE data[]); |
2255 | |
2256 | private: |
2257 | TADDR m_moduleBaseAddress; |
2258 | volatile ULONG m_refCount; |
2259 | }; |
2260 | |
2261 | // An adapter class that DIA consumes so that it can read PE data from the an image |
2262 | // This implementation gets the backing data from the image loaded in debuggee memory |
2263 | // that has been layed out in LoadLibrary format |
2264 | class PERvaMemoryReader : IDiaReadExeAtRVACallback |
2265 | { |
2266 | public: |
2267 | PERvaMemoryReader(TADDR moduleBaseAddress); |
2268 | |
2269 | // IUnknown implementation |
2270 | HRESULT __stdcall QueryInterface(REFIID riid, VOID** ppInterface); |
2271 | ULONG __stdcall AddRef(); |
2272 | ULONG __stdcall Release(); |
2273 | |
2274 | // IDiaReadExeAtOffsetCallback implementation |
2275 | HRESULT __stdcall ReadExecutableAtRVA(DWORD relativeVirtualAddress, DWORD cbData, DWORD* pcbData, BYTE data[]); |
2276 | |
2277 | private: |
2278 | TADDR m_moduleBaseAddress; |
2279 | volatile ULONG m_refCount; |
2280 | }; |
2281 | |
2282 | #endif // !FEATURE_PAL |
2283 | |
2284 | static const char *SymbolReaderDllName = "SOS.NETCore" ; |
2285 | static const char *SymbolReaderClassName = "SOS.SymbolReader" ; |
2286 | |
2287 | typedef int (*ReadMemoryDelegate)(ULONG64, char *, int); |
2288 | typedef PVOID (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate); |
2289 | typedef void (*DisposeDelegate)(PVOID); |
2290 | typedef BOOL (*ResolveSequencePointDelegate)(PVOID, const char*, unsigned int, unsigned int*, unsigned int*); |
2291 | typedef BOOL (*GetLocalVariableName)(PVOID, int, int, BSTR*); |
2292 | typedef BOOL (*GetLineByILOffsetDelegate)(PVOID, mdMethodDef, ULONG64, ULONG *, BSTR*); |
2293 | |
2294 | class SymbolReader |
2295 | { |
2296 | private: |
2297 | #ifndef FEATURE_PAL |
2298 | ISymUnmanagedReader* m_pSymReader; |
2299 | #endif |
2300 | PVOID m_symbolReaderHandle; |
2301 | |
2302 | static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate; |
2303 | static DisposeDelegate disposeDelegate; |
2304 | static ResolveSequencePointDelegate resolveSequencePointDelegate; |
2305 | static GetLocalVariableName getLocalVariableNameDelegate; |
2306 | static GetLineByILOffsetDelegate getLineByILOffsetDelegate; |
2307 | static HRESULT PrepareSymbolReader(); |
2308 | |
2309 | HRESULT GetNamedLocalVariable(___in ISymUnmanagedScope* pScope, ___in ICorDebugILFrame* pILFrame, ___in mdMethodDef methodToken, ___in ULONG localIndex, |
2310 | __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue); |
2311 | HRESULT LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout); |
2312 | HRESULT LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize, |
2313 | ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize); |
2314 | |
2315 | public: |
2316 | SymbolReader() |
2317 | { |
2318 | #ifndef FEATURE_PAL |
2319 | m_pSymReader = NULL; |
2320 | #endif |
2321 | m_symbolReaderHandle = 0; |
2322 | } |
2323 | |
2324 | ~SymbolReader() |
2325 | { |
2326 | #ifndef FEATURE_PAL |
2327 | if(m_pSymReader != NULL) |
2328 | { |
2329 | m_pSymReader->Release(); |
2330 | m_pSymReader = NULL; |
2331 | } |
2332 | #endif |
2333 | if (m_symbolReaderHandle != 0) |
2334 | { |
2335 | disposeDelegate(m_symbolReaderHandle); |
2336 | m_symbolReaderHandle = 0; |
2337 | } |
2338 | } |
2339 | |
2340 | HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule); |
2341 | HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule); |
2342 | HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName); |
2343 | HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue); |
2344 | HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset); |
2345 | }; |
2346 | |
2347 | HRESULT |
2348 | GetLineByOffset( |
2349 | ___in ULONG64 IP, |
2350 | ___out ULONG *pLinenum, |
2351 | __out_ecount(cchFileName) WCHAR* pwszFileName, |
2352 | ___in ULONG cchFileName); |
2353 | |
2354 | /// X86 Context |
2355 | #define X86_SIZE_OF_80387_REGISTERS 80 |
2356 | #define X86_MAXIMUM_SUPPORTED_EXTENSION 512 |
2357 | |
2358 | typedef struct { |
2359 | DWORD ControlWord; |
2360 | DWORD StatusWord; |
2361 | DWORD TagWord; |
2362 | DWORD ErrorOffset; |
2363 | DWORD ErrorSelector; |
2364 | DWORD DataOffset; |
2365 | DWORD DataSelector; |
2366 | BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS]; |
2367 | DWORD Cr0NpxState; |
2368 | } X86_FLOATING_SAVE_AREA; |
2369 | |
2370 | typedef struct { |
2371 | |
2372 | DWORD ContextFlags; |
2373 | DWORD Dr0; |
2374 | DWORD Dr1; |
2375 | DWORD Dr2; |
2376 | DWORD Dr3; |
2377 | DWORD Dr6; |
2378 | DWORD Dr7; |
2379 | |
2380 | X86_FLOATING_SAVE_AREA FloatSave; |
2381 | |
2382 | DWORD SegGs; |
2383 | DWORD SegFs; |
2384 | DWORD SegEs; |
2385 | DWORD SegDs; |
2386 | |
2387 | DWORD Edi; |
2388 | DWORD Esi; |
2389 | DWORD Ebx; |
2390 | DWORD Edx; |
2391 | DWORD Ecx; |
2392 | DWORD Eax; |
2393 | |
2394 | DWORD Ebp; |
2395 | DWORD Eip; |
2396 | DWORD SegCs; |
2397 | DWORD EFlags; |
2398 | DWORD Esp; |
2399 | DWORD SegSs; |
2400 | |
2401 | BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION]; |
2402 | |
2403 | } X86_CONTEXT; |
2404 | |
2405 | typedef struct { |
2406 | ULONGLONG Low; |
2407 | LONGLONG High; |
2408 | } M128A_XPLAT; |
2409 | |
2410 | |
2411 | /// AMD64 Context |
2412 | typedef struct { |
2413 | WORD ControlWord; |
2414 | WORD StatusWord; |
2415 | BYTE TagWord; |
2416 | BYTE Reserved1; |
2417 | WORD ErrorOpcode; |
2418 | DWORD ErrorOffset; |
2419 | WORD ErrorSelector; |
2420 | WORD Reserved2; |
2421 | DWORD DataOffset; |
2422 | WORD DataSelector; |
2423 | WORD Reserved3; |
2424 | DWORD MxCsr; |
2425 | DWORD MxCsr_Mask; |
2426 | M128A_XPLAT FloatRegisters[8]; |
2427 | |
2428 | #if defined(_WIN64) |
2429 | M128A_XPLAT XmmRegisters[16]; |
2430 | BYTE Reserved4[96]; |
2431 | #else |
2432 | M128A_XPLAT XmmRegisters[8]; |
2433 | BYTE Reserved4[220]; |
2434 | |
2435 | DWORD Cr0NpxState; |
2436 | #endif |
2437 | |
2438 | } AMD64_XMM_SAVE_AREA32; |
2439 | |
2440 | typedef struct { |
2441 | |
2442 | DWORD64 P1Home; |
2443 | DWORD64 P2Home; |
2444 | DWORD64 P3Home; |
2445 | DWORD64 P4Home; |
2446 | DWORD64 P5Home; |
2447 | DWORD64 P6Home; |
2448 | |
2449 | DWORD ContextFlags; |
2450 | DWORD MxCsr; |
2451 | |
2452 | WORD SegCs; |
2453 | WORD SegDs; |
2454 | WORD SegEs; |
2455 | WORD SegFs; |
2456 | WORD SegGs; |
2457 | WORD SegSs; |
2458 | DWORD EFlags; |
2459 | |
2460 | DWORD64 Dr0; |
2461 | DWORD64 Dr1; |
2462 | DWORD64 Dr2; |
2463 | DWORD64 Dr3; |
2464 | DWORD64 Dr6; |
2465 | DWORD64 Dr7; |
2466 | |
2467 | DWORD64 Rax; |
2468 | DWORD64 Rcx; |
2469 | DWORD64 Rdx; |
2470 | DWORD64 Rbx; |
2471 | DWORD64 Rsp; |
2472 | DWORD64 Rbp; |
2473 | DWORD64 Rsi; |
2474 | DWORD64 Rdi; |
2475 | DWORD64 R8; |
2476 | DWORD64 R9; |
2477 | DWORD64 R10; |
2478 | DWORD64 R11; |
2479 | DWORD64 R12; |
2480 | DWORD64 R13; |
2481 | DWORD64 R14; |
2482 | DWORD64 R15; |
2483 | |
2484 | DWORD64 Rip; |
2485 | |
2486 | union { |
2487 | AMD64_XMM_SAVE_AREA32 FltSave; |
2488 | struct { |
2489 | M128A_XPLAT [2]; |
2490 | M128A_XPLAT Legacy[8]; |
2491 | M128A_XPLAT Xmm0; |
2492 | M128A_XPLAT Xmm1; |
2493 | M128A_XPLAT Xmm2; |
2494 | M128A_XPLAT Xmm3; |
2495 | M128A_XPLAT Xmm4; |
2496 | M128A_XPLAT Xmm5; |
2497 | M128A_XPLAT Xmm6; |
2498 | M128A_XPLAT Xmm7; |
2499 | M128A_XPLAT Xmm8; |
2500 | M128A_XPLAT Xmm9; |
2501 | M128A_XPLAT Xmm10; |
2502 | M128A_XPLAT Xmm11; |
2503 | M128A_XPLAT Xmm12; |
2504 | M128A_XPLAT Xmm13; |
2505 | M128A_XPLAT Xmm14; |
2506 | M128A_XPLAT Xmm15; |
2507 | } DUMMYSTRUCTNAME; |
2508 | } DUMMYUNIONNAME; |
2509 | |
2510 | M128A_XPLAT VectorRegister[26]; |
2511 | DWORD64 VectorControl; |
2512 | |
2513 | DWORD64 DebugControl; |
2514 | DWORD64 LastBranchToRip; |
2515 | DWORD64 LastBranchFromRip; |
2516 | DWORD64 LastExceptionToRip; |
2517 | DWORD64 LastExceptionFromRip; |
2518 | |
2519 | } AMD64_CONTEXT; |
2520 | |
2521 | typedef struct{ |
2522 | __int64 LowPart; |
2523 | __int64 HighPart; |
2524 | } FLOAT128_XPLAT; |
2525 | |
2526 | |
2527 | /// ARM Context |
2528 | #define ARM_MAX_BREAKPOINTS_CONST 8 |
2529 | #define ARM_MAX_WATCHPOINTS_CONST 1 |
2530 | typedef DECLSPEC_ALIGN(8) struct { |
2531 | |
2532 | DWORD ContextFlags; |
2533 | |
2534 | DWORD R0; |
2535 | DWORD R1; |
2536 | DWORD R2; |
2537 | DWORD R3; |
2538 | DWORD R4; |
2539 | DWORD R5; |
2540 | DWORD R6; |
2541 | DWORD R7; |
2542 | DWORD R8; |
2543 | DWORD R9; |
2544 | DWORD R10; |
2545 | DWORD R11; |
2546 | DWORD R12; |
2547 | |
2548 | DWORD Sp; |
2549 | DWORD Lr; |
2550 | DWORD Pc; |
2551 | DWORD Cpsr; |
2552 | |
2553 | DWORD Fpscr; |
2554 | DWORD Padding; |
2555 | union { |
2556 | M128A_XPLAT Q[16]; |
2557 | ULONGLONG D[32]; |
2558 | DWORD S[32]; |
2559 | } DUMMYUNIONNAME; |
2560 | |
2561 | DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST]; |
2562 | DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST]; |
2563 | DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST]; |
2564 | DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST]; |
2565 | |
2566 | DWORD Padding2[2]; |
2567 | |
2568 | } ARM_CONTEXT; |
2569 | |
2570 | // On ARM this mask is or'ed with the address of code to get an instruction pointer |
2571 | #ifndef THUMB_CODE |
2572 | #define THUMB_CODE 1 |
2573 | #endif |
2574 | |
2575 | ///ARM64 Context |
2576 | #define ARM64_MAX_BREAKPOINTS 8 |
2577 | #define ARM64_MAX_WATCHPOINTS 2 |
2578 | typedef struct { |
2579 | |
2580 | DWORD ContextFlags; |
2581 | DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel |
2582 | union { |
2583 | struct { |
2584 | DWORD64 X0; |
2585 | DWORD64 X1; |
2586 | DWORD64 X2; |
2587 | DWORD64 X3; |
2588 | DWORD64 X4; |
2589 | DWORD64 X5; |
2590 | DWORD64 X6; |
2591 | DWORD64 X7; |
2592 | DWORD64 X8; |
2593 | DWORD64 X9; |
2594 | DWORD64 X10; |
2595 | DWORD64 X11; |
2596 | DWORD64 X12; |
2597 | DWORD64 X13; |
2598 | DWORD64 X14; |
2599 | DWORD64 X15; |
2600 | DWORD64 X16; |
2601 | DWORD64 X17; |
2602 | DWORD64 X18; |
2603 | DWORD64 X19; |
2604 | DWORD64 X20; |
2605 | DWORD64 X21; |
2606 | DWORD64 X22; |
2607 | DWORD64 X23; |
2608 | DWORD64 X24; |
2609 | DWORD64 X25; |
2610 | DWORD64 X26; |
2611 | DWORD64 X27; |
2612 | DWORD64 X28; |
2613 | }; |
2614 | |
2615 | DWORD64 X[29]; |
2616 | }; |
2617 | |
2618 | DWORD64 Fp; |
2619 | DWORD64 Lr; |
2620 | DWORD64 Sp; |
2621 | DWORD64 Pc; |
2622 | |
2623 | |
2624 | M128A_XPLAT V[32]; |
2625 | DWORD Fpcr; |
2626 | DWORD Fpsr; |
2627 | |
2628 | DWORD Bcr[ARM64_MAX_BREAKPOINTS]; |
2629 | DWORD64 Bvr[ARM64_MAX_BREAKPOINTS]; |
2630 | DWORD Wcr[ARM64_MAX_WATCHPOINTS]; |
2631 | DWORD64 Wvr[ARM64_MAX_WATCHPOINTS]; |
2632 | |
2633 | } ARM64_CONTEXT; |
2634 | |
2635 | typedef struct _CROSS_PLATFORM_CONTEXT { |
2636 | |
2637 | _CROSS_PLATFORM_CONTEXT() {} |
2638 | |
2639 | union { |
2640 | X86_CONTEXT X86Context; |
2641 | AMD64_CONTEXT Amd64Context; |
2642 | ARM_CONTEXT ArmContext; |
2643 | ARM64_CONTEXT Arm64Context; |
2644 | }; |
2645 | |
2646 | } CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT; |
2647 | |
2648 | |
2649 | |
2650 | WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true); |
2651 | WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE); |
2652 | HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount); |
2653 | WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE); |
2654 | |
2655 | /* This cache is used to read data from the target process if the reads are known |
2656 | * to be sequential. |
2657 | */ |
2658 | class LinearReadCache |
2659 | { |
2660 | public: |
2661 | LinearReadCache(ULONG pageSize = 0x10000); |
2662 | ~LinearReadCache(); |
2663 | |
2664 | /* Reads an address out of the target process, caching the page of memory read. |
2665 | * Params: |
2666 | * addr - The address to read out of the target process. |
2667 | * t - A pointer to the data to stuff it in. We will read sizeof(T) data |
2668 | * from the process and write it into the location t points to. This |
2669 | * parameter must be non-null. |
2670 | * Returns: |
2671 | * True if the read succeeded. False if it did not, usually as a result |
2672 | * of the memory simply not being present in the target process. |
2673 | * Note: |
2674 | * The state of *t is undefined if this function returns false. We may |
2675 | * have written partial data to it if we return false, so you must |
2676 | * absolutely NOT use it if Read returns false. |
2677 | */ |
2678 | template <class T> |
2679 | bool Read(TADDR addr, T *t, bool update = true) |
2680 | { |
2681 | _ASSERTE(t); |
2682 | |
2683 | // Unfortunately the ctor can fail the alloc for the byte array. In this case |
2684 | // we'll just fall back to non-cached reads. |
2685 | if (mPage == NULL) |
2686 | return MisalignedRead(addr, t); |
2687 | |
2688 | // Is addr on the current page? If not read the page of memory addr is on. |
2689 | // If this fails, we will fall back to a raw read out of the process (which |
2690 | // is what MisalignedRead does). |
2691 | if ((addr < mCurrPageStart) || (addr - mCurrPageStart > mCurrPageSize)) |
2692 | if (!update || !MoveToPage(addr)) |
2693 | return MisalignedRead(addr, t); |
2694 | |
2695 | // If MoveToPage succeeds, we MUST be on the right page. |
2696 | _ASSERTE(addr >= mCurrPageStart); |
2697 | |
2698 | // However, the amount of data requested may fall off of the page. In that case, |
2699 | // fall back to MisalignedRead. |
2700 | TADDR offset = addr - mCurrPageStart; |
2701 | if (offset + sizeof(T) > mCurrPageSize) |
2702 | return MisalignedRead(addr, t); |
2703 | |
2704 | // If we reach here we know we are on the right page of memory in the cache, and |
2705 | // that the read won't fall off of the end of the page. |
2706 | #ifdef _DEBUG |
2707 | mReads++; |
2708 | #endif |
2709 | |
2710 | *t = *reinterpret_cast<T*>(mPage+offset); |
2711 | return true; |
2712 | } |
2713 | |
2714 | void EnsureRangeInCache(TADDR start, unsigned int size) |
2715 | { |
2716 | if (mCurrPageStart == start) |
2717 | { |
2718 | if (size <= mCurrPageSize) |
2719 | return; |
2720 | |
2721 | // Total bytes to read, don't overflow buffer. |
2722 | unsigned int total = size + mCurrPageSize; |
2723 | if (total + mCurrPageSize > mPageSize) |
2724 | total = mPageSize-mCurrPageSize; |
2725 | |
2726 | // Read into the middle of the buffer, update current page size. |
2727 | ULONG read = 0; |
2728 | HRESULT hr = g_ExtData->ReadVirtual(mCurrPageStart+mCurrPageSize, mPage+mCurrPageSize, total, &read); |
2729 | mCurrPageSize += read; |
2730 | |
2731 | if (hr != S_OK) |
2732 | { |
2733 | mCurrPageStart = 0; |
2734 | mCurrPageSize = 0; |
2735 | } |
2736 | } |
2737 | else |
2738 | { |
2739 | MoveToPage(start, size); |
2740 | } |
2741 | } |
2742 | |
2743 | void ClearStats() |
2744 | { |
2745 | #ifdef _DEBUG |
2746 | mMisses = 0; |
2747 | mReads = 0; |
2748 | mMisaligned = 0; |
2749 | #endif |
2750 | } |
2751 | |
2752 | void PrintStats(const char *func) |
2753 | { |
2754 | #ifdef _DEBUG |
2755 | char buffer[1024]; |
2756 | sprintf_s(buffer, _countof(buffer), "Cache (%s): %d reads (%2.1f%% hits), %d misses (%2.1f%%), %d misaligned (%2.1f%%).\n" , |
2757 | func, mReads, 100*(mReads-mMisses)/(float)(mReads+mMisaligned), mMisses, |
2758 | 100*mMisses/(float)(mReads+mMisaligned), mMisaligned, 100*mMisaligned/(float)(mReads+mMisaligned)); |
2759 | OutputDebugStringA(buffer); |
2760 | #endif |
2761 | } |
2762 | |
2763 | private: |
2764 | /* Sets the cache to the page specified by addr, or false if we could not move to |
2765 | * that page. |
2766 | */ |
2767 | bool MoveToPage(TADDR addr, unsigned int size = 0x18); |
2768 | |
2769 | /* Attempts to read from the target process if the data is possibly hanging off |
2770 | * the end of a page. |
2771 | */ |
2772 | template<class T> |
2773 | inline bool MisalignedRead(TADDR addr, T *t) |
2774 | { |
2775 | ULONG fetched = 0; |
2776 | HRESULT hr = g_ExtData->ReadVirtual(addr, (BYTE*)t, sizeof(T), &fetched); |
2777 | |
2778 | if (FAILED(hr) || fetched != sizeof(T)) |
2779 | return false; |
2780 | |
2781 | mMisaligned++; |
2782 | return true; |
2783 | } |
2784 | |
2785 | private: |
2786 | TADDR mCurrPageStart; |
2787 | ULONG mPageSize, mCurrPageSize; |
2788 | BYTE *mPage; |
2789 | |
2790 | int mMisses, mReads, mMisaligned; |
2791 | }; |
2792 | |
2793 | |
2794 | /////////////////////////////////////////////////////////////////////////////////////////// |
2795 | // |
2796 | // Methods for creating a database out of the gc heap and it's roots in xml format or CLRProfiler format |
2797 | // |
2798 | |
2799 | #include <unordered_map> |
2800 | #include <unordered_set> |
2801 | #include <list> |
2802 | |
2803 | class TypeTree; |
2804 | enum { FORMAT_XML=0, FORMAT_CLRPROFILER=1 }; |
2805 | enum { TYPE_START=0,TYPE_TYPES=1,TYPE_ROOTS=2,TYPE_OBJECTS=3,TYPE_HIGHEST=4}; |
2806 | class HeapTraverser |
2807 | { |
2808 | private: |
2809 | TypeTree *m_pTypeTree; |
2810 | size_t m_curNID; |
2811 | FILE *m_file; |
2812 | int m_format; // from the enum above |
2813 | size_t m_objVisited; // for UI updates |
2814 | bool m_verify; |
2815 | LinearReadCache mCache; |
2816 | |
2817 | std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap; |
2818 | |
2819 | public: |
2820 | HeapTraverser(bool verify); |
2821 | ~HeapTraverser(); |
2822 | |
2823 | FILE *getFile() { return m_file; } |
2824 | |
2825 | BOOL Initialize(); |
2826 | BOOL CreateReport (FILE *fp, int format); |
2827 | |
2828 | private: |
2829 | // First all types are added to a tree |
2830 | void insert(size_t mTable); |
2831 | size_t getID(size_t mTable); |
2832 | |
2833 | // Functions for writing to the output file. |
2834 | void PrintType(size_t ID,LPCWSTR name); |
2835 | |
2836 | void PrintObjectHead(size_t objAddr,size_t typeID,size_t Size); |
2837 | void PrintObjectMember(size_t memberValue, bool dependentHandle); |
2838 | void PrintLoaderAllocator(size_t memberValue); |
2839 | void PrintObjectTail(); |
2840 | |
2841 | void PrintRootHead(); |
2842 | void PrintRoot(LPCWSTR kind,size_t Value); |
2843 | void PrintRootTail(); |
2844 | |
2845 | void PrintSection(int Type,BOOL bOpening); |
2846 | |
2847 | // Root and object member helper functions |
2848 | void FindGCRootOnStacks(); |
2849 | void PrintRefs(size_t obj, size_t methodTable, size_t size); |
2850 | |
2851 | // Callback functions used during traversals |
2852 | static void GatherTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token); |
2853 | static void PrintHeap(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable, LPVOID token); |
2854 | static void PrintOutTree(size_t methodTable, size_t ID, LPVOID token); |
2855 | void TraceHandles(); |
2856 | }; |
2857 | |
2858 | |
2859 | class GCRootImpl |
2860 | { |
2861 | private: |
2862 | struct MTInfo |
2863 | { |
2864 | TADDR MethodTable; |
2865 | WCHAR *TypeName; |
2866 | |
2867 | TADDR *Buffer; |
2868 | CGCDesc *GCDesc; |
2869 | |
2870 | TADDR LoaderAllocatorObjectHandle; |
2871 | bool ArrayOfVC; |
2872 | bool ContainsPointers; |
2873 | bool Collectible; |
2874 | size_t BaseSize; |
2875 | size_t ComponentSize; |
2876 | |
2877 | const WCHAR *GetTypeName() |
2878 | { |
2879 | if (!TypeName) |
2880 | TypeName = CreateMethodTableName(MethodTable); |
2881 | |
2882 | if (!TypeName) |
2883 | return W("<error>" ); |
2884 | |
2885 | return TypeName; |
2886 | } |
2887 | |
2888 | MTInfo() |
2889 | : MethodTable(0), TypeName(0), Buffer(0), GCDesc(0), |
2890 | ArrayOfVC(false), ContainsPointers(false), Collectible(false), BaseSize(0), ComponentSize(0) |
2891 | { |
2892 | } |
2893 | |
2894 | ~MTInfo() |
2895 | { |
2896 | if (Buffer) |
2897 | delete [] Buffer; |
2898 | |
2899 | if (TypeName) |
2900 | delete [] TypeName; |
2901 | } |
2902 | }; |
2903 | |
2904 | struct RootNode |
2905 | { |
2906 | RootNode *Next; |
2907 | RootNode *Prev; |
2908 | TADDR Object; |
2909 | MTInfo *MTInfo; |
2910 | |
2911 | bool FilledRefs; |
2912 | bool FromDependentHandle; |
2913 | RootNode *GCRefs; |
2914 | |
2915 | |
2916 | const WCHAR *GetTypeName() |
2917 | { |
2918 | if (!MTInfo) |
2919 | return W("<unknown>" ); |
2920 | |
2921 | return MTInfo->GetTypeName(); |
2922 | } |
2923 | |
2924 | RootNode() |
2925 | : Next(0), Prev(0) |
2926 | { |
2927 | Clear(); |
2928 | } |
2929 | |
2930 | void Clear() |
2931 | { |
2932 | if (Next && Next->Prev == this) |
2933 | Next->Prev = NULL; |
2934 | |
2935 | if (Prev && Prev->Next == this) |
2936 | Prev->Next = NULL; |
2937 | |
2938 | Next = 0; |
2939 | Prev = 0; |
2940 | Object = 0; |
2941 | MTInfo = 0; |
2942 | FilledRefs = false; |
2943 | FromDependentHandle = false; |
2944 | GCRefs = 0; |
2945 | } |
2946 | |
2947 | void Remove(RootNode *&list) |
2948 | { |
2949 | RootNode *curr_next = Next; |
2950 | |
2951 | // We've already considered this object, remove it. |
2952 | if (Prev == NULL) |
2953 | { |
2954 | // If we've filtered out the head, update it. |
2955 | list = curr_next; |
2956 | |
2957 | if (curr_next) |
2958 | curr_next->Prev = NULL; |
2959 | } |
2960 | else |
2961 | { |
2962 | // Otherwise remove the current item from the list |
2963 | Prev->Next = curr_next; |
2964 | |
2965 | if (curr_next) |
2966 | curr_next->Prev = Prev; |
2967 | } |
2968 | } |
2969 | }; |
2970 | |
2971 | public: |
2972 | static void GetDependentHandleMap(std::unordered_map<TADDR, std::list<TADDR>> &map); |
2973 | |
2974 | public: |
2975 | // Finds all objects which root "target" and prints the path from the root |
2976 | // to "target". If all is true, all possible paths to the object are printed. |
2977 | // If all is false, only completely unique paths will be printed. |
2978 | int PrintRootsForObject(TADDR obj, bool all, bool noStacks); |
2979 | |
2980 | // Finds a path from root to target if it exists and prints it out. Returns |
2981 | // true if it found a path, false otherwise. |
2982 | bool PrintPathToObject(TADDR root, TADDR target); |
2983 | |
2984 | // Calculates the size of the closure of objects kept alive by root. |
2985 | size_t ObjSize(TADDR root); |
2986 | |
2987 | // Walks each root, printing out the total amount of memory held alive by it. |
2988 | void ObjSize(); |
2989 | |
2990 | // Returns the set of all live objects in the process. |
2991 | const std::unordered_set<TADDR> &GetLiveObjects(bool excludeFQ = false); |
2992 | |
2993 | // See !FindRoots. |
2994 | int FindRoots(int gen, TADDR target); |
2995 | |
2996 | private: |
2997 | // typedefs |
2998 | typedef void (*ReportCallback)(TADDR root, RootNode *path, bool ); |
2999 | |
3000 | // Book keeping and debug. |
3001 | void ClearAll(); |
3002 | void ClearNodes(); |
3003 | void ClearSizeData(); |
3004 | |
3005 | // Printing roots |
3006 | int PrintRootsOnHandleTable(int gen = -1); |
3007 | int PrintRootsOnAllThreads(); |
3008 | int PrintRootsOnThread(DWORD osThreadId); |
3009 | int PrintRootsOnFQ(bool notReadyForFinalization = false); |
3010 | int PrintRootsInOlderGen(); |
3011 | int PrintRootsInRange(LinearReadCache &cache, TADDR start, TADDR stop, ReportCallback func, bool ); |
3012 | |
3013 | // Calculate gc root |
3014 | RootNode *FilterRoots(RootNode *&list); |
3015 | RootNode *FindPathToTarget(TADDR root); |
3016 | RootNode *GetGCRefs(RootNode *path, RootNode *node); |
3017 | |
3018 | void InitDependentHandleMap(); |
3019 | |
3020 | //Reporting: |
3021 | void ReportOneHandlePath(const SOSHandleData &handle, RootNode *node, bool ); |
3022 | void ReportOnePath(DWORD thread, const SOSStackRefData &stackRef, RootNode *node, bool printThread, bool printFrame); |
3023 | static void ReportOneFQEntry(TADDR root, RootNode *path, bool ); |
3024 | static void ReportOlderGenEntry(TADDR root, RootNode *path, bool ); |
3025 | void ReportSizeInfo(const SOSHandleData &handle, TADDR obj); |
3026 | void ReportSizeInfo(DWORD thread, const SOSStackRefData &ref, TADDR obj); |
3027 | |
3028 | // Data reads: |
3029 | TADDR ReadPointer(TADDR location); |
3030 | TADDR ReadPointerCached(TADDR location); |
3031 | |
3032 | // Object/MT data: |
3033 | MTInfo *GetMTInfo(TADDR mt); |
3034 | DWORD GetComponents(TADDR obj, TADDR mt); |
3035 | size_t GetSizeOfObject(TADDR obj, MTInfo *info); |
3036 | |
3037 | // RootNode management: |
3038 | RootNode *NewNode(TADDR obj = 0, MTInfo *mtinfo = 0, bool fromDependent = false); |
3039 | void DeleteNode(RootNode *node); |
3040 | |
3041 | private: |
3042 | |
3043 | bool mAll, // Print all roots or just unique roots? |
3044 | mSize; // Print rooting information or total size info? |
3045 | |
3046 | std::list<RootNode*> mCleanupList; // A list of RootNode's we've newed up. This is only used to delete all of them later. |
3047 | std::list<RootNode*> mRootNewList; // A list of unused RootNodes that are free to use instead of having to "new" up more. |
3048 | |
3049 | std::unordered_map<TADDR, MTInfo*> mMTs; // The MethodTable cache which maps from MT -> MethodTable data (size, gcdesc, string typename) |
3050 | std::unordered_map<TADDR, RootNode*> mTargets; // The objects that we are searching for. |
3051 | std::unordered_set<TADDR> mConsidered; // A hashtable of objects we've already visited. |
3052 | std::unordered_map<TADDR, size_t> mSizes; // A mapping from object address to total size of data the object roots. |
3053 | |
3054 | std::unordered_map<TADDR, std::list<TADDR>> mDependentHandleMap; |
3055 | |
3056 | LinearReadCache mCache; // A linear cache which stops us from having to read from the target process more than 1-2 times per object. |
3057 | }; |
3058 | |
3059 | // |
3060 | // Helper class used for type-safe bitflags |
3061 | // T - the enum type specifying the individual bit flags |
3062 | // U - the underlying/storage type |
3063 | // Requirement: |
3064 | // sizeof(T) <= sizeof(U) |
3065 | // |
3066 | template <typename T, typename U> |
3067 | struct Flags |
3068 | { |
3069 | typedef T UnderlyingType; |
3070 | typedef U BitFlagEnumType; |
3071 | |
3072 | static_assert_no_msg(sizeof(BitFlagEnumType) <= sizeof(UnderlyingType)); |
3073 | |
3074 | Flags(UnderlyingType v) |
3075 | : m_val(v) |
3076 | { } |
3077 | |
3078 | Flags(BitFlagEnumType v) |
3079 | : m_val(v) |
3080 | { } |
3081 | |
3082 | Flags(const Flags& other) |
3083 | : m_val(other.m_val) |
3084 | { } |
3085 | |
3086 | Flags& operator = (const Flags& other) |
3087 | { m_val = other.m_val; return *this; } |
3088 | |
3089 | Flags operator | (Flags other) const |
3090 | { return Flags<T, U>(m_val | other._val); } |
3091 | |
3092 | void operator |= (Flags other) |
3093 | { m_val |= other.m_val; } |
3094 | |
3095 | Flags operator & (Flags other) const |
3096 | { return Flags<T, U>(m_val & other.m_val); } |
3097 | |
3098 | void operator &= (Flags other) |
3099 | { m_val &= other.m_val; } |
3100 | |
3101 | Flags operator ^ (Flags other) const |
3102 | { return Flags<T, U>(m_val ^ other._val); } |
3103 | |
3104 | void operator ^= (Flags other) |
3105 | { m_val ^= other.m_val; } |
3106 | |
3107 | BOOL operator == (Flags other) const |
3108 | { return m_val == other.m_val; } |
3109 | |
3110 | BOOL operator != (Flags other) const |
3111 | { return m_val != other.m_val; } |
3112 | |
3113 | |
3114 | private: |
3115 | UnderlyingType m_val; |
3116 | }; |
3117 | |
3118 | #ifndef FEATURE_PAL |
3119 | |
3120 | // Flags defining activation policy for COM objects |
3121 | enum CIOptionsBits |
3122 | { |
3123 | cciLatestFx = 0x01, // look in the most recent .NETFx installation |
3124 | cciMatchFx = 0x02, // NYI: Look in the .NETFx installation matching the debuggee's runtime |
3125 | cciAnyFx = 0x04, // look in any .NETFx installation |
3126 | cciFxMask = 0x0f, |
3127 | cciDbiColocated = 0x10, // NYI: Look next to the already loaded DBI module |
3128 | cciDacColocated = 0x20, // Look next to the already loaded DAC module |
3129 | cciDbgPath = 0x40, // Look in all folders in the debuggers symbols and binary path |
3130 | }; |
3131 | |
3132 | typedef Flags<DWORD, CIOptionsBits> CIOptions; |
3133 | |
3134 | /**********************************************************************\ |
3135 | * Routine Description: * |
3136 | * * |
3137 | * CreateInstanceCustom() provides a way to activate a COM object w/o * |
3138 | * triggering the FeatureOnDemand dialog. In order to do this we * |
3139 | * must avoid using the CoCreateInstance() API, which, on a machine * |
3140 | * with v4+ installed and w/o v2, would trigger this. * |
3141 | * CreateInstanceCustom() activates the requested COM object according * |
3142 | * to the specified passed in CIOptions, in the following order * |
3143 | * (skipping the steps not enabled in the CIOptions flags passed in): * |
3144 | * 1. Attempt to activate the COM object using a framework install: * |
3145 | * a. If the debugger machine has a V4+ shell shim use the shim * |
3146 | * to activate the object * |
3147 | * b. Otherwise simply call CoCreateInstance * |
3148 | * 2. If unsuccessful attempt to activate looking for the dllName in * |
3149 | * the same folder as the DAC was loaded from * |
3150 | * 3. If unsuccessful attempt to activate the COM object looking in * |
3151 | * every path specified in the debugger's .exepath and .sympath * |
3152 | \**********************************************************************/ |
3153 | HRESULT CreateInstanceCustom( |
3154 | REFCLSID clsid, |
3155 | REFIID iid, |
3156 | LPCWSTR dllName, |
3157 | CIOptions cciOptions, |
3158 | void** ppItf); |
3159 | |
3160 | |
3161 | //------------------------------------------------------------------------ |
3162 | // A typesafe version of GetProcAddress |
3163 | //------------------------------------------------------------------------ |
3164 | template <typename T> |
3165 | BOOL |
3166 | GetProcAddressT( |
3167 | ___in PCSTR FunctionName, |
3168 | __in_opt PCWSTR DllName, |
3169 | __inout T* OutFunctionPointer, |
3170 | __inout HMODULE* InOutDllHandle |
3171 | ) |
3172 | { |
3173 | _ASSERTE(InOutDllHandle != NULL); |
3174 | _ASSERTE(OutFunctionPointer != NULL); |
3175 | |
3176 | T FunctionPointer = NULL; |
3177 | HMODULE DllHandle = *InOutDllHandle; |
3178 | if (DllHandle == NULL) |
3179 | { |
3180 | DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); |
3181 | if (DllHandle != NULL) |
3182 | *InOutDllHandle = DllHandle; |
3183 | } |
3184 | if (DllHandle != NULL) |
3185 | { |
3186 | FunctionPointer = (T) GetProcAddress(DllHandle, FunctionName); |
3187 | } |
3188 | *OutFunctionPointer = FunctionPointer; |
3189 | return FunctionPointer != NULL; |
3190 | } |
3191 | |
3192 | |
3193 | #endif // FEATURE_PAL |
3194 | |
3195 | struct ImageInfo |
3196 | { |
3197 | ULONG64 modBase; |
3198 | }; |
3199 | |
3200 | // Helper class used in ClrStackFromPublicInterface() to keep track of explicit EE Frames |
3201 | // (i.e., "internal frames") on the stack. Call Init() with the appropriate |
3202 | // ICorDebugThread3, and this class will initialize itself with the set of internal |
3203 | // frames. You can then call PrintPrecedingInternalFrames during your stack walk to |
3204 | // have this class output any internal frames that "precede" (i.e., that are closer to |
3205 | // the leaf than) the specified ICorDebugFrame. |
3206 | class InternalFrameManager |
3207 | { |
3208 | private: |
3209 | // TODO: Verify constructor AND destructor is called for each array element |
3210 | // TODO: Comment about hard-coding 1000 |
3211 | ToRelease<ICorDebugInternalFrame2> m_rgpInternalFrame2[1000]; |
3212 | ULONG32 m_cInternalFramesActual; |
3213 | ULONG32 m_iInternalFrameCur; |
3214 | |
3215 | public: |
3216 | InternalFrameManager(); |
3217 | HRESULT Init(ICorDebugThread3 * pThread3); |
3218 | HRESULT PrintPrecedingInternalFrames(ICorDebugFrame * pFrame); |
3219 | |
3220 | private: |
3221 | HRESULT PrintCurrentInternalFrame(); |
3222 | }; |
3223 | |
3224 | #endif // __util_h__ |
3225 | |