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 | |
30 | class LinearReadCache; |
31 | class CGCDesc; |
32 | class CGCDescSeries; |
33 | |
34 | namespace 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 () 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 (ULONG &) 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 ¶m); |
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 (TADDR addr, unsigned int size, __inout_ecount(size) WCHAR *buffer); |
799 | } |
800 | |