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#pragma once
6
7#include "strike.h"
8#include "util.h"
9
10#ifndef SOS_Assert
11#ifdef _DEBUG
12#define SOS_Assert(x) do { if (!(x)) sos::Throw<sos::Exception>("SOS Assert Failure: %s\n", #x); } while(0)
13#else
14#define SOS_Assert(x) (void)0
15#endif
16#endif
17
18#ifdef throw
19#undef throw
20#endif
21
22#ifdef try
23#undef try
24#endif
25
26#ifdef catch
27#undef catch
28#endif
29
30class LinearReadCache;
31class CGCDesc;
32class CGCDescSeries;
33
34namespace sos
35{
36 class GCHeap;
37
38 /* The base SOS Exception. Note that most commands should not attempt to be
39 * resilient to exceptions thrown by most functions here. Instead a top level
40 * try/catch at the beginning of the command which prints out the exception's
41 * message should be sufficient.
42 * Note you should not throw these directly, instead use the sos::Throw function.
43 */
44 class Exception
45 {
46 public:
47 Exception(const char *format, va_list args)
48 {
49 vsprintf_s(mMsg, _countof(mMsg), format, args);
50
51 va_end(args);
52 }
53
54 inline virtual ~Exception() {}
55
56 // from std::exception
57 virtual const char *what() const
58 {
59 return mMsg;
60 }
61
62 const char *GetMesssage() const
63 {
64 return mMsg;
65 }
66
67 protected:
68 char mMsg[1024];
69 };
70
71 /* Thrown when we could not read data we expected out of the target process.
72 * This can be due to heap corruption, or it could just be an invalid pointer.
73 */
74 class DataRead : public Exception
75 {
76 public:
77 DataRead(const char *format, va_list args)
78 : Exception(format, args)
79 {
80 }
81 };
82
83 /* This is thrown when we detect heap corruption in the process.
84 */
85 class HeapCorruption : public Exception
86 {
87 public:
88 HeapCorruption(const char *format, va_list args)
89 : Exception(format, args)
90 {
91 }
92 };
93
94 // Internal helper method. Use SOS_Throw macros instead.
95 template <class T>
96 void Throw(const char *format, ...)
97 {
98 va_list args;
99 va_start(args, format);
100
101 throw T(format, args);
102 }
103
104 /* Checks to see if the user hit control-c. Throws an exception to escape SOS
105 * if so.
106 */
107 inline void CheckInterrupt()
108 {
109 if (g_ExtControl->GetInterrupt() == S_OK)
110 Throw<Exception>("User interrupt.");
111 }
112
113 /* ThinLock struct. Use Object::GetThinLock to fill the struct.
114 */
115 struct ThinLockInfo
116 {
117 int ThreadId;
118 TADDR ThreadPtr;
119 int Recursion;
120
121 ThinLockInfo()
122 : ThreadId(0), ThreadPtr(0), Recursion(0)
123 {
124 }
125 };
126
127 /* The MethodTable for an Object. The general pattern should be:
128 * MethodTable mt = someObject.GetMT();
129 */
130 class MethodTable
131 {
132 public:
133 /* Returns whether an object is from an AppDomain that has been unloaded.
134 * If so, we cannot validate the object's members.
135 * Params:
136 * mt - The address of the MethodTable to test for.
137 */
138 static bool IsZombie(TADDR mt);
139
140 /* Returns the method table for arrays.
141 */
142 inline static TADDR GetArrayMT()
143 {
144 return TO_TADDR(g_special_usefulGlobals.ArrayMethodTable);
145 }
146
147 /* Returns the method table for String objects.
148 */
149 inline static TADDR GetStringMT()
150 {
151 return TO_TADDR(g_special_usefulGlobals.StringMethodTable);
152 }
153
154 /* Returns the method table for Free objects.
155 */
156 inline static TADDR GetFreeMT()
157 {
158 return TO_TADDR(g_special_usefulGlobals.FreeMethodTable);
159 }
160
161 /* Returns true if the given method table is that of a Free object.
162 */
163 inline static bool IsFreeMT(TADDR mt)
164 {
165 return GetFreeMT() == mt;
166 }
167
168 /* Returns true if the given method table is that of an Array.
169 */
170 inline static bool IsArrayMT(TADDR mt)
171 {
172 return GetArrayMT() == mt;
173 }
174
175 /* Returns true if the given method table is that of a System.String object.
176 */
177 inline static bool IsStringMT(TADDR mt)
178 {
179 return GetStringMT() == mt;
180 }
181
182 inline static bool IsValid(TADDR mt)
183 {
184 DacpMethodTableData data;
185 return data.Request(g_sos, TO_CDADDR(mt)) == S_OK;
186 }
187
188 public:
189 MethodTable(TADDR mt)
190 : mMT(mt), mName(0)
191 {
192 }
193
194 MethodTable(const MethodTable &mt)
195 : mMT(mt.mMT), mName(mt.mName)
196 {
197 // Acquire the calculated mName field. Since we are making a copy, we will likely use
198 // the copy instead of the original.
199 mt.mName = NULL;
200 }
201
202 const MethodTable &operator=(const MethodTable &mt)
203 {
204 Clear();
205
206 // Acquire the calculated mName field. Since we are making a copy, we will likely use
207 // the copy instead of the original.
208 mMT = mt.mMT;
209 mName = mt.mName;
210 mt.mName = NULL;
211
212 return *this;
213 }
214
215 ~MethodTable()
216 {
217 Clear();
218 }
219
220 /* Returns the class name of this MethodTable. The pointer returned is
221 * valid through the lifetime of the MethodTable object and should not be
222 * freed.
223 */
224 const WCHAR *GetName() const;
225
226 private:
227 void Clear();
228
229 private:
230 TADDR mMT;
231 mutable WCHAR *mName;
232 };
233
234 /* This represents an object on the GC heap in the target process. This class
235 * represents a single object, and is immutable after construction. All
236 * information about this class is lazily evaluated, so it is entirely possible
237 * to get exceptions when calling any member function. If this is a concern,
238 * call validate before attempting to call any other method on this object.
239 */
240 class Object
241 {
242 public:
243 /* Attempts to determine if the target address points to a valid object.
244 * Note that this is a heuristic based check, so false positives could
245 * be possible.
246 * Params:
247 * address - The address of the object to inspect.
248 * verifyFields - Whether or not to validate that the fields the object
249 * points to are also valid. (If the object contains a
250 * corrupted pointer, passing true to this parameter will
251 * cause IsValid to return false.) In general passing
252 * true will make IsValid return less false positives.
253 */
254 static bool IsValid(TADDR address, bool verifyFields=false);
255
256 static int GetStringDataOffset()
257 {
258#ifndef _TARGET_WIN64_
259 return 8;
260#else
261 return 0xc;
262#endif
263 }
264
265 public:
266 /* Constructor. Use Object(TADDR, TADDR) instead if you know the method table.
267 * Parameters:
268 * addr - an address to an object on the managed heap
269 * Throws:
270 * Exception - if addr is misaligned.
271 */
272 Object(TADDR addr);
273
274 /* Constructor. Use this constructor if you already know the method table for
275 * the object in question. This will save a read if the method table is needed.
276 * Parameters:
277 * addr - an address to an object on the managed heap
278 * Throws:
279 * Exception - if addr is misaligned.
280 */
281 Object(TADDR addr, TADDR mt);
282
283 Object(const Object &rhs);
284
285 inline ~Object()
286 {
287 if (mMTData)
288 delete mMTData;
289
290 if (mTypeName)
291 delete mTypeName;
292 }
293
294 const Object &operator=(TADDR addr);
295
296 // Comparison operators. These compare the underlying address of
297 // the object to the parameter.
298 inline bool operator<=(TADDR addr) { return mAddress <= addr; }
299 inline bool operator>=(TADDR addr) { return mAddress >= addr; }
300 inline bool operator<(TADDR addr) { return mAddress < addr; }
301 inline bool operator>(TADDR addr) { return mAddress > addr; }
302 inline bool operator==(TADDR addr) { return mAddress == addr; }
303
304 /* Returns the target address of the object this represents.
305 */
306 inline TADDR GetAddress() const
307 {
308 return mAddress;
309 }
310
311 /* Returns the target address of the object this represents.
312 */
313 inline operator TADDR() const
314 {
315 return GetAddress();
316 }
317
318 /* Returns the object header for this object.
319 * Throws:
320 * DataRead - we failed to read the object header.
321 */
322 ULONG GetHeader() const;
323
324 /* Gets the header for the current object, does not throw any exception.
325 * Params:
326 * outHeader - filled with the header if this function was successful.
327 * Returns:
328 * True if we successfully read the object header, false otherwise.
329 */
330 bool TryGetHeader(ULONG &outHeader) const;
331
332 /* Returns the method table of the object this represents.
333 * Throws:
334 * DataRead - If we failed to read the method table from the address.
335 * This is usually indicative of heap corruption.
336 * HeapCorruption - If we successfully read the target method table
337 * but it is invalid. (We do not do a very deep
338 * verification here.)
339 */
340 TADDR GetMT() const;
341
342 /* Returns the component method table of the object. For example, if
343 * this object is an array, the method table will be the general array
344 * MT. Calling this function tells you what type of objects can be
345 * placed in the array.
346 * Throws:
347 * DataRead - If we failed to read the method table from the address.
348 * This is usually indicative of heap corruption.
349 * HeapCorruption - If we successfully read the target method table
350 * but it is invalid. (We do not do a very deep
351 * verification here.)
352 */
353 TADDR GetComponentMT() const;
354
355 /* Returns the size of the object this represents. Note that this size
356 * may not be pointer aligned.
357 * Throws:
358 * DataRead - If we failed to read the method table data (which contains
359 * the size of the object).
360 */
361 size_t GetSize() const;
362
363 /* Returns true if this object contains pointers to other objects.
364 * Throws:
365 * DataRead - if we failed to read out of the object's method table.
366 */
367 bool HasPointers() const;
368
369 /* Gets the thinlock information for this object.
370 * Params:
371 * out - The ThinLockInfo to be filled.
372 * Returns:
373 * True if the object has a thinlock, false otherwise. If this function
374 * returns false, then out will be untouched.
375 * Throws:
376 * DataRead - If we could not read the object header from the object.
377 */
378 bool GetThinLock(ThinLockInfo &out) const;
379
380 /* Returns true if this object is a Free object (meaning it points to free
381 * space in the GC heap.
382 * Throws:
383 * The same as GetMT().
384 */
385 inline bool IsFree() const
386 {
387 return GetMT() == MethodTable::GetFreeMT();
388 }
389
390 /* Returns true if this object is a string.
391 * Throws:
392 * The same as GetMT().
393 */
394 inline bool IsString() const
395 {
396 return GetMT() == MethodTable::GetStringMT();
397 }
398
399 /* Returns the length of the String, if this is a string object. This
400 * function assumes that you have called IsString first to ensure that
401 * the object is indeed a string.
402 * Throws:
403 * DataRead if we could not read the contents of the object.
404 */
405 size_t GetStringLength() const;
406
407 /* Fills the given buffer with the contents of the String. This
408 * function assumes you have called IsString first to ensure that this
409 * object is actually a System.String. This function does not throw,
410 * but the results are undefined if this object is not a string.
411 * Params:
412 * buffer - The buffer to fill with the string contents.
413 * size - The total size of the buffer.
414 * Returns:
415 * True if the string data was successfully requested and placed in
416 * buffer, false otherwise.
417 */
418 bool GetStringData(__out_ecount(size) WCHAR *buffer, size_t size) const;
419
420 /* Returns the name of the type of this object. E.g. System.String.
421 * Throws:
422 * DataRead if we could not read the contents of the object.
423 * Returns:
424 * A string containing the type of the object.
425 */
426 const WCHAR *GetTypeName() const;
427
428 private:
429 void FillMTData() const;
430 void CalculateSizeAndPointers() const;
431 static bool VerifyMemberFields(TADDR pMT, TADDR obj);
432 static bool VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields);
433
434 protected:
435 // Conceptually, this class is never modified after you pass in the the object address.
436 // That is, there can't be anything the user does to point this object to a different
437 // object after construction. Since we lazy evaluate *everything*, we must be able to
438 // modify these variables. Hence they are mutable.
439 TADDR mAddress;
440 mutable TADDR mMT;
441 mutable size_t mSize;
442 mutable bool mPointers;
443 mutable DacpMethodTableData *mMTData;
444 mutable WCHAR *mTypeName;
445 };
446
447 /* Enumerates all the GC references (objects) contained in an object. This uses the GCDesc
448 * map exactly as the GC does.
449 */
450 class RefIterator
451 {
452 public:
453 RefIterator(TADDR obj, LinearReadCache *cache = NULL);
454 RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache = NULL);
455 ~RefIterator();
456
457 /* Moves to the next reference in the object.
458 */
459 const RefIterator &operator++();
460
461 /* Returns the address of the current reference.
462 */
463 TADDR operator*() const;
464
465 /* Gets the offset into the object where the current reference comes from.
466 */
467 TADDR GetOffset() const;
468
469 /* Returns true if there are more objects in the iteration, false otherwise.
470 * Used as:
471 * if (itr)
472 * ...
473 */
474 inline operator void *() const
475 {
476 return (void*)!mDone;
477 }
478
479 bool IsLoaderAllocator() const
480 {
481 return mLoaderAllocatorObjectHandle == mCurr;
482 }
483
484 private:
485 void Init();
486 inline TADDR ReadPointer(TADDR addr) const
487 {
488 if (mCache)
489 {
490 if (!mCache->Read(addr, &addr, false))
491 Throw<DataRead>("Could not read address %p.", addr);
492 }
493 else
494 {
495 MOVE(addr, addr);
496 }
497
498 return addr;
499 }
500
501 private:
502 LinearReadCache *mCache;
503 CGCDesc *mGCDesc;
504 bool mArrayOfVC, mDone;
505
506 TADDR *mBuffer;
507 CGCDescSeries *mCurrSeries;
508
509 TADDR mLoaderAllocatorObjectHandle;
510
511 int i, mCount;
512
513 TADDR mCurr, mStop, mObject;
514 size_t mObjSize;
515 };
516
517
518 /* The Iterator used to walk the managed objects on the GC heap.
519 * The general usage pattern for this class is:
520 * for (ObjectIterator itr = gcheap.WalkHeap(); itr; ++itr)
521 * itr->SomeObjectMethod();
522 */
523 class ObjectIterator
524 {
525 friend class GCHeap;
526 public:
527
528 /* Returns the next object in the GCHeap. Note that you must ensure
529 * that there are more objects to walk before calling this function by
530 * checking "if (iterator)". If this function throws an exception,
531 * the the iterator is invalid, and should no longer be used to walk
532 * the heap. This should generally only happen if we cannot read the
533 * MethodTable of the object to move to the next object.
534 * Throws:
535 * DataRead
536 */
537 const ObjectIterator &operator++();
538
539 /* Dereference operator. This allows you to take a reference to the
540 * current object. Note the lifetime of this reference is valid for
541 * either the lifetime of the iterator or until you call operator++,
542 * whichever is shorter. For example.
543 * void Foo(const Object &param);
544 * void Bar(const ObjectIterator &itr)
545 * {
546 * Foo(*itr);
547 * }
548 */
549 const Object &operator*() const;
550
551 /* Returns a pointer to the current Object to call members on it.
552 * The usage pattern for the iterator is to simply use operator->
553 * to call methods on the Object it points to without taking a
554 * direct reference to the underlying Object if at all possible.
555 */
556 const Object *operator->() const;
557
558 /* Returns false when the iterator has reached the end of the managed
559 * heap.
560 */
561 inline operator void *() const
562 {
563 return (void*)(SIZE_T)(mCurrHeap == mNumHeaps ? 0 : 1);
564 }
565
566 /* Do not use.
567 * TODO: Replace this functionality with int Object::GetGeneration().
568 */
569 bool IsCurrObjectOnLOH() const
570 {
571 SOS_Assert(*this);
572 return bLarge;
573 }
574
575 /* Verifies the current object. Returns true if the current object is valid.
576 * Returns false and fills 'buffer' with the reason the object is corrupted.
577 * This is a deeper validation than Object::IsValid as it checks the card
578 * table entires for the object in addition to the rest of the references.
579 * This function does not throw exceptions.
580 * Params:
581 * buffer - out buffer that is filled if and only if this function returns
582 * false.
583 * size - the total size of the buffer
584 * Returns:
585 * True if the object is valid, false otherwise.
586 */
587 bool Verify(__out_ecount(size) char *buffer, size_t size) const;
588
589 /* The same as Verify(char*, size_t), except it does not write out the failure
590 * reason to a provided buffer.
591 * See:
592 * ObjectIterator::Verify(char *, size_t)
593 */
594 bool Verify() const;
595
596 /* Attempts to move to the next object (similar to ObjectIterator++), but
597 * attempts to recover from any heap corruption by skipping to the next
598 * segment. If Verify returns false, meaning it detected heap corruption
599 * at the current object, you can use MoveToNextObjectCarefully instead of
600 * ObjectIterator++ to attempt to keep reading from the heap. If possible,
601 * this function attempts to move to the next object in the same segment,
602 * but if that's not possible then it skips to the next segment and
603 * continues from there.
604 * Note:
605 * This function can throw, and if it does then the iterator is no longer
606 * in a valid state. No further attempts to move to the next object will
607 * be possible.
608 * Throws:
609 * DataRead - if the heap is corrupted and it's not possible to continue
610 * walking the heap
611 */
612 void MoveToNextObjectCarefully();
613
614 private:
615 ObjectIterator(const DacpGcHeapDetails *heap, int numHeaps, TADDR start, TADDR stop);
616
617 bool VerifyObjectMembers(__out_ecount(size) char *buffer, size_t size) const;
618 void BuildError(__out_ecount(count) char *out, size_t count, const char *format, ...) const;
619
620 void AssertSanity() const;
621 bool NextSegment();
622 bool CheckSegmentRange();
623 void MoveToNextObject();
624
625 private:
626 DacpHeapSegmentData mSegment;
627 bool bLarge;
628 Object mCurrObj;
629 TADDR mLastObj, mStart, mEnd, mSegmentEnd;
630 AllocInfo mAllocInfo;
631 const DacpGcHeapDetails *mHeaps;
632 int mNumHeaps;
633 int mCurrHeap;
634 };
635
636 /* Reprensents an entry in the sync block table.
637 */
638 class SyncBlk
639 {
640 friend class SyncBlkIterator;
641 public:
642 /* Constructor.
643 * Params:
644 * index - the index of the syncblk entry you wish to inspect.
645 * This should be in range [1, MaxEntries], but in general
646 * you should always use the SyncBlk iterator off of GCHeap
647 * and not construct these directly.
648 * Throws:
649 * DataRead - if we could not read the syncblk entry for the given index.
650 */
651 explicit SyncBlk(int index);
652
653 /* Returns whether or not the current entry is a "Free" SyncBlk table entry
654 * or not. This should be called *before* any other function here.
655 */
656 bool IsFree() const;
657
658 /* Returns the address of this syncblk entry (generally for display purposes).
659 */
660 TADDR GetAddress() const;
661
662 /* Returns the address of the object which this is syncblk is pointing to.
663 */
664 TADDR GetObject() const;
665
666 /* Returns the index of this entry.
667 */
668 int GetIndex() const;
669
670 /* Returns the COMFlags for the SyncBlk object. The return value of this
671 * function is undefined if FEATURE_COMINTEROP is not defined, so you should
672 * #ifdef the calling region yourself.
673 */
674 DWORD GetCOMFlags() const;
675
676 unsigned int GetMonitorHeldCount() const;
677 unsigned int GetRecursion() const;
678 unsigned int GetAdditionalThreadCount() const;
679
680 /* Returns the thread which holds this monitor (this is the clr!Thread object).
681 */
682 TADDR GetHoldingThread() const;
683 TADDR GetAppDomain() const;
684
685 private:
686 /* Copy constructor unimplemented due to how expensive this is. Use references
687 * instead.
688 */
689 SyncBlk(const SyncBlk &rhs);
690 SyncBlk();
691 void Init();
692 const SyncBlk &operator=(int index);
693
694 private:
695 int mIndex;
696 DacpSyncBlockData mData;
697 };
698
699 /* An iterator over syncblks. The common usage for this class is:
700 * for (SyncBlkIterator itr; itr; ++itr)
701 * itr->SomeSyncBlkFunction();
702 */
703 class SyncBlkIterator
704 {
705 public:
706 SyncBlkIterator();
707
708 /* Moves to the next SyncBlk in the table.
709 */
710 inline const SyncBlkIterator &operator++()
711 {
712 SOS_Assert(mCurr <= mTotal);
713 mSyncBlk = ++mCurr;
714
715 return *this;
716 }
717
718 inline const SyncBlk &operator*() const
719 {
720 SOS_Assert(mCurr <= mTotal);
721 return mSyncBlk;
722 }
723
724 inline const SyncBlk *operator->() const
725 {
726 SOS_Assert(mCurr <= mTotal);
727 return &mSyncBlk;
728 }
729
730 inline operator void *() const
731 {
732 return (void*)(SIZE_T)(mCurr <= mTotal ? 1 : 0);
733 }
734
735 private:
736 int mCurr, mTotal;
737 SyncBlk mSyncBlk;
738 };
739
740 /* An class which contains information about the GCHeap.
741 */
742 class GCHeap
743 {
744 public:
745 static const TADDR HeapStart; // A constant signifying the start of the GC heap.
746 static const TADDR HeapEnd; // A constant signifying the end of the GC heap.
747
748 public:
749 /* Constructor.
750 * Throws:
751 * DataRead
752 */
753 GCHeap();
754
755 /* Returns an ObjectIterator which allows you to walk the objects on the managed heap.
756 * This ObjectIterator is valid for the duration of the GCHeap's lifetime. Note that
757 * if you specify an address at which you wish to start walking the heap it need
758 * not point directly to a managed object. However, if it does not, WalkHeap
759 * will need to walk the segment that address resides in to find the first object
760 * after that address, and if it encounters any heap corruption along the way,
761 * it may be impossible to walk the heap from the address specified.
762 *
763 * Params:
764 * start - The starting address at which you want to start walking the heap.
765 * This need not point directly to an object on the heap.
766 * end - The ending address at which you want to stop walking the heap. This
767 * need not point directly to an object on the heap.
768 * validate - Whether or not you wish to validate the GC heap as you walk it.
769 * Throws:
770 * DataRead
771 */
772 ObjectIterator WalkHeap(TADDR start = HeapStart, TADDR stop = HeapEnd) const;
773
774 /* Returns true if the GC Heap structures are in a valid state for traversal.
775 * Returns false if not (e.g. if we are in the middle of a relocation).
776 */
777 bool AreGCStructuresValid() const;
778
779 private:
780 DacpGcHeapDetails *mHeaps;
781 DacpGcHeapData mHeapData;
782 int mNumHeaps;
783 };
784
785 // convenience functions
786 /* A temporary wrapper function for Object::IsValid. There are too many locations
787 * in SOS which need to use IsObject but have a wide variety of internal
788 * representations for an object address. Until it can all be unified as TADDR,
789 * this is what they will use.
790 */
791 template <class T>
792 bool IsObject(T addr, bool verifyFields=false)
793 {
794 return Object::IsValid(TO_TADDR(addr), verifyFields);
795 }
796
797
798 void BuildTypeWithExtraInfo(TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer);
799}
800