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 | // rspriv. |
6 | // |
7 | |
8 | // |
9 | // Common include file for right-side of debugger. |
10 | //***************************************************************************** |
11 | |
12 | #ifndef RSPRIV_H |
13 | #define RSPRIV_H |
14 | |
15 | #include <winwrap.h> |
16 | #include <windows.h> |
17 | |
18 | #include <utilcode.h> |
19 | |
20 | |
21 | #ifdef _DEBUG |
22 | #define LOGGING |
23 | #endif |
24 | |
25 | #include <log.h> |
26 | #include <corerror.h> |
27 | |
28 | #include "cor.h" |
29 | |
30 | #include "cordebug.h" |
31 | #include "xcordebug.h" |
32 | #include "cordbpriv.h" |
33 | #include "mscoree.h" |
34 | |
35 | #include <cordbpriv.h> |
36 | #include <dbgipcevents.h> |
37 | |
38 | #include "common.h" |
39 | #include "primitives.h" |
40 | |
41 | #include "dacdbiinterface.h" |
42 | |
43 | #include "helpers.h" |
44 | |
45 | struct MachineInfo; |
46 | |
47 | #include "processdescriptor.h" |
48 | #include "nativepipeline.h" |
49 | #include "stringcopyholder.h" |
50 | |
51 | |
52 | #include "eventchannel.h" |
53 | |
54 | #undef ASSERT |
55 | #define CRASH(x) _ASSERTE(!x) |
56 | #define ASSERT(x) _ASSERTE(x) |
57 | |
58 | // We want to keep the 'worst' HRESULT - if one has failed (..._E_...) & the |
59 | // other hasn't, take the failing one. If they've both/neither failed, then |
60 | // it doesn't matter which we take. |
61 | // Note that this macro favors retaining the first argument |
62 | #define WORST_HR(hr1,hr2) (FAILED(hr1)?hr1:hr2) |
63 | |
64 | // #UseDataTarget |
65 | // Forbid usage of OS APIs that we should be using the data-target for |
66 | #define ReadProcessMemory DONT_USE_READPROCESS_MEMORY |
67 | #define WriteProcessMemory DONT_USE_WRITEPROCESS_MEMORY |
68 | |
69 | |
70 | /* ------------------------------------------------------------------------- * |
71 | * Forward class declarations |
72 | * ------------------------------------------------------------------------- */ |
73 | |
74 | class CordbBase; |
75 | class CordbValue; |
76 | class CordbModule; |
77 | class CordbClass; |
78 | class CordbFunction; |
79 | class CordbCode; |
80 | class CordbFrame; |
81 | class CordbJITILFrame; |
82 | class CordbInternalFrame; |
83 | class CordbContext; |
84 | class CordbThread; |
85 | class CordbVariableHome; |
86 | |
87 | #ifdef FEATURE_INTEROP_DEBUGGING |
88 | class CordbUnmanagedThread; |
89 | struct CordbUnmanagedEvent; |
90 | #endif |
91 | |
92 | class CordbProcess; |
93 | class CordbAppDomain; |
94 | class CordbAssembly; |
95 | class CordbBreakpoint; |
96 | class CordbStepper; |
97 | class Cordb; |
98 | class CordbEnCSnapshot; |
99 | class CordbWin32EventThread; |
100 | class CordbRCEventThread; |
101 | class CordbRegisterSet; |
102 | class CordbNativeFrame; |
103 | class CordbObjectValue; |
104 | class CordbEnCErrorInfo; |
105 | class CordbEnCErrorInfoEnum; |
106 | class Instantiation; |
107 | class CordbType; |
108 | class CordbNativeCode; |
109 | class CordbILCode; |
110 | class CordbReJitILCode; |
111 | class CordbEval; |
112 | |
113 | class CordbMDA; |
114 | |
115 | class CorpubPublish; |
116 | class CorpubProcess; |
117 | class CorpubAppDomain; |
118 | class CorpubProcessEnum; |
119 | class CorpubAppDomainEnum; |
120 | |
121 | |
122 | class RSLock; |
123 | class NeuterList; |
124 | |
125 | class IDacDbiInterface; |
126 | |
127 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
128 | class DbgTransportTarget; |
129 | class DbgTransportSession; |
130 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
131 | |
132 | // @dbgtodo private shim hook - the RS has private hooks into the shim to help bridge the V2/V3 gap. |
133 | // This helps provide a working dogfooding story throughout our transition. |
134 | // These hooks must be removed before shipping. |
135 | class ShimProcess; |
136 | |
137 | |
138 | #ifndef FEATURE_PAL |
139 | extern HINSTANCE GetModuleInst(); |
140 | #endif |
141 | |
142 | |
143 | template <class T> |
144 | class CordbSafeHashTable; |
145 | |
146 | |
147 | //--------------------------------------------------------------------------------------- |
148 | // |
149 | // This is an encapsulation of the information necessary to connect to the debugger proxy on a remote machine. |
150 | // It includes the IP address and the port number. The IP address can be set via the env var |
151 | // COMPlus_DbgTransportProxyAddress, and the port number is fixed when Mac debugging is configured. |
152 | // |
153 | |
154 | struct MachineInfo |
155 | { |
156 | public: |
157 | void Init(DWORD dwIPAddress, USHORT usPort) |
158 | { |
159 | m_dwIPAddress = dwIPAddress; |
160 | m_usPort = usPort; |
161 | } |
162 | |
163 | void Clear() |
164 | { |
165 | m_dwIPAddress = 0; |
166 | m_usPort = 0; |
167 | } |
168 | |
169 | DWORD GetIPAddress() {return m_dwIPAddress;}; |
170 | USHORT GetPort() {return m_usPort;}; |
171 | |
172 | private: |
173 | DWORD m_dwIPAddress; |
174 | USHORT m_usPort; |
175 | }; |
176 | |
177 | extern forDbiWorker forDbi; |
178 | |
179 | // for dbi we just default to new, but we need to have these defined for both dac and dbi |
180 | inline void * operator new(size_t lenBytes, const forDbiWorker &) |
181 | { |
182 | void * result = new BYTE[lenBytes]; |
183 | if (result == NULL) |
184 | { |
185 | ThrowOutOfMemory(); |
186 | } |
187 | return result; |
188 | } |
189 | |
190 | inline void * operator new[](size_t lenBytes, const forDbiWorker &) |
191 | { |
192 | void * result = new BYTE[lenBytes]; |
193 | if (result == NULL) |
194 | { |
195 | ThrowOutOfMemory(); |
196 | } |
197 | return result; |
198 | } |
199 | |
200 | // Helper to delete memory used with the IDacDbiInterface::IAllocator interface. |
201 | template<class T> inline |
202 | void DeleteDbiMemory(T *p) |
203 | { |
204 | delete p; |
205 | } |
206 | |
207 | |
208 | |
209 | //--------------------------------------------------------------------------------------- |
210 | // |
211 | // Simple array of holders (either RSSmartPtrs or RSExtSmartPtrs). |
212 | // Holds a reference to each element. |
213 | // |
214 | // Notes: |
215 | // T is the base type and HOLDER_T is the type of the holder. All functions implemented on this base |
216 | // class must work for both RSSmartPtrs and RSExtSmartPtrs. For example, there is no concept of neutering |
217 | // for RSExtSmartPtrs. |
218 | // |
219 | |
220 | template<typename T, typename HOLDER_T> |
221 | class BaseRSPtrArray |
222 | { |
223 | public: |
224 | BaseRSPtrArray() |
225 | { |
226 | m_pArray = NULL; |
227 | m_cElements = 0; |
228 | } |
229 | |
230 | // Is the array emtpy? |
231 | bool IsEmpty() const |
232 | { |
233 | return (m_pArray == NULL); |
234 | } |
235 | |
236 | // Allocate an array of ptrs. |
237 | // Returns false if not enough memory; else true. |
238 | bool Alloc(unsigned int cElements) |
239 | { |
240 | // Caller should have already Neutered |
241 | _ASSERTE(IsEmpty()); |
242 | |
243 | // It's legal to allocate 0 items. We'll succeed the allocation, but still claim that IsEmpty() == true. |
244 | if (cElements == 0) |
245 | { |
246 | return true; |
247 | } |
248 | |
249 | // RSSmartPtr ctor will ensure all elements are null initialized. |
250 | m_pArray = new (nothrow) HOLDER_T [cElements]; |
251 | if (m_pArray == NULL) |
252 | { |
253 | return false; |
254 | } |
255 | |
256 | m_cElements = cElements; |
257 | return true; |
258 | } |
259 | |
260 | // Allocate an array of ptrs. |
261 | // Throw on failure |
262 | void AllocOrThrow(unsigned int cElements) |
263 | { |
264 | if (!Alloc(cElements)) |
265 | { |
266 | ThrowOutOfMemory(); |
267 | } |
268 | } |
269 | |
270 | // Release each element and empty the array. |
271 | void Clear() |
272 | { |
273 | // this Invoke dtors on each element which will release each element |
274 | delete [] m_pArray; |
275 | |
276 | m_pArray = NULL; |
277 | m_cElements = 0; |
278 | } |
279 | |
280 | // Array lookup. Caller gaurantees this is in range. |
281 | // Used for reading |
282 | T* operator [] (unsigned int index) const |
283 | { |
284 | _ASSERTE(m_pArray != NULL); |
285 | CONSISTENCY_CHECK_MSGF((index <= m_cElements), ("Index out of range. Index=%u, Max=%u\n" , index, m_cElements)); |
286 | |
287 | return m_pArray[index]; |
288 | } |
289 | |
290 | // Assign a given index to the given value. The array holder will increment the internal reference on the value. |
291 | void Assign(unsigned int index, T* pValue) |
292 | { |
293 | _ASSERTE(m_pArray != NULL); |
294 | CONSISTENCY_CHECK_MSGF((index <= m_cElements), ("Index out of range. Index=%u, Max=%u\n" , index, m_cElements)); |
295 | |
296 | m_pArray[index].Assign(pValue); |
297 | } |
298 | |
299 | // Get lenght of array in elements. |
300 | unsigned int Length() const |
301 | { |
302 | return m_cElements; |
303 | } |
304 | |
305 | // Some things need to get the address of an element in the table. |
306 | // For example, CordbThreads have an array of CordbFrame objects, and then CordbChains describe a range |
307 | // or frames via pointers into the CordbThread's array. |
308 | // This is a dangerous operation because it lets us side-step reference counting and protection. |
309 | T ** UnsafeGetAddrOfIndex(unsigned int index) |
310 | { |
311 | return m_pArray[index].UnsafeGetAddr(); |
312 | } |
313 | |
314 | protected: |
315 | // Raw array of values. |
316 | HOLDER_T * m_pArray; |
317 | |
318 | // Number of elements in m_pArray. Note the following is always true: (m_cElements == 0) == (m_pArray == NULL); |
319 | unsigned int m_cElements; |
320 | }; |
321 | |
322 | |
323 | //----------------------------------------------------------------------------- |
324 | // |
325 | // Simple array holder of RSSmartPtrs (internal pointers). |
326 | // Holds a reference to each element. |
327 | // |
328 | // Notes: |
329 | // This derived class adds the concept of neutering to the base pointer array. |
330 | // Allows automatic Clear()ing; do not use this unless it is safe to do so in |
331 | // all cases - e.g. you're holding a local. |
332 | // |
333 | |
334 | template< typename T, typename HOLDER_T = RSSmartPtr<T> > // We need to use HOLDER_T to make gcc happy. |
335 | class RSPtrArray : public BaseRSPtrArray<T, HOLDER_T> |
336 | { |
337 | private: |
338 | typedef BaseRSPtrArray<T, HOLDER_T> Super; |
339 | BOOL m_autoClear; |
340 | |
341 | public: |
342 | RSPtrArray() : m_autoClear(FALSE) |
343 | { |
344 | } |
345 | |
346 | ~RSPtrArray() |
347 | { |
348 | if (m_autoClear) |
349 | { |
350 | Super::Clear(); |
351 | } |
352 | else |
353 | { |
354 | // Caller should have already Neutered |
355 | _ASSERTE(Super::IsEmpty()); |
356 | } |
357 | } |
358 | |
359 | void EnableAutoClear() |
360 | { |
361 | m_autoClear = TRUE; |
362 | } |
363 | |
364 | // Neuter all elements in the array. |
365 | void NeuterAndClear() |
366 | { |
367 | for(unsigned int i = 0; i < Super::m_cElements; i++) |
368 | { |
369 | if (Super::m_pArray[i] != NULL) |
370 | { |
371 | Super::m_pArray[i]->Neuter(); |
372 | } |
373 | } |
374 | |
375 | Super::Clear(); |
376 | } |
377 | }; |
378 | |
379 | |
380 | //----------------------------------------------------------------------------- |
381 | // |
382 | // Simple array holder of RSExtSmartPtrs (external pointers). |
383 | // Holds a reference to each element. |
384 | // |
385 | // Notes: |
386 | // This derived class clears the array in its destructor. |
387 | // |
388 | |
389 | template< typename T, typename HOLDER_T = RSExtSmartPtr<T> > // We need to use HOLDER_T to make gcc happy. |
390 | class RSExtPtrArray : public BaseRSPtrArray<T, HOLDER_T> |
391 | { |
392 | private: |
393 | typedef BaseRSPtrArray<T, HOLDER_T> Super; |
394 | |
395 | public: |
396 | ~RSExtPtrArray() |
397 | { |
398 | Super::Clear(); |
399 | } |
400 | }; |
401 | |
402 | |
403 | |
404 | //----------------------------------------------------------------------------- |
405 | // Table for RSptrs |
406 | // This lets us map cookies <--> RSPTR_*, |
407 | // Then we just put the cookie in the IPC block instead of the raw RSPTR. |
408 | // This will also adjust the internal-reference count on the T* object. |
409 | // This isolates the RS from bugs in the LS. |
410 | // We templatize by type for type safety. |
411 | // Caller must syncrhonize all access (preferably w/ the stop-go lock). |
412 | //----------------------------------------------------------------------------- |
413 | template <class T> |
414 | class RsPtrTable |
415 | { |
416 | public: |
417 | RsPtrTable() |
418 | { |
419 | m_pTable = NULL; |
420 | m_cEntries = 0; |
421 | } |
422 | ~RsPtrTable() |
423 | { |
424 | Clear(); |
425 | } |
426 | void Clear() |
427 | { |
428 | for(UINT i = 0; i < m_cEntries; i++) |
429 | { |
430 | if (m_pTable[i]) |
431 | { |
432 | m_pTable[i]->InternalRelease(); |
433 | } |
434 | } |
435 | delete [] m_pTable; |
436 | m_pTable = NULL; |
437 | m_cEntries = 0; |
438 | } |
439 | |
440 | // Add a value into table. Value can't be NULL. |
441 | // Returns 0 on failure (such as oom), |
442 | // Returns a non-zero cookie on success. |
443 | UINT Add(T* pValue) |
444 | { |
445 | _ASSERTE(pValue != NULL); |
446 | // skip 0 because it's an invalid handle. |
447 | for(UINT i = 1; ; i++) |
448 | { |
449 | // If we've run out of space, allocate new space |
450 | if( i >= m_cEntries ) |
451 | { |
452 | if( !Grow() ) |
453 | { |
454 | return 0; // failed to grow |
455 | } |
456 | _ASSERTE( i < m_cEntries ); |
457 | _ASSERTE( m_pTable[i] == NULL ); |
458 | // Since we grew, the next slot should now be open. |
459 | } |
460 | |
461 | if (m_pTable[i] == NULL) |
462 | { |
463 | m_pTable[i] = pValue; |
464 | pValue->InternalAddRef(); |
465 | return i; |
466 | } |
467 | } |
468 | UNREACHABLE(); |
469 | } |
470 | |
471 | // Lookup the value based off the cookie, which was obtained via "Add". |
472 | // return NULL on error. |
473 | T* Lookup(UINT cookie) |
474 | { |
475 | _ASSERTE(cookie != 0); |
476 | if (cookie >= m_cEntries) |
477 | { |
478 | CONSISTENCY_CHECK_MSGF(false, ("Cookie out of range.Cookie=0x%x. Size=0x%x.\n" , cookie, m_cEntries)); |
479 | return NULL; |
480 | } |
481 | T* p = m_pTable[cookie]; |
482 | if (p == NULL) |
483 | { |
484 | CONSISTENCY_CHECK_MSGF(false, ("Cookie is for empty slot.Cookie=0x%x.\n" , cookie)); |
485 | return NULL; // empty! |
486 | } |
487 | return p; |
488 | } |
489 | |
490 | T* LookupAndRemove(UINT cookie) |
491 | { |
492 | _ASSERTE(cookie != 0); |
493 | T* p = Lookup(cookie); |
494 | if (p != NULL) |
495 | { |
496 | m_pTable[cookie] = NULL; |
497 | p->InternalRelease(); |
498 | } |
499 | return p; |
500 | } |
501 | |
502 | protected: |
503 | // Resize the m_pTable array. |
504 | bool Grow() |
505 | { |
506 | if (m_pTable == NULL) |
507 | { |
508 | _ASSERTE(m_cEntries == 0); |
509 | size_t cSize = 10; |
510 | m_pTable = new (nothrow) T*[cSize]; |
511 | if (m_pTable == NULL) |
512 | { |
513 | return false; |
514 | } |
515 | m_cEntries = cSize; |
516 | ZeroMemory(m_pTable, sizeof(T*) * m_cEntries); |
517 | return true; |
518 | } |
519 | size_t cNewSize = (m_cEntries * 3 / 2) + 1; |
520 | _ASSERTE(cNewSize > m_cEntries); |
521 | T** p = new (nothrow) T*[cNewSize]; |
522 | if (p == NULL) |
523 | { |
524 | return false; |
525 | } |
526 | ZeroMemory(p, sizeof(T*) * cNewSize); |
527 | |
528 | |
529 | // Copy over old stuff |
530 | memcpy(p, m_pTable, sizeof(T*) * m_cEntries); |
531 | delete [] m_pTable; |
532 | |
533 | m_pTable = p; |
534 | m_cEntries = cNewSize; |
535 | return true; |
536 | } |
537 | |
538 | T** m_pTable; |
539 | size_t m_cEntries; |
540 | }; |
541 | |
542 | |
543 | |
544 | //----------------------------------------------------------------------------- |
545 | // Simple Holder for RS object intialization to cooperate with Neutering |
546 | // semantics. |
547 | // The ctor will do an addref. |
548 | // The dtor (invoked in exception) will neuter and release the object. This |
549 | // release will likely be the final release to cause a delete. |
550 | // If the object is created successfully, caller should do a SuppressRelease() |
551 | // to avoid it getting neutered. |
552 | // |
553 | // Example: |
554 | // RSInitHolder<CordbFoo> pFoo(new CordbFoo(x,y,z)); |
555 | // pFoo->InitMore(a,b,c); |
556 | // GiveOwnershipToSomebodyElse(pFoo); // now somebody else owns and will clean up |
557 | // pFoo.ClearAndMarkDontNeuter(); // we no longer need to |
558 | // |
559 | // So if an exception is thrown before ClearAndMarkDontNeuter(), the dtor is invoked |
560 | // and the object is properly destroyed (deleted and neutered). |
561 | // |
562 | // Another common pattern is when initializing an object to hand off to an external: |
563 | // RSInitHolder<CordbFoo> pFoo(new CordbFoo(x,y,z)); |
564 | // pFoo->InitMore(a,b,c); |
565 | // pFoo.TransferOwnershipExternal(ppOutParameter); |
566 | // TransferOwnershipExternal will assign to ppOutParameter, inc external ref, and |
567 | // call ClearAndMarkDontNeuter() |
568 | //----------------------------------------------------------------------------- |
569 | template<class T> |
570 | class RSInitHolder |
571 | { |
572 | public: |
573 | // Default ctor. Must call Assign() later. |
574 | RSInitHolder() |
575 | { |
576 | }; |
577 | RSInitHolder(T * pObject) |
578 | { |
579 | Assign(pObject); |
580 | } |
581 | |
582 | void Assign(T * pObject) |
583 | { |
584 | _ASSERTE(m_pObject == NULL); // only assign once. |
585 | m_pObject.Assign(pObject); |
586 | } |
587 | ~RSInitHolder(); |
588 | |
589 | FORCEINLINE operator T *() const |
590 | { |
591 | return m_pObject; |
592 | |
593 | } |
594 | FORCEINLINE T * operator->() |
595 | { |
596 | return m_pObject; |
597 | } |
598 | |
599 | // This will null out m_pObject such that the dtor will not neuter it. |
600 | // This will also release the ref we took in the ctor. |
601 | // This will clear the current pointer. |
602 | void ClearAndMarkDontNeuter() |
603 | { |
604 | m_pObject.Clear(); |
605 | } |
606 | |
607 | // |
608 | // Transfer ownership to a pointer |
609 | // |
610 | // Arguments: |
611 | // ppOutParam - pointer to get ownership. External Reference is incremented. |
612 | // this pointer should do an external release. |
613 | // |
614 | // Notes: |
615 | // This calls ClearAndMarkDontNeuter(). This holder is Empty after this. |
616 | template <class TOther> |
617 | void TransferOwnershipExternal(TOther ** ppOutParam) |
618 | { |
619 | *ppOutParam = static_cast<TOther*> (m_pObject); |
620 | m_pObject->ExternalAddRef(); |
621 | |
622 | ClearAndMarkDontNeuter(); |
623 | } |
624 | |
625 | |
626 | // |
627 | // Transfer the ownership of the wrapped object to the given hash table. |
628 | // |
629 | // Arguments: |
630 | // pHashTable - hash table to take ownership. |
631 | // |
632 | // Returns: |
633 | // the contianing object for convenience. Throws on error (particularly |
634 | // if it fails adding to the hash). |
635 | // |
636 | // Notes: |
637 | // This calls ClearAndMarkDontNeuter(). This holder is Empty after this. |
638 | T* TransferOwnershipToHash(CordbSafeHashTable<T> * pHashtable) |
639 | { |
640 | T* pObject = m_pObject; |
641 | pHashtable->AddBaseOrThrow(m_pObject); |
642 | ClearAndMarkDontNeuter(); |
643 | return pObject; |
644 | } |
645 | |
646 | // |
647 | // Used to pass into a function that will assign to us. |
648 | // |
649 | // Returns: |
650 | // Address of this holder. This is like the & operator. |
651 | // This is provided for consistency with other holders which |
652 | // override the &operator. |
653 | RSInitHolder<T> * GetAddr() |
654 | { |
655 | return this; |
656 | } |
657 | |
658 | |
659 | protected: |
660 | RSSmartPtr<T> m_pObject; |
661 | }; |
662 | |
663 | |
664 | |
665 | //----------------------------------------------------------------------------- |
666 | // Have the extra level of indirection is useful for catching Cordbg errors. |
667 | //----------------------------------------------------------------------------- |
668 | #ifdef _DEBUG |
669 | // On debug, we have an opportunity to catch failing hresults during reproes. |
670 | #define ErrWrapper(hr) ErrWrapperHelper(hr, __FILE__, __LINE__) |
671 | |
672 | inline HRESULT ErrWrapperHelper(HRESULT hr, const char * szFile, int line) |
673 | { |
674 | if (FAILED(hr)) |
675 | { |
676 | DWORD dwErr = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgBreakOnErr); |
677 | if (dwErr) |
678 | { |
679 | CONSISTENCY_CHECK_MSGF(false, ("Dbg Error break, hr=0x%08x, '%s':%d" , hr, szFile, line)); |
680 | } |
681 | } |
682 | return hr; |
683 | } |
684 | #else |
685 | // On release, it's just an identity function |
686 | #define ErrWrapper(hr) (hr) |
687 | #endif |
688 | |
689 | //----------------------------------------------------------------------------- |
690 | // Quick helpers for threading semantics |
691 | //----------------------------------------------------------------------------- |
692 | |
693 | bool IsWin32EventThread(CordbProcess* p); |
694 | bool IsRCEventThread(Cordb* p); |
695 | |
696 | /* ------------------------------------------------------------------------- * |
697 | * Typedefs |
698 | * ------------------------------------------------------------------------- */ |
699 | |
700 | typedef void* REMOTE_PTR; |
701 | |
702 | |
703 | //----------------------------------------------------------------------------- |
704 | // Wrapper class for locks. This is like Crst on the LS |
705 | //----------------------------------------------------------------------------- |
706 | |
707 | class RSLock |
708 | { |
709 | public: |
710 | // Attrs, can be bitwise-or together. |
711 | enum ELockAttr |
712 | { |
713 | cLockUninit = 0x00000000, |
714 | cLockReentrant = 0x00000001, |
715 | cLockFlat = 0x00000002, |
716 | |
717 | // (unusual). Not considered a debug API lock, for purposes of deciding whether |
718 | // to count this lock in m_cTotalDbgApiLocks, which is asserted to be 0 on entry |
719 | // to public APIs. Example of such a lock: LL_SHIM_PROCESS_DISPOSE_LOCK |
720 | cLockNonDbgApi = 0x00000004, |
721 | }; |
722 | |
723 | // To prevent deadlocks, we order all locks. |
724 | // A thread must acquire higher-numbered locks before lower numbered locks. |
725 | // These are used as indices into an array, so number them accordingly! |
726 | enum ERSLockLevel |
727 | { |
728 | // Size of the array.. |
729 | LL_MAX = 6, |
730 | |
731 | // The Stop-Go lock is used to make Stop + Continue be atomic operations. |
732 | // These methods will toggle the Process-lock b/c they go between multiple threads. |
733 | // This lock can never be taken on the Win32 ET. |
734 | LL_STOP_GO_LOCK = 5, |
735 | |
736 | // The win32-event-thread behaves as if it held a lock at this level. |
737 | LL_WIN32_EVENT_THREAD = 4, |
738 | |
739 | // This held for the duration of ShimProcess::Dispose(), and protects |
740 | // ShimProcess::m_fIsDisposed, so that other ShimProcess functions can |
741 | // safely execute serially with ShimProcess::Dispose(). This needs to be |
742 | // a high-level lock, since ShimProcess methods that take this lock also |
743 | // call into CorDb* objects which take many of the other locks. In contrast, |
744 | // LL_SHIM_LOCK must remain low-level, as there exists at least one place where |
745 | // LL_SHIM_LOCK is taken while the CorDbProcess lock is also held (see |
746 | // CordbThread::GetActiveFunctions which takes the CorDbProcess lock while |
747 | // calling GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this), which |
748 | // takes LL_SHIM_LOCK). |
749 | LL_SHIM_PROCESS_DISPOSE_LOCK = 3, |
750 | |
751 | // The process lock is the primary lock for a CordbProcess object. It synchronizes |
752 | // between RCET, W32ET, and user threads. |
753 | LL_PROCESS_LOCK = 2, |
754 | |
755 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
756 | LL_DBG_TRANSPORT_MANAGER_LOCK = 1, |
757 | |
758 | LL_DBG_TRANSPORT_TARGET_LOCK = 0, |
759 | |
760 | LL_DD_MARSHAL_LOCK = 0, |
761 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
762 | |
763 | // These are all leaf locks (they don't take any other lock once they're held). |
764 | LL_PROCESS_LIST_LOCK = 0, |
765 | |
766 | // Win32 send lock is shared by all processes accessing a single w32et. |
767 | LL_WIN32_SEND_LOCK = 0, |
768 | |
769 | // Small lock around sending IPC events to support workarounds in func-eval abort. |
770 | // See code:CordbEval::Abort for details. |
771 | LL_FUNC_EVAL_ABORT_HACK_LOCK = 0, |
772 | |
773 | // Leaf-level lock used in the shim. |
774 | LL_SHIM_LOCK = 0 |
775 | }; |
776 | |
777 | // Initialize a lock w/ debugging info. szTag must be a string literal. |
778 | void Init(const char * szTag, int eAttr, ERSLockLevel level); |
779 | void Destroy(); |
780 | |
781 | void Lock(); |
782 | void Unlock(); |
783 | |
784 | protected: |
785 | // Accessors for holders. |
786 | static void HolderEnter(RSLock * pLock) |
787 | { |
788 | pLock->Lock(); |
789 | } |
790 | static void HolderLeave(RSLock * pLock) |
791 | { |
792 | pLock->Unlock(); |
793 | } |
794 | |
795 | |
796 | CRITICAL_SECTION m_lock; |
797 | |
798 | #ifdef _DEBUG |
799 | public: |
800 | RSLock(); |
801 | ~RSLock(); |
802 | |
803 | const char * Name() { return m_szTag; } |
804 | |
805 | // Returns true if this thread has the lock. |
806 | bool HasLock(); |
807 | |
808 | // Returns true if this is safe to take on this thread (ie, this thread |
809 | // doesn't already hold bigger locks). |
810 | // bool IsSafeToTake(); |
811 | |
812 | ERSLockLevel GetLevel() { return m_level; } |
813 | |
814 | // If we're inited, we must have either cLockReentrant or cLockFlat specified. |
815 | bool IsInit() { return m_eAttr != 0; } |
816 | bool IsReentrant() { return (m_eAttr & cLockReentrant) == cLockReentrant; } |
817 | bool IsDbgApiLock() { return ((m_eAttr & cLockNonDbgApi) == 0); } |
818 | |
819 | protected: |
820 | ERSLockLevel m_level; |
821 | int m_eAttr; // Bitwise combination of ELockAttr values |
822 | int m_count; |
823 | DWORD m_tidOwner; |
824 | const char * m_szTag; |
825 | |
826 | #endif // #if debug |
827 | |
828 | public: |
829 | typedef Holder<RSLock *, RSLock::HolderEnter, RSLock::HolderLeave> RSLockHolder; |
830 | typedef Holder<RSLock *, RSLock::HolderLeave, RSLock::HolderEnter> RSInverseLockHolder; |
831 | |
832 | }; |
833 | |
834 | typedef RSLock::RSLockHolder RSLockHolder; |
835 | typedef RSLock::RSInverseLockHolder RSInverseLockHolder; |
836 | |
837 | // In the RS, we should be using RSLocks instead of raw critical sections. |
838 | #define CRITICAL_SECTION USE_RSLOCK_INSTEAD_OF_CRITICAL_SECTION |
839 | |
840 | |
841 | /* ------------------------------------------------------------------------- * |
842 | * Helper macros. Use the ATT_* macros below instead of these. |
843 | * ------------------------------------------------------------------------- */ |
844 | |
845 | // This serves as glue for exceptions. Eventually, we shouldn't have unrecoverable |
846 | // error, and instead, errors should just propogate up. |
847 | #define SetUnrecoverableIfFailed(__p, __hr) \ |
848 | if (FAILED(__hr)) \ |
849 | { \ |
850 | CORDBSetUnrecoverableError(__p, __hr, 0); \ |
851 | } |
852 | |
853 | #define CORDBSetUnrecoverableError(__p, __hr, __code) \ |
854 | ((__p)->UnrecoverableError((__hr), (__code), __FILE__, __LINE__)) |
855 | |
856 | #define _CORDBCheckProcessStateOK(__p) \ |
857 | (!((__p)->m_unrecoverableError) && !((__p)->m_terminated) && !((__p)->m_detached)) |
858 | |
859 | #define _CORDBCheckProcessStateOKAndSync(__p, __c) \ |
860 | (!((__p)->m_unrecoverableError) && !((__p)->m_terminated) && !((__p)->m_detached) && \ |
861 | (__p)->GetSynchronized()) |
862 | |
863 | // Worker to get failure HR from given state. If not in a failure state, it yields __defaultHR. |
864 | // If a caller knows that we're in a failure state, it can pass in a failure value for __defaultHR. |
865 | #define CORDBHRFromProcessStateWorker(__p, __c, __defaultHR) \ |
866 | ((__p)->m_unrecoverableError ? CORDBG_E_UNRECOVERABLE_ERROR : \ |
867 | ((__p)->m_detached ? CORDBG_E_PROCESS_DETACHED : \ |
868 | ((__p)->m_terminated ? CORDBG_E_PROCESS_TERMINATED : \ |
869 | (!(__p)->GetSynchronized() ? CORDBG_E_PROCESS_NOT_SYNCHRONIZED \ |
870 | : (__defaultHR))))) |
871 | |
872 | #define CORDBHRFromProcessState(__p, __c) \ |
873 | CORDBHRFromProcessStateWorker(__p, __c, S_OK) \ |
874 | |
875 | |
876 | // Have a set of helper macros to check the process state and return a failure code. |
877 | // These only should be used at public interface boundaries, in which case we should |
878 | // not be holding the process lock. But we have enough places where we use them internally, |
879 | // so we can't really assert that we're not holding the lock. |
880 | |
881 | // We're very restricted in what APIs we can call on the w32et. Have |
882 | // a convenient check for this. |
883 | // If we have no shim, then nop this check because everything becomes like the w32-event-thread. |
884 | #define CORDBFailOrThrowIfOnWin32EventThread(__p, errorAction) \ |
885 | { \ |
886 | if (((__p)->GetShim() != NULL) && (__p)->IsWin32EventThread()) \ |
887 | { \ |
888 | _ASSERTE(!"Don't call on this thread"); \ |
889 | errorAction(ErrWrapper(CORDBG_E_CANT_CALL_ON_THIS_THREAD)); \ |
890 | } \ |
891 | } |
892 | |
893 | #define CORDBFailIfOnWin32EventThread(__p) CORDBFailOrThrowIfOnWin32EventThread(__p, return) |
894 | |
895 | #define CORDBRequireProcessStateOK(__p) { \ |
896 | if (!_CORDBCheckProcessStateOK(__p)) \ |
897 | return ErrWrapper(CORDBHRFromProcessState(__p, NULL)); } |
898 | |
899 | // If we need to be synced, then we shouldn't be on the win32 Event-Thread. |
900 | #define CORDBRequireProcessStateOKAndSync(__p,__c) { \ |
901 | CORDBFailIfOnWin32EventThread(__p); \ |
902 | if (!_CORDBCheckProcessStateOKAndSync(__p, __c)) \ |
903 | return ErrWrapper(CORDBHRFromProcessState(__p, __c)); } |
904 | |
905 | #define CORDBRequireProcessSynchronized(__p, __c) { \ |
906 | CORDBFailIfOnWin32EventThread(__p); \ |
907 | if (!(__p)->GetSynchronized()) return ErrWrapper(CORDBG_E_PROCESS_NOT_SYNCHRONIZED);} |
908 | |
909 | |
910 | |
911 | |
912 | //----------------------------------------------------------------------------- |
913 | // All public APIS fall into 2 categories regarding their API Threading Type (ATT) |
914 | // We use a standard set of macros to define & enforce each type. |
915 | // |
916 | // (1) ATT_REQUIRE_STOPPED |
917 | // We must be stopped (either synced or at a win32 event) to call this API. |
918 | // - We'll fail if we're not stopped. |
919 | // - If we're stopped, we'll sync. Thus after this API, we're always synced, |
920 | // and Cordbg must call Continue to resume the process. |
921 | // - We'll take the Stop-Go-lock. This prevents another thread from continuing underneath us. |
922 | // - We may send IPC events. |
923 | // Common for APIs like Stacktracing |
924 | // |
925 | // (2) ATT_ALLOW_LIVE |
926 | // We do not have to be stopped to call this API. |
927 | // - We can be live, thus we can not take the stop-go lock (unless it's from a SC-holder). |
928 | // - If we're going to send IPC events, we must use a Stop-Continue holder. |
929 | // - Our stop-status is the same after this API as it was before. |
930 | // Common usage: read-only APIs. |
931 | // |
932 | // (2b) ATT_ALLOW_LIVE_DO_STOPGO. |
933 | // - shortcut macro to do #2, but throw in a stop-continue holder. These really |
934 | // should be in camp #1, but that would require an interface change. |
935 | //----------------------------------------------------------------------------- |
936 | |
937 | // Helper macros for the ATT stuff |
938 | |
939 | // Do checks that need to be done before we take the SG lock. These include checks |
940 | // where if we fail them, taking the SG lock could deadlock (such as being on win32 thread). |
941 | #define DO_PRE_STOP_GO_CHECKS(errorAction) \ |
942 | CORDBFailOrThrowIfOnWin32EventThread(__proc_for_ATT, errorAction) \ |
943 | if ((__proc_for_ATT)->m_unrecoverableError) { errorAction(CORDBG_E_UNRECOVERABLE_ERROR); } \ |
944 | |
945 | // Do checks after we take the SG lock. These include checks that rely on state protected |
946 | // by the SG lock. |
947 | #define DO_POST_STOP_GO_CHECKS(errorAction) \ |
948 | _ASSERTE((this->GetProcess() == __proc_for_ATT) || this->IsNeutered()); \ |
949 | if (this->IsNeutered()) { errorAction(CORDBG_E_OBJECT_NEUTERED); } \ |
950 | |
951 | // #1 |
952 | // The exact details here are rocket-science. |
953 | // We cache the __proc value to a local variable (__proc_for_ATT) so that we don't re-evaluate __proc. (It also forces type-safety). |
954 | // This is essential in case __proc is something like "this->GetProcess()" and which can start returning NULL if 'this' |
955 | // gets neutered underneath us. Caching guarantees that we'll be able to make it to the StopGo-lock. |
956 | // |
957 | // We explicitily check some things before taking the Stop-Go lock: |
958 | // - CORDBG_E_UNRECOVERABLE_ERROR before the lock because if that's set, |
959 | // we may have leaked locks to the outside world, so taking the StopGo lock later could fail. |
960 | // - Are we on the W32et - can't take sg lock if on W32et |
961 | // Then we immediately take the stop-go lock to prevent another thread from continuing underneath us. |
962 | // Then, if we're stopped, we ensure that we're also synced. |
963 | // Stopped includes: |
964 | // - Win32-stopped |
965 | // - fake win32-stopped. Eg, between SuspendUnmanagedThreads & ResumeUnmanagedThreads |
966 | // (one way to get here is getting debug events during the special-deferment region) |
967 | // - synchronized |
968 | // If we're not stopped, then we fail. This macro must never return S_OK. |
969 | // |
970 | // If not-shimmed (using V3 pipeline), then skip all checks about stop-state. |
971 | #define ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(__proc, errorAction) \ |
972 | CordbProcess * __proc_for_ATT = (__proc); \ |
973 | DO_PRE_STOP_GO_CHECKS(errorAction); \ |
974 | RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \ |
975 | DO_POST_STOP_GO_CHECKS(errorAction); \ |
976 | if ((__proc_for_ATT)->GetShim() != NULL) { \ |
977 | if (!__proc_for_ATT->m_initialized) { errorAction(CORDBG_E_NOTREADY); } \ |
978 | if ((__proc_for_ATT)->IsStopped()) { \ |
979 | HRESULT _hr2 = (__proc_for_ATT)->StartSyncFromWin32Stop(NULL); \ |
980 | if (FAILED(_hr2)) errorAction(_hr2); \ |
981 | } \ |
982 | if (!_CORDBCheckProcessStateOKAndSync(__proc_for_ATT, NULL)) \ |
983 | errorAction(CORDBHRFromProcessStateWorker(__proc_for_ATT, NULL, E_FAIL)); \ |
984 | } |
985 | |
986 | #define ATT_REQUIRE_STOPPED_MAY_FAIL(__proc)ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(__proc, return) |
987 | |
988 | // #1b - allows it to be non-inited. This should look just like ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW |
989 | // except it doesn't do SSFW32Stop and doesn't have the m_initialized check. |
990 | #define ATT_REQUIRE_SYNCED_OR_NONINIT_MAY_FAIL(__proc) \ |
991 | CordbProcess * __proc_for_ATT = (__proc); \ |
992 | DO_PRE_STOP_GO_CHECKS(return); \ |
993 | RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \ |
994 | DO_POST_STOP_GO_CHECKS(return); \ |
995 | if ((__proc_for_ATT)->GetShim() != NULL) { \ |
996 | if (!_CORDBCheckProcessStateOKAndSync(__proc_for_ATT, NULL)) \ |
997 | return CORDBHRFromProcessStateWorker(__proc_for_ATT, NULL, E_FAIL); \ |
998 | } |
999 | |
1000 | |
1001 | |
1002 | // Gross variant on #1. |
1003 | // This is a very dangerous ATT contract; but we need to support it for backwards compat. |
1004 | // Some APIs, like ICDProcess:EnumerateThreads can be used before the process is actually |
1005 | // initialized (kind of for interop-debugging). |
1006 | // These can't check the m_initialized flag b/c that may not be set yet. |
1007 | // They also can't sync the runtime. |
1008 | // This should only be used for non-blocking leaf activity. |
1009 | #define ATT_EVERETT_HACK_REQUIRE_STOPPED_ALLOW_NONINIT(__proc) \ |
1010 | CordbProcess * __proc_for_ATT = (__proc); \ |
1011 | DO_PRE_STOP_GO_CHECKS(return); \ |
1012 | RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \ |
1013 | DO_POST_STOP_GO_CHECKS(return); \ |
1014 | if (((__proc_for_ATT)->GetShim() != NULL) && !(__proc_for_ATT)->IsStopped()) { return CORDBG_E_PROCESS_NOT_SYNCHRONIZED; } \ |
1015 | |
1016 | |
1017 | // #2 - caller may think debuggee is live, but throw in a Stop-Continue holder. |
1018 | #define ATT_ALLOW_LIVE_DO_STOPGO(__proc) \ |
1019 | CordbProcess * __proc_for_ATT = (__proc); \ |
1020 | DO_PRE_STOP_GO_CHECKS(return); \ |
1021 | CORDBRequireProcessStateOK(__proc_for_ATT); \ |
1022 | RSLockHolder __ch(__proc_for_ATT->GetStopGoLock()); \ |
1023 | DO_POST_STOP_GO_CHECKS(return); \ |
1024 | StopContinueHolder __hStopGo; \ |
1025 | if ((__proc_for_ATT)->GetShim() != NULL) \ |
1026 | { \ |
1027 | HRESULT _hr2 = __hStopGo.Init(__proc_for_ATT); \ |
1028 | if (FAILED(_hr2)) return _hr2; \ |
1029 | _ASSERTE((__proc_for_ATT)->GetSynchronized()); \ |
1030 | } \ |
1031 | |
1032 | |
1033 | |
1034 | |
1035 | //----------------------------------------------------------------------------- |
1036 | // StopContinueHolder. Ensure that we're synced during a certain region. |
1037 | // (Particularly when sending an IPCEvent) |
1038 | // Calls ICorDebugProcess::Stop & IMDArocess::Continue. |
1039 | // Example usage: |
1040 | // |
1041 | // { |
1042 | // StopContinueHolder h; |
1043 | // IfFailRet(h.Init(process)) |
1044 | // SendIPCEvent |
1045 | // } // continue automatically called. |
1046 | //----------------------------------------------------------------------------- |
1047 | |
1048 | class CordbProcess; |
1049 | class StopContinueHolder |
1050 | { |
1051 | public: |
1052 | StopContinueHolder() : m_p(NULL) { }; |
1053 | |
1054 | HRESULT Init(CordbProcess * p); |
1055 | ~StopContinueHolder(); |
1056 | |
1057 | protected: |
1058 | CordbProcess * m_p; |
1059 | }; |
1060 | |
1061 | |
1062 | /* ------------------------------------------------------------------------- * |
1063 | * Base class |
1064 | * ------------------------------------------------------------------------- */ |
1065 | |
1066 | #define COM_METHOD HRESULT STDMETHODCALLTYPE |
1067 | |
1068 | typedef enum { |
1069 | enumCordbUnknown, // 0 |
1070 | enumCordb, // 1 1 [1]x1 |
1071 | enumCordbProcess, // 2 1 [1]x1 |
1072 | enumCordbAppDomain, // 3 1 [1]x1 |
1073 | enumCordbAssembly, // 4 |
1074 | enumCordbModule, // 5 15 [27-38,55-57]x1 |
1075 | enumCordbClass, // 6 |
1076 | enumCordbFunction, // 7 |
1077 | enumCordbThread, // 8 2 [4,7]x1 |
1078 | enumCordbCode, // 9 |
1079 | enumCordbChain, // 10 |
1080 | enumCordbChainEnum, // 11 |
1081 | enumCordbContext, // 12 |
1082 | enumCordbFrame, // 13 |
1083 | enumCordbFrameEnum, // 14 |
1084 | enumCordbValueEnum, // 15 |
1085 | enumCordbRegisterSet, // 16 |
1086 | enumCordbJITILFrame, // 17 |
1087 | enumCordbBreakpoint, // 18 |
1088 | enumCordbStepper, // 19 |
1089 | enumCordbValue, // 20 |
1090 | enumCordbEnCSnapshot, // 21 |
1091 | enumCordbEval, // 22 |
1092 | enumCordbUnmanagedThread,// 23 |
1093 | enumCorpubPublish, // 24 |
1094 | enumCorpubProcess, // 25 |
1095 | enumCorpubAppDomain, // 26 |
1096 | enumCorpubProcessEnum, // 27 |
1097 | enumCorpubAppDomainEnum,// 28 |
1098 | enumCordbEnumFilter, // 29 |
1099 | enumCordbEnCErrorInfo, // 30 |
1100 | enumCordbEnCErrorInfoEnum,//31 |
1101 | enumCordbUnmanagedEvent,// 32 |
1102 | enumCordbWin32EventThread,//33 |
1103 | enumCordbRCEventThread, // 34 |
1104 | enumCordbNativeFrame, // 35 |
1105 | enumCordbObjectValue, // 36 |
1106 | enumCordbType, // 37 |
1107 | enumCordbNativeCode, // 38 |
1108 | enumCordbILCode, // 39 |
1109 | enumCordbEval2, // 40 |
1110 | enumCordbMDA, // 41 |
1111 | enumCordbHashTableEnum, // 42 |
1112 | enumCordbCodeEnum, // 43 |
1113 | enumCordbStackWalk, // 44 |
1114 | enumCordbEnumerator, // 45 |
1115 | enumCordbHeap, // 48 |
1116 | enumCordbHeapSegments, // 47 |
1117 | enumMaxDerived, // |
1118 | enumMaxThis = 1024 |
1119 | } enumCordbDerived; |
1120 | |
1121 | |
1122 | |
1123 | //----------------------------------------------------------------------------- |
1124 | // Support for Native Breakpoints |
1125 | //----------------------------------------------------------------------------- |
1126 | struct NativePatch |
1127 | { |
1128 | void * pAddress; // pointer into the LS address space. |
1129 | PRD_TYPE opcode; // opcode to restore with. |
1130 | |
1131 | inline bool operator==(NativePatch p2) |
1132 | { |
1133 | return memcmp(this, &p2, sizeof(p2)) == 0; |
1134 | } |
1135 | }; |
1136 | |
1137 | //----------------------------------------------------------------------------- |
1138 | // Cross-platform patch operations |
1139 | //----------------------------------------------------------------------------- |
1140 | |
1141 | // Remove the int3 from the remote address |
1142 | HRESULT RemoveRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE opcode); |
1143 | |
1144 | // This flavor is assuming our caller already knows the opcode. |
1145 | HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress); |
1146 | |
1147 | // Apply the patch and get the opcode that we're replacing. |
1148 | HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE * pOpcode); |
1149 | |
1150 | |
1151 | class CordbHashTable; |
1152 | |
1153 | #define CORDB_COMMON_BASE_SIGNATURE 0x0d00d96a |
1154 | #define CORDB_COMMON_BASE_SIGNATURE_DEAD 0x0dead0b1 |
1155 | |
1156 | // Common base for both CorPublish + CorDebug objects. |
1157 | class CordbCommonBase : public IUnknown |
1158 | { |
1159 | public: |
1160 | // GENERIC: made this private as I'm changing the use of m_id for CordbClass, and |
1161 | // I want to make sure I catch all the places where m_id is used directly and cast |
1162 | // to/from tokens and/or (void*). |
1163 | UINT_PTR m_id; |
1164 | |
1165 | #ifdef _DEBUG |
1166 | static LONG m_saDwInstance[enumMaxDerived]; // instance x this |
1167 | static LONG m_saDwAlive[enumMaxDerived]; |
1168 | static PVOID m_sdThis[enumMaxDerived][enumMaxThis]; |
1169 | DWORD m_dwInstance; |
1170 | enumCordbDerived m_type; |
1171 | #endif |
1172 | |
1173 | |
1174 | |
1175 | private: |
1176 | DWORD m_signature : 30; |
1177 | |
1178 | // Sticky bit set when we neuter an object. All methods (besides AddRef,Release,QI) |
1179 | // should check this bit and fail via the FAIL_IF_NEUTERED macro. |
1180 | DWORD m_fIsNeutered : 1; |
1181 | |
1182 | // Mark that this object can be "neutered at will". NeuterList::SweepAllNeuterAtWillObjects |
1183 | // looks at this bit. |
1184 | // For some objects, we don't explicitly mark when the lifetime is up. The only way |
1185 | // we know is when external count goes to 0. This avoids forcing us to do cleanup |
1186 | // in the dtor (which may come at a bad time). Sticky bit set in BaseRelease(). |
1187 | DWORD m_fNeuterAtWill : 1; |
1188 | public: |
1189 | |
1190 | static LONG s_CordbObjectUID; // Unique ID for each object. |
1191 | static LONG s_TotalObjectCount; // total number of outstanding objects. |
1192 | |
1193 | |
1194 | void ValidateObject() |
1195 | { |
1196 | if( !IsValidObject() ) |
1197 | { |
1198 | STRESS_LOG1(LF_ASSERT, LL_ALWAYS, "CordbCommonBase::IsValidObject() failed: %x\n" , this); |
1199 | _ASSERTE(!"CordbCommonBase::IsValidObject() failed" ); |
1200 | FreeBuildDebugBreak(); |
1201 | } |
1202 | } |
1203 | |
1204 | bool IsValidObject() |
1205 | { |
1206 | return (m_signature == CORDB_COMMON_BASE_SIGNATURE); |
1207 | } |
1208 | |
1209 | CordbCommonBase(UINT_PTR id, enumCordbDerived type) |
1210 | { |
1211 | init(id, type); |
1212 | } |
1213 | |
1214 | CordbCommonBase(UINT_PTR id) |
1215 | { |
1216 | init(id, enumCordbUnknown); |
1217 | } |
1218 | |
1219 | void init(UINT_PTR id, enumCordbDerived type) |
1220 | { |
1221 | // To help us track object leaks, we want to log when we create & destory CordbBase objects. |
1222 | #ifdef _DEBUG |
1223 | InterlockedIncrement(&s_TotalObjectCount); |
1224 | InterlockedIncrement(&s_CordbObjectUID); |
1225 | |
1226 | LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object allocated: this=%p, count=%d, id=%p, Type=%d\n" , this, s_CordbObjectUID, id, type)); |
1227 | #endif |
1228 | |
1229 | m_signature = CORDB_COMMON_BASE_SIGNATURE; |
1230 | m_fNeuterAtWill = 0; |
1231 | m_fIsNeutered = 0; |
1232 | |
1233 | m_id = id; |
1234 | m_RefCount = 0; |
1235 | |
1236 | #ifdef _DEBUG |
1237 | m_type = type; |
1238 | //m_dwInstance = CordbBase::m_saDwInstance[m_type]; |
1239 | //InterlockedIncrement(&CordbBase::m_saDwInstance[m_type]); |
1240 | //InterlockedIncrement(&CordbBase::m_saDwAlive[m_type]); |
1241 | //if (m_dwInstance < enumMaxThis) |
1242 | //{ |
1243 | // m_sdThis[m_type][m_dwInstance] = this; |
1244 | //} |
1245 | #endif |
1246 | } |
1247 | |
1248 | virtual ~CordbCommonBase() |
1249 | { |
1250 | // If we're deleting, we really should have released any outstanding reference. |
1251 | // If we call Release() on a deleted object, we'll av (especially b/c Release |
1252 | // may call delete again). |
1253 | CONSISTENCY_CHECK_MSGF(m_RefCount == 0, ("Deleting w/ non-zero ref count. 0x%08x" , m_RefCount)); |
1254 | |
1255 | #ifdef _DEBUG |
1256 | //InterlockedDecrement(&CordbBase::m_saDwAlive[m_type]); |
1257 | //if (m_dwInstance < enumMaxThis) |
1258 | //{ |
1259 | // m_sdThis[m_type][m_dwInstance] = NULL; |
1260 | //} |
1261 | #endif |
1262 | // To help us track object leaks, we want to log when we create & destory CordbBase objects. |
1263 | LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object deleted: this=%p, id=%p, Refcount=0x%x\n" , this, m_id, m_RefCount)); |
1264 | |
1265 | #ifdef _DEBUG |
1266 | LONG newTotalObjectsCount = InterlockedDecrement(&s_TotalObjectCount); |
1267 | _ASSERTE(newTotalObjectsCount >= 0); |
1268 | #endif |
1269 | |
1270 | // Don't shutdown logic until everybody is done with it. |
1271 | // If we leak objects, this may mean that we never shutdown logging at all! |
1272 | #if defined(_DEBUG) && defined(LOGGING) |
1273 | if (newTotalObjectsCount == 0) |
1274 | { |
1275 | ShutdownLogging(); |
1276 | } |
1277 | #endif |
1278 | } |
1279 | |
1280 | /* |
1281 | Member function behavior of a neutered COM object: |
1282 | |
1283 | 1. AddRef(), Release(), QueryInterface() work as normal. |
1284 | a. This gives folks who are responsible for pairing a Release() with |
1285 | an AddRef() a chance to dereference their pointer and call Release() |
1286 | when they are informed, explicitly or implicitly, that the object is neutered. |
1287 | |
1288 | 2. Any other member function will return an error code unless documented. |
1289 | a. If a member function returns information when the COM object is |
1290 | neutered then the semantics of that function need to be documented. |
1291 | (ie. If an AppDomain is unloaded and you have a reference to the COM |
1292 | object representing the AppDomain, how _should_ it behave? That behavior |
1293 | should be documented) |
1294 | |
1295 | |
1296 | Postcondions of Neuter(): |
1297 | |
1298 | 1. All circular references (aka back-pointers) are "broken". They are broken |
1299 | by calling Release() on all "Weak References" to the object. If you're a purist, |
1300 | these pointers should also be NULLed out. |
1301 | a. Weak References/Strong References: |
1302 | i. If any objects are not "reachable" from the root (ie. stack or from global pointers) |
1303 | they should be reclaimed. If they are not, they are leaked and there is an issue. |
1304 | ii. There must be a partial order on the objects such that if A < B then: |
1305 | 1. A has a reference to B. This reference is a "strong reference" |
1306 | 2. A, and thus B, is reachable from the root |
1307 | iii. If a reference belongs in the partial order then it is a "strong reference" else |
1308 | it is a weak reference. |
1309 | *** 2. Sufficient conditions to ensure no COM objects are leaked: *** |
1310 | a. When Neuter() is invoked: |
1311 | i. Calles Release on all its weak references. |
1312 | ii. Then, for each strong reference: |
1313 | 1. invoke Neuter() |
1314 | 2. invoke Release() |
1315 | iii. If it's derived from a CordbXXX class, call Neuter() on the base class. |
1316 | 1. Sense Neuter() is virtual, use the scope specifier Cordb[BaseClass]::Neuter(). |
1317 | 3. All members return error codes, except: |
1318 | a. Members of IUknown, AddRef(), Release(), QueryInterfac() |
1319 | b. Those documented to have functionality when the object is neutered. |
1320 | i. Neuter() still works w/o error. If it is invoke a second time it will have already |
1321 | released all its strong and weak references so it could just return. |
1322 | |
1323 | |
1324 | Alternate design ideas: |
1325 | |
1326 | DESIGN: Note that it's possible for object B to have two parents in the partial order |
1327 | and it must be documented which one is responsible for calling Neuter() on B. |
1328 | 1. For example, CordbCode could reasonably be a sibling of CordbFunction and CordbNativeFrame. |
1329 | Which one should call Release()? For now we have CordbFunction call Release() on CordbCode. |
1330 | |
1331 | DESIGN: It is not a necessary condition in that Neuter() invoke Release() on all |
1332 | it's strong references. Instead, it would be sufficient to ensure all object are released, that |
1333 | each object call Release() on all its strong pointers in its destructor. |
1334 | 1. This might be done if its necessary for some member to return "tombstone" |
1335 | information after the object has been netuered() which involves the siblings (wrt poset) |
1336 | of the object. However, no sibling could access a parent (wrt poset) because |
1337 | Neuter called Release() on all its weak pointers. |
1338 | |
1339 | DESIGN: Rename Neuter() to some name that more accurately reflect the semantics. |
1340 | 1. The three operations are: |
1341 | a. ReleaseWeakPointers() |
1342 | b. NeuterStrongPointers() |
1343 | c. ReleaseStrongPointers() |
1344 | 1. Assert that it's done after NeuterStrongPointers() |
1345 | 2. That would introduce a bunch of functions... but it would be clear. |
1346 | |
1347 | DESIGN: CordbBase could provide a function to register strong and weak references. That way CordbBase |
1348 | could implement a general version of ReleaseWeak/ReleaseStrong/NeuterStrongPointers(). This |
1349 | would provide a very error resistant framework for extending the object model plus it would |
1350 | be very explicit about what is going on. |
1351 | One thing that might trip this is idea up is that if an object has two parents, |
1352 | like the CordbCode might, then either both objects call Neuter or one is reference |
1353 | is made weak. |
1354 | |
1355 | |
1356 | Our implementation: |
1357 | |
1358 | The graph formed by the strong references must remain acyclic. |
1359 | It's up to the developer (YOU!) to ensure that each Neuter |
1360 | function maintains that invariant. |
1361 | |
1362 | Here is the current Partial Order on CordbXXX objects. (All these classes |
1363 | eventually chain to CordbBase.Neuter() for completeness.) |
1364 | |
1365 | Cordb |
1366 | CordbProcess |
1367 | CordbAppDomain |
1368 | CordbBreakPoints |
1369 | CordbAssembly |
1370 | CordbModule |
1371 | CordbClass |
1372 | CordbFunction |
1373 | CordbCode (Can we assert a thread will not reference |
1374 | the same CordbCode as a CordbFunction?) |
1375 | CordbThread |
1376 | CordbChains |
1377 | CordbNativeFrame -> CordbFrame (Chain to baseClass) |
1378 | CordbJITILFrame |
1379 | |
1380 | |
1381 | <TODO>TODO: Some Neuter functions have not yet been implemented due to time restrictions.</TODO> |
1382 | |
1383 | <TODO>TODO: Some weak references never have AddRef() called on them. If that's cool then |
1384 | it should be stated in the documentation. Else it should be changed.</TODO> |
1385 | */ |
1386 | |
1387 | virtual void Neuter(); |
1388 | |
1389 | // Unsafe neuter for an object that's already dead. |
1390 | void UnsafeNeuterDeadObject(); |
1391 | |
1392 | |
1393 | #ifdef _DEBUG |
1394 | // For debugging (asserts, logging, etc) provide a pretty name (this is 1:1 w/ the VTable) |
1395 | // We provide a default impl in the base object in case this gets called from a dtor (virtuals |
1396 | // called from dtors use the base version, not the derived). A pure call would AV in that case. |
1397 | virtual const char * DbgGetName() { return "CordbBase" ; }; |
1398 | #endif |
1399 | |
1400 | bool IsNeutered() const {LIMITED_METHOD_CONTRACT; return m_fIsNeutered == 1; } |
1401 | bool IsNeuterAtWill() const { LIMITED_METHOD_CONTRACT; return m_fNeuterAtWill == 1; } |
1402 | void MarkNeuterAtWill() { LIMITED_METHOD_CONTRACT; m_fNeuterAtWill = 1; } |
1403 | |
1404 | //----------------------------------------------------------- |
1405 | // IUnknown support |
1406 | //---------------------------------------------------------- |
1407 | |
1408 | private: |
1409 | // We maintain both an internal + external refcount. This allows us to catch |
1410 | // if an external caller has too many releases. |
1411 | // low bits are internal count, high bits are external count |
1412 | // so Total count = (m_RefCount & CordbBase_InternalRefCountMask) + (m_RefCount >> CordbBase_ExternalRefCountShift); |
1413 | typedef LONGLONG MixedRefCountSigned; |
1414 | typedef ULONGLONG MixedRefCountUnsigned; |
1415 | typedef LONG ExternalRefCount; |
1416 | MixedRefCountUnsigned m_RefCount; |
1417 | public: |
1418 | |
1419 | // Adjust the internal ref count. |
1420 | // These aren't available to the external world, so only internal code can manipulate the internal count. |
1421 | void InternalAddRef(); |
1422 | void InternalRelease(); |
1423 | |
1424 | // Derived versions of AddRef / Release will call these. |
1425 | // External AddRef & Release |
1426 | // These do not have any additional Asserts to enforce that we're not manipulating the external count |
1427 | // from internal. |
1428 | ULONG STDMETHODCALLTYPE BaseAddRef(); |
1429 | ULONG STDMETHODCALLTYPE BaseRelease(); |
1430 | |
1431 | // External ref count versions, with extra debug count to enforce that this is done externally. |
1432 | // When derive classes use these versions, it Asserts that we're not adjusting external counts from inside. |
1433 | // Thus we can be confident that we're *never* leaking external refs to these objects. |
1434 | // @todo - eventually everything should use these. |
1435 | ULONG STDMETHODCALLTYPE BaseAddRefEnforceExternal(); |
1436 | ULONG STDMETHODCALLTYPE BaseReleaseEnforceExternal(); |
1437 | |
1438 | // Do an AddRef against the External count. This is a semantics issue. |
1439 | // We use this when an internal component Addrefs out-parameters (which Cordbg will call Release on). |
1440 | // This just does a regular external AddRef(). |
1441 | void ExternalAddRef(); |
1442 | |
1443 | protected: |
1444 | |
1445 | static void InitializeCommon(); |
1446 | |
1447 | private: |
1448 | static void AddDebugPrivilege(); |
1449 | }; |
1450 | |
1451 | #define CordbBase_ExternalRefCountShift 32 |
1452 | #define CordbBase_InternalRefCountMask 0xFFFFFFFF |
1453 | #define CordbBase_InternalRefCountMax 0x7FFFFFFF |
1454 | |
1455 | #ifdef _DEBUG |
1456 | // Does the given Cordb object type have affinity to a CordbProcess object? |
1457 | // This is only used for certain asserts. |
1458 | inline bool DoesCordbObjectTypeHaveProcessPtr(enumCordbDerived type) |
1459 | { |
1460 | return |
1461 | (type != enumCordbCodeEnum) && |
1462 | (type != enumCordb) && |
1463 | (type != enumCordbHashTableEnum); |
1464 | } |
1465 | #endif |
1466 | |
1467 | // Base class specifically for CorDebug objects |
1468 | class CordbBase : public CordbCommonBase |
1469 | { |
1470 | public: |
1471 | CordbBase(CordbProcess * pProcess, UINT_PTR id, enumCordbDerived type) : CordbCommonBase(id, type) |
1472 | { |
1473 | // CordbProcess can't pass 'this' to base class, per error C4355. So we pass null and set later. |
1474 | _ASSERTE((pProcess != NULL) || |
1475 | ((type) == enumCordbProcess) || |
1476 | !DoesCordbObjectTypeHaveProcessPtr(type)); |
1477 | |
1478 | m_pProcess.Assign(pProcess); |
1479 | } |
1480 | |
1481 | CordbBase(CordbProcess * pProcess, UINT_PTR id) : CordbCommonBase(id) |
1482 | { |
1483 | _ASSERTE(pProcess != NULL); |
1484 | m_pProcess.Assign(pProcess); |
1485 | } |
1486 | |
1487 | virtual ~CordbBase() |
1488 | { |
1489 | // Derived classes should not have cleared out our pointer. |
1490 | // CordbProcess's Neuter explicitly nulls out its pointer to avoid circular reference. |
1491 | _ASSERTE(m_pProcess!= NULL || |
1492 | (CordbCommonBase::m_type == enumCordbProcess) || |
1493 | !DoesCordbObjectTypeHaveProcessPtr(CordbCommonBase::m_type)); |
1494 | |
1495 | // Ideally, all CorDebug objects to be neutered by the time their dtor is called. |
1496 | // @todo - we're still working out neutering semantics for a few remaining objects, so we exclude |
1497 | // those from the assert. |
1498 | _ASSERTE(IsNeutered() || |
1499 | (m_type == enumCordbBreakpoint) || |
1500 | (m_type == enumCordbStepper)); |
1501 | } |
1502 | |
1503 | // Neuter just the right-side state. |
1504 | virtual void Neuter(); |
1505 | |
1506 | // Neuter both left-side state and right-side state. |
1507 | virtual void NeuterLeftSideResources(); |
1508 | |
1509 | // Get the CordbProcess object that this CordbBase object is associated with (or NULL if there's none). |
1510 | CordbProcess * GetProcess() const |
1511 | { |
1512 | return m_pProcess; |
1513 | } |
1514 | protected: |
1515 | // All objects need a strong pointer back to the process so that they can get access to key locks |
1516 | // held by the process (StopGo lock) so that they can synchronize their operations against neutering. |
1517 | // This pointer is cleared in our dtor, and not when we're neutered. Since we can't control when the |
1518 | // dtor is called (it's controlled by external references), we classify this as an external reference too. |
1519 | // |
1520 | // This is the only "strong" reference backpointer that objects need have. All other backpointers can be weak references |
1521 | // because when a parent object is neutered, it will null out all weak reference pointers in all of its children. |
1522 | // That will also break any potential cycles. |
1523 | RSUnsafeExternalSmartPtr<CordbProcess> m_pProcess; |
1524 | |
1525 | }; |
1526 | |
1527 | |
1528 | |
1529 | |
1530 | |
1531 | //----------------------------------------------------------------------------- |
1532 | // Macro to check if a CordbXXX object is neutered, and return a standard |
1533 | // error code if it is. |
1534 | // We pass the 'this' pointer of the object in because it gives us some extra |
1535 | // flexibility and lets us log debug info. |
1536 | // It is an API breach to access a neutered object. |
1537 | //----------------------------------------------------------------------------- |
1538 | #define FAIL_IF_NEUTERED(pThis) \ |
1539 | int _____Neuter_Status_Already_Marked; \ |
1540 | _____Neuter_Status_Already_Marked = 0; \ |
1541 | {\ |
1542 | if (pThis->IsNeutered()) { \ |
1543 | LOG((LF_CORDB, LL_ALWAYS, "Accessing a neutered object at %p\n", pThis)); \ |
1544 | return ErrWrapper(CORDBG_E_OBJECT_NEUTERED); \ |
1545 | } \ |
1546 | } |
1547 | |
1548 | //----------------------------------------------------------------------------- |
1549 | // Macro to check if a CordbXXX object is neutered, and return a standard |
1550 | // error code if it is. |
1551 | // We pass the 'this' pointer of the object in because it gives us some extra |
1552 | // flexibility and lets us log debug info. |
1553 | // It is an API breach to access a neutered object. |
1554 | //----------------------------------------------------------------------------- |
1555 | #define THROW_IF_NEUTERED(pThis) \ |
1556 | int _____Neuter_Status_Already_Marked; \ |
1557 | _____Neuter_Status_Already_Marked = 0; \ |
1558 | {\ |
1559 | if (pThis->IsNeutered()) { \ |
1560 | LOG((LF_CORDB, LL_ALWAYS, "Accessing a neutered object at %p\n", pThis)); \ |
1561 | ThrowHR(CORDBG_E_OBJECT_NEUTERED); \ |
1562 | } \ |
1563 | } |
1564 | |
1565 | // We have an OK_IF_NEUTERED macro to say that this method can be safely |
1566 | // called if we're neutered. Mostly for semantic benefits. |
1567 | // Also, if a method is marked OK, then somebody won't go and add a 'fail' |
1568 | // This is an extremely dangerous quality because: |
1569 | // 1) it means that we have no synchronization (can't take the Stop-Go lock) |
1570 | // 2) none of our backpointers are usable (they may be nulled out at anytime by another thread). |
1571 | // - this also means we absolutely can't send IPC events (since that requires a CordbProcess) |
1572 | // 3) The only safe data are blittalbe embedded fields (eg, a pid or stack range) |
1573 | // |
1574 | // Any usage of this macro should clearly specify why this is safe. |
1575 | #define OK_IF_NEUTERED(pThis) \ |
1576 | int _____Neuter_Status_Already_Marked; \ |
1577 | _____Neuter_Status_Already_Marked = 0; |
1578 | |
1579 | |
1580 | //------------------------------------------------------------------------------- |
1581 | // Simple COM enumerator pattern on a fixed list of items |
1582 | //-------------------------------------------------------------------------------- |
1583 | template< typename ElemType, |
1584 | typename ElemPublicType, |
1585 | typename EnumInterfaceType, |
1586 | ElemPublicType (*GetPublicType)(ElemType)> |
1587 | class CordbEnumerator : public CordbBase, public EnumInterfaceType |
1588 | { |
1589 | private: |
1590 | // the list of items being enumerated over |
1591 | ElemType *m_items; |
1592 | // the number of items in the list |
1593 | DWORD m_countItems; |
1594 | // the index of the next item to be returned in the enumeration |
1595 | DWORD m_nextIndex; |
1596 | |
1597 | public: |
1598 | // makes a copy of the elements in the "items" array |
1599 | CordbEnumerator(CordbProcess* pProcess, ElemType *items, DWORD elemCount); |
1600 | // assumes ownership of the elements in the "*items" array. |
1601 | // this avoids an extra allocation + copy |
1602 | CordbEnumerator(CordbProcess* pProcess, ElemType **items, DWORD elemCount); |
1603 | ~CordbEnumerator(); |
1604 | |
1605 | // IUnknown interface |
1606 | virtual COM_METHOD QueryInterface(REFIID riid, VOID** ppInterface); |
1607 | virtual ULONG STDMETHODCALLTYPE AddRef(); |
1608 | virtual ULONG STDMETHODCALLTYPE Release(); |
1609 | |
1610 | // ICorDebugEnum interface |
1611 | virtual COM_METHOD Clone(ICorDebugEnum **ppEnum); |
1612 | virtual COM_METHOD GetCount(ULONG *pcelt); |
1613 | virtual COM_METHOD Reset(); |
1614 | virtual COM_METHOD Skip(ULONG celt); |
1615 | |
1616 | // ICorDebugXXXEnum interface |
1617 | virtual COM_METHOD Next(ULONG celt, ElemPublicType items[], ULONG *pceltFetched); |
1618 | |
1619 | // CordbBase overrides |
1620 | virtual VOID Neuter(); |
1621 | }; |
1622 | |
1623 | // Converts T to U* by using QueryInterface |
1624 | template<typename T, typename U> |
1625 | U* QueryInterfaceConvert(T obj); |
1626 | |
1627 | // No conversion, just returns the argument |
1628 | template<typename T> |
1629 | T IdentityConvert(T obj); |
1630 | |
1631 | // CorDebugGuidToTypeMapping-adapter used by CordbGuidToTypeEnumerator |
1632 | // in the CordbEnumerator pattern |
1633 | struct RsGuidToTypeMapping |
1634 | { |
1635 | GUID iid; |
1636 | RSSmartPtr<CordbType> spType; |
1637 | }; |
1638 | |
1639 | inline |
1640 | CorDebugGuidToTypeMapping GuidToTypeMappingConvert(RsGuidToTypeMapping m) |
1641 | { |
1642 | CorDebugGuidToTypeMapping result; |
1643 | result.iid = m.iid; |
1644 | result.pType = (ICorDebugType*)(m.spType.GetValue()); |
1645 | result.pType->AddRef(); |
1646 | return result; |
1647 | } |
1648 | |
1649 | // |
1650 | // Some useful enumerators |
1651 | // |
1652 | typedef CordbEnumerator<RSSmartPtr<CordbThread>, |
1653 | ICorDebugThread*, |
1654 | ICorDebugThreadEnum, |
1655 | QueryInterfaceConvert<RSSmartPtr<CordbThread>, ICorDebugThread> > CordbThreadEnumerator; |
1656 | |
1657 | typedef CordbEnumerator<CorDebugBlockingObject, |
1658 | CorDebugBlockingObject, |
1659 | ICorDebugBlockingObjectEnum, |
1660 | IdentityConvert<CorDebugBlockingObject> > CordbBlockingObjectEnumerator; |
1661 | |
1662 | // Template classes must be fully defined rather than just declared in the header |
1663 | #include "rsenumerator.hpp" |
1664 | |
1665 | |
1666 | typedef CordbEnumerator<COR_SEGMENT, |
1667 | COR_SEGMENT, |
1668 | ICorDebugHeapSegmentEnum, |
1669 | IdentityConvert<COR_SEGMENT> > CordbHeapSegmentEnumerator; |
1670 | |
1671 | typedef CordbEnumerator<CorDebugExceptionObjectStackFrame, |
1672 | CorDebugExceptionObjectStackFrame, |
1673 | ICorDebugExceptionObjectCallStackEnum, |
1674 | IdentityConvert<CorDebugExceptionObjectStackFrame> > CordbExceptionObjectCallStackEnumerator; |
1675 | |
1676 | typedef CordbEnumerator<RsGuidToTypeMapping, |
1677 | CorDebugGuidToTypeMapping, |
1678 | ICorDebugGuidToTypeEnum, |
1679 | GuidToTypeMappingConvert > CordbGuidToTypeEnumerator; |
1680 | |
1681 | typedef CordbEnumerator<RSSmartPtr<CordbVariableHome>, |
1682 | ICorDebugVariableHome*, |
1683 | ICorDebugVariableHomeEnum, |
1684 | QueryInterfaceConvert<RSSmartPtr<CordbVariableHome>, ICorDebugVariableHome> > CordbVariableHomeEnumerator; |
1685 | |
1686 | // ---------------------------------------------------------------------------- |
1687 | // Hash table for CordbBase objects. |
1688 | // - Uses Internal AddRef/Release (not external) |
1689 | // - Templatize for type-safety w/ Cordb objects |
1690 | // - Many hashtables are implicitly protected by a lock. For debug-only, we |
1691 | // explicitly associate w/ an optional RSLock and assert that lock is held on access. |
1692 | // ---------------------------------------------------------------------------- |
1693 | |
1694 | struct CordbHashEntry |
1695 | { |
1696 | FREEHASHENTRY entry; |
1697 | CordbBase *pBase; |
1698 | }; |
1699 | |
1700 | class CordbHashTable : private CHashTableAndData<CNewDataNoThrow> |
1701 | { |
1702 | private: |
1703 | bool m_initialized; |
1704 | SIZE_T m_count; |
1705 | |
1706 | BOOL Cmp(SIZE_T k1, const HASHENTRY * pc2) |
1707 | { |
1708 | LIMITED_METHOD_CONTRACT; |
1709 | |
1710 | return ((ULONG_PTR)k1) != (reinterpret_cast<const CordbHashEntry *>(pc2))->pBase->m_id; |
1711 | } |
1712 | |
1713 | ULONG HASH(ULONG_PTR id) |
1714 | { |
1715 | return (ULONG)(id); |
1716 | } |
1717 | |
1718 | SIZE_T KEY(UINT_PTR id) |
1719 | { |
1720 | return (SIZE_T)id; |
1721 | } |
1722 | |
1723 | public: |
1724 | bool IsInitialized(); |
1725 | |
1726 | #ifndef DACCESS_COMPILE |
1727 | CordbHashTable(ULONG size) |
1728 | : CHashTableAndData<CNewDataNoThrow>(size), m_initialized(false), m_count(0) |
1729 | { |
1730 | #ifdef _DEBUG |
1731 | m_pDbgLock = NULL; |
1732 | m_dbgChangeCount = 0; |
1733 | #endif |
1734 | } |
1735 | virtual ~CordbHashTable(); |
1736 | |
1737 | #ifdef _DEBUG |
1738 | // CordbHashTables may be protected by a lock. For debug-builds, we can associate |
1739 | // the hash w/ that lock and then assert if it's not held. |
1740 | void DebugSetRSLock(RSLock * pLock) |
1741 | { |
1742 | m_pDbgLock = pLock; |
1743 | } |
1744 | int GetChangeCount() { return m_dbgChangeCount; } |
1745 | private: |
1746 | void AssertIsProtected(); |
1747 | |
1748 | // Increment the Change count. This can be used to check if the hashtable changes while being enumerated. |
1749 | void DbgIncChangeCount() { m_dbgChangeCount++; } |
1750 | |
1751 | int m_dbgChangeCount; |
1752 | RSLock * m_pDbgLock; |
1753 | #else |
1754 | // RSLock association is a no-op on free builds. |
1755 | void AssertIsProtected() { }; |
1756 | void DbgIncChangeCount() { }; |
1757 | #endif // _DEBUG |
1758 | |
1759 | public: |
1760 | |
1761 | |
1762 | #endif |
1763 | |
1764 | ULONG32 GetCount() |
1765 | { |
1766 | return ((ULONG32)m_count); |
1767 | } |
1768 | |
1769 | // These operators are unsafe b/c they have no typesafety. |
1770 | // Use a derived CordbSafeHashTable<T> instead. |
1771 | HRESULT UnsafeAddBase(CordbBase *pBase); |
1772 | HRESULT UnsafeSwapBase(CordbBase* pBaseOld, CordbBase* pBaseNew); |
1773 | CordbBase *UnsafeGetBase(ULONG_PTR id, BOOL fFab = TRUE); |
1774 | CordbBase *UnsafeRemoveBase(ULONG_PTR id); |
1775 | |
1776 | CordbBase *UnsafeFindFirst(HASHFIND *find); |
1777 | CordbBase *UnsafeFindNext(HASHFIND *find); |
1778 | |
1779 | // Unlocked versions don't assert that the lock us held. |
1780 | CordbBase *UnsafeUnlockedFindFirst(HASHFIND *find); |
1781 | CordbBase *UnsafeUnlockedFindNext(HASHFIND *find); |
1782 | |
1783 | }; |
1784 | |
1785 | |
1786 | // Typesafe wrapper around a normal hash table |
1787 | // T is expected to be a derived clas of CordbBase |
1788 | // Note that this still isn't fully typesafe. Ideally we'd take a strongly-typed key |
1789 | // instead of UINT_PTR (the type could have a fixed relationship to T, or could be |
1790 | // an additional template argument like standard template hash tables like std::hash_map<K,V>) |
1791 | template <class T> |
1792 | class CordbSafeHashTable : public CordbHashTable |
1793 | { |
1794 | public: |
1795 | #ifndef DACCESS_COMPILE |
1796 | CordbSafeHashTable<T>(ULONG size) : CordbHashTable(size) |
1797 | { |
1798 | } |
1799 | #endif |
1800 | // Typesafe wrappers |
1801 | HRESULT AddBase(T * pBase) { return UnsafeAddBase(pBase); } |
1802 | |
1803 | // Either add (eg, future cals to GetBase will succeed) or throw. |
1804 | void AddBaseOrThrow(T * pBase) |
1805 | { |
1806 | HRESULT hr = AddBase(pBase); |
1807 | IfFailThrow(hr); |
1808 | } |
1809 | HRESULT SwapBase(T* pBaseOld, T* pBaseNew) { return UnsafeSwapBase(pBaseOld, pBaseNew); } |
1810 | // Move the function definition of GetBase to rspriv.inl to work around gcc 2.9.5 warnings |
1811 | T* GetBase(ULONG_PTR id, BOOL fFab = TRUE); |
1812 | T* GetBaseOrThrow(ULONG_PTR id, BOOL fFab = TRUE); |
1813 | |
1814 | T* RemoveBase(ULONG_PTR id) { return static_cast<T*>(UnsafeRemoveBase(id)); } |
1815 | |
1816 | T* FindFirst(HASHFIND *find) { return static_cast<T*>(UnsafeFindFirst(find)); } |
1817 | T* FindNext(HASHFIND *find) { return static_cast<T*>(UnsafeFindNext(find)); } |
1818 | |
1819 | // Neuter all items and clear |
1820 | void NeuterAndClear(RSLock * pLock); |
1821 | |
1822 | void (RSPtrArray<T> * pArray); |
1823 | void (RSPtrArray<T> * pArray); |
1824 | }; |
1825 | |
1826 | |
1827 | class CordbHashTableEnum : public CordbBase, |
1828 | public ICorDebugProcessEnum, |
1829 | public ICorDebugBreakpointEnum, |
1830 | public ICorDebugStepperEnum, |
1831 | public ICorDebugThreadEnum, |
1832 | public ICorDebugModuleEnum, |
1833 | public ICorDebugAppDomainEnum, |
1834 | public ICorDebugAssemblyEnum |
1835 | { |
1836 | // Private ctors. Use build function to access. |
1837 | CordbHashTableEnum( |
1838 | CordbBase * pOwnerObj, |
1839 | NeuterList * pOwnerList, |
1840 | CordbHashTable *table, |
1841 | const _GUID &id); |
1842 | |
1843 | public: |
1844 | static void BuildOrThrow( |
1845 | CordbBase * pOwnerObj, |
1846 | NeuterList * pOwnerList, |
1847 | CordbHashTable *table, |
1848 | const _GUID &id, |
1849 | RSInitHolder<CordbHashTableEnum> * pHolder); |
1850 | |
1851 | CordbHashTableEnum(CordbHashTableEnum *cloneSrc); |
1852 | |
1853 | ~CordbHashTableEnum(); |
1854 | virtual void Neuter(); |
1855 | |
1856 | |
1857 | #ifdef _DEBUG |
1858 | // For debugging (asserts, logging, etc) provide a pretty name (this is 1:1 w/ the VTable) |
1859 | virtual const char * DbgGetName() { return "CordbHashTableEnum" ; }; |
1860 | #endif |
1861 | |
1862 | |
1863 | HRESULT Next(ULONG celt, CordbBase *bases[], ULONG *pceltFetched); |
1864 | |
1865 | //----------------------------------------------------------- |
1866 | // IUnknown |
1867 | //----------------------------------------------------------- |
1868 | |
1869 | ULONG STDMETHODCALLTYPE AddRef() |
1870 | { |
1871 | return (BaseAddRef()); |
1872 | } |
1873 | ULONG STDMETHODCALLTYPE Release() |
1874 | { |
1875 | return (BaseRelease()); |
1876 | } |
1877 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
1878 | |
1879 | //----------------------------------------------------------- |
1880 | // ICorDebugEnum |
1881 | //----------------------------------------------------------- |
1882 | |
1883 | COM_METHOD Skip(ULONG celt); |
1884 | COM_METHOD Reset(); |
1885 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
1886 | COM_METHOD GetCount(ULONG *pcelt); |
1887 | |
1888 | //----------------------------------------------------------- |
1889 | // ICorDebugProcessEnum |
1890 | //----------------------------------------------------------- |
1891 | |
1892 | COM_METHOD Next(ULONG celt, ICorDebugProcess *processes[], |
1893 | ULONG *pceltFetched) |
1894 | { |
1895 | VALIDATE_POINTER_TO_OBJECT_ARRAY(processes, ICorDebugProcess *, |
1896 | celt, true, true); |
1897 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1898 | |
1899 | return (Next(celt, (CordbBase **)processes, pceltFetched)); |
1900 | } |
1901 | |
1902 | //----------------------------------------------------------- |
1903 | // ICorDebugBreakpointEnum |
1904 | //----------------------------------------------------------- |
1905 | |
1906 | COM_METHOD Next(ULONG celt, ICorDebugBreakpoint *breakpoints[], |
1907 | ULONG *pceltFetched) |
1908 | { |
1909 | VALIDATE_POINTER_TO_OBJECT_ARRAY(breakpoints, ICorDebugBreakpoint *, |
1910 | celt, true, true); |
1911 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1912 | |
1913 | return (Next(celt, (CordbBase **)breakpoints, pceltFetched)); |
1914 | } |
1915 | |
1916 | //----------------------------------------------------------- |
1917 | // ICorDebugStepperEnum |
1918 | //----------------------------------------------------------- |
1919 | |
1920 | COM_METHOD Next(ULONG celt, ICorDebugStepper *steppers[], |
1921 | ULONG *pceltFetched) |
1922 | { |
1923 | VALIDATE_POINTER_TO_OBJECT_ARRAY(steppers, ICorDebugStepper *, |
1924 | celt, true, true); |
1925 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1926 | |
1927 | return (Next(celt, (CordbBase **)steppers, pceltFetched)); |
1928 | } |
1929 | |
1930 | //----------------------------------------------------------- |
1931 | // ICorDebugThreadEnum |
1932 | //----------------------------------------------------------- |
1933 | |
1934 | COM_METHOD Next(ULONG celt, ICorDebugThread *threads[], |
1935 | ULONG *pceltFetched) |
1936 | { |
1937 | VALIDATE_POINTER_TO_OBJECT_ARRAY(threads, ICorDebugThread *, |
1938 | celt, true, true); |
1939 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1940 | |
1941 | return (Next(celt, (CordbBase **)threads, pceltFetched)); |
1942 | } |
1943 | |
1944 | //----------------------------------------------------------- |
1945 | // ICorDebugModuleEnum |
1946 | //----------------------------------------------------------- |
1947 | |
1948 | COM_METHOD Next(ULONG celt, ICorDebugModule *modules[], |
1949 | ULONG *pceltFetched) |
1950 | { |
1951 | VALIDATE_POINTER_TO_OBJECT_ARRAY(modules, ICorDebugModule *, |
1952 | celt, true, true); |
1953 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1954 | |
1955 | return (Next(celt, (CordbBase **)modules, pceltFetched)); |
1956 | } |
1957 | |
1958 | //----------------------------------------------------------- |
1959 | // ICorDebugAppDomainEnum |
1960 | //----------------------------------------------------------- |
1961 | |
1962 | COM_METHOD Next(ULONG celt, ICorDebugAppDomain *appdomains[], |
1963 | ULONG *pceltFetched) |
1964 | { |
1965 | VALIDATE_POINTER_TO_OBJECT_ARRAY(appdomains, ICorDebugAppDomain *, |
1966 | celt, true, true); |
1967 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1968 | |
1969 | return (Next(celt, (CordbBase **)appdomains, pceltFetched)); |
1970 | } |
1971 | //----------------------------------------------------------- |
1972 | // ICorDebugAssemblyEnum |
1973 | //----------------------------------------------------------- |
1974 | |
1975 | COM_METHOD Next(ULONG celt, ICorDebugAssembly *assemblies[], |
1976 | ULONG *pceltFetched) |
1977 | { |
1978 | VALIDATE_POINTER_TO_OBJECT_ARRAY(assemblies, ICorDebugAssembly *, |
1979 | celt, true, true); |
1980 | VALIDATE_POINTER_TO_OBJECT(pceltFetched, ULONG *); |
1981 | |
1982 | return (Next(celt, (CordbBase **)assemblies, pceltFetched)); |
1983 | } |
1984 | private: |
1985 | // Owning object is our link to the CordbProcess* tree. Never null until we're neutered. |
1986 | // NeuterList is related to the owning object. Need to cache it so that we can pass it on |
1987 | // to our clones. |
1988 | CordbBase * m_pOwnerObj; // provides us w/ a CordbProcess* |
1989 | NeuterList * m_pOwnerNeuterList; |
1990 | |
1991 | |
1992 | CordbHashTable *m_table; |
1993 | bool m_started; |
1994 | bool m_done; |
1995 | HASHFIND m_hashfind; |
1996 | REFIID m_guid; |
1997 | ULONG m_iCurElt; |
1998 | ULONG m_count; |
1999 | BOOL m_fCountInit; |
2000 | |
2001 | #ifdef _DEBUG |
2002 | // timestampt of hashtable when we start enumerating it. Useful for detecting if the table |
2003 | // changes underneath us. |
2004 | int m_DbgChangeCount; |
2005 | void AssertValid(); |
2006 | #else |
2007 | void AssertValid() { } |
2008 | #endif |
2009 | |
2010 | private: |
2011 | //These factor code between Next & Skip |
2012 | HRESULT PrepForEnum(CordbBase **pBase); |
2013 | |
2014 | // Note that the set of types advanced by Pre & by Post are disjoint, and |
2015 | // that the union of these two sets are all possible types enuerated by |
2016 | // the CordbHashTableEnum. |
2017 | HRESULT AdvancePreAssign(CordbBase **pBase); |
2018 | HRESULT AdvancePostAssign(CordbBase **pBase, |
2019 | CordbBase **b, |
2020 | CordbBase **bEnd); |
2021 | |
2022 | // This factors some code that initializes the module enumerator. |
2023 | HRESULT SetupModuleEnum(); |
2024 | |
2025 | }; |
2026 | |
2027 | |
2028 | //----------------------------------------------------------------------------- |
2029 | // Neuter List |
2030 | // Dtors can be called at any time (whenever Cordbg calls Release, which is outside |
2031 | // of our control), so we never want to do significant work in a dtor |
2032 | // (this includes sending IPC events + neutering). |
2033 | // So objects can queue themselves up to be neutered at a safe time. |
2034 | // |
2035 | // Items in a NeuterList should only contain state in the Right-Side. |
2036 | // If the item holds resources in the left-side, it should be placed on a |
2037 | // code:LeftSideResourceCleanupList |
2038 | //----------------------------------------------------------------------------- |
2039 | class NeuterList |
2040 | { |
2041 | public: |
2042 | NeuterList(); |
2043 | ~NeuterList(); |
2044 | |
2045 | // Add an object to be neutered. |
2046 | // Anybody calls this to add themselves to the list. |
2047 | // This will add it to the list and maintain an internal reference to it. |
2048 | void Add(CordbProcess * pProcess, CordbBase * pObject); |
2049 | |
2050 | // Add w/o checking for safety. Should only be used by Process-list enum. |
2051 | void UnsafeAdd(CordbProcess * pProcess, CordbBase * pObject); |
2052 | |
2053 | // Neuter everything on the list. |
2054 | // This should only be called by the "owner", but we can't really enforce that. |
2055 | // This will release all internal references and empty the list. |
2056 | void NeuterAndClear(CordbProcess * pProcess); |
2057 | |
2058 | // Sweep for all objects that are marked as 'm_fNeuterAtWill'. |
2059 | // Neuter and remove these. |
2060 | void SweepAllNeuterAtWillObjects(CordbProcess * pProcess); |
2061 | |
2062 | protected: |
2063 | struct Node |
2064 | { |
2065 | RSSmartPtr<CordbBase> m_pObject; |
2066 | Node * m_pNext; |
2067 | }; |
2068 | |
2069 | // Manipulating the list is done under the Process lock. |
2070 | Node * m_pHead; |
2071 | }; |
2072 | |
2073 | //----------------------------------------------------------------------------- |
2074 | // This list is for objects that hold left-side resources. |
2075 | // If the object does not hold left-side resources, it can be placed on a |
2076 | // code:NeuterList |
2077 | //----------------------------------------------------------------------------- |
2078 | class LeftSideResourceCleanupList : public NeuterList |
2079 | { |
2080 | public: |
2081 | // dispose everything contained in the list by calling SafeDispose() on each element |
2082 | void SweepNeuterLeftSideResources(CordbProcess * pProcess); |
2083 | void NeuterLeftSideResourcesAndClear(CordbProcess * pProcess); |
2084 | }; |
2085 | |
2086 | //------------------------------------------------------------------------- |
2087 | // |
2088 | // Optional<T> |
2089 | // Stores a value along with a bit indicating whether the value is valid. |
2090 | // |
2091 | // This is particularly useful for LS data read via DAC. We need to gracefully |
2092 | // handle missing data, and we may want to track independent pieces of data |
2093 | // separately (often with lazy initialization). It's essential that we can't |
2094 | // easily lose track of whether the data has been cached yet or not. So |
2095 | // rather than have extra "isValid" bools everywhere, we use this class to |
2096 | // encapsulate the validity bit in with the data, and ASSERT that it is true |
2097 | // whenever reading out the data. |
2098 | // Note that the client must still remember to call GetValue only when HasValue |
2099 | // is true. Since C++ doesn't have type-safe sum types, we can't enforce this |
2100 | // explicitly at compile time (ML-style datatypes and pattern matching is perfect |
2101 | // for this). |
2102 | // |
2103 | // Note that we could consider adding some operator overloads to make using |
2104 | // instances of this class more transparent. Experience will tell if this |
2105 | // is a good idea or not. |
2106 | // |
2107 | template <typename T> |
2108 | class Optional |
2109 | { |
2110 | public: |
2111 | // By default, initialize to invalid |
2112 | Optional() : m_fHasValue(false), m_value(T()) {} |
2113 | |
2114 | // Allow implicit initialization from a value (for copyable T) |
2115 | Optional(const T& val) : m_fHasValue(true), m_value(val) {} |
2116 | |
2117 | // Returns true if a value has been stored |
2118 | bool HasValue() const { return m_fHasValue; } |
2119 | |
2120 | // Extract the value. Can only be called when HasValue is true. |
2121 | const T& GetValue() { _ASSERTE(m_fHasValue); return m_value; } |
2122 | |
2123 | // Get a writable pointer to the value structure, for filling in uncopyable data structures |
2124 | T * GetValueAddr() { return &m_value; } |
2125 | |
2126 | // Explicitly mark this object as having a value (for use after writing to it directly using |
2127 | // GetValueAddr. Not necessary for simple/primitive types). |
2128 | void SetHasValue() { m_fHasValue = true; } |
2129 | |
2130 | // Also gets compiler-default copy constructor and assignment operator if T has them |
2131 | |
2132 | private: |
2133 | bool m_fHasValue; |
2134 | T m_value; |
2135 | }; |
2136 | |
2137 | |
2138 | /* ------------------------------------------------------------------------- * |
2139 | * Cordb class |
2140 | * ------------------------------------------------------------------------- */ |
2141 | |
2142 | class Cordb : public CordbBase, public ICorDebug, public ICorDebugRemote |
2143 | { |
2144 | public: |
2145 | Cordb(CorDebugInterfaceVersion iDebuggerVersion); |
2146 | virtual ~Cordb(); |
2147 | virtual void Neuter(); |
2148 | |
2149 | |
2150 | |
2151 | #ifdef _DEBUG_IMPL |
2152 | virtual const char * DbgGetName() { return "Cordb" ; } |
2153 | |
2154 | // Under Debug, we keep some extra state for tracking leaks. The goal is that |
2155 | // we can assert that we aren't leaking internal refs. We'd like to assert that |
2156 | // we're not leaking external refs, but since we can't force Cordbg to release, |
2157 | // we can't really assert that. |
2158 | // So the idea is that when Cordbg has released its last Cordb object, that |
2159 | // all internal references have been released. |
2160 | // Unfortunately, certain CordbBase objects are unrooted and thus we have no |
2161 | // good time to neuter them and clean up any internal references they may hold. |
2162 | // So we keep count of those guys too. |
2163 | static LONG s_DbgMemTotalOutstandingCordb; |
2164 | static LONG s_DbgMemTotalOutstandingInternalRefs; |
2165 | #endif |
2166 | |
2167 | // |
2168 | // Turn this on to enable an array which will contain all objects that have |
2169 | // not been completely released. |
2170 | // |
2171 | // #define TRACK_OUTSTANDING_OBJECTS 1 |
2172 | |
2173 | #ifdef TRACK_OUTSTANDING_OBJECTS |
2174 | |
2175 | #define MAX_TRACKED_OUTSTANDING_OBJECTS 256 |
2176 | static void *Cordb::s_DbgMemOutstandingObjects[MAX_TRACKED_OUTSTANDING_OBJECTS]; |
2177 | static LONG Cordb::s_DbgMemOutstandingObjectMax; |
2178 | #endif |
2179 | |
2180 | |
2181 | //----------------------------------------------------------- |
2182 | // IUnknown |
2183 | //----------------------------------------------------------- |
2184 | |
2185 | ULONG STDMETHODCALLTYPE AddRef() |
2186 | { |
2187 | return (BaseAddRef()); |
2188 | } |
2189 | ULONG STDMETHODCALLTYPE Release() |
2190 | { |
2191 | return (BaseRelease()); |
2192 | } |
2193 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
2194 | |
2195 | //----------------------------------------------------------- |
2196 | // ICorDebug |
2197 | //----------------------------------------------------------- |
2198 | |
2199 | HRESULT SetTargetCLR(HMODULE hmodTargetCLR); |
2200 | |
2201 | COM_METHOD Initialize(); |
2202 | COM_METHOD Terminate(); |
2203 | COM_METHOD SetManagedHandler(ICorDebugManagedCallback *pCallback); |
2204 | COM_METHOD SetUnmanagedHandler(ICorDebugUnmanagedCallback *pCallback); |
2205 | COM_METHOD CreateProcess(LPCWSTR lpApplicationName, |
2206 | __in_z LPWSTR lpCommandLine, |
2207 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
2208 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
2209 | BOOL bInheritHandles, |
2210 | DWORD dwCreationFlags, |
2211 | PVOID lpEnvironment, |
2212 | LPCWSTR lpCurrentDirectory, |
2213 | LPSTARTUPINFOW lpStartupInfo, |
2214 | LPPROCESS_INFORMATION lpProcessInformation, |
2215 | CorDebugCreateProcessFlags debuggingFlags, |
2216 | ICorDebugProcess **ppProcess); |
2217 | COM_METHOD DebugActiveProcess(DWORD dwProcessId, BOOL fWin32Attach, ICorDebugProcess **ppProcess); |
2218 | COM_METHOD EnumerateProcesses(ICorDebugProcessEnum **ppProcess); |
2219 | COM_METHOD GetProcess(DWORD dwProcessId, ICorDebugProcess **ppProcess); |
2220 | COM_METHOD CanLaunchOrAttach(DWORD dwProcessId, BOOL win32DebuggingEnabled); |
2221 | |
2222 | //----------------------------------------------------------- |
2223 | // CorDebug |
2224 | //----------------------------------------------------------- |
2225 | |
2226 | static COM_METHOD CreateObjectV1(REFIID id, void **object); |
2227 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
2228 | static COM_METHOD CreateObjectTelesto(REFIID id, void ** pObject); |
2229 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
2230 | static COM_METHOD CreateObject(CorDebugInterfaceVersion iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, REFIID id, void **object); |
2231 | |
2232 | //----------------------------------------------------------- |
2233 | // ICorDebugRemote |
2234 | //----------------------------------------------------------- |
2235 | |
2236 | COM_METHOD CreateProcessEx(ICorDebugRemoteTarget * pRemoteTarget, |
2237 | LPCWSTR lpApplicationName, |
2238 | __in_z LPWSTR lpCommandLine, |
2239 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
2240 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
2241 | BOOL bInheritHandles, |
2242 | DWORD dwCreationFlags, |
2243 | PVOID lpEnvironment, |
2244 | LPCWSTR lpCurrentDirectory, |
2245 | LPSTARTUPINFOW lpStartupInfo, |
2246 | LPPROCESS_INFORMATION lpProcessInformation, |
2247 | CorDebugCreateProcessFlags debuggingFlags, |
2248 | ICorDebugProcess ** ppProcess); |
2249 | |
2250 | COM_METHOD DebugActiveProcessEx(ICorDebugRemoteTarget * pRemoteTarget, |
2251 | DWORD dwProcessId, |
2252 | BOOL fWin32Attach, |
2253 | ICorDebugProcess ** ppProcess); |
2254 | |
2255 | |
2256 | //----------------------------------------------------------- |
2257 | // Methods not exposed via a COM interface. |
2258 | //----------------------------------------------------------- |
2259 | |
2260 | HRESULT CreateProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, |
2261 | LPCWSTR lpApplicationName, |
2262 | __in_z LPWSTR lpCommandLine, |
2263 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
2264 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
2265 | BOOL bInheritHandles, |
2266 | DWORD dwCreationFlags, |
2267 | PVOID lpEnvironment, |
2268 | LPCWSTR lpCurrentDirectory, |
2269 | LPSTARTUPINFOW lpStartupInfo, |
2270 | LPPROCESS_INFORMATION lpProcessInformation, |
2271 | CorDebugCreateProcessFlags debuggingFlags, |
2272 | ICorDebugProcess **ppProcess); |
2273 | |
2274 | HRESULT DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, DWORD id, BOOL win32Attach, ICorDebugProcess **ppProcess); |
2275 | |
2276 | void EnsureCanLaunchOrAttach(BOOL fWin32DebuggingEnabled); |
2277 | |
2278 | void EnsureAllowAnotherProcess(); |
2279 | void AddProcess(CordbProcess* process); |
2280 | void RemoveProcess(CordbProcess* process); |
2281 | CordbSafeHashTable<CordbProcess> *GetProcessList(); |
2282 | |
2283 | void LockProcessList(); |
2284 | void UnlockProcessList(); |
2285 | |
2286 | #ifdef _DEBUG |
2287 | bool ThreadHasProcessListLock(); |
2288 | #endif |
2289 | |
2290 | |
2291 | HRESULT SendIPCEvent(CordbProcess * pProcess, |
2292 | DebuggerIPCEvent * pEvent, |
2293 | SIZE_T eventSize); |
2294 | |
2295 | void ProcessStateChanged(); |
2296 | |
2297 | HRESULT WaitForIPCEventFromProcess(CordbProcess* process, |
2298 | CordbAppDomain *appDomain, |
2299 | DebuggerIPCEvent* event); |
2300 | |
2301 | private: |
2302 | Cordb(CorDebugInterfaceVersion iDebuggerVersion, const ProcessDescriptor& pd); |
2303 | |
2304 | //----------------------------------------------------------- |
2305 | // Data members |
2306 | //----------------------------------------------------------- |
2307 | |
2308 | public: |
2309 | RSExtSmartPtr<ICorDebugManagedCallback> m_managedCallback; |
2310 | RSExtSmartPtr<ICorDebugManagedCallback2> m_managedCallback2; |
2311 | RSExtSmartPtr<ICorDebugManagedCallback3> m_managedCallback3; |
2312 | RSExtSmartPtr<ICorDebugManagedCallback4> m_managedCallback4; |
2313 | RSExtSmartPtr<ICorDebugUnmanagedCallback> m_unmanagedCallback; |
2314 | |
2315 | CordbRCEventThread* m_rcEventThread; |
2316 | |
2317 | CorDebugInterfaceVersion GetDebuggerVersion() const; |
2318 | |
2319 | #ifdef FEATURE_CORESYSTEM |
2320 | HMODULE GetTargetCLR() { return m_targetCLR; } |
2321 | #endif |
2322 | |
2323 | private: |
2324 | bool IsCreateProcessSupported(); |
2325 | bool IsInteropDebuggingSupported(); |
2326 | void CheckCompatibility(); |
2327 | |
2328 | CordbSafeHashTable<CordbProcess> m_processes; |
2329 | |
2330 | // List to track outstanding CordbProcessEnum objects. |
2331 | NeuterList m_pProcessEnumList; |
2332 | |
2333 | RSLock m_processListMutex; |
2334 | BOOL m_initialized; |
2335 | |
2336 | // This is the version of the ICorDebug APIs that the debugger believes it's consuming. |
2337 | CorDebugInterfaceVersion m_debuggerSpecifiedVersion; |
2338 | |
2339 | // Store information about the process to be debugged |
2340 | ProcessDescriptor m_pd; |
2341 | |
2342 | //Note - this code could be useful outside coresystem, but keeping the change localized |
2343 | // because we are late in the win8 release |
2344 | #ifdef FEATURE_CORESYSTEM |
2345 | HMODULE m_targetCLR; |
2346 | #endif |
2347 | }; |
2348 | |
2349 | |
2350 | |
2351 | |
2352 | /* ------------------------------------------------------------------------- * |
2353 | * AppDomain class |
2354 | * ------------------------------------------------------------------------- */ |
2355 | |
2356 | // Provides the implementation for ICorDebugAppDomain, ICorDebugAppDomain2, |
2357 | // and ICorDebugAppDomain3 |
2358 | class CordbAppDomain : public CordbBase, |
2359 | public ICorDebugAppDomain, |
2360 | public ICorDebugAppDomain2, |
2361 | public ICorDebugAppDomain3, |
2362 | public ICorDebugAppDomain4 |
2363 | { |
2364 | public: |
2365 | // Create a CordbAppDomain object based on a pointer to the AppDomain instance in the CLR |
2366 | CordbAppDomain(CordbProcess * pProcess, |
2367 | VMPTR_AppDomain vmAppDomain); |
2368 | |
2369 | virtual ~CordbAppDomain(); |
2370 | |
2371 | virtual void Neuter(); |
2372 | |
2373 | using CordbBase::GetProcess; |
2374 | |
2375 | #ifdef _DEBUG |
2376 | virtual const char * DbgGetName() { return "CordbAppDomain" ; } |
2377 | #endif |
2378 | |
2379 | |
2380 | //----------------------------------------------------------- |
2381 | // IUnknown |
2382 | //----------------------------------------------------------- |
2383 | |
2384 | ULONG STDMETHODCALLTYPE AddRef() |
2385 | { |
2386 | return (BaseAddRef()); |
2387 | } |
2388 | ULONG STDMETHODCALLTYPE Release() |
2389 | { |
2390 | return (BaseRelease()); |
2391 | } |
2392 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
2393 | |
2394 | //----------------------------------------------------------- |
2395 | // ICorDebugController |
2396 | //----------------------------------------------------------- |
2397 | |
2398 | COM_METHOD Stop(DWORD dwTimeout); |
2399 | COM_METHOD Continue(BOOL fIsOutOfBand); |
2400 | COM_METHOD IsRunning(BOOL * pbRunning); |
2401 | COM_METHOD HasQueuedCallbacks(ICorDebugThread * pThread, |
2402 | BOOL * pbQueued); |
2403 | COM_METHOD EnumerateThreads(ICorDebugThreadEnum ** ppThreads); |
2404 | COM_METHOD SetAllThreadsDebugState(CorDebugThreadState state, ICorDebugThread * pExceptThisThread); |
2405 | |
2406 | // Deprecated, returns E_NOTIMPL |
2407 | COM_METHOD Detach(); |
2408 | |
2409 | COM_METHOD Terminate(unsigned int exitCode); |
2410 | |
2411 | COM_METHOD CanCommitChanges( |
2412 | ULONG cSnapshots, |
2413 | ICorDebugEditAndContinueSnapshot * pSnapshots[], |
2414 | ICorDebugErrorInfoEnum ** pError); |
2415 | |
2416 | COM_METHOD CommitChanges( |
2417 | ULONG cSnapshots, |
2418 | ICorDebugEditAndContinueSnapshot * pSnapshots[], |
2419 | ICorDebugErrorInfoEnum ** pError); |
2420 | |
2421 | //----------------------------------------------------------- |
2422 | // ICorDebugAppDomain |
2423 | //----------------------------------------------------------- |
2424 | /* |
2425 | * GetProcess returns the process containing the app domain |
2426 | */ |
2427 | |
2428 | COM_METHOD GetProcess(ICorDebugProcess ** ppProcess); |
2429 | |
2430 | /* |
2431 | * EnumerateAssemblies enumerates all assemblies in the app domain |
2432 | */ |
2433 | |
2434 | COM_METHOD EnumerateAssemblies(ICorDebugAssemblyEnum ** ppAssemblies); |
2435 | |
2436 | COM_METHOD GetModuleFromMetaDataInterface(IUnknown * pIMetaData, |
2437 | ICorDebugModule ** ppModule); |
2438 | /* |
2439 | * EnumerateBreakpoints returns an enum of all active breakpoints |
2440 | * in the app domain. This includes all types of breakpoints : |
2441 | * function breakpoints, data breakpoints, etc. |
2442 | */ |
2443 | |
2444 | COM_METHOD EnumerateBreakpoints(ICorDebugBreakpointEnum ** ppBreakpoints); |
2445 | |
2446 | /* |
2447 | * EnumerateSteppers returns an enum of all active steppers in the app domain. |
2448 | */ |
2449 | |
2450 | COM_METHOD EnumerateSteppers(ICorDebugStepperEnum ** ppSteppers); |
2451 | |
2452 | // Deprecated, always returns true. |
2453 | COM_METHOD IsAttached(BOOL * pfAttached); |
2454 | |
2455 | // Returns the friendly name of the AppDomain |
2456 | COM_METHOD GetName(ULONG32 cchName, |
2457 | ULONG32 * pcchName, |
2458 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
2459 | |
2460 | /* |
2461 | * GetObject returns the runtime app domain object. |
2462 | * Note: This method is not yet implemented. |
2463 | */ |
2464 | |
2465 | COM_METHOD GetObject(ICorDebugValue ** ppObject); |
2466 | |
2467 | // Deprecated, does nothing |
2468 | COM_METHOD Attach(); |
2469 | COM_METHOD GetID(ULONG32 * pId); |
2470 | |
2471 | //----------------------------------------------------------- |
2472 | // ICorDebugAppDomain2 APIs |
2473 | //----------------------------------------------------------- |
2474 | COM_METHOD GetArrayOrPointerType(CorElementType elementType, |
2475 | ULONG32 nRank, |
2476 | ICorDebugType * pTypeArg, |
2477 | ICorDebugType ** ppResultType); |
2478 | |
2479 | COM_METHOD GetFunctionPointerType(ULONG32 cTypeArgs, |
2480 | ICorDebugType * rgpTypeArgs[], |
2481 | ICorDebugType ** ppResultType); |
2482 | |
2483 | //----------------------------------------------------------- |
2484 | // ICorDebugAppDomain3 APIs |
2485 | //----------------------------------------------------------- |
2486 | COM_METHOD GetCachedWinRTTypesForIIDs( |
2487 | ULONG32 cGuids, |
2488 | GUID * guids, |
2489 | ICorDebugTypeEnum * * ppTypesEnum); |
2490 | |
2491 | COM_METHOD GetCachedWinRTTypes( |
2492 | ICorDebugGuidToTypeEnum * * ppType); |
2493 | |
2494 | //----------------------------------------------------------- |
2495 | // ICorDebugAppDomain4 |
2496 | //----------------------------------------------------------- |
2497 | COM_METHOD GetObjectForCCW(CORDB_ADDRESS ccwPointer, ICorDebugValue **ppManagedObject); |
2498 | |
2499 | // Get the VMPTR for this appdomain. |
2500 | VMPTR_AppDomain GetADToken() { return m_vmAppDomain; } |
2501 | |
2502 | // Given a metadata interface, find the module in this appdomain that matches it. |
2503 | CordbModule * GetModuleFromMetaDataInterface(IUnknown *pIMetaData); |
2504 | |
2505 | // Lookup a module from the cache. Create and to the cache if needed. |
2506 | CordbModule * LookupOrCreateModule(VMPTR_Module vmModuleToken, VMPTR_DomainFile vmDomainFileToken); |
2507 | |
2508 | // Lookup a module from the cache. Create and to the cache if needed. |
2509 | CordbModule * LookupOrCreateModule(VMPTR_DomainFile vmDomainFileToken); |
2510 | |
2511 | // Callback from DAC for module enumeration |
2512 | static void ModuleEnumerationCallback(VMPTR_DomainFile vmModule, void * pUserData); |
2513 | |
2514 | // Use DAC to add any modules for this assembly. |
2515 | void PrepopulateModules(); |
2516 | |
2517 | void InvalidateName() { m_strAppDomainName.Clear(); } |
2518 | |
2519 | public: |
2520 | ULONG m_AppDomainId; |
2521 | |
2522 | CordbAssembly * LookupOrCreateAssembly(VMPTR_DomainAssembly vmDomainAssembly); |
2523 | CordbAssembly * LookupOrCreateAssembly(VMPTR_Assembly vmAssembly); |
2524 | void RemoveAssemblyFromCache(VMPTR_DomainAssembly vmDomainAssembly); |
2525 | |
2526 | |
2527 | CordbSafeHashTable<CordbBreakpoint> m_breakpoints; |
2528 | |
2529 | // Unique objects that represent the use of some |
2530 | // basic ELEMENT_TYPE's as type parameters. These |
2531 | // are shared acrosss the entire process. We could |
2532 | // go and try to find the classes corresponding to these |
2533 | // element types but it seems simpler just to keep |
2534 | // them as special cases. |
2535 | CordbSafeHashTable<CordbType> m_sharedtypes; |
2536 | |
2537 | CordbAssembly * CacheAssembly(VMPTR_DomainAssembly vmDomainAssembly); |
2538 | CordbAssembly * CacheAssembly(VMPTR_Assembly vmAssembly); |
2539 | |
2540 | |
2541 | // Cache of modules in this appdomain. In the VM, modules live in an assembly. |
2542 | // This cache lives on the appdomain because we generally want to do appdomain (or process) |
2543 | // wide lookup. |
2544 | // This is indexed by VMPTR_DomainFile, which has appdomain affinity. |
2545 | // This is populated by code:CordbAppDomain::LookupOrCreateModule (which may be invoked |
2546 | // anytime the RS gets hold of a VMPTR), and are removed at the unload event. |
2547 | CordbSafeHashTable<CordbModule> m_modules; |
2548 | private: |
2549 | // Cache of assemblies in this appdomain. |
2550 | // This is indexed by VMPTR_DomainAssembly, which has appdomain affinity. |
2551 | // This is populated by code:CordbAppDomain::LookupOrCreateAssembly (which may be invoked |
2552 | // anytime the RS gets hold of a VMPTR), and are removed at the unload event. |
2553 | CordbSafeHashTable<CordbAssembly> m_assemblies; |
2554 | |
2555 | static void AssemblyEnumerationCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pThis); |
2556 | void PrepopulateAssembliesOrThrow(); |
2557 | |
2558 | // Use DAC to refresh our name |
2559 | HRESULT RefreshName(); |
2560 | |
2561 | StringCopyHolder m_strAppDomainName; |
2562 | |
2563 | NeuterList m_TypeNeuterList; // List of types owned by this AppDomain. |
2564 | |
2565 | // List of Sweepable objects owned by this AppDomain. |
2566 | // This includes some objects taht hold resources in the left-side (mainly |
2567 | // as CordbHandleValue, see code:CordbHandleValue::Dispose), as well as: |
2568 | // - Cordb*Value objects that survive across continues and have appdomain affinity. |
2569 | LeftSideResourceCleanupList m_SweepableNeuterList; |
2570 | |
2571 | VMPTR_AppDomain m_vmAppDomain; |
2572 | public: |
2573 | // The "Long" exit list is for items that don't get neutered until the appdomain exits. |
2574 | // The "Sweepable" exit list is for items that may be neuterable sooner than AD exit. |
2575 | // By splitting out the list, we can just try to sweep the "Sweepable" list and we |
2576 | // don't waste any time sweeping things on the "Long" list that aren't neuterable anyways. |
2577 | NeuterList * GetLongExitNeuterList() { return &m_TypeNeuterList; } |
2578 | LeftSideResourceCleanupList * GetSweepableExitNeuterList() { return &m_SweepableNeuterList; } |
2579 | |
2580 | void AddToTypeList(CordbBase *pObject); |
2581 | |
2582 | }; |
2583 | |
2584 | |
2585 | /* ------------------------------------------------------------------------- * |
2586 | * Assembly class |
2587 | * ------------------------------------------------------------------------- */ |
2588 | |
2589 | class CordbAssembly : public CordbBase, public ICorDebugAssembly, ICorDebugAssembly2 |
2590 | { |
2591 | public: |
2592 | CordbAssembly(CordbAppDomain * pAppDomain, |
2593 | VMPTR_Assembly vmAssembly, |
2594 | VMPTR_DomainAssembly vmDomainAssembly); |
2595 | virtual ~CordbAssembly(); |
2596 | virtual void Neuter(); |
2597 | |
2598 | using CordbBase::GetProcess; |
2599 | |
2600 | #ifdef _DEBUG |
2601 | virtual const char * DbgGetName() { return "CordbAssembly" ; } |
2602 | #endif |
2603 | |
2604 | |
2605 | //----------------------------------------------------------- |
2606 | // IUnknown |
2607 | //----------------------------------------------------------- |
2608 | |
2609 | ULONG STDMETHODCALLTYPE AddRef() |
2610 | { |
2611 | return (BaseAddRef()); |
2612 | } |
2613 | ULONG STDMETHODCALLTYPE Release() |
2614 | { |
2615 | return (BaseRelease()); |
2616 | } |
2617 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
2618 | |
2619 | //----------------------------------------------------------- |
2620 | // ICorDebugAssembly |
2621 | //----------------------------------------------------------- |
2622 | |
2623 | /* |
2624 | * GetProcess returns the process containing the assembly |
2625 | */ |
2626 | COM_METHOD GetProcess(ICorDebugProcess ** ppProcess); |
2627 | |
2628 | // Gets the AppDomain containing this assembly |
2629 | COM_METHOD GetAppDomain(ICorDebugAppDomain ** ppAppDomain); |
2630 | |
2631 | /* |
2632 | * EnumerateModules enumerates all modules in the assembly |
2633 | */ |
2634 | COM_METHOD EnumerateModules(ICorDebugModuleEnum ** ppModules); |
2635 | |
2636 | /* |
2637 | * GetCodeBase returns the code base used to load the assembly |
2638 | */ |
2639 | COM_METHOD GetCodeBase(ULONG32 cchName, |
2640 | ULONG32 * pcchName, |
2641 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
2642 | |
2643 | // returns the filename of the assembly, or "<unknown>" for in-memory assemblies |
2644 | COM_METHOD GetName(ULONG32 cchName, |
2645 | ULONG32 * pcchName, |
2646 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
2647 | |
2648 | |
2649 | //----------------------------------------------------------- |
2650 | // ICorDebugAssembly2 |
2651 | //----------------------------------------------------------- |
2652 | |
2653 | /* |
2654 | * IsFullyTrusted returns a flag indicating whether the security system |
2655 | * has granted the assembly full trust. |
2656 | */ |
2657 | COM_METHOD IsFullyTrusted(BOOL * pbFullyTrusted); |
2658 | |
2659 | //----------------------------------------------------------- |
2660 | // internal accessors |
2661 | //----------------------------------------------------------- |
2662 | |
2663 | #ifdef _DEBUG |
2664 | void DbgAssertAssemblyDeleted(); |
2665 | |
2666 | static void DbgAssertAssemblyDeletedCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pUserData); |
2667 | #endif // _DEBUG |
2668 | |
2669 | CordbAppDomain * GetAppDomain() { return m_pAppDomain; } |
2670 | |
2671 | VMPTR_DomainAssembly GetDomainAssemblyPtr() { return m_vmDomainAssembly; } |
2672 | private: |
2673 | VMPTR_Assembly m_vmAssembly; |
2674 | VMPTR_DomainAssembly m_vmDomainAssembly; |
2675 | CordbAppDomain * m_pAppDomain; |
2676 | |
2677 | StringCopyHolder m_strAssemblyFileName; |
2678 | Optional<BOOL> m_foptIsFullTrust; |
2679 | }; |
2680 | |
2681 | |
2682 | //----------------------------------------------------------------------------- |
2683 | // Describe what to do w/ a win32 debug event |
2684 | //----------------------------------------------------------------------------- |
2685 | class Reaction |
2686 | { |
2687 | public: |
2688 | enum Type |
2689 | { |
2690 | // Inband events: Dispatch to Cordbg |
2691 | // safe for stopping the shell and communicating with the runtime |
2692 | cInband, |
2693 | |
2694 | // workaround. Inband event, but NewEvent =false |
2695 | cInband_NotNewEvent, |
2696 | |
2697 | // This is a debug event that corresponds with getting to the beginning |
2698 | // of a first chance hijack. |
2699 | cFirstChanceHijackStarted, |
2700 | |
2701 | // This is the debug event that corresponds with getting to the end of |
2702 | // a hijack. To continue we need to restore an unhijacked context |
2703 | cInbandHijackComplete, |
2704 | |
2705 | // This is a debug event which corresponds to re-hiting a previous |
2706 | // IB event after returning from the hijack. Now we have already dispatched it |
2707 | // so we know how the user wants it to be continued |
2708 | // Continue immediately with the previously determined |
2709 | cInbandExceptionRetrigger, |
2710 | |
2711 | // This debug event is a breakpoint in unmanaged code that we placed. It will need |
2712 | // the M2UHandoffHijack to run the in process breakpoint handling code. |
2713 | cBreakpointRequiringHijack, |
2714 | |
2715 | // Oob events: Dispatch to Cordbg |
2716 | // Not safe stopping events. They must be continued immediately. |
2717 | cOOB, |
2718 | |
2719 | // CLR internal exception, Continue(not_handled), don't dispatch |
2720 | // The CLR expects this exception and will deal with it properly. |
2721 | cCLR, |
2722 | |
2723 | // Don't dispatch. Continue(DBG_CONTINUE). |
2724 | // Common for flare. |
2725 | cIgnore |
2726 | }; |
2727 | |
2728 | Type GetType() const { return m_type; }; |
2729 | |
2730 | #ifdef _DEBUG |
2731 | const char * GetReactionName() |
2732 | { |
2733 | switch(m_type) |
2734 | { |
2735 | case cInband: return "cInband" ; |
2736 | case cInband_NotNewEvent: return "cInband_NotNewEvent" ; |
2737 | case cFirstChanceHijackStarted: return "cFirstChanceHijackStarted" ; |
2738 | case cInbandHijackComplete: return "cInbandHijackComplete" ; |
2739 | case cInbandExceptionRetrigger: return "cInbandExceptionRetrigger" ; |
2740 | case cBreakpointRequiringHijack: return "cBreakpointRequiringHijack" ; |
2741 | case cOOB: return "cOOB" ; |
2742 | case cCLR: return "cCLR" ; |
2743 | case cIgnore: return "cIgnore" ; |
2744 | default: return "<unknown>" ; |
2745 | } |
2746 | } |
2747 | int GetLine() |
2748 | { |
2749 | return m_line; |
2750 | } |
2751 | #endif |
2752 | |
2753 | Reaction(Type t, int line) : m_type(t) { |
2754 | #ifdef _DEBUG |
2755 | m_line = line; |
2756 | |
2757 | LOG((LF_CORDB, LL_EVERYTHING, "Reaction:%s (determined on line: %d)\n" , GetReactionName(), line)); |
2758 | #endif |
2759 | }; |
2760 | |
2761 | void operator=(const Reaction & other) |
2762 | { |
2763 | m_type = other.m_type; |
2764 | #ifdef _DEBUG |
2765 | m_line = other.m_line; |
2766 | #endif |
2767 | } |
2768 | |
2769 | protected: |
2770 | Type m_type; |
2771 | |
2772 | #ifdef _DEBUG |
2773 | // Under a debug build, track the line # for where this came from. |
2774 | int m_line; |
2775 | #endif |
2776 | }; |
2777 | |
2778 | // Macro for creating a Reaction. |
2779 | #define REACTION(type) Reaction(Reaction::type, __LINE__) |
2780 | |
2781 | // Different forms of Unmanaged Continue |
2782 | enum EUMContinueType |
2783 | { |
2784 | cOobUMContinue, |
2785 | cInternalUMContinue, |
2786 | cRealUMContinue |
2787 | }; |
2788 | |
2789 | /* ------------------------------------------------------------------------- * |
2790 | * Process class |
2791 | * ------------------------------------------------------------------------- */ |
2792 | |
2793 | |
2794 | #ifdef _DEBUG |
2795 | // On debug, we can afford a larger native event queue.. |
2796 | const int DEBUG_EVENTQUEUE_SIZE = 30; |
2797 | #else |
2798 | const int DEBUG_EVENTQUEUE_SIZE = 10; |
2799 | #endif |
2800 | |
2801 | void DeleteIPCEventHelper(DebuggerIPCEvent *pDel); |
2802 | |
2803 | |
2804 | // Private interface on CordbProcess that ShimProcess needs to emulate V2 functionality. |
2805 | // The fact that we need private hooks means that V3 is not sufficiently finished to allow building |
2806 | // a V2 debugger. This interface should shrink over time (and eventually go away) as the functionality gets exposed |
2807 | // publicly. |
2808 | // CordbProcess calls back into ShimProcess too, so the public surface of code:ShimProcess plus |
2809 | // the spots in CordbProcess that call them are additional surface area that may need to addressed |
2810 | // to make the shim public. |
2811 | class IProcessShimHooks |
2812 | { |
2813 | public: |
2814 | // Get the OS Process descriptor of the target. |
2815 | virtual const ProcessDescriptor* GetProcessDescriptor() = 0; |
2816 | |
2817 | // Request a synchronization for attach. |
2818 | // This essentially just sends an AsyncBreak to the left-side. Once the target is |
2819 | // synchronized, the Shim can use inspection to send all the various fake-attach events. |
2820 | // |
2821 | // Once the shim has a way of requesting a synchronization from out-of-process for an |
2822 | // arbitrary running target that's not stopped at a managed debug event, we can |
2823 | // remove this. |
2824 | virtual void QueueManagedAttachIfNeeded() = 0; |
2825 | |
2826 | // Hijack a thread at an unhandled exception to allow us to resume executing the target so |
2827 | // that the helper thread can run and service IPC requests. This is also needed to allow |
2828 | // func-eval at a 2nd-chance exception |
2829 | // |
2830 | // This will require an architectural change to remove. Either: |
2831 | // - actions like func-eval / synchronization may call this directly themselves. |
2832 | // - the CLR's managed Unhandled-exception event is moved out of the native |
2833 | // unhandled-exception event, thus making native unhandled exceptions uninteresting to ICorDebug. |
2834 | // - everything is out-of-process, and so the CLR doesn't need to continue after an unhandled |
2835 | // native exception. |
2836 | virtual BOOL HijackThreadForUnhandledExceptionIfNeeded(DWORD dwThreadId) = 0; |
2837 | |
2838 | #ifdef FEATURE_INTEROP_DEBUGGING |
2839 | // Private hook to do the bulk of the interop-debugging goo. This includes hijacking inband |
2840 | // events and queueing them so that the helper-thread can run. |
2841 | // |
2842 | // We can remove this once we kill the helper-thread, or after enough functionality is |
2843 | // out-of-process that the debugger doesn't need the helper thread when stopped at an event. |
2844 | virtual void HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEvent) = 0; |
2845 | #endif // FEATURE_INTEROP_DEBUGGING |
2846 | |
2847 | // Get the modules in the order that they were loaded. This is needed to send the fake-attach events |
2848 | // for module load in the right order. |
2849 | // |
2850 | // This can be removed once ICorDebug's enumerations are ordered. |
2851 | virtual void GetModulesInLoadOrder( |
2852 | ICorDebugAssembly * pAssembly, |
2853 | RSExtSmartPtr<ICorDebugModule>* pModules, |
2854 | ULONG countModules) = 0; |
2855 | |
2856 | // Get the assemblies in the order that they were loaded. This is needed to send the fake-attach events |
2857 | // for assembly load in the right order. |
2858 | // |
2859 | // This can be removed once ICorDebug's enumerations are ordered. |
2860 | virtual void GetAssembliesInLoadOrder( |
2861 | ICorDebugAppDomain * pAppDomain, |
2862 | RSExtSmartPtr<ICorDebugAssembly>* pAssemblies, |
2863 | ULONG countAssemblies) = 0; |
2864 | |
2865 | // Queue up fake connection events for attach. |
2866 | // ICorDebug doesn't expose any enumeration for connections, so the shim needs to call into a |
2867 | // private hook to enumerate them for attach. |
2868 | virtual void QueueFakeConnectionEvents() = 0; |
2869 | |
2870 | // This finishes initializing the IPC channel between the LS + RS, which includes duplicating |
2871 | // some handles and events. |
2872 | // |
2873 | // This can be removed once the IPC channel is completely gone and all communication goes |
2874 | // soley through the data-target. |
2875 | virtual void FinishInitializeIPCChannel() = 0; |
2876 | |
2877 | // Called when stopped at a managed debug event to request a synchronization. |
2878 | // This can be replaced when we expose synchronization from ICorDebug. |
2879 | // The fact that the debuggee is at a managed debug event greatly simplifies the request here |
2880 | // (in contrast to QueueManagedAttachIfNeeded). It means that we can just flip a flag from |
2881 | // out-of-process, and when the debuggee thread resumes, it can check that flag and do the |
2882 | // synchronization from in-process. |
2883 | virtual void RequestSyncAtEvent()= 0; |
2884 | |
2885 | virtual bool IsThreadSuspendedOrHijacked(ICorDebugThread * pThread) = 0; |
2886 | }; |
2887 | |
2888 | |
2889 | // entry for the array of connections in EnumerateConnectionsData |
2890 | struct EnumerateConnectionsEntry |
2891 | { |
2892 | public: |
2893 | StringCopyHolder m_pName; // name of the connection |
2894 | DWORD m_dwID; // ID of the connection |
2895 | }; |
2896 | |
2897 | // data structure used in the callback for enumerating connections (code:CordbProcess::QueueFakeConnectionEvents) |
2898 | struct EnumerateConnectionsData |
2899 | { |
2900 | public: |
2901 | ~EnumerateConnectionsData() |
2902 | { |
2903 | if (m_pEntryArray != NULL) |
2904 | { |
2905 | delete [] m_pEntryArray; |
2906 | m_pEntryArray = NULL; |
2907 | } |
2908 | } |
2909 | |
2910 | CordbProcess * m_pThis; // the "this" process |
2911 | EnumerateConnectionsEntry * m_pEntryArray; // an array of connections to be filled in |
2912 | UINT32 m_uIndex; // the next entry in the array to be filled |
2913 | }; |
2914 | |
2915 | // data structure used in the callback for asserting that an appdomain has been deleted |
2916 | // (code:CordbProcess::DbgAssertAppDomainDeleted) |
2917 | struct DbgAssertAppDomainDeletedData |
2918 | { |
2919 | public: |
2920 | CordbProcess * m_pThis; |
2921 | VMPTR_AppDomain m_vmAppDomainDeleted; |
2922 | }; |
2923 | |
2924 | class CordbProcess : |
2925 | public CordbBase, |
2926 | public ICorDebugProcess, |
2927 | public ICorDebugProcess2, |
2928 | public ICorDebugProcess3, |
2929 | public ICorDebugProcess4, |
2930 | public ICorDebugProcess5, |
2931 | public ICorDebugProcess7, |
2932 | public ICorDebugProcess8, |
2933 | public ICorDebugProcess10, |
2934 | public IDacDbiInterface::IAllocator, |
2935 | public IDacDbiInterface::IMetaDataLookup, |
2936 | public IProcessShimHooks |
2937 | #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL |
2938 | , public ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly |
2939 | #endif |
2940 | { |
2941 | // Ctor is private. Use OpenVirtualProcess instead. |
2942 | CordbProcess(ULONG64 clrInstanceId, IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, const ProcessDescriptor * pProcessDescriptor, ShimProcess * pShim); |
2943 | |
2944 | public: |
2945 | |
2946 | virtual ~CordbProcess(); |
2947 | virtual void Neuter(); |
2948 | |
2949 | // Neuter left-side resources for all children |
2950 | void NeuterChildrenLeftSideResources(); |
2951 | |
2952 | // Neuter all of all children, but not the actual process object. |
2953 | void NeuterChildren(); |
2954 | |
2955 | |
2956 | // The way to instantiate a new CordbProcess object. |
2957 | // @dbgtodo managed pipeline - this is not fully active in all scenarios yet. |
2958 | static HRESULT OpenVirtualProcess(ULONG64 clrInstanceId, |
2959 | IUnknown * pDataTarget, |
2960 | HMODULE hDacModule, |
2961 | Cordb * pCordb, |
2962 | const ProcessDescriptor * pProcessDescriptor, |
2963 | ShimProcess * pShim, |
2964 | CordbProcess ** ppProcess); |
2965 | |
2966 | // Helper function to determine whether this ICorDebug is compatibile with a debugger |
2967 | // designed for the specified major version |
2968 | static bool IsCompatibleWith(DWORD clrMajorVersion); |
2969 | |
2970 | //----------------------------------------------------------- |
2971 | // IMetaDataLookup |
2972 | // ----------------------------------------------------------- |
2973 | IMDInternalImport * LookupMetaData(VMPTR_PEFile vmPEFile, bool &isILMetaDataForNGENImage); |
2974 | |
2975 | // Helper functions for LookupMetaData implementation |
2976 | IMDInternalImport * LookupMetaDataFromDebugger(VMPTR_PEFile vmPEFile, |
2977 | bool &isILMetaDataForNGENImage, |
2978 | CordbModule * pModule); |
2979 | |
2980 | IMDInternalImport * LookupMetaDataFromDebuggerForSingleFile(CordbModule * pModule, |
2981 | LPCWSTR pwszImagePath, |
2982 | DWORD dwTimeStamp, |
2983 | DWORD dwImageSize); |
2984 | |
2985 | |
2986 | //----------------------------------------------------------- |
2987 | // IDacDbiInterface::IAllocator |
2988 | //----------------------------------------------------------- |
2989 | |
2990 | void * Alloc(SIZE_T lenBytes); |
2991 | void Free(void * p); |
2992 | |
2993 | #ifdef _DEBUG |
2994 | virtual const char * DbgGetName() { return "CordbProcess" ; } |
2995 | #endif |
2996 | |
2997 | //----------------------------------------------------------- |
2998 | // IUnknown |
2999 | //----------------------------------------------------------- |
3000 | |
3001 | ULONG STDMETHODCALLTYPE AddRef() |
3002 | { |
3003 | return BaseAddRefEnforceExternal(); |
3004 | } |
3005 | ULONG STDMETHODCALLTYPE Release() |
3006 | { |
3007 | return BaseReleaseEnforceExternal(); |
3008 | } |
3009 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
3010 | |
3011 | //----------------------------------------------------------- |
3012 | // ICorDebugController |
3013 | //----------------------------------------------------------- |
3014 | |
3015 | COM_METHOD Stop(DWORD dwTimeout); |
3016 | COM_METHOD Deprecated_Continue(); |
3017 | COM_METHOD IsRunning(BOOL *pbRunning); |
3018 | COM_METHOD HasQueuedCallbacks(ICorDebugThread *pThread, BOOL *pbQueued); |
3019 | COM_METHOD EnumerateThreads(ICorDebugThreadEnum **ppThreads); |
3020 | COM_METHOD SetAllThreadsDebugState(CorDebugThreadState state, |
3021 | ICorDebugThread *pExceptThisThread); |
3022 | COM_METHOD Detach(); |
3023 | COM_METHOD Terminate(unsigned int exitCode); |
3024 | |
3025 | COM_METHOD CanCommitChanges( |
3026 | ULONG cSnapshots, |
3027 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
3028 | ICorDebugErrorInfoEnum **pError); |
3029 | |
3030 | COM_METHOD CommitChanges( |
3031 | ULONG cSnapshots, |
3032 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
3033 | ICorDebugErrorInfoEnum **pError); |
3034 | |
3035 | COM_METHOD Continue(BOOL fIsOutOfBand); |
3036 | COM_METHOD ThreadForFiberCookie(DWORD fiberCookie, |
3037 | ICorDebugThread **ppThread); |
3038 | COM_METHOD GetHelperThreadID(DWORD *pThreadID); |
3039 | |
3040 | //----------------------------------------------------------- |
3041 | // ICorDebugProcess |
3042 | //----------------------------------------------------------- |
3043 | |
3044 | COM_METHOD GetID(DWORD *pdwProcessId); |
3045 | COM_METHOD GetHandle(HANDLE *phProcessHandle); |
3046 | COM_METHOD EnableSynchronization(BOOL bEnableSynchronization); |
3047 | COM_METHOD GetThread(DWORD dwThreadId, ICorDebugThread **ppThread); |
3048 | COM_METHOD EnumerateBreakpoints(ICorDebugBreakpointEnum **ppBreakpoints); |
3049 | COM_METHOD EnumerateSteppers(ICorDebugStepperEnum **ppSteppers); |
3050 | COM_METHOD EnumerateObjects(ICorDebugObjectEnum **ppObjects); |
3051 | COM_METHOD IsTransitionStub(CORDB_ADDRESS address, BOOL *pbTransitionStub); |
3052 | COM_METHOD EnumerateModules(ICorDebugModuleEnum **ppModules); |
3053 | COM_METHOD GetModuleFromMetaDataInterface(IUnknown *pIMetaData, |
3054 | ICorDebugModule **ppModule); |
3055 | COM_METHOD SetStopState(DWORD threadID, CorDebugThreadState state); |
3056 | COM_METHOD IsOSSuspended(DWORD threadID, BOOL *pbSuspended); |
3057 | COM_METHOD GetThreadContext(DWORD threadID, ULONG32 contextSize, |
3058 | BYTE context[]); |
3059 | COM_METHOD SetThreadContext(DWORD threadID, ULONG32 contextSize, |
3060 | BYTE context[]); |
3061 | COM_METHOD ReadMemory(CORDB_ADDRESS address, DWORD size, BYTE buffer[], |
3062 | SIZE_T *read); |
3063 | COM_METHOD WriteMemory(CORDB_ADDRESS address, DWORD size, BYTE buffer[], |
3064 | SIZE_T *written); |
3065 | |
3066 | COM_METHOD ClearCurrentException(DWORD threadID); |
3067 | |
3068 | /* |
3069 | * EnableLogMessages enables/disables sending of log messages to the |
3070 | * debugger for logging. |
3071 | */ |
3072 | COM_METHOD EnableLogMessages(BOOL fOnOff); |
3073 | |
3074 | /* |
3075 | * ModifyLogSwitch modifies the specified switch's severity level. |
3076 | */ |
3077 | COM_METHOD ModifyLogSwitch(__in_z WCHAR *pLogSwitchName, LONG lLevel); |
3078 | |
3079 | COM_METHOD EnumerateAppDomains(ICorDebugAppDomainEnum **ppAppDomains); |
3080 | COM_METHOD GetObject(ICorDebugValue **ppObject); |
3081 | |
3082 | //----------------------------------------------------------- |
3083 | // ICorDebugProcess2 |
3084 | //----------------------------------------------------------- |
3085 | |
3086 | COM_METHOD GetThreadForTaskID(TASKID taskId, ICorDebugThread2 ** ppThread); |
3087 | COM_METHOD GetVersion(COR_VERSION* pInfo); |
3088 | |
3089 | COM_METHOD SetUnmanagedBreakpoint(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen); |
3090 | COM_METHOD ClearUnmanagedBreakpoint(CORDB_ADDRESS address); |
3091 | COM_METHOD GetCodeAtAddress(CORDB_ADDRESS address, ICorDebugCode ** pCode, ULONG32 * offset); |
3092 | |
3093 | COM_METHOD SetDesiredNGENCompilerFlags(DWORD pdwFlags); |
3094 | COM_METHOD GetDesiredNGENCompilerFlags(DWORD *pdwFlags ); |
3095 | |
3096 | COM_METHOD GetReferenceValueFromGCHandle(UINT_PTR handle, ICorDebugReferenceValue **pOutValue); |
3097 | |
3098 | //----------------------------------------------------------- |
3099 | // ICorDebugProcess3 |
3100 | //----------------------------------------------------------- |
3101 | |
3102 | // enables or disables CustomNotifications of a given type |
3103 | COM_METHOD SetEnableCustomNotification(ICorDebugClass * pClass, BOOL fEnable); |
3104 | |
3105 | //----------------------------------------------------------- |
3106 | // ICorDebugProcess4 |
3107 | //----------------------------------------------------------- |
3108 | COM_METHOD Filter( |
3109 | const BYTE pRecord[], |
3110 | DWORD countBytes, |
3111 | CorDebugRecordFormat format, |
3112 | DWORD dwFlags, |
3113 | DWORD dwThreadId, |
3114 | ICorDebugManagedCallback *pCallback, |
3115 | DWORD * pContinueStatus); |
3116 | |
3117 | COM_METHOD ProcessStateChanged(CorDebugStateChange eChange); |
3118 | |
3119 | //----------------------------------------------------------- |
3120 | // ICorDebugProcess5 |
3121 | //----------------------------------------------------------- |
3122 | COM_METHOD GetGCHeapInformation(COR_HEAPINFO *pHeapInfo); |
3123 | COM_METHOD EnumerateHeap(ICorDebugHeapEnum **ppObjects); |
3124 | COM_METHOD EnumerateHeapRegions(ICorDebugHeapSegmentEnum **ppRegions); |
3125 | COM_METHOD GetObject(CORDB_ADDRESS addr, ICorDebugObjectValue **pObject); |
3126 | COM_METHOD EnableNGENPolicy(CorDebugNGENPolicy ePolicy); |
3127 | COM_METHOD EnumerateGCReferences(BOOL enumerateWeakReferences, ICorDebugGCReferenceEnum **ppEnum); |
3128 | COM_METHOD EnumerateHandles(CorGCReferenceType types, ICorDebugGCReferenceEnum **ppEnum); |
3129 | COM_METHOD GetTypeID(CORDB_ADDRESS obj, COR_TYPEID *pId); |
3130 | COM_METHOD GetTypeForTypeID(COR_TYPEID id, ICorDebugType **ppType); |
3131 | COM_METHOD GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout); |
3132 | COM_METHOD GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout); |
3133 | COM_METHOD GetTypeFields(COR_TYPEID id, ULONG32 celt, COR_FIELD fields[], ULONG32 *pceltNeeded); |
3134 | |
3135 | //----------------------------------------------------------- |
3136 | // ICorDebugProcess7 |
3137 | //----------------------------------------------------------- |
3138 | COM_METHOD SetWriteableMetadataUpdateMode(WriteableMetadataUpdateMode flags); |
3139 | |
3140 | //----------------------------------------------------------- |
3141 | // ICorDebugProcess8 |
3142 | //----------------------------------------------------------- |
3143 | COM_METHOD EnableExceptionCallbacksOutsideOfMyCode(BOOL enableExceptionsOutsideOfJMC); |
3144 | |
3145 | //----------------------------------------------------------- |
3146 | // ICorDebugProcess10 |
3147 | //----------------------------------------------------------- |
3148 | COM_METHOD EnableGCNotificationEvents(BOOL fEnable); |
3149 | |
3150 | #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL |
3151 | // --------------------------------------------------------------- |
3152 | // ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly |
3153 | // --------------------------------------------------------------- |
3154 | |
3155 | COM_METHOD InvokePauseCallback(); |
3156 | COM_METHOD InvokeResumeCallback(); |
3157 | |
3158 | #endif |
3159 | |
3160 | //----------------------------------------------------------- |
3161 | // Methods not exposed via a COM interface. |
3162 | //----------------------------------------------------------- |
3163 | |
3164 | HRESULT ContinueInternal(BOOL fIsOutOfBand); |
3165 | HRESULT StopInternal(DWORD dwTimeout, VMPTR_AppDomain pAppDomainToken); |
3166 | |
3167 | // Sets an unmanaged breakpoint at the target address |
3168 | HRESULT SetUnmanagedBreakpointInternal(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen); |
3169 | |
3170 | // Allocate a buffer within the target and return the range. Throws on error. |
3171 | TargetBuffer GetRemoteBuffer(ULONG cbBuffer); // throws |
3172 | |
3173 | // Same as above except also copy-in the contents of a RS buffer using WriteProcessMemory |
3174 | HRESULT GetAndWriteRemoteBuffer(CordbAppDomain *pDomain, unsigned int bufferSize, const void *bufferFrom, void **ppBuffer); |
3175 | |
3176 | /* |
3177 | * This will release a previously allocated left side buffer. |
3178 | * Often they are deallocated by the LS itself. |
3179 | */ |
3180 | HRESULT ReleaseRemoteBuffer(void **ppBuffer); |
3181 | |
3182 | |
3183 | void TargetConsistencyCheck(bool fExpression); |
3184 | |
3185 | // Activate interop-debugging, after the process has initially been Init() |
3186 | void EnableInteropDebugging(); |
3187 | |
3188 | HRESULT Init(); |
3189 | void DeleteQueuedEvents(); |
3190 | void CleanupHalfBakedLeftSide(); |
3191 | void Terminating(BOOL fDetach); |
3192 | |
3193 | CordbThread * TryLookupThread(VMPTR_Thread vmThread); |
3194 | CordbThread * TryLookupOrCreateThreadByVolatileOSId(DWORD dwThreadId); |
3195 | CordbThread * TryLookupThreadByVolatileOSId(DWORD dwThreadId); |
3196 | CordbThread * LookupOrCreateThread(VMPTR_Thread vmThread); |
3197 | |
3198 | void QueueManagedAttachIfNeeded(); |
3199 | void QueueManagedAttachIfNeededWorker(); |
3200 | HRESULT QueueManagedAttach(); |
3201 | |
3202 | void DetachShim(); |
3203 | |
3204 | // Flush for when the process is running. |
3205 | void FlushProcessRunning(); |
3206 | |
3207 | // Flush all state. |
3208 | void FlushAll(); |
3209 | |
3210 | BOOL HijackThreadForUnhandledExceptionIfNeeded(DWORD dwThreadId); |
3211 | |
3212 | // Filter a CLR notification (subset of exceptions). |
3213 | void FilterClrNotification( |
3214 | DebuggerIPCEvent * pManagedEvent, |
3215 | RSLockHolder * pLockHolder, |
3216 | ICorDebugManagedCallback * pCallback); |
3217 | |
3218 | // Wrapper to invoke IClrDataTarget4::ContinueStatusChanged |
3219 | void ContinueStatusChanged(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus); |
3220 | |
3221 | |
3222 | // Request a synchronization to occur after a debug event is dispatched. |
3223 | void RequestSyncAtEvent(); |
3224 | |
3225 | // |
3226 | // Basic managed event plumbing |
3227 | // |
3228 | |
3229 | // This is called on the first IPC event from the debuggee. It initializes state. |
3230 | void FinishInitializeIPCChannel(); |
3231 | void FinishInitializeIPCChannelWorker(); |
3232 | |
3233 | // This is called on each IPC event from the debuggee. |
3234 | void HandleRCEvent(DebuggerIPCEvent * pManagedEvent, RSLockHolder * pLockHolder, ICorDebugManagedCallback * pCallback); |
3235 | |
3236 | // Queue the RC event. |
3237 | void QueueRCEvent(DebuggerIPCEvent * pManagedEvent); |
3238 | |
3239 | // This marshals a managed debug event from the |
3240 | void MarshalManagedEvent(DebuggerIPCEvent * pManagedEvent); |
3241 | |
3242 | // This copies a managed debug event from the IPC block and to pManagedEvent. |
3243 | // The event still needs to be marshalled. |
3244 | void CopyRCEventFromIPCBlock(DebuggerIPCEvent * pManagedEvent); |
3245 | |
3246 | // This copies a managed debug event out of the Native-Debug event envelope. |
3247 | // The event still needs to be marshalled. |
3248 | bool CopyManagedEventFromTarget(const EXCEPTION_RECORD * pRecord, DebuggerIPCEvent * pLocalManagedEvent); |
3249 | |
3250 | // Helper for Filter() to verify parameters and return a type-safe exception record. |
3251 | const EXCEPTION_RECORD * ValidateExceptionRecord( |
3252 | const BYTE pRawRecord[], |
3253 | DWORD countBytes, |
3254 | CorDebugRecordFormat format); |
3255 | |
3256 | // Helper to read a structure from the target. |
3257 | template<typename T> |
3258 | HRESULT SafeReadStruct(CORDB_ADDRESS pRemotePtr, T* pLocalBuffer); |
3259 | |
3260 | // Helper to write a structure into the target. |
3261 | template<typename T> |
3262 | HRESULT SafeWriteStruct(CORDB_ADDRESS pRemotePtr, const T* pLocalBuffer); |
3263 | |
3264 | // Reads a buffer from the target |
3265 | HRESULT SafeReadBuffer(TargetBuffer tb, BYTE * pLocalBuffer, BOOL throwOnError = TRUE); |
3266 | |
3267 | // Writes a buffer to the target |
3268 | void SafeWriteBuffer(TargetBuffer tb, const BYTE * pLocalBuffer); |
3269 | |
3270 | #if defined(FEATURE_INTEROP_DEBUGGING) |
3271 | void DuplicateHandleToLocalProcess(HANDLE * pLocalHandle, RemoteHANDLE * pRemoteHandle); |
3272 | #endif // FEATURE_INTEROP_DEBUGGING |
3273 | |
3274 | bool IsThreadSuspendedOrHijacked(ICorDebugThread * pICorDebugThread); |
3275 | |
3276 | // Helper to get ProcessDescriptor internally. |
3277 | const ProcessDescriptor* GetProcessDescriptor(); |
3278 | |
3279 | HRESULT GetRuntimeOffsets(); |
3280 | |
3281 | // Are we blocked waiting fo ran OOB event to be continue? |
3282 | bool IsWaitingForOOBEvent() |
3283 | { |
3284 | #ifdef FEATURE_INTEROP_DEBUGGING |
3285 | return m_outOfBandEventQueue != NULL; |
3286 | #else |
3287 | // If no interop, then we're never waiting for an OOB event. |
3288 | return false; |
3289 | #endif |
3290 | } |
3291 | |
3292 | // |
3293 | // Shim callbacks to simulate fake attach events. |
3294 | // |
3295 | |
3296 | |
3297 | // Callback for Shim to get the assemblies in load order |
3298 | void GetAssembliesInLoadOrder( |
3299 | ICorDebugAppDomain * pAppDomain, |
3300 | RSExtSmartPtr<ICorDebugAssembly>* pAssemblies, |
3301 | ULONG countAssemblies); |
3302 | |
3303 | // Callback for Shim to get the modules in load order |
3304 | void GetModulesInLoadOrder( |
3305 | ICorDebugAssembly * pAssembly, |
3306 | RSExtSmartPtr<ICorDebugModule>* pModules, |
3307 | ULONG countModules); |
3308 | |
3309 | // Functions to queue fake Connection events on attach. |
3310 | static void CountConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData); |
3311 | static void EnumerateConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData); |
3312 | void QueueFakeConnectionEvents(); |
3313 | |
3314 | |
3315 | |
3316 | void DispatchRCEvent(); |
3317 | |
3318 | // Dispatch a single event via the callbacks. |
3319 | void RawDispatchEvent( |
3320 | DebuggerIPCEvent * pEvent, |
3321 | RSLockHolder * pLockHolder, |
3322 | ICorDebugManagedCallback * pCallback1, |
3323 | ICorDebugManagedCallback2 * pCallback2, |
3324 | ICorDebugManagedCallback3 * pCallback3, |
3325 | ICorDebugManagedCallback4 * pCallback4); |
3326 | |
3327 | void MarkAllThreadsDirty(); |
3328 | |
3329 | bool CheckIfLSExited(); |
3330 | |
3331 | void Lock() |
3332 | { |
3333 | // Lock Hierarchy - shouldn't have List lock when taking/release the process lock. |
3334 | |
3335 | m_processMutex.Lock(); |
3336 | LOG((LF_CORDB, LL_EVERYTHING, "P::Lock enter, this=0x%p\n" , this)); |
3337 | } |
3338 | |
3339 | void Unlock() |
3340 | { |
3341 | // Lock Hierarchy - shouldn't have List lock when taking/releasing the process lock. |
3342 | |
3343 | LOG((LF_CORDB, LL_EVERYTHING, "P::Lock leave, this=0x%p\n" , this)); |
3344 | m_processMutex.Unlock(); |
3345 | } |
3346 | |
3347 | #ifdef _DEBUG |
3348 | bool ThreadHoldsProcessLock() |
3349 | { |
3350 | return m_processMutex.HasLock(); |
3351 | } |
3352 | #endif |
3353 | |
3354 | // Expose the process lock. |
3355 | // This is the main lock in V3. |
3356 | RSLock * GetProcessLock() |
3357 | { |
3358 | return &m_processMutex; |
3359 | } |
3360 | |
3361 | |
3362 | // @dbgtodo synchronization - the SG lock goes away in V3. |
3363 | // Expose the stop-go lock b/c varios Cordb objects in our process tree may need to take it. |
3364 | RSLock * GetStopGoLock() |
3365 | { |
3366 | return &m_StopGoLock; |
3367 | } |
3368 | |
3369 | |
3370 | void UnrecoverableError(HRESULT errorHR, |
3371 | unsigned int errorCode, |
3372 | const char *errorFile, |
3373 | unsigned int errorLine); |
3374 | HRESULT CheckForUnrecoverableError(); |
3375 | void VerifyControlBlock(); |
3376 | |
3377 | // The implementation of EnumerateThreads without the public API error checks |
3378 | VOID InternalEnumerateThreads(RSInitHolder<CordbHashTableEnum> * ppThreads); |
3379 | |
3380 | //----------------------------------------------------------- |
3381 | // Convenience routines |
3382 | //----------------------------------------------------------- |
3383 | |
3384 | // Is it safe to send events to the LS? |
3385 | bool IsSafeToSendEvents() { return !m_unrecoverableError && !m_terminated && !m_detached; } |
3386 | |
3387 | bool IsWin32EventThread(); |
3388 | |
3389 | void HandleSyncCompleteRecieved(); |
3390 | |
3391 | // Send a truly asynchronous IPC event. |
3392 | void SendAsyncIPCEvent(DebuggerIPCEventType t); |
3393 | |
3394 | HRESULT SendIPCEvent(DebuggerIPCEvent *event, SIZE_T eventSize) |
3395 | { |
3396 | // @dbgtodo - eventually remove this when all IPC events are gone. |
3397 | // In V3 paths, we can't send IPC events. |
3398 | if (GetShim() == NULL) |
3399 | { |
3400 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "!! Can't send IPC event in V3. %s" , IPCENames::GetName(event->type)); |
3401 | return E_NOTIMPL; |
3402 | } |
3403 | _ASSERTE(m_cordb != NULL); |
3404 | return (m_cordb->SendIPCEvent(this, event, eventSize)); |
3405 | } |
3406 | |
3407 | void InitAsyncIPCEvent(DebuggerIPCEvent *ipce, |
3408 | DebuggerIPCEventType type, |
3409 | VMPTR_AppDomain vmAppDomain) |
3410 | { |
3411 | // Async events only allowed for the following: |
3412 | _ASSERTE(type == DB_IPCE_ATTACHING); |
3413 | |
3414 | InitIPCEvent(ipce, type, false, vmAppDomain); |
3415 | ipce->asyncSend = true; |
3416 | } |
3417 | |
3418 | void InitIPCEvent(DebuggerIPCEvent *ipce, |
3419 | DebuggerIPCEventType type, |
3420 | bool twoWay, |
3421 | VMPTR_AppDomain vmAppDomain |
3422 | ) |
3423 | { |
3424 | // zero out the event in case we try and use any uninitialized fields |
3425 | memset( ipce, 0, sizeof(DebuggerIPCEvent) ); |
3426 | |
3427 | _ASSERTE((!vmAppDomain.IsNull()) || |
3428 | type == DB_IPCE_GET_GCHANDLE_INFO || |
3429 | type == DB_IPCE_ENABLE_LOG_MESSAGES || |
3430 | type == DB_IPCE_MODIFY_LOGSWITCH || |
3431 | type == DB_IPCE_ASYNC_BREAK || |
3432 | type == DB_IPCE_CONTINUE || |
3433 | type == DB_IPCE_GET_BUFFER || |
3434 | type == DB_IPCE_RELEASE_BUFFER || |
3435 | type == DB_IPCE_IS_TRANSITION_STUB || |
3436 | type == DB_IPCE_ATTACHING || |
3437 | type == DB_IPCE_APPLY_CHANGES || |
3438 | type == DB_IPCE_CONTROL_C_EVENT_RESULT || |
3439 | type == DB_IPCE_SET_REFERENCE || |
3440 | type == DB_IPCE_SET_ALL_DEBUG_STATE || |
3441 | type == DB_IPCE_GET_THREAD_FOR_TASKID || |
3442 | type == DB_IPCE_DETACH_FROM_PROCESS || |
3443 | type == DB_IPCE_INTERCEPT_EXCEPTION || |
3444 | type == DB_IPCE_GET_NGEN_COMPILER_FLAGS || |
3445 | type == DB_IPCE_SET_NGEN_COMPILER_FLAGS || |
3446 | type == DB_IPCE_SET_VALUE_CLASS); |
3447 | |
3448 | ipce->type = type; |
3449 | ipce->hr = S_OK; |
3450 | ipce->processId = 0; |
3451 | ipce->vmAppDomain = vmAppDomain; |
3452 | ipce->vmThread = VMPTR_Thread::NullPtr(); |
3453 | ipce->replyRequired = twoWay; |
3454 | ipce->asyncSend = false; |
3455 | ipce->next = NULL; |
3456 | } |
3457 | |
3458 | // Looks up a previously constructed CordbClass instance without creating. May return NULL if the |
3459 | // CordbClass instance doesn't exist. |
3460 | CordbClass * LookupClass(ICorDebugAppDomain * pAppDomain, VMPTR_DomainFile vmDomainFile, mdTypeDef classToken); |
3461 | |
3462 | CordbModule * LookupOrCreateModule(VMPTR_DomainFile vmDomainFile); |
3463 | |
3464 | #ifdef FEATURE_INTEROP_DEBUGGING |
3465 | CordbUnmanagedThread *GetUnmanagedThread(DWORD dwThreadId) |
3466 | { |
3467 | _ASSERTE(ThreadHoldsProcessLock()); |
3468 | return m_unmanagedThreads.GetBase(dwThreadId); |
3469 | } |
3470 | #endif // FEATURE_INTEROP_DEBUGGING |
3471 | |
3472 | /* |
3473 | * This will cleanup the patch table, releasing memory,etc. |
3474 | */ |
3475 | void ClearPatchTable(); |
3476 | |
3477 | /* |
3478 | * This will grab the patch table from the left side & go through |
3479 | * it to gather info needed for faster access. If address,size,buffer |
3480 | * are passed in, while going through the table we'll undo patches |
3481 | * in buffer at the same time |
3482 | */ |
3483 | HRESULT RefreshPatchTable(CORDB_ADDRESS address = NULL, SIZE_T size = NULL, BYTE buffer[] = NULL); |
3484 | |
3485 | // Find if a patch exists at a given address. |
3486 | HRESULT FindPatchByAddress(CORDB_ADDRESS address, bool *patchFound, bool *patchIsUnmanaged); |
3487 | |
3488 | enum AB_MODE |
3489 | { |
3490 | AB_READ, |
3491 | AB_WRITE |
3492 | }; |
3493 | |
3494 | /* |
3495 | * Once we've called RefreshPatchTable to get the patch table, |
3496 | * this routine will iterate through the patches & either apply |
3497 | * or unapply the patches to buffer. AB_READ => Replaces patches |
3498 | * in buffer with the original opcode, AB_WRTE => replace opcode |
3499 | * with breakpoint instruction, caller is responsible for |
3500 | * updating the patchtable back to the left side. |
3501 | * |
3502 | * <TODO>@todo Perf Instead of a copy, undo the changes |
3503 | * Since the 'buffer' arg is an [in] param, we're not supposed to |
3504 | * change it. If we do, we'll allocate & copy it to bufferCopy |
3505 | * (we'll also set *pbUpdatePatchTable to true), otherwise we |
3506 | * don't manipuldate bufferCopy (so passing a NULL in for |
3507 | * reading is fine).</TODO> |
3508 | */ |
3509 | HRESULT AdjustBuffer(CORDB_ADDRESS address, |
3510 | SIZE_T size, |
3511 | BYTE buffer[], |
3512 | BYTE **bufferCopy, |
3513 | AB_MODE mode, |
3514 | BOOL *pbUpdatePatchTable = NULL); |
3515 | |
3516 | /* |
3517 | * AdjustBuffer, above, doesn't actually update the local patch table |
3518 | * if asked to do a write. It stores the changes alongside the table, |
3519 | * and this will cause the changes to be written to the table (for |
3520 | * a range of left-side addresses |
3521 | */ |
3522 | void CommitBufferAdjustments(CORDB_ADDRESS start, |
3523 | CORDB_ADDRESS end); |
3524 | |
3525 | /* |
3526 | * Clear the stored changes, or they'll sit there until we |
3527 | * accidentally commit them |
3528 | */ |
3529 | void ClearBufferAdjustments(); |
3530 | |
3531 | |
3532 | |
3533 | |
3534 | //----------------------------------------------------------- |
3535 | // Accessors for key synchronization fields. |
3536 | //----------------------------------------------------------- |
3537 | |
3538 | // If CAD is NULL, returns true if all appdomains (ie, the entire process) |
3539 | // is synchronized. Otherwise, returns true if the specified appdomain is |
3540 | // synch'd. |
3541 | bool GetSynchronized(); |
3542 | void SetSynchronized(bool fSynch); |
3543 | |
3544 | void IncStopCount(); |
3545 | void DecStopCount(); |
3546 | |
3547 | // Gets the exact stop count. You need the Proecss lock for this. |
3548 | int GetStopCount(); |
3549 | |
3550 | // Just gets whether we're stopped or not (m_stopped > 0). |
3551 | // You only need the StopGo lock for this. |
3552 | // This is biases towards returning false. |
3553 | bool IsStopped(); |
3554 | |
3555 | bool GetSyncCompleteRecv(); |
3556 | void SetSyncCompleteRecv(bool fSyncRecv); |
3557 | |
3558 | |
3559 | // Cordbg may not always continue during a callback; but we really shouldn't do meaningful |
3560 | // work after a callback has returned yet before they've called continue. Thus we may need |
3561 | // to remember some state at the time of dispatch so that we do stuff at continue. |
3562 | // Only example here is neutering... we'd like to Neuter an object X after the ExitX callback, |
3563 | // but we can't neuter it until Continue. So remember X when we dispatch, and neuter this at continue. |
3564 | // Use a smart ptr to keep it alive until we neuter it. |
3565 | |
3566 | // Add objects to various neuter lists. |
3567 | // NeuterOnContinue is for all objects that can be neutered once we continue. |
3568 | // NeuterOnExit is for all objects that can survive continues (but are neutered on process shutdown). |
3569 | // If an object's external ref count goes to 0, it gets promoted to the NeuterOnContinue list. |
3570 | void AddToNeuterOnExitList(CordbBase *pObject); |
3571 | void AddToNeuterOnContinueList(CordbBase *pObject); |
3572 | |
3573 | NeuterList * GetContinueNeuterList() { return &m_ContinueNeuterList; } |
3574 | NeuterList * GetExitNeuterList() { return &m_ExitNeuterList; } |
3575 | |
3576 | void AddToLeftSideResourceCleanupList(CordbBase * pObject); |
3577 | |
3578 | // Routines to read and write thread context records between the processes safely. |
3579 | HRESULT SafeReadThreadContext(LSPTR_CONTEXT pRemoteContext, DT_CONTEXT * pCtx); |
3580 | HRESULT SafeWriteThreadContext(LSPTR_CONTEXT pRemoteContext, const DT_CONTEXT * pCtx); |
3581 | |
3582 | #ifdef FEATURE_INTEROP_DEBUGGING |
3583 | // Record a win32 event for debugging purposes. |
3584 | void DebugRecordWin32Event(const DEBUG_EVENT * pEvent, CordbUnmanagedThread * pUThread); |
3585 | #endif // FEATURE_INTEROP_DEBUGGING |
3586 | |
3587 | //----------------------------------------------------------- |
3588 | // Interop Helpers |
3589 | //----------------------------------------------------------- |
3590 | |
3591 | // Get the DAC interface. |
3592 | IDacDbiInterface * GetDAC(); |
3593 | |
3594 | // Get the data-target, which provides access to the debuggee. |
3595 | ICorDebugDataTarget * GetDataTarget(); |
3596 | |
3597 | BOOL IsDacInitialized(); |
3598 | |
3599 | void ForceDacFlush(); |
3600 | |
3601 | |
3602 | #ifdef FEATURE_INTEROP_DEBUGGING |
3603 | // Deal with native debug events for the interop-debugging scenario. |
3604 | void HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEvent); |
3605 | |
3606 | void ResumeHijackedThreads(); |
3607 | |
3608 | //@todo - We should try to make these all private |
3609 | CordbUnmanagedThread *HandleUnmanagedCreateThread(DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase); |
3610 | |
3611 | HRESULT ContinueOOB(); |
3612 | void QueueUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent); |
3613 | void DequeueUnmanagedEvent(CordbUnmanagedThread *pUThread); |
3614 | void QueueOOBUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent); |
3615 | void DequeueOOBUnmanagedEvent(CordbUnmanagedThread *pUThread); |
3616 | void DispatchUnmanagedInBandEvent(); |
3617 | void DispatchUnmanagedOOBEvent(); |
3618 | bool ExceptionIsFlare(DWORD exceptionCode, const void *exceptionAddress); |
3619 | |
3620 | bool IsSpecialStackOverflowCase(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent); |
3621 | |
3622 | HRESULT SuspendUnmanagedThreads(); |
3623 | HRESULT ResumeUnmanagedThreads(); |
3624 | |
3625 | HRESULT HijackIBEvent(CordbUnmanagedEvent * pUnmanagedEvent); |
3626 | |
3627 | BOOL HasUndispatchedNativeEvents(); |
3628 | BOOL HasUserUncontinuedNativeEvents(); |
3629 | #endif // FEATURE_INTEROP_DEBUGGING |
3630 | |
3631 | HRESULT StartSyncFromWin32Stop(BOOL * pfAsyncBreakSent); |
3632 | |
3633 | |
3634 | // For interop attach, we first do native, and then once Cordbg continues from |
3635 | // the loader-bp, we kick off the managed attach. This field remembers that |
3636 | // whether we need the managed attach. |
3637 | // @dbgtodo managed pipeline - hoist to shim. |
3638 | bool m_fDoDelayedManagedAttached; |
3639 | |
3640 | |
3641 | |
3642 | // Table of CordbEval objects that we've sent over to the LS. |
3643 | // This is synced via the process lock. |
3644 | RsPtrTable<CordbEval> m_EvalTable; |
3645 | |
3646 | void PrepopulateThreadsOrThrow(); |
3647 | |
3648 | // Lookup or create an appdomain. |
3649 | CordbAppDomain * LookupOrCreateAppDomain(VMPTR_AppDomain vmAppDomain); |
3650 | |
3651 | // Get the shared app domain. |
3652 | CordbAppDomain * GetSharedAppDomain(); |
3653 | |
3654 | // Get metadata dispenser. |
3655 | IMetaDataDispenserEx * GetDispenser(); |
3656 | |
3657 | // Sets a bitfield reflecting the managed debugging state at the time of |
3658 | // the jit attach. |
3659 | HRESULT GetAttachStateFlags(CLR_DEBUGGING_PROCESS_FLAGS *pFlags); |
3660 | |
3661 | HRESULT GetTypeForObject(CORDB_ADDRESS obj, CordbAppDomain* pAppDomainOverride, CordbType **ppType, CordbAppDomain **pAppDomain = NULL); |
3662 | |
3663 | WriteableMetadataUpdateMode GetWriteableMetadataUpdateMode() { return m_writableMetadataUpdateMode; } |
3664 | private: |
3665 | |
3666 | #ifdef _DEBUG |
3667 | // Assert that vmAppDomainDeleted doesn't show up in dac enumerations |
3668 | void DbgAssertAppDomainDeleted(VMPTR_AppDomain vmAppDomainDeleted); |
3669 | |
3670 | // Callback helper for DbgAssertAppDomainDeleted. |
3671 | static void DbgAssertAppDomainDeletedCallback(VMPTR_AppDomain vmAppDomain, void * pUserData); |
3672 | #endif // _DEBUG |
3673 | |
3674 | static void ThreadEnumerationCallback(VMPTR_Thread vmThread, void * pUserData); |
3675 | |
3676 | |
3677 | // Callback for AppDomain enumeration |
3678 | static void AppDomainEnumerationCallback(VMPTR_AppDomain vmAppDomain, void * pUserData); |
3679 | |
3680 | // Helper to create a new CordbAppDomain around the vmptr and cache it |
3681 | CordbAppDomain * CacheAppDomain(VMPTR_AppDomain vmAppDomain); |
3682 | |
3683 | // Helper to traverse Appdomains in target and build up our cache. |
3684 | void PrepopulateAppDomainsOrThrow(); |
3685 | |
3686 | |
3687 | void ProcessFirstLogMessage (DebuggerIPCEvent *event); |
3688 | void ProcessContinuedLogMessage (DebuggerIPCEvent *event); |
3689 | |
3690 | void CloseIPCHandles(); |
3691 | void UpdateThreadsForAdUnload( CordbAppDomain* pAppDomain ); |
3692 | |
3693 | #ifdef FEATURE_INTEROP_DEBUGGING |
3694 | // Each win32 debug event needs to be triaged to get a Reaction. |
3695 | Reaction TriageBreakpoint(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent); |
3696 | Reaction TriageSyncComplete(); |
3697 | Reaction Triage1stChanceNonSpecial(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent); |
3698 | Reaction TriageExcep1stChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent); |
3699 | Reaction TriageExcep2ndChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent); |
3700 | Reaction TriageWin32DebugEvent(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent); |
3701 | #endif // FEATURE_INTEROP_DEBUGGING |
3702 | |
3703 | //----------------------------------------------------------- |
3704 | // Data members |
3705 | //----------------------------------------------------------- |
3706 | |
3707 | public: |
3708 | RSSmartPtr<Cordb> m_cordb; |
3709 | |
3710 | private: |
3711 | // OS process handle to live process. |
3712 | // @dbgtodo - , Move this into the Shim. This should only be needed in the live-process |
3713 | // case. Get rid of this since it breaks the data-target abstraction. |
3714 | // For Mac debugging, this handle is of course not the real process handle. This is just a handle to |
3715 | // wait on for process termination. |
3716 | HANDLE m_handle; |
3717 | |
3718 | // Process descriptor - holds PID and App group ID for Mac debugging |
3719 | ProcessDescriptor m_processDescriptor; |
3720 | |
3721 | public: |
3722 | // Wrapper to get the OS process handle. This is unsafe because it breaks the data-target abstraction. |
3723 | // The only things that need this should be calls to DuplicateHandle, and some shimming work. |
3724 | HANDLE UnsafeGetProcessHandle() |
3725 | { |
3726 | return m_handle; |
3727 | } |
3728 | |
3729 | // Set when code:CordbProcess::Detach is called. |
3730 | // Public APIs can check this and return CORDBG_E_PROCESS_DETACHED. |
3731 | // @dbgtodo managed pipeline - really could merge this with neuter. |
3732 | bool m_detached; |
3733 | |
3734 | // True if we code:CordbProcess::Stop is called before the managed CreateProcess event. |
3735 | // In this case, m_initialized is false, and we can't send an AsyncBreak event to the LS. |
3736 | // (since the LS isn't going to send a SyncComplete event back since the CLR isn't loaded/ready). |
3737 | // @dbgtodo managed pipeline - move into shim, along with Stop/Continue. |
3738 | bool m_uninitializedStop; |
3739 | |
3740 | |
3741 | // m_exiting is true if we know the LS is starting to exit (if the |
3742 | // RS is telling the LS to exit) or if we know the LS has already exited. |
3743 | bool m_exiting; |
3744 | |
3745 | |
3746 | // m_terminated can only be set to true if we know 100% the LS has exited (ie, somebody |
3747 | // waited on the LS process handle). |
3748 | bool m_terminated; |
3749 | |
3750 | bool m_unrecoverableError; |
3751 | |
3752 | bool m_specialDeferment; |
3753 | bool m_helperThreadDead; // flag used for interop |
3754 | |
3755 | // This tracks if the loader breakpoint has been received during interop-debugging. |
3756 | // The Loader Breakpoint is an breakpoint event raised by the OS once the debugger is attached. |
3757 | // It comes in both Attach and Launch scenarios. |
3758 | // This is also used in fake-native debugging scenarios. |
3759 | bool m_loaderBPReceived; |
3760 | |
3761 | private: |
3762 | |
3763 | // MetaData dispenser. |
3764 | RSExtSmartPtr<IMetaDataDispenserEx> m_pMetaDispenser; |
3765 | |
3766 | // |
3767 | // Count of the number of outstanding CordbEvals in the process. |
3768 | // |
3769 | LONG m_cOutstandingEvals; |
3770 | |
3771 | // Number of oustanding code:CordbHandleValue objects containing |
3772 | // Left-side resources. This can be used to tell if ICorDebug needs to |
3773 | // cleanup gc handles. |
3774 | LONG m_cOutstandingHandles; |
3775 | |
3776 | // Pointer to the CordbModule instance that can currently change the Jit flags. |
3777 | // There can be at most one of these. It will represent a module that has just been loaded, before the |
3778 | // Continue is sent. See code:CordbProcess::RawDispatchEvent and code:CordbProcess::ContinueInternal. |
3779 | CordbModule * m_pModuleThatCanChangeJitFlags; |
3780 | |
3781 | public: |
3782 | LONG OutstandingEvalCount() |
3783 | { |
3784 | return m_cOutstandingEvals; |
3785 | } |
3786 | |
3787 | void IncrementOutstandingEvalCount() |
3788 | { |
3789 | InterlockedIncrement(&m_cOutstandingEvals); |
3790 | } |
3791 | |
3792 | void DecrementOutstandingEvalCount() |
3793 | { |
3794 | InterlockedDecrement(&m_cOutstandingEvals); |
3795 | } |
3796 | |
3797 | LONG OutstandingHandles(); |
3798 | void IncrementOutstandingHandles(); |
3799 | void DecrementOutstandingHandles(); |
3800 | |
3801 | // |
3802 | // Is it OK to detach at this time |
3803 | // |
3804 | HRESULT IsReadyForDetach(); |
3805 | |
3806 | |
3807 | private: |
3808 | // This is a target pointer that uniquely identifies the runtime in the target. |
3809 | // This lets ICD discriminate between multiple CLRs within a single process. |
3810 | // On windows, this is the base-address of mscorwks.dll in the target. |
3811 | // If this is 0, then we have V2 semantics where there was only 1 CLR in the target. |
3812 | // In that case, we can lazily initialize it in code:CordbProcess::CopyManagedEventFromTarget. |
3813 | // This is just used for backwards compat. |
3814 | CORDB_ADDRESS m_clrInstanceId; |
3815 | |
3816 | // List of things that get neutered on process exit and Continue respectively. |
3817 | NeuterList m_ExitNeuterList; |
3818 | NeuterList m_ContinueNeuterList; |
3819 | |
3820 | // List of objects that hold resources into the left-side. |
3821 | // This is currently for funceval, which cleans up resources in code:CordbEval::SendCleanup. |
3822 | // @dbgtodo - , (func-eval feature crew): we can get rid of this |
3823 | // list if we make func-eval not hold resources after it's complete. |
3824 | LeftSideResourceCleanupList m_LeftSideResourceCleanupList; |
3825 | |
3826 | // m_stopCount, m_synchronized, & m_syncCompleteReceived are key fields describing |
3827 | // the processes' sync status. |
3828 | DWORD m_stopCount; |
3829 | |
3830 | // m_synchronized is the Debugger's view of SyncStatus. It will go high & low for each |
3831 | // callback. Continue() will set this to false. |
3832 | // This flag is true roughly from the time that we've dispatched a managed callback |
3833 | // until the time that it's continued. |
3834 | bool m_synchronized; |
3835 | |
3836 | // m_syncCompleteReceived tells us if the runtime is _actually_ sychronized. It goes |
3837 | // high once we get a SyncComplete, and it goes low once we actually send the continue. |
3838 | // This is always set by the thread that receives the sync-complete. In interop, that's the w32et. |
3839 | // Thus this is the most accurate indication of wether the Debuggee is _actually_ synchronized or not. |
3840 | bool m_syncCompleteReceived; |
3841 | |
3842 | |
3843 | // Back pointer to Shim process. This is used for hooks back into the shim. |
3844 | // If this is Non-null, then we're emulating the V2 case. If this is NULL, then it's the real V3 pipeline. |
3845 | RSExtSmartPtr<ShimProcess> m_pShim; |
3846 | |
3847 | CordbSafeHashTable<CordbThread> m_userThreads; |
3848 | |
3849 | public: |
3850 | ShimProcess* GetShim(); |
3851 | |
3852 | bool m_oddSync; |
3853 | |
3854 | |
3855 | void BuildThreadEnum(CordbBase * pOwnerObj, NeuterList * pOwnerList, RSInitHolder<CordbHashTableEnum> * pHolder); |
3856 | |
3857 | #ifdef FEATURE_INTEROP_DEBUGGING |
3858 | // List of unmanaged threads. This is only populated for interop-debugging. |
3859 | CordbSafeHashTable<CordbUnmanagedThread> m_unmanagedThreads; |
3860 | #endif // FEATURE_INTEROP_DEBUGGING |
3861 | |
3862 | CordbSafeHashTable<CordbAppDomain> m_appDomains; |
3863 | |
3864 | CordbAppDomain * m_sharedAppDomain; |
3865 | |
3866 | // Since a stepper can begin in one appdomain, and complete in another, |
3867 | // we put the hashtable here, rather than on specific appdomains. |
3868 | CordbSafeHashTable<CordbStepper> m_steppers; |
3869 | |
3870 | // Used to figure out if we have to refresh any reference objects |
3871 | // on the left side. Gets incremented each time a continue is called, or |
3872 | // global debugee state is modified in some other way. |
3873 | UINT m_continueCounter; |
3874 | |
3875 | // Used to track whether the DAC cache has been flushed. |
3876 | // We use this information to determine whether CordbStackWalk instances need to |
3877 | // be refreshed. |
3878 | UINT m_flushCounter; |
3879 | |
3880 | // The DCB is essentially a buffer area used to temporarily hold information read from the debugger |
3881 | // control block residing on the LS helper thread. We make no assumptions about the validity of this |
3882 | // information over time, so before using a value from it on the RS, we will always update this buffer |
3883 | // with a call to UpdateRightSideDCB. This uses a ReadProcessMemory to get the current information from |
3884 | // the LS DCB. |
3885 | DebuggerIPCControlBlock * GetDCB() {return ((m_pEventChannel == NULL) ? NULL : m_pEventChannel->GetDCB());} |
3886 | |
3887 | |
3888 | DebuggerIPCRuntimeOffsets m_runtimeOffsets; |
3889 | HANDLE m_leftSideEventAvailable; |
3890 | HANDLE m_leftSideEventRead; |
3891 | #if defined(FEATURE_INTEROP_DEBUGGING) |
3892 | HANDLE m_leftSideUnmanagedWaitEvent; |
3893 | #endif // FEATURE_INTEROP_DEBUGGING |
3894 | |
3895 | |
3896 | // This becomes true when the RS receives its first managed event. |
3897 | // This goes false in shutdown cases. |
3898 | // If this is true, we can assume: |
3899 | // - the CLR is loaded. |
3900 | // - the IPC block is opened and initialized. |
3901 | // - DAC is initialized (see code:CordbProcess::IsDacInitialized) |
3902 | // |
3903 | // If this is false, we can assume: |
3904 | // - the CLR may not be loaded into the target process. |
3905 | // - We can't send IPC events to the LS (because we can't expect a response) |
3906 | // |
3907 | // Many APIs can check this bit and return CORDBG_E_NOTREADY if it's false. |
3908 | bool m_initialized; |
3909 | |
3910 | #ifdef _DEBUG |
3911 | void * m_pDBGLastIPCEventType; |
3912 | #endif |
3913 | |
3914 | bool m_stopRequested; |
3915 | HANDLE m_stopWaitEvent; |
3916 | RSLock m_processMutex; |
3917 | |
3918 | #ifdef FEATURE_INTEROP_DEBUGGING |
3919 | // The number of threads which are IsFirstChanceHijacked |
3920 | DWORD m_cFirstChanceHijackedThreads; |
3921 | |
3922 | CordbUnmanagedEvent *m_unmanagedEventQueue; |
3923 | CordbUnmanagedEvent *m_lastQueuedUnmanagedEvent; |
3924 | CordbUnmanagedEvent *m_lastQueuedOOBEvent; |
3925 | CordbUnmanagedEvent *m_outOfBandEventQueue; |
3926 | |
3927 | CordbUnmanagedEvent *m_lastDispatchedIBEvent; |
3928 | bool m_dispatchingUnmanagedEvent; |
3929 | bool m_dispatchingOOBEvent; |
3930 | bool m_doRealContinueAfterOOBBlock; |
3931 | |
3932 | enum |
3933 | { |
3934 | PS_WIN32_STOPPED = 0x0001, |
3935 | PS_HIJACKS_IN_PLACE = 0x0002, |
3936 | PS_SOME_THREADS_SUSPENDED = 0x0004, |
3937 | PS_WIN32_ATTACHED = 0x0008, |
3938 | PS_WIN32_OUTOFBAND_STOPPED = 0x0010, |
3939 | }; |
3940 | |
3941 | unsigned int m_state; |
3942 | #endif // FEATURE_INTEROP_DEBUGGING |
3943 | |
3944 | // True if we're interop-debugging, else false. |
3945 | bool IsInteropDebugging(); |
3946 | |
3947 | DWORD m_helperThreadId; // helper thread ID calculated from sniffing from UM thread-create events. |
3948 | |
3949 | // Is the given thread id a helper thread (real or worker?) |
3950 | bool IsHelperThreadWorked(DWORD tid); |
3951 | |
3952 | // |
3953 | // We cache the LS patch table on the RS. |
3954 | // |
3955 | |
3956 | // The array of entries. (The patchtable is a hash implemented as a single-array) |
3957 | // This array includes empty entries. |
3958 | // There is an auxillary bucket structure used to map hash codes to array indices. |
3959 | // We traverse the array, and we recognize an empty slot |
3960 | // if DebuggerControllerPatch::opcode == 0. |
3961 | // If we haven't gotten the table, then m_pPatchTable is NULL |
3962 | BYTE* m_pPatchTable; |
3963 | |
3964 | // The number of entries (both used & unused) in m_pPatchTable. |
3965 | UINT m_cPatch; |
3966 | |
3967 | // so we know where to write the changes patchtable back to |
3968 | // This has m_cPatch elements. |
3969 | BYTE *m_rgData; |
3970 | |
3971 | // Cached value of iNext entries such that: |
3972 | // m_rgNextPatch[i] = ((DebuggerControllerPatch*)m_pPatchTable)[i]->iNext; |
3973 | // where 0 <= i < m_cPatch |
3974 | // This provides a linked list (via indices) to traverse the used entries of m_pPatchTable. |
3975 | // This has m_cPatch elements. |
3976 | ULONG *m_rgNextPatch; |
3977 | |
3978 | // This has m_cPatch elements. |
3979 | PRD_TYPE *m_rgUncommitedOpcode; |
3980 | |
3981 | // CORDB_ADDRESS's are UINT_PTR's (64 bit under _WIN64, 32 bit otherwise) |
3982 | #if defined(DBG_TARGET_WIN64) |
3983 | #define MAX_ADDRESS (_UI64_MAX) |
3984 | #else |
3985 | #define MAX_ADDRESS (ULONG_MAX) |
3986 | #endif |
3987 | #define MIN_ADDRESS (0x0) |
3988 | CORDB_ADDRESS m_minPatchAddr; //smallest patch in table |
3989 | CORDB_ADDRESS m_maxPatchAddr; |
3990 | |
3991 | // <TODO>@todo port : if slots of CHashTable change, so should these</TODO> |
3992 | #define DPT_TERMINATING_INDEX (UINT32_MAX) |
3993 | // Index into m_pPatchTable of the first patch (first used entry). |
3994 | ULONG m_iFirstPatch; |
3995 | |
3996 | // Initializes the DAC |
3997 | void InitDac(); |
3998 | |
3999 | // copy new data from LS DCB to RS buffer |
4000 | void UpdateRightSideDCB(); |
4001 | |
4002 | // copy new data from RS DCB buffer to LS DCB |
4003 | void UpdateLeftSideDCBField(void * rsFieldAddr, SIZE_T size); |
4004 | |
4005 | // allocate and initialize the RS DCB buffer |
4006 | void GetEventBlock(BOOL * pfBlockExists); |
4007 | |
4008 | IEventChannel * GetEventChannel(); |
4009 | |
4010 | bool SupportsVersion(CorDebugInterfaceVersion featureVersion); |
4011 | |
4012 | void StartEventDispatch(DebuggerIPCEventType event); |
4013 | void FinishEventDispatch(); |
4014 | bool AreDispatchingEvent(); |
4015 | |
4016 | HANDLE GetHelperThreadHandle() { return m_hHelperThread; } |
4017 | |
4018 | CordbAppDomain* GetDefaultAppDomain() { return m_pDefaultAppDomain; } |
4019 | |
4020 | #ifdef FEATURE_INTEROP_DEBUGGING |
4021 | // Lookup if there's a native BP at the given address. Return NULL not found. |
4022 | NativePatch * GetNativePatch(const void * pAddress); |
4023 | #endif // FEATURE_INTEROP_DEBUGGING |
4024 | |
4025 | bool IsBreakOpcodeAtAddress(const void * address); |
4026 | |
4027 | private: |
4028 | // |
4029 | // handle to helper thread. Used for managed debugging. |
4030 | // Initialized only after we get the tid from the DCB. |
4031 | HANDLE m_hHelperThread; |
4032 | |
4033 | DebuggerIPCEventType m_dispatchedEvent; // what event are we currently dispatching? |
4034 | |
4035 | RSLock m_StopGoLock; |
4036 | |
4037 | // Each process has exactly one Default AppDomain |
4038 | // @dbgtodo appdomain : We should try and simplify things by removing this. |
4039 | // At the moment it's necessary for CordbProcess::UpdateThreadsForAdUnload. |
4040 | CordbAppDomain* m_pDefaultAppDomain; // owned by m_appDomains |
4041 | |
4042 | #ifdef FEATURE_INTEROP_DEBUGGING |
4043 | // Helpers |
4044 | CordbUnmanagedThread * GetUnmanagedThreadFromEvent(const DEBUG_EVENT * pEvent); |
4045 | #endif // FEATURE_INTEROP_DEBUGGING |
4046 | |
4047 | // Ensure we have a CLR Instance ID to debug |
4048 | HRESULT EnsureClrInstanceIdSet(); |
4049 | |
4050 | #ifdef FEATURE_INTEROP_DEBUGGING |
4051 | // // The full debug event is too large, so we just remember the important stuff. |
4052 | struct MiniDebugEvent |
4053 | { |
4054 | BYTE code; // event code from the debug event |
4055 | CordbUnmanagedThread * pUThread; // unmanaged thread this was on. |
4056 | // @todo - we should have some misc data. |
4057 | union |
4058 | { |
4059 | struct { |
4060 | void * pAddress; // address of an exception |
4061 | DWORD dwCode; |
4062 | } ExceptionData; |
4063 | struct { |
4064 | void * pBaseAddress; // for module load & unload |
4065 | } ModuleData; |
4066 | } u; |
4067 | }; |
4068 | |
4069 | // Group fields that are just used for debug support here. |
4070 | // Some are included even in retail builds to help debug retail failures. |
4071 | struct DebugSupport |
4072 | { |
4073 | // For debugging, we keep a rolling queue of the last N Win32 debug events. |
4074 | MiniDebugEvent m_DebugEventQueue[DEBUG_EVENTQUEUE_SIZE]; |
4075 | int m_DebugEventQueueIdx; |
4076 | int m_TotalNativeEvents; |
4077 | |
4078 | // Breakdown of different types of native events |
4079 | int m_TotalIB; |
4080 | int m_TotalOOB; |
4081 | int m_TotalCLR; |
4082 | } m_DbgSupport; |
4083 | |
4084 | CUnorderedArray<NativePatch, 10> m_NativePatchList; |
4085 | #endif // FEATURE_INTEROP_DEBUGGING |
4086 | |
4087 | // |
4088 | // DAC |
4089 | // |
4090 | |
4091 | // Try to initalize DAC, may fail |
4092 | BOOL TryInitializeDac(); |
4093 | |
4094 | // Expect DAC initialize to succeed. |
4095 | void InitializeDac(); |
4096 | |
4097 | |
4098 | void CreateDacDbiInterface(); |
4099 | |
4100 | // Free DAC. |
4101 | void FreeDac(); |
4102 | |
4103 | |
4104 | HModuleHolder m_hDacModule; |
4105 | RSExtSmartPtr<ICorDebugDataTarget> m_pDACDataTarget; |
4106 | |
4107 | // The mutable version of the data target, or null if read-only |
4108 | RSExtSmartPtr<ICorDebugMutableDataTarget> m_pMutableDataTarget; |
4109 | |
4110 | RSExtSmartPtr<ICorDebugMetaDataLocator> m_pMetaDataLocator; |
4111 | |
4112 | IDacDbiInterface * m_pDacPrimitives; |
4113 | |
4114 | IEventChannel * m_pEventChannel; |
4115 | |
4116 | // If true, then we'll ASSERT if we detect the target is corrupt or inconsistent |
4117 | // This switch is for diagnostics purposes only and should always be false in retail builds. |
4118 | bool m_fAssertOnTargetInconsistency; |
4119 | |
4120 | // When a successful attempt to read runtime offsets from LS occurs, this flag is set. |
4121 | bool m_runtimeOffsetsInitialized; |
4122 | |
4123 | // controls how metadata updated in the target is handled |
4124 | WriteableMetadataUpdateMode m_writableMetadataUpdateMode; |
4125 | |
4126 | COM_METHOD GetObjectInternal(CORDB_ADDRESS addr, CordbAppDomain* pAppDomainOverride, ICorDebugObjectValue **pObject); |
4127 | }; |
4128 | |
4129 | // Some IMDArocess APIs are supported as interop-only. |
4130 | #define FAIL_IF_MANAGED_ONLY(pProcess) \ |
4131 | { CordbProcess * __Proc = pProcess; if (!__Proc->IsInteropDebugging()) return CORDBG_E_MUST_BE_INTEROP_DEBUGGING; } |
4132 | |
4133 | |
4134 | /* ------------------------------------------------------------------------- * |
4135 | * Module class |
4136 | * ------------------------------------------------------------------------- */ |
4137 | |
4138 | class CordbModule : public CordbBase, |
4139 | public ICorDebugModule, |
4140 | public ICorDebugModule2, |
4141 | public ICorDebugModule3 |
4142 | { |
4143 | public: |
4144 | CordbModule(CordbProcess * process, |
4145 | VMPTR_Module vmModule, |
4146 | VMPTR_DomainFile vmDomainFile); |
4147 | |
4148 | virtual ~CordbModule(); |
4149 | virtual void Neuter(); |
4150 | |
4151 | using CordbBase::GetProcess; |
4152 | |
4153 | #ifdef _DEBUG |
4154 | virtual const char * DbgGetName() { return "CordbModule" ; } |
4155 | #endif |
4156 | |
4157 | |
4158 | //----------------------------------------------------------- |
4159 | // IUnknown |
4160 | //----------------------------------------------------------- |
4161 | |
4162 | ULONG STDMETHODCALLTYPE AddRef() |
4163 | { |
4164 | return (BaseAddRef()); |
4165 | } |
4166 | ULONG STDMETHODCALLTYPE Release() |
4167 | { |
4168 | return (BaseRelease()); |
4169 | } |
4170 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
4171 | |
4172 | //----------------------------------------------------------- |
4173 | // ICorDebugModule |
4174 | //----------------------------------------------------------- |
4175 | |
4176 | COM_METHOD GetProcess(ICorDebugProcess **ppProcess); |
4177 | COM_METHOD GetBaseAddress(CORDB_ADDRESS *pAddress); |
4178 | COM_METHOD GetAssembly(ICorDebugAssembly **ppAssembly); |
4179 | COM_METHOD GetName(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
4180 | COM_METHOD EnableJITDebugging(BOOL bTrackJITInfo, BOOL bAllowJitOpts); |
4181 | COM_METHOD EnableClassLoadCallbacks(BOOL bClassLoadCallbacks); |
4182 | |
4183 | // Gets the latest version of a function given the methodDef token |
4184 | COM_METHOD GetFunctionFromToken(mdMethodDef methodDef, |
4185 | ICorDebugFunction **ppFunction); |
4186 | COM_METHOD GetFunctionFromRVA(CORDB_ADDRESS rva, ICorDebugFunction **ppFunction); |
4187 | COM_METHOD GetClassFromToken(mdTypeDef typeDef, |
4188 | ICorDebugClass **ppClass); |
4189 | COM_METHOD CreateBreakpoint(ICorDebugModuleBreakpoint **ppBreakpoint); |
4190 | |
4191 | // Not implemented - legacy |
4192 | COM_METHOD GetEditAndContinueSnapshot( |
4193 | ICorDebugEditAndContinueSnapshot **ppEditAndContinueSnapshot); |
4194 | |
4195 | COM_METHOD GetMetaDataInterface(REFIID riid, IUnknown **ppObj); |
4196 | COM_METHOD GetToken(mdModule *pToken); |
4197 | COM_METHOD IsDynamic(BOOL *pDynamic); |
4198 | COM_METHOD GetGlobalVariableValue(mdFieldDef fieldDef, |
4199 | ICorDebugValue **ppValue); |
4200 | COM_METHOD GetSize(ULONG32 *pcBytes); |
4201 | COM_METHOD IsInMemory(BOOL *pInMemory); |
4202 | |
4203 | //----------------------------------------------------------- |
4204 | // ICorDebugModule2 |
4205 | //----------------------------------------------------------- |
4206 | COM_METHOD SetJMCStatus( |
4207 | BOOL fIsUserCode, |
4208 | ULONG32 cOthers, |
4209 | mdToken others[]); |
4210 | |
4211 | // Applies an EnC edit to the module |
4212 | COM_METHOD ApplyChanges( |
4213 | ULONG cbMetaData, |
4214 | BYTE pbMetaData[], |
4215 | ULONG cbIL, |
4216 | BYTE pbIL[]); |
4217 | |
4218 | // Resolve an assembly given an AssemblyRef token. Note that |
4219 | // this will not trigger the loading of assembly. If assembly is not yet loaded, |
4220 | // this will return an CORDBG_E_CANNOT_RESOLVE_ASSEMBLY error |
4221 | COM_METHOD ResolveAssembly(mdToken tkAssemblyRef, |
4222 | ICorDebugAssembly **ppAssembly); |
4223 | |
4224 | // Sets EnC and optimization flags |
4225 | COM_METHOD SetJITCompilerFlags(DWORD dwFlags); |
4226 | |
4227 | // Gets EnC and optimization flags |
4228 | COM_METHOD GetJITCompilerFlags(DWORD *pdwFlags); |
4229 | |
4230 | //----------------------------------------------------------- |
4231 | // ICorDebugModule3 |
4232 | //----------------------------------------------------------- |
4233 | COM_METHOD CreateReaderForInMemorySymbols(REFIID riid, |
4234 | void** ppObj); |
4235 | |
4236 | //----------------------------------------------------------- |
4237 | // Internal members |
4238 | //----------------------------------------------------------- |
4239 | |
4240 | #ifdef _DEBUG |
4241 | // Debug helper to ensure that module is no longer discoverable |
4242 | void DbgAssertModuleDeleted(); |
4243 | #endif // _DEBUG |
4244 | |
4245 | // Internal help to get the "name" (filename or pretty name) of the module. |
4246 | HRESULT GetNameWorker(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
4247 | |
4248 | // Marks that the module's metadata has become invalid and needs to be refetched. |
4249 | void RefreshMetaData(); |
4250 | |
4251 | // Cache the current continue counter as the one that the LoadEvent is |
4252 | // dispatched in. |
4253 | void SetLoadEventContinueMarker(); |
4254 | |
4255 | // Return CORDBG_E_MUST_BE_IN_LOAD_MODULE if this module is not in its load callback. |
4256 | HRESULT EnsureModuleIsInLoadCallback(); |
4257 | |
4258 | BOOL IsDynamic(); |
4259 | |
4260 | // Gets the latest version of the function for the methodDef, if any |
4261 | CordbFunction * LookupFunctionLatestVersion(mdMethodDef methodToken); |
4262 | |
4263 | // Gets the latest version of the function. Creates a new instance if none exists yet. |
4264 | CordbFunction* LookupOrCreateFunctionLatestVersion(mdMethodDef funcMetaDataToken); |
4265 | |
4266 | // Finds or creates a function for the first time (not for use on EnC if function doesn't exist yet) |
4267 | CordbFunction * LookupOrCreateFunction(mdMethodDef token, SIZE_T enCVersion); |
4268 | |
4269 | // Creates an CordbFunction instances for the first time (not for use on EnC) |
4270 | CordbFunction * CreateFunction(mdMethodDef token, SIZE_T enCVersion); |
4271 | |
4272 | // Creates a CordbFunction object to represent the specified EnC version |
4273 | HRESULT UpdateFunction(mdMethodDef token, |
4274 | SIZE_T newEnCVersion, |
4275 | CordbFunction** ppFunction); |
4276 | |
4277 | CordbClass* LookupClass(mdTypeDef classToken); |
4278 | HRESULT LookupOrCreateClass(mdTypeDef classToken, CordbClass** ppClass); |
4279 | HRESULT CreateClass(mdTypeDef classToken, CordbClass** ppClass); |
4280 | HRESULT LookupClassByToken(mdTypeDef token, CordbClass **ppClass); |
4281 | HRESULT ResolveTypeRef(mdTypeRef token, CordbClass **ppClass); |
4282 | HRESULT ResolveTypeRefOrDef(mdToken token, CordbClass **ppClass); |
4283 | |
4284 | // Sends the event to the left side to apply the changes to the debugee |
4285 | HRESULT ApplyChangesInternal( |
4286 | ULONG cbMetaData, |
4287 | BYTE pbMetaData[], |
4288 | ULONG cbIL, |
4289 | BYTE pbIL[]); |
4290 | |
4291 | // Pulls new metadata if needed in order to ensure the availability of |
4292 | // the given token |
4293 | void UpdateMetaDataCacheIfNeeded(mdToken token); |
4294 | |
4295 | HRESULT InitPublicMetaDataFromFile(const WCHAR * pszFullPathName, DWORD dwOpenFlags, bool validateFileInfo); |
4296 | |
4297 | // Creates a CordbNativeCode (if it's not already created) and adds it to the |
4298 | // hash table of CordbNativeCodes belonging to the module. |
4299 | CordbNativeCode * LookupOrCreateNativeCode(mdMethodDef methodToken, |
4300 | VMPTR_MethodDesc methodDesc, |
4301 | CORDB_ADDRESS startAddress); |
4302 | |
4303 | private: |
4304 | // Set the metadata (both public and internal) for the module. |
4305 | void InitMetaData(TargetBuffer buffer, BOOL useFileMappingOptimization); |
4306 | |
4307 | // Checks if the given token is in the cached metadata |
4308 | BOOL CheckIfTokenInMetaData(mdToken token); |
4309 | |
4310 | // Update the public metadata given a buffer in the target. |
4311 | void UpdatePublicMetaDataFromRemote(TargetBuffer bufferRemoteMetaData); |
4312 | |
4313 | // Initialize just the public metadata by reading from an on-disk module |
4314 | HRESULT InitPublicMetaDataFromFile(); |
4315 | // Initialize just the public metadata by reading new metadata from the buffer |
4316 | void InitPublicMetaData(TargetBuffer buffer); |
4317 | |
4318 | // Rebuild the internal metadata given the public one. |
4319 | void UpdateInternalMetaData(); |
4320 | |
4321 | // Determines whether the on-disk metadata for this module is usable as the |
4322 | // current metadata |
4323 | BOOL IsFileMetaDataValid(); |
4324 | |
4325 | // Helper to copy metadata buffer from the Target to the host. |
4326 | void CopyRemoteMetaData(TargetBuffer buffer, CoTaskMemHolder<VOID> * pLocalBuffer); |
4327 | |
4328 | |
4329 | CordbAssembly * ResolveAssemblyInternal(mdToken tkAssemblyRef); |
4330 | |
4331 | BOOL IsWinMD(); |
4332 | |
4333 | //----------------------------------------------------------- |
4334 | // Convenience routines |
4335 | //----------------------------------------------------------- |
4336 | |
4337 | public: |
4338 | CordbAppDomain *GetAppDomain() |
4339 | { |
4340 | return m_pAppDomain; |
4341 | } |
4342 | |
4343 | CordbAssembly * GetCordbAssembly (); |
4344 | |
4345 | // Get the module filename, or NULL if none. Throws on error. |
4346 | const WCHAR * GetModulePath(); |
4347 | |
4348 | const WCHAR * GetNGenImagePath(); |
4349 | |
4350 | const VMPTR_DomainFile GetRuntimeDomainFile () |
4351 | { |
4352 | return m_vmDomainFile; |
4353 | } |
4354 | |
4355 | const VMPTR_Module GetRuntimeModule() |
4356 | { |
4357 | return m_vmModule; |
4358 | } |
4359 | |
4360 | // Get symbol stream for in-memory modules. |
4361 | IDacDbiInterface::SymbolFormat GetInMemorySymbolStream(IStream ** ppStream); |
4362 | |
4363 | // accessor for PE file |
4364 | VMPTR_PEFile GetPEFile(); |
4365 | |
4366 | |
4367 | IMetaDataImport * GetMetaDataImporter(); |
4368 | |
4369 | // accessor for Internal MetaData importer. |
4370 | IMDInternalImport * GetInternalMD(); |
4371 | |
4372 | //----------------------------------------------------------- |
4373 | // Data members |
4374 | //----------------------------------------------------------- |
4375 | |
4376 | public: |
4377 | CordbAssembly* m_pAssembly; |
4378 | CordbAppDomain* m_pAppDomain; |
4379 | CordbSafeHashTable<CordbClass> m_classes; |
4380 | |
4381 | // A collection, indexed by methodDef, of the latest version of functions in this module |
4382 | // The collection is filled lazily by LookupOrCreateFunction |
4383 | CordbSafeHashTable<CordbFunction> m_functions; |
4384 | |
4385 | // The real handle into the VM for a module. This is appdomain aware. |
4386 | // This is the primary VM counterpart for the CordbModule. |
4387 | VMPTR_DomainFile m_vmDomainFile; |
4388 | |
4389 | VMPTR_Module m_vmModule; |
4390 | |
4391 | DWORD m_EnCCount; |
4392 | |
4393 | private: |
4394 | |
4395 | enum ILWinMDState |
4396 | { |
4397 | Uninitialized, |
4398 | False, |
4399 | True |
4400 | }; |
4401 | |
4402 | // Base Address and size of this module in debuggee's process. Maybe null if unknown. |
4403 | TargetBuffer m_PEBuffer; |
4404 | |
4405 | BOOL m_fDynamic; // Dynamic modules can grow (like Reflection Emit) |
4406 | BOOL m_fInMemory; // In memory modules don't have file-backing. |
4407 | ILWinMDState m_isIlWinMD; // WinMD modules don't support all metadata interfaces |
4408 | |
4409 | // Indicates that the module must serialize its metadata in process as part of metadata |
4410 | // refresh. This is required for modules updated on the fly by the profiler |
4411 | BOOL m_fForceMetaDataSerialize; |
4412 | |
4413 | // Full path to module's image, if any. Empty if none, NULL if not yet set. |
4414 | StringCopyHolder m_strModulePath; |
4415 | |
4416 | // Full path to the ngen file. Empty if not ngenned, NULL if not yet set. |
4417 | // This isn't exposed publicly, but we may use it internally for loading metadata. |
4418 | StringCopyHolder m_strNGenImagePath; |
4419 | |
4420 | // "Global" class for this module. Global functions + vars exist in this class. |
4421 | RSSmartPtr<CordbClass> m_pClass; |
4422 | |
4423 | // Handle to PEFile, useful for metadata lookups. |
4424 | // this should always be non-null. |
4425 | VMPTR_PEFile m_vmPEFile; |
4426 | |
4427 | |
4428 | // Public metadata importer. This is lazily initialized and accessed from code:GetMetaDataImporter |
4429 | // This is handed out to debugger clients via code:CordbModule::GetMetaDataInterface |
4430 | // This is also tightly coupled to the internal metadata importer, m_pInternalMetaDataImport. |
4431 | RSExtSmartPtr<IMetaDataImport> m_pIMImport; |
4432 | |
4433 | // Internal metadata object. This is closely tied to the public metadata object (m_pIMImport). |
4434 | // They share the same backing storage, but expose different interfaces to that storage. |
4435 | // Debugger authors and tools use the public interfaces. |
4436 | // DAC-ized operations in the VM require an IMDInternalImport. |
4437 | // The public and internal must be updated together. |
4438 | // This ultimately gets handed back to DAC via code:CordbProcess::LookupMetaData |
4439 | RSExtSmartPtr<IMDInternalImport> m_pInternalMetaDataImport; |
4440 | |
4441 | // Continue counter of when the module was loaded. |
4442 | // See code:CordbModule::SetLoadEventContinueMarker for details |
4443 | UINT m_nLoadEventContinueCounter; |
4444 | |
4445 | // This is a table of all NativeCode objects in the module indexed |
4446 | // by start address |
4447 | // The collection is filled lazily by LookupOrCreateNativeCode |
4448 | CordbSafeHashTable<CordbNativeCode> m_nativeCodeTable; |
4449 | }; |
4450 | |
4451 | |
4452 | //----------------------------------------------------------------------------- |
4453 | // Cordb MDA notification |
4454 | //----------------------------------------------------------------------------- |
4455 | class CordbMDA : public CordbBase, public ICorDebugMDA |
4456 | { |
4457 | public: |
4458 | CordbMDA(CordbProcess * pProc, DebuggerMDANotification * pData); |
4459 | ~CordbMDA(); |
4460 | |
4461 | virtual void Neuter(); |
4462 | |
4463 | #ifdef _DEBUG |
4464 | virtual const char * DbgGetName() { return "CordbMDA" ; } |
4465 | #endif |
4466 | |
4467 | //----------------------------------------------------------- |
4468 | // IUnknown |
4469 | //----------------------------------------------------------- |
4470 | |
4471 | ULONG STDMETHODCALLTYPE AddRef() |
4472 | { |
4473 | return (BaseAddRefEnforceExternal()); |
4474 | } |
4475 | ULONG STDMETHODCALLTYPE Release() |
4476 | { |
4477 | return (BaseReleaseEnforceExternal()); |
4478 | } |
4479 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
4480 | |
4481 | //----------------------------------------------------------- |
4482 | // ICorDebugMDA |
4483 | //----------------------------------------------------------- |
4484 | |
4485 | // Get the string for the type of the MDA. Never empty. |
4486 | // This is a convenient performant alternative to getting the XML stream and extracting |
4487 | // the type from that based off the schema. |
4488 | COM_METHOD GetName(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
4489 | |
4490 | // Get a string description of the MDA. This may be empty (0-length). |
4491 | COM_METHOD GetDescription(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
4492 | |
4493 | // Get the full associated XML for the MDA. This may be empty. |
4494 | // This could be a potentially expensive operation if the xml stream is large. |
4495 | // See the MDA documentation for the schema for this XML stream. |
4496 | COM_METHOD GetXML(ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
4497 | |
4498 | COM_METHOD GetFlags(CorDebugMDAFlags * pFlags); |
4499 | |
4500 | // Thread that the MDA is fired on. We use the os tid instead of an ICDThread in case an MDA is fired on a |
4501 | // native thread (or a managed thread that hasn't yet entered managed code and so we don't have a ICDThread |
4502 | // object for it yet) |
4503 | COM_METHOD GetOSThreadId(DWORD * pOsTid); |
4504 | |
4505 | private: |
4506 | NewArrayHolder<WCHAR> m_szName; |
4507 | NewArrayHolder<WCHAR> m_szDescription; |
4508 | NewArrayHolder<WCHAR> m_szXml; |
4509 | |
4510 | DWORD m_dwOSTID; |
4511 | CorDebugMDAFlags m_flags; |
4512 | }; |
4513 | |
4514 | |
4515 | |
4516 | struct CordbHangingField |
4517 | { |
4518 | FREEHASHENTRY entry; |
4519 | FieldData data; |
4520 | }; |
4521 | |
4522 | // A hashtable for storing EnC hanging field information |
4523 | // FieldData.m_fldMetadataToken is the key |
4524 | class CordbHangingFieldTable : public CHashTableAndData<CNewDataNoThrow> |
4525 | { |
4526 | private: |
4527 | |
4528 | BOOL Cmp(SIZE_T k1, const HASHENTRY *pc2) |
4529 | { |
4530 | LIMITED_METHOD_CONTRACT; |
4531 | return (ULONG)(UINT_PTR)(k1) != |
4532 | (reinterpret_cast<const CordbHangingField *>(pc2))->data.m_fldMetadataToken; |
4533 | } |
4534 | |
4535 | ULONG HASH(mdFieldDef fldToken) |
4536 | { |
4537 | LIMITED_METHOD_CONTRACT; |
4538 | return fldToken; |
4539 | } |
4540 | |
4541 | SIZE_T KEY(mdFieldDef fldToken) |
4542 | { |
4543 | return (SIZE_T)fldToken; |
4544 | } |
4545 | |
4546 | public: |
4547 | |
4548 | #ifndef DACCESS_COMPILE |
4549 | |
4550 | CordbHangingFieldTable() : CHashTableAndData<CNewDataNoThrow>(11) |
4551 | { |
4552 | NewInit(11, sizeof(CordbHangingField), 11); |
4553 | } |
4554 | |
4555 | FieldData * AddFieldInfo(FieldData * pInfo) |
4556 | { |
4557 | _ASSERTE(pInfo != NULL); |
4558 | |
4559 | CordbHangingField *pEntry = (CordbHangingField *)Add(HASH(pInfo->m_fldMetadataToken)); |
4560 | pEntry->data = *pInfo; // copy everything over |
4561 | |
4562 | // Return a pointer to the data |
4563 | return &(pEntry->data); |
4564 | } |
4565 | |
4566 | void RemoveFieldInfo(mdFieldDef fldToken) |
4567 | { |
4568 | CordbHangingField *entry = (CordbHangingField*)Find(HASH(fldToken), KEY(fldToken)); |
4569 | _ASSERTE(entry != NULL); |
4570 | Delete(HASH(fldToken), (HASHENTRY*)entry); |
4571 | } |
4572 | |
4573 | #endif // #ifndef DACCESS_COMPILE |
4574 | |
4575 | FieldData * GetFieldInfo(mdFieldDef fldToken) |
4576 | { |
4577 | CordbHangingField * entry = (CordbHangingField *)Find(HASH(fldToken), KEY(fldToken)); |
4578 | return (entry!=NULL?&(entry->data):NULL); |
4579 | } |
4580 | }; |
4581 | |
4582 | |
4583 | /* ------------------------------------------------------------------------- * |
4584 | * Instantiation. |
4585 | * |
4586 | * This struct stores a set of type parameters. It is used in |
4587 | * the heap-allocated data structures CordbType and CordbNativeCode. |
4588 | * |
4589 | * CordbType::m_inst. Stores the class type parameters if any, |
4590 | * or the solitary array type parameter, or the solitary parameter |
4591 | * to a byref type. |
4592 | * |
4593 | * CordbJITILFrame::m_genericArgs. Stores exact generic parameters for the generic method frame if available |
4594 | * Need not be identicial if code is shared between generic instantiations. |
4595 | * May be inexact if real instantiation has been optimized away off |
4596 | * the frame (nb this gets reported by the left side) |
4597 | * |
4598 | * This is conceptually an array of Type-parameters, with the split (m_cClassTyPars) between |
4599 | * where the Type's type-parameters end and the Method's type-parameters begin. |
4600 | * ------------------------------------------------------------------------- */ |
4601 | class Instantiation |
4602 | { |
4603 | public: |
4604 | // Empty ctor |
4605 | Instantiation() |
4606 | : m_cInst(0), m_ppInst(NULL), m_cClassTyPars (0) |
4607 | { } |
4608 | |
4609 | // Instantiation for Type. 0 Method type-parameters. |
4610 | Instantiation(unsigned int _cClassInst, CordbType **_ppClassInst) |
4611 | : m_cInst(_cClassInst), m_ppInst(_ppClassInst), m_cClassTyPars(_cClassInst) |
4612 | {LIMITED_METHOD_CONTRACT; } |
4613 | |
4614 | // Instantiation for Type + Function. |
4615 | Instantiation(unsigned int _cInst, CordbType **_ppInst, unsigned int numClassTyPars) |
4616 | : m_cInst(_cInst), m_ppInst(_ppInst), |
4617 | m_cClassTyPars (numClassTyPars) |
4618 | { } |
4619 | |
4620 | // Copy constructor. |
4621 | Instantiation(const Instantiation &inst) |
4622 | : m_cInst(inst.m_cInst), m_ppInst(inst.m_ppInst), m_cClassTyPars (inst.m_cClassTyPars) |
4623 | { } |
4624 | |
4625 | // Number of elements in array pointed to by m_ppInst |
4626 | unsigned int m_cInst; |
4627 | |
4628 | // Pointer to array of CordbType objects. Length of array is m_cInst. |
4629 | // Array is Class Type parameters followed by Function's Type parameters. |
4630 | // Eg, Instantiation for Class<Foo, Goo>::Func<Bar> would be {Foo, Goo, Bar}. |
4631 | // m_cInst = 3, m_cClassTyPars = 2. |
4632 | // In contrast, Instantiation for Class::Func<Foo, Goo, Bar> would have same |
4633 | // array, but m_cClassTyPars = 0. |
4634 | CordbType **m_ppInst; |
4635 | |
4636 | // Track the split between Type vs. Method type-params. |
4637 | unsigned int m_cClassTyPars; |
4638 | }; |
4639 | |
4640 | //------------------------------------------------------------------------ |
4641 | // CordbType: replaces the use of signatures. |
4642 | // |
4643 | // Left Side & Right Side |
4644 | // --------------------------- |
4645 | // CordbTypes may come from either the Right Side (via being built up from |
4646 | // ICorDebug), or from the Left-Side (being handed back from LS operations |
4647 | // like getting the type from an Object the LS handed back). |
4648 | // The RightSide CordbType corresponds to a Left-Side TypeHandle. |
4649 | // CordbTypes are communicated across the LS/RS boundary by marshalling |
4650 | // to BasicTypeData + ExpandedTypeData IPC events. |
4651 | // |
4652 | // |
4653 | // Invariants on CordbType |
4654 | // --------------------------- |
4655 | // |
4656 | // The m_elementType is NEVER ELEMENT_TYPE_VAR or ELEMENT_TYPE_MVAR or ELEMENT_TYPE_GENERICINST |
4657 | // CordbTypes are always _ground_ types (fully instantiated generics or non-generic types). If |
4658 | // they represent an instantiated type like List<int> then m_inst will be non-empty. |
4659 | // |
4660 | // |
4661 | // !!!! The m_elementType is NEVER ELEMENT_TYPE_VALUETYPE !!!! |
4662 | // !!!! To find out if it is a value type call CordbType::IsValueType() !!!! |
4663 | // |
4664 | // Where CordbTypes are stored |
4665 | // --------------------------- |
4666 | // |
4667 | // Because we could have a significant number of different instantiations for a given templated type, |
4668 | // we need an efficient way to store and retrieve the CordbType instances for these instantiations. |
4669 | // For this reason, we use a tree-like scheme to hash-cons types. To implement this we use the following |
4670 | // scheme: |
4671 | // - CordbTypes are created for "partially instantiated" types, |
4672 | // e.g. CordbTypes exist for "Dict" and "Dict<int>" even if the real |
4673 | // type being manipulated by the user is "Dict<int,string>" |
4674 | // - Subordinate types (E.g. Dict<int,string> is subordinate to Dict<int>, |
4675 | // which is itself subordinate to the type for Dict) get stored |
4676 | // in the m_spinetypes hash table of the parent type. |
4677 | // - In m_spinetypes the pointers of the CordbTypes themselves |
4678 | // are used for the unique ids for entries in the table. |
4679 | // Note that CordbType instances that are created for "partially instantiated" types |
4680 | // are never used for any purpose other than efficient hashing. Specifically, the debugger will |
4681 | // never have reason to expose a partially instantiated type outside of the hashing algorithm. |
4682 | // |
4683 | // CordbTypes have object identity: if 2 CordbTypes represent the same type (in the same AppDomain), |
4684 | // then they will be the same CordbType instance. |
4685 | // |
4686 | // Thus the representation for "Dict<class String,class Foo, class Foo* >" goes as follows: |
4687 | // 1. Assume the type Foo is represented by CordbClass *5678x |
4688 | // 1b. Assume the hashtable m_sharedtypes in the AppDomain maps E_T_STRING to the CordbType *0ABCx |
4689 | // Assume m_type in class Foo (i.e. CordbClass *5678x) is the CordbType *0DEFx |
4690 | // Assume m_type in class Foo maps E_T_PTR to the CordbType *0647x |
4691 | // 2. The hash table m_spinetypes in "Dict" maps "0ABCx" to a new CordbType |
4692 | // representing Dict<String> (a single type application) |
4693 | // 3. The hash table m_spinetypes in this new CordbType maps "0DEFx" to a |
4694 | // new CordbType representing Dict<class String,class Foo> |
4695 | // 3. The hash table m_spinetypes in this new CordbType maps "0647" to a |
4696 | // new CordbType representing Dict<class String,class Foo, class Foo*> |
4697 | // |
4698 | // This lets us reuse the existing hash table scheme to build |
4699 | // up instantiated types of arbitrary size. |
4700 | // |
4701 | // Array types are similar, excpet that they start with a head type |
4702 | // for the "type constructor", e.g. "_ []" is a type constructor with rank 1 |
4703 | // and m_elementType = ELEMENT_TYPE_SZARRAY. These head constructors are |
4704 | // stored in the m_sharedtypes table in the appdomain. The actual instantiations |
4705 | // of the array types are then subordinate types to the array constructor type. |
4706 | // |
4707 | // Other types are simpler, and have unique objects stored in the m_sharedtypes |
4708 | // table in the appdomain. This table is indexed by CORDBTYPE_ID in RsType.cpp |
4709 | // |
4710 | // |
4711 | // Memory Management of CordbTypes |
4712 | // --------------------------- |
4713 | // All CordbTypes are ultimately stored off the CordbAppDomain object. |
4714 | // The most common place is in the AppDomain's neuter-list. |
4715 | // |
4716 | // See definition of ICorDebugType for further invariants on types. |
4717 | // |
4718 | |
4719 | class CordbType : public CordbBase, public ICorDebugType, public ICorDebugType2 |
4720 | { |
4721 | public: |
4722 | CordbType(CordbAppDomain *appdomain, CorElementType ty, unsigned int rank); |
4723 | CordbType(CordbAppDomain *appdomain, CorElementType ty, CordbClass *c); |
4724 | CordbType(CordbType *tycon, CordbType *tyarg); |
4725 | virtual ~CordbType(); |
4726 | virtual void Neuter(); |
4727 | |
4728 | #ifdef _DEBUG |
4729 | virtual const char * DbgGetName() { return "CordbType" ; } |
4730 | #endif |
4731 | |
4732 | // If you want to force the init to happen even if we think the class |
4733 | // is up to date, set fForceInit to TRUE |
4734 | HRESULT Init(BOOL fForceInit); |
4735 | |
4736 | //----------------------------------------------------------- |
4737 | // IUnknown |
4738 | //----------------------------------------------------------- |
4739 | |
4740 | ULONG STDMETHODCALLTYPE AddRef(); |
4741 | ULONG STDMETHODCALLTYPE Release(); |
4742 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
4743 | |
4744 | //----------------------------------------------------------- |
4745 | // ICorDebugType |
4746 | //----------------------------------------------------------- |
4747 | |
4748 | COM_METHOD GetType(CorElementType *ty); |
4749 | COM_METHOD GetClass(ICorDebugClass **ppClass); |
4750 | COM_METHOD EnumerateTypeParameters(ICorDebugTypeEnum **ppTyParEnum); |
4751 | COM_METHOD GetFirstTypeParameter(ICorDebugType **ppType); |
4752 | COM_METHOD GetBase(ICorDebugType **ppType); |
4753 | COM_METHOD GetStaticFieldValue(mdFieldDef fieldDef, |
4754 | ICorDebugFrame * pFrame, |
4755 | ICorDebugValue ** ppValue); |
4756 | COM_METHOD GetRank(ULONG32 *pnRank); |
4757 | |
4758 | //----------------------------------------------------------- |
4759 | // ICorDebugType2 |
4760 | //----------------------------------------------------------- |
4761 | COM_METHOD GetTypeID(COR_TYPEID *pId); |
4762 | |
4763 | //----------------------------------------------------------- |
4764 | // Non-COM members |
4765 | //----------------------------------------------------------- |
4766 | |
4767 | //----------------------------------------------------------- |
4768 | // Basic constructor operations for the algebra of types. |
4769 | // These all create unique objects within an AppDomain. |
4770 | //----------------------------------------------------------- |
4771 | |
4772 | // This one is used to create simple types, e.g. int32, int64, typedbyref etc. |
4773 | static HRESULT MkType(CordbAppDomain * pAppDomain, |
4774 | CorElementType elementType, |
4775 | CordbType ** ppResultType); |
4776 | |
4777 | // This one is used to create array, pointer and byref types |
4778 | static HRESULT MkType(CordbAppDomain * pAppDomain, |
4779 | CorElementType elementType, |
4780 | ULONG rank, |
4781 | CordbType * pType, |
4782 | CordbType ** ppResultType); |
4783 | |
4784 | // This one is used to create function pointer types. et must be ELEMENT_TYPE_FNPTR |
4785 | static HRESULT MkType(CordbAppDomain * pAppDomain, |
4786 | CorElementType elementType, |
4787 | const Instantiation * pInst, |
4788 | CordbType ** ppResultType); |
4789 | |
4790 | // This one is used to class and value class types, e.g. "class MyClass" or "class ArrayList<int>" |
4791 | static HRESULT MkType(CordbAppDomain * pAppDomain, |
4792 | CorElementType elementType, |
4793 | CordbClass * pClass, |
4794 | const Instantiation * pInst, |
4795 | CordbType ** ppResultType); |
4796 | |
4797 | // Some derived constructors... Use this one if the type is definitely not |
4798 | // a paramterized type, e.g. to implement functions on the API where types cannot |
4799 | // be parameterized. |
4800 | static HRESULT MkUnparameterizedType(CordbAppDomain *appdomain, CorElementType et, CordbClass *cl, CordbType **ppType); |
4801 | |
4802 | //----------------------------------------------------------- |
4803 | // Basic destructor operations over the algebra |
4804 | //----------------------------------------------------------- |
4805 | void DestUnaryType(CordbType **pRes) ; |
4806 | void DestConstructedType(CordbClass **pClass, Instantiation *pInst); |
4807 | void DestNaryType(Instantiation *pInst); |
4808 | |
4809 | CorElementType GetElementType() { return m_elementType; } |
4810 | VMPTR_DomainFile GetDomainFile(); |
4811 | VMPTR_Module GetModule(); |
4812 | |
4813 | // If this is a ptr type, get the CordbType that it points to. |
4814 | // Eg, for CordbType("Int*"), returns CordbType("Int"). |
4815 | // If not a ptr type, returns null. |
4816 | // Since it's all internal, no reference counting. |
4817 | // This is effectively a specialized version of DestUnaryType. |
4818 | CordbType * GetPointerElementType(); |
4819 | |
4820 | |
4821 | // Create a type from metadata |
4822 | static HRESULT SigToType(CordbModule * pModule, SigParser * pSigParser, const Instantiation * pInst, CordbType ** ppResultType); |
4823 | |
4824 | // Create a type from from the data received from the left-side |
4825 | static HRESULT TypeDataToType(CordbAppDomain *appdomain, DebuggerIPCE_ExpandedTypeData *data, CordbType **pRes); |
4826 | static HRESULT TypeDataToType(CordbAppDomain *appdomain, DebuggerIPCE_BasicTypeData *data, CordbType **pRes); |
4827 | static HRESULT InstantiateFromTypeHandle(CordbAppDomain * appdomain, |
4828 | VMPTR_TypeHandle vmTypeHandle, |
4829 | CorElementType et, |
4830 | CordbClass * tycon, |
4831 | CordbType ** pRes); |
4832 | |
4833 | // Prepare data to send back to left-side during Init() and FuncEval. Fail if the the exact |
4834 | // type data is requested but was not fetched correctly during Init() |
4835 | HRESULT TypeToBasicTypeData(DebuggerIPCE_BasicTypeData *data); |
4836 | void TypeToExpandedTypeData(DebuggerIPCE_ExpandedTypeData *data); |
4837 | void TypeToTypeArgData(DebuggerIPCE_TypeArgData *data); |
4838 | |
4839 | void CountTypeDataNodes(unsigned int *count); |
4840 | static void CountTypeDataNodesForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], unsigned int *count); |
4841 | static void GatherTypeData(CordbType *type, DebuggerIPCE_TypeArgData **curr_tyargData); |
4842 | static void GatherTypeDataForInstantiation(unsigned int genericArgsCount, ICorDebugType *genericArgs[], DebuggerIPCE_TypeArgData **curr_tyargData); |
4843 | |
4844 | HRESULT GetParentType(CordbClass * baseClass, CordbType ** ppRes); |
4845 | |
4846 | // These are available after Init() has been called.... |
4847 | HRESULT GetUnboxedObjectSize(ULONG32 *res); |
4848 | HRESULT GetFieldInfo(mdFieldDef fldToken, FieldData ** ppFieldData); |
4849 | |
4850 | CordbAppDomain *GetAppDomain() { return m_appdomain; } |
4851 | |
4852 | bool IsValueType(); |
4853 | |
4854 | // Is this type a GC-root. |
4855 | bool IsGCRoot(); |
4856 | |
4857 | #ifdef FEATURE_64BIT_ALIGNMENT |
4858 | // checks if the type requires 8-byte alignment. |
4859 | // this is not exposed via ICorDebug at present. |
4860 | HRESULT CordbType::RequiresAlign8(BOOL* isRequired); |
4861 | #endif |
4862 | |
4863 | //----------------------------------------------------------- |
4864 | // Data members |
4865 | //----------------------------------------------------------- |
4866 | |
4867 | public: |
4868 | // Internal representation of the element type. This may not map exactly to the public element type. |
4869 | // Specifically, m_elementType is NEVER: |
4870 | // ELEMENT_TYPE_VAR, ELEMENT_TYPE_MVAR, ELEMENT_TYPE_GENERICINST, |
4871 | // or ELEMENT_TYPE_VALUETYPE. |
4872 | // To find out if this CordbType corresponds to a value type (instead of Reference type) call CordbType::IsValueType() |
4873 | CorElementType m_elementType; |
4874 | |
4875 | // The appdomain that this type lives in. Types (and their type-parameters) are all contained in a single appdomain. |
4876 | // (alhtough the types may be from different modules). |
4877 | // This is valid for all CordbType objects, regardless of m_elementType; |
4878 | CordbAppDomain * m_appdomain; |
4879 | |
4880 | // The matching class for this type. |
4881 | // Initially only set for E_T_CLASS, lazily computed for E_T_STRING and E_T_OBJECT if needed |
4882 | CordbClass * m_pClass; |
4883 | |
4884 | ULONG m_rank; // Only set for E_T_ARRAY etc. |
4885 | |
4886 | // Array of Type Parameters for this Type. |
4887 | Instantiation m_inst; |
4888 | |
4889 | // A unique mapping from CordbType objects that are type parameters to CordbType objects. Each mapping |
4890 | // represents the use of the containing type as type constructor. e.g. If the containing type |
4891 | // is CordbType(CordbClass "List") then the table here will map parameters such as (CordbType(CordbClass "String")) to |
4892 | // the constructed type CordbType(CordbClass "List", <CordbType(CordbClass "String")>) |
4893 | // @dbgtodo synchronization - this is currently protected by the Stop-Go lock. Transition to process-lock. |
4894 | CordbSafeHashTable<CordbType> m_spinetypes; |
4895 | |
4896 | // Valid after Init(), only for E_T_ARRAY etc.and E_T_CLASS when m_pClass->m_classInfo.m_genericArgsCount > 0. |
4897 | // m_typeHandleExact is the precise Runtime type handle for this type. |
4898 | VMPTR_TypeHandle m_typeHandleExact; |
4899 | |
4900 | // Valid after Init(), only for E_T_CLASS, and when m_pClass->m_classInfo.m_genericArgsCount > 0. |
4901 | // May not be set correctly if m_fieldInfoNeedsInit. |
4902 | SIZE_T m_objectSize; |
4903 | |
4904 | // DON'T KEEP POINTERS TO ELEMENTS OF m_pFields AROUND!! |
4905 | // This may be deleted if the class gets EnC'd. |
4906 | // |
4907 | // Valid after Init(), only for E_T_CLASS, and when m_pClass->m_classInfo.m_genericArgsCount > 0 |
4908 | // All fields will be valid if we have m_typeHandleExact. |
4909 | // |
4910 | // Only some fields will be valid if we have called Init() but still have m_fieldInfoNeedsInit. |
4911 | DacDbiArrayList<FieldData> m_fieldList; |
4912 | |
4913 | HRESULT ReturnedByValue(); |
4914 | |
4915 | private: |
4916 | static HRESULT MkTyAppType(CordbAppDomain * pAddDomain, |
4917 | CordbType * pType, |
4918 | const Instantiation * pInst, |
4919 | CordbType ** pResultType); |
4920 | |
4921 | BOOL m_fieldInfoNeedsInit; |
4922 | |
4923 | private: |
4924 | HRESULT InitInstantiationTypeHandle(BOOL fForceInit); |
4925 | HRESULT InitInstantiationFieldInfo(BOOL fForceInit); |
4926 | HRESULT InitStringOrObjectClass(BOOL fForceInit); |
4927 | }; |
4928 | |
4929 | /* ------------------------------------------------------------------------- * |
4930 | * Class class |
4931 | * ------------------------------------------------------------------------- */ |
4932 | |
4933 | class CordbClass : public CordbBase, public ICorDebugClass, public ICorDebugClass2 |
4934 | { |
4935 | public: |
4936 | CordbClass(CordbModule* m, mdTypeDef token); |
4937 | virtual ~CordbClass(); |
4938 | virtual void Neuter(); |
4939 | |
4940 | using CordbBase::GetProcess; |
4941 | |
4942 | #ifdef _DEBUG |
4943 | virtual const char * DbgGetName() { return "CordbClass" ; } |
4944 | #endif |
4945 | |
4946 | |
4947 | //----------------------------------------------------------- |
4948 | // IUnknown |
4949 | //----------------------------------------------------------- |
4950 | |
4951 | ULONG STDMETHODCALLTYPE AddRef() |
4952 | { |
4953 | return (BaseAddRef()); |
4954 | } |
4955 | ULONG STDMETHODCALLTYPE Release() |
4956 | { |
4957 | return (BaseRelease()); |
4958 | } |
4959 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
4960 | |
4961 | //----------------------------------------------------------- |
4962 | // ICorDebugClass |
4963 | //----------------------------------------------------------- |
4964 | |
4965 | COM_METHOD GetStaticFieldValue(mdFieldDef fieldDef, |
4966 | ICorDebugFrame *pFrame, |
4967 | ICorDebugValue **ppValue); |
4968 | COM_METHOD GetModule(ICorDebugModule **pModule); |
4969 | COM_METHOD GetToken(mdTypeDef *pTypeDef); |
4970 | //----------------------------------------------------------- |
4971 | // ICorDebugClass2 |
4972 | //----------------------------------------------------------- |
4973 | COM_METHOD GetParameterizedType(CorElementType elementType, |
4974 | ULONG32 cTypeArgs, |
4975 | ICorDebugType * rgpTypeArgs[], |
4976 | ICorDebugType ** ppType); |
4977 | |
4978 | COM_METHOD SetJMCStatus(BOOL fIsUserCode); |
4979 | |
4980 | //----------------------------------------------------------- |
4981 | // Convenience routines and Accessors |
4982 | //----------------------------------------------------------- |
4983 | |
4984 | // Helper to get containing module |
4985 | CordbModule * GetModule() |
4986 | { |
4987 | return m_pModule; |
4988 | } |
4989 | |
4990 | // get the metadata token for this class |
4991 | mdTypeDef GetToken() { return m_token; } |
4992 | |
4993 | // Helper to get the AppDomain the class lives in. |
4994 | CordbAppDomain * GetAppDomain() |
4995 | { |
4996 | return m_pModule->GetAppDomain(); |
4997 | } |
4998 | |
4999 | // This only very roughly resembles the CLASS_LOAD_LEVEL concept in the VM. |
5000 | // because DBI's needs are far more coarse grained. Also DBI |
5001 | // may contain more, equal, or less information than what is available in |
5002 | // native runtime data structures. We can have less when we are being lazy |
5003 | // and haven't yet fetched it. We can have more if use an independent data |
5004 | // source such as the metadata blob and then compute some type data ourselves |
5005 | typedef enum |
5006 | { |
5007 | // At this state the constructor has been run. |
5008 | // m_module and m_token will be valid |
5009 | Constructed, |
5010 | |
5011 | // At this state we have additionally certain to have initialized |
5012 | // m_fIsValueClass and m_fHasTypeParams |
5013 | // Calls to IsValueClass() and HasTypeParams() are valid |
5014 | // This stage should be achievable as long as a runtime type handle |
5015 | // exists, even if it is unrestored |
5016 | BasicInfo, |
5017 | |
5018 | //Everything is loaded, or at least anything created lazily from this |
5019 | //point on should be certain to succeed (ie m_type) |
5020 | FullInfo |
5021 | } |
5022 | ClassLoadLevel; |
5023 | |
5024 | ClassLoadLevel GetLoadLevel() |
5025 | { |
5026 | return m_loadLevel; |
5027 | } |
5028 | |
5029 | // determine if a load event has been sent for this class |
5030 | BOOL LoadEventSent() { return m_fLoadEventSent; } |
5031 | |
5032 | // set value of m_fLoadEventSent |
5033 | void SetLoadEventSent(BOOL fEventSent) { m_fLoadEventSent = fEventSent; } |
5034 | |
5035 | // determine if the class has been unloaded |
5036 | BOOL HasBeenUnloaded() { return m_fHasBeenUnloaded; } |
5037 | |
5038 | // set value of m_fHasBeenUnloaded |
5039 | void SetHasBeenUnloaded(BOOL fUnloaded) { m_fHasBeenUnloaded = (fUnloaded == TRUE); } |
5040 | |
5041 | // determine if this is a value class |
5042 | BOOL IsValueClassNoInit() { return m_fIsValueClass; } |
5043 | |
5044 | // set value of m_fIsValueClass |
5045 | void SetIsValueClass(BOOL fIsValueClass) { m_fIsValueClass = (fIsValueClass == TRUE); } |
5046 | |
5047 | // determine if the value class is known |
5048 | BOOL IsValueClassKnown() { return m_fIsValueClassKnown; } |
5049 | |
5050 | // set value of m_fIsValueClassKnown |
5051 | void SetIsValueClassKnown(BOOL fIsValueClassKnown) { m_fIsValueClassKnown = (fIsValueClassKnown == TRUE); } |
5052 | |
5053 | // get value of m_type |
5054 | CordbType * GetType() { return m_type; } |
5055 | |
5056 | void SetType(CordbType * pType) { m_type.Assign(pType); } |
5057 | |
5058 | // get the type parameter count |
5059 | bool HasTypeParams() { _ASSERTE(m_loadLevel >= BasicInfo); return m_fHasTypeParams; } |
5060 | |
5061 | // get the object size |
5062 | SIZE_T ObjectSize() { return m_classInfo.m_objectSize; } |
5063 | |
5064 | // get the metadata token for this class |
5065 | mdTypeDef MDToken() { return m_token; } |
5066 | |
5067 | // get the number of fields |
5068 | unsigned int FieldCount() { return m_classInfo.m_fieldList.Count(); } |
5069 | |
5070 | //----------------------------------------------------------- |
5071 | // Functionality shared for CordbType and CordbClass |
5072 | //----------------------------------------------------------- |
5073 | |
5074 | static HRESULT SearchFieldInfo(CordbModule * module, |
5075 | DacDbiArrayList<FieldData> * pFieldList, |
5076 | mdTypeDef classToken, |
5077 | mdFieldDef fldToken, |
5078 | FieldData ** ppFieldData); |
5079 | |
5080 | static HRESULT GetStaticFieldValue2(CordbModule * pModule, |
5081 | FieldData * pFieldData, |
5082 | BOOL fEnCHangingField, |
5083 | const Instantiation * pInst, |
5084 | ICorDebugFrame * pFrame, |
5085 | ICorDebugValue ** ppValue); |
5086 | |
5087 | //----------------------------------------------------------- |
5088 | // Non-COM methods |
5089 | //----------------------------------------------------------- |
5090 | |
5091 | // Get information about a field that was added by EnC |
5092 | HRESULT GetEnCHangingField(mdFieldDef fldToken, |
5093 | FieldData ** ppFieldData, |
5094 | CordbObjectValue * pObject); |
5095 | |
5096 | private: |
5097 | // Get information via the DAC about a field added with Edit and Continue. |
5098 | FieldData * GetEnCFieldFromDac(BOOL fStatic, |
5099 | CordbObjectValue * pObject, |
5100 | mdFieldDef fieldToken); |
5101 | |
5102 | // Initialize an instance of EnCHangingFieldInfo. |
5103 | void InitEnCFieldInfo(EnCHangingFieldInfo * pEncField, |
5104 | BOOL fStatic, |
5105 | CordbObjectValue * pObject, |
5106 | mdFieldDef fieldToken, |
5107 | mdTypeDef classToken); |
5108 | |
5109 | |
5110 | public: |
5111 | |
5112 | // set or clear the custom notifications flag to control whether we ignore custom debugger notifications |
5113 | void SetCustomNotifications(BOOL fEnable) { m_fCustomNotificationsEnabled = fEnable; } |
5114 | BOOL CustomNotificationsEnabled () { return m_fCustomNotificationsEnabled; } |
5115 | |
5116 | HRESULT GetFieldInfo(mdFieldDef fldToken, FieldData ** ppFieldData); |
5117 | |
5118 | // If you want to force the init to happen even if we think the class |
5119 | // is up to date, set fForceInit to TRUE |
5120 | void Init(ClassLoadLevel desiredLoadLevel = FullInfo); |
5121 | |
5122 | // determine if any fields for a type are unallocated statics |
5123 | BOOL GotUnallocatedStatic(DacDbiArrayList<FieldData> * pFieldList); |
5124 | |
5125 | bool IsValueClass(); |
5126 | HRESULT GetThisType(const Instantiation * pInst, CordbType ** ppResultType); |
5127 | static HRESULT PostProcessUnavailableHRESULT(HRESULT hr, |
5128 | IMetaDataImport *pImport, |
5129 | mdFieldDef fieldDef); |
5130 | mdTypeDef GetTypeDef() { return (mdTypeDef)m_id; } |
5131 | |
5132 | #ifdef EnC_SUPPORTED |
5133 | // when we get an added field or method, mark the class to force re-init when we access it |
5134 | void MakeOld() |
5135 | { |
5136 | m_loadLevel = Constructed; |
5137 | } |
5138 | #endif // EnC_SUPPORTED |
5139 | |
5140 | //----------------------------------------------------------- |
5141 | // Data members |
5142 | //----------------------------------------------------------- |
5143 | private: |
5144 | // contains information about the type: size and |
5145 | // field information |
5146 | ClassInfo m_classInfo; |
5147 | |
5148 | ClassLoadLevel m_loadLevel; |
5149 | |
5150 | // @dbgtodo managed pipeline - can we get rid of both of these fields? |
5151 | BOOL m_fLoadEventSent; |
5152 | bool m_fHasBeenUnloaded; |
5153 | |
5154 | // [m_type] is the type object for when this class is used |
5155 | // as a type. If the class is a value class then it can represent |
5156 | // either the boxed or unboxed type - it depends on the context where the |
5157 | // type is used. For example on a CordbBoxValue it represents the type of the |
5158 | // boxed VC, on a CordbVCObjectValue it represents the type of the unboxed VC. |
5159 | // |
5160 | // The type field starts of NULL as there |
5161 | // is no need to create the type object until it is needed. |
5162 | RSSmartPtr<CordbType> m_type; |
5163 | |
5164 | // Module that this Class lives in. Valid at the Constructed type level. |
5165 | CordbModule * m_pModule; |
5166 | |
5167 | // the token for the type constructor - m_id cannot be used for constructed types |
5168 | // valid at the Constructed type level |
5169 | mdTypeDef m_token; |
5170 | |
5171 | // Whether the class is a VC or not is discovered either by |
5172 | // seeing the class used in a signature after ELEMENT_TYPE_VALUETYPE |
5173 | // or ELEMENT_TYPE_CLASS or by going and asking the EE. |
5174 | bool m_fIsValueClassKnown; |
5175 | |
5176 | // Whether the class is a VC or not |
5177 | bool m_fIsValueClass; |
5178 | |
5179 | // Whether the class has generic type parameters in its definition |
5180 | bool m_fHasTypeParams; |
5181 | |
5182 | // Timestamp from GetProcess()->m_continueCounter, which we can use to tell if |
5183 | // the process has been continued since we last took a snapshot. |
5184 | UINT m_continueCounterLastSync; |
5185 | |
5186 | // if we add static fields with EnC after this class is loaded (in the debuggee), |
5187 | // their value will be hung off the FieldDesc. Hold information about such fields here. |
5188 | CordbHangingFieldTable m_hangingFieldsStatic; |
5189 | |
5190 | // this indicates whether we should send custom debugger notifications |
5191 | BOOL m_fCustomNotificationsEnabled; |
5192 | |
5193 | }; |
5194 | |
5195 | |
5196 | /* ------------------------------------------------------------------------- * |
5197 | * TypeParameter enumerator class |
5198 | * ------------------------------------------------------------------------- */ |
5199 | |
5200 | class CordbTypeEnum : public CordbBase, public ICorDebugTypeEnum |
5201 | { |
5202 | public: |
5203 | // Factory method: Create a new instance of this class. Returns NULL on out-of-memory. |
5204 | // On success, returns a new initialized instance of CordbTypeEnum with ref-count 0 (just like a ctor). |
5205 | // the life expectancy of the enumerator varies by caller so we require them to specify the applicable neuter list here. |
5206 | static CordbTypeEnum* Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, CordbType **ppTypars); |
5207 | static CordbTypeEnum* Build(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, RSSmartPtr<CordbType>*ppTypars); |
5208 | |
5209 | virtual ~CordbTypeEnum() ; |
5210 | |
5211 | virtual void Neuter(); |
5212 | |
5213 | |
5214 | #ifdef _DEBUG |
5215 | virtual const char * DbgGetName() { return "CordbTypeEnum" ; } |
5216 | #endif |
5217 | |
5218 | |
5219 | //----------------------------------------------------------- |
5220 | // IUnknown |
5221 | //----------------------------------------------------------- |
5222 | |
5223 | ULONG STDMETHODCALLTYPE AddRef() |
5224 | { |
5225 | return (BaseAddRef()); |
5226 | } |
5227 | ULONG STDMETHODCALLTYPE Release() |
5228 | { |
5229 | return (BaseRelease()); |
5230 | } |
5231 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
5232 | |
5233 | //----------------------------------------------------------- |
5234 | // ICorDebugEnum |
5235 | //----------------------------------------------------------- |
5236 | |
5237 | COM_METHOD Skip(ULONG celt); |
5238 | COM_METHOD Reset(); |
5239 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
5240 | COM_METHOD GetCount(ULONG *pcelt); |
5241 | |
5242 | //----------------------------------------------------------- |
5243 | // ICorDebugTypeEnum |
5244 | //----------------------------------------------------------- |
5245 | |
5246 | COM_METHOD Next(ULONG celt, ICorDebugType *Types[], ULONG *pceltFetched); |
5247 | |
5248 | private: |
5249 | // Private constructor, only partially initializes the object. |
5250 | // Clients should use the 'Build' factory method to create an instance of this class. |
5251 | CordbTypeEnum( CordbAppDomain * pAppDomain, NeuterList * pNeuterList ); |
5252 | template<class T> static CordbTypeEnum* BuildImpl(CordbAppDomain * pAppDomain, NeuterList * pNeuterList, unsigned int cTypars, T* ppTypars ); |
5253 | |
5254 | // Owning object. |
5255 | CordbAppDomain * m_pAppDomain; |
5256 | |
5257 | // Array of Types. We own the array, and share refs to the types. |
5258 | // @todo- since these are guaranteed to be kept alive as long as we're not neutered, |
5259 | // we don't need to keep refs to them. |
5260 | RSSmartPtr<CordbType> * m_ppTypars; |
5261 | UINT m_iCurrent; |
5262 | UINT m_iMax; |
5263 | }; |
5264 | |
5265 | /* ------------------------------------------------------------------------- * |
5266 | * Code enumerator class |
5267 | * ------------------------------------------------------------------------- */ |
5268 | |
5269 | class CordbCodeEnum : public CordbBase, public ICorDebugCodeEnum |
5270 | { |
5271 | public: |
5272 | (unsigned int cCode, RSSmartPtr<CordbCode> * ppCode); |
5273 | virtual ~CordbCodeEnum() ; |
5274 | |
5275 | |
5276 | #ifdef _DEBUG |
5277 | virtual const char * DbgGetName() { return "CordbCodeEnum" ; } |
5278 | #endif |
5279 | |
5280 | |
5281 | //----------------------------------------------------------- |
5282 | // IUnknown |
5283 | //----------------------------------------------------------- |
5284 | |
5285 | ULONG STDMETHODCALLTYPE AddRef() |
5286 | { |
5287 | return (BaseAddRef()); |
5288 | } |
5289 | ULONG STDMETHODCALLTYPE Release() |
5290 | { |
5291 | return (BaseRelease()); |
5292 | } |
5293 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
5294 | |
5295 | //----------------------------------------------------------- |
5296 | // ICorDebugEnum |
5297 | //----------------------------------------------------------- |
5298 | |
5299 | COM_METHOD Skip(ULONG celt); |
5300 | COM_METHOD Reset(); |
5301 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
5302 | COM_METHOD GetCount(ULONG *pcelt); |
5303 | |
5304 | //----------------------------------------------------------- |
5305 | // ICorDebugCodeEnum |
5306 | //----------------------------------------------------------- |
5307 | |
5308 | COM_METHOD Next(ULONG celt, ICorDebugCode *Codes[], ULONG *pceltFetched); |
5309 | |
5310 | private: |
5311 | // Ptr to an array of CordbCode* |
5312 | // We own the array. |
5313 | RSSmartPtr<CordbCode> * m_ppCodes; |
5314 | UINT m_iCurrent; |
5315 | UINT m_iMax; |
5316 | }; |
5317 | |
5318 | |
5319 | |
5320 | |
5321 | |
5322 | typedef CUnorderedArray<CordbCode*,11> UnorderedCodeArray; |
5323 | //<TODO>@todo port: different SIZE_T size/</TODO> |
5324 | const int DMI_VERSION_INVALID = 0; |
5325 | const int DMI_VERSION_MOST_RECENTLY_JITTED = 1; |
5326 | const int DMI_VERSION_MOST_RECENTLY_EnCED = 2; |
5327 | |
5328 | |
5329 | /* ------------------------------------------------------------------------- * |
5330 | * Function class |
5331 | * |
5332 | * @review . The CordbFunction class now keeps a multiple MethodDescInfo |
5333 | * structures in a hash table indexed by tokens provided by the left-side. |
5334 | * In 99.9% of cases this hash table will only contain one entry - we only |
5335 | * use a hashtable to cover the case where we have multiple JITtings of |
5336 | * a single version of a function, in particular multiple JITtings of generic |
5337 | * code under different instantiations. This will increase space usage. |
5338 | * The way around it is to store one CordbNativeCode in-line in the CordbFunction |
5339 | * class, or at least store one such pointer so no hash table will normally |
5340 | * be needed. This is similar to other cases, e.g. the hash table in |
5341 | * CordbClass used to indicate different CordbTypes made from that class - |
5342 | * again in the normal case these tables will only contain one element. |
5343 | * |
5344 | * However, for the moment I've focused on correctness and we can minimize |
5345 | * this space usage in due course. |
5346 | * ------------------------------------------------------------------------- */ |
5347 | |
5348 | const BOOL bNativeCode = FALSE; |
5349 | const BOOL bILCode = TRUE; |
5350 | |
5351 | // |
5352 | // Each E&C version gets its own function object. So the IL that a function |
5353 | // is associated w/ does not change. |
5354 | // B/C of generics, a single IL function may get jitted multiple times and |
5355 | // be associated w/ multiple native code blobs (CordbNativeCode). |
5356 | // |
5357 | class CordbFunction : public CordbBase, |
5358 | public ICorDebugFunction, |
5359 | public ICorDebugFunction2, |
5360 | public ICorDebugFunction3, |
5361 | public ICorDebugFunction4 |
5362 | { |
5363 | public: |
5364 | //----------------------------------------------------------- |
5365 | // Create from scope and member objects. |
5366 | //----------------------------------------------------------- |
5367 | CordbFunction(CordbModule * m, |
5368 | mdMethodDef token, |
5369 | SIZE_T enCVersion); |
5370 | virtual ~CordbFunction(); |
5371 | virtual void Neuter(); |
5372 | |
5373 | |
5374 | |
5375 | #ifdef _DEBUG |
5376 | virtual const char * DbgGetName() { return "CordbFunction" ; } |
5377 | #endif |
5378 | |
5379 | |
5380 | //----------------------------------------------------------- |
5381 | // IUnknown |
5382 | //----------------------------------------------------------- |
5383 | |
5384 | ULONG STDMETHODCALLTYPE AddRef() |
5385 | { |
5386 | return (BaseAddRef()); |
5387 | } |
5388 | ULONG STDMETHODCALLTYPE Release() |
5389 | { |
5390 | return (BaseRelease()); |
5391 | } |
5392 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
5393 | |
5394 | //----------------------------------------------------------- |
5395 | // ICorDebugFunction |
5396 | //----------------------------------------------------------- |
5397 | COM_METHOD GetModule(ICorDebugModule **pModule); |
5398 | COM_METHOD GetClass(ICorDebugClass **ppClass); |
5399 | COM_METHOD GetToken(mdMethodDef *pMemberDef); |
5400 | COM_METHOD GetILCode(ICorDebugCode **ppCode); |
5401 | COM_METHOD GetNativeCode(ICorDebugCode **ppCode); |
5402 | COM_METHOD CreateBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint); |
5403 | COM_METHOD GetLocalVarSigToken(mdSignature *pmdSig); |
5404 | COM_METHOD GetCurrentVersionNumber(ULONG32 *pnCurrentVersion); |
5405 | |
5406 | //----------------------------------------------------------- |
5407 | // ICorDebugFunction2 |
5408 | //----------------------------------------------------------- |
5409 | COM_METHOD SetJMCStatus(BOOL fIsUserCode); |
5410 | COM_METHOD GetJMCStatus(BOOL * pfIsUserCode); |
5411 | COM_METHOD EnumerateNativeCode(ICorDebugCodeEnum **ppCodeEnum) { return E_NOTIMPL; } |
5412 | COM_METHOD GetVersionNumber(ULONG32 *pnCurrentVersion); |
5413 | |
5414 | //----------------------------------------------------------- |
5415 | // ICorDebugFunction3 |
5416 | //----------------------------------------------------------- |
5417 | COM_METHOD GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedILCode); |
5418 | |
5419 | //----------------------------------------------------------- |
5420 | // ICorDebugFunction4 |
5421 | //----------------------------------------------------------- |
5422 | COM_METHOD CreateNativeBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint); |
5423 | |
5424 | //----------------------------------------------------------- |
5425 | // Internal members |
5426 | //----------------------------------------------------------- |
5427 | protected: |
5428 | // Returns the function's ILCode and SigToken |
5429 | HRESULT GetILCodeAndSigToken(); |
5430 | |
5431 | // Get the metadata token for the class to which a function belongs. |
5432 | mdTypeDef InitParentClassOfFunctionHelper(mdToken funcMetaDataToken); |
5433 | |
5434 | // Get information about one of the native code blobs for this function |
5435 | HRESULT InitNativeCodeInfo(); |
5436 | |
5437 | public: |
5438 | |
5439 | // Get the class to which a given function belongs |
5440 | HRESULT InitParentClassOfFunction(); |
5441 | |
5442 | void NotifyCodeCreated(CordbNativeCode* nativeCode); |
5443 | |
5444 | HRESULT GetSig(SigParser *pMethodSigParser, |
5445 | ULONG *pFunctionArgCount, |
5446 | BOOL *pFunctionIsStatic); |
5447 | |
5448 | HRESULT GetArgumentType(DWORD dwIndex, const Instantiation * pInst, CordbType ** ppResultType); |
5449 | |
5450 | |
5451 | //----------------------------------------------------------- |
5452 | // Internal routines |
5453 | //----------------------------------------------------------- |
5454 | |
5455 | // Get the existing IL code object |
5456 | HRESULT GetILCode(CordbILCode ** ppCode); |
5457 | |
5458 | // Finds or creates an ILCode for a given rejit request |
5459 | HRESULT LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode, |
5460 | CordbReJitILCode** ppILCode); |
5461 | |
5462 | |
5463 | #ifdef EnC_SUPPORTED |
5464 | void MakeOld(); |
5465 | #endif |
5466 | |
5467 | //----------------------------------------------------------- |
5468 | // Accessors |
5469 | //----------------------------------------------------------- |
5470 | |
5471 | // Get the AppDomain that this function lives in. |
5472 | CordbAppDomain * GetAppDomain() |
5473 | { |
5474 | return (m_pModule->GetAppDomain()); |
5475 | } |
5476 | |
5477 | // Get the CordbModule that this Function lives in. |
5478 | CordbModule * GetModule() |
5479 | { |
5480 | return m_pModule; |
5481 | } |
5482 | |
5483 | // Get the CordbClass this of which this function is a member |
5484 | CordbClass * GetClass() |
5485 | { |
5486 | return m_pClass; |
5487 | } |
5488 | |
5489 | // Get the IL code blob corresponding to this function |
5490 | CordbILCode * GetILCode() |
5491 | { |
5492 | return m_pILCode; |
5493 | } |
5494 | |
5495 | // Get metadata token for this function |
5496 | mdMethodDef GetMetadataToken() |
5497 | { |
5498 | return m_MDToken; |
5499 | } |
5500 | |
5501 | SIZE_T GetEnCVersionNumber() |
5502 | { |
5503 | return m_dwEnCVersionNumber; |
5504 | } |
5505 | |
5506 | CordbFunction * GetPrevVersion() |
5507 | { |
5508 | return m_pPrevVersion; |
5509 | } |
5510 | |
5511 | void SetPrevVersion(CordbFunction * prevVersion) |
5512 | { |
5513 | m_pPrevVersion.Assign(prevVersion); |
5514 | } |
5515 | |
5516 | typedef enum {kNativeOnly, kHasIL, kUnknownImpl} ImplementationKind; |
5517 | ImplementationKind IsNativeImpl() |
5518 | { |
5519 | return (m_fIsNativeImpl); |
5520 | } |
5521 | |
5522 | // determine whether we have a native-only implementation |
5523 | void InitNativeImpl(); |
5524 | |
5525 | |
5526 | //----------------------------------------------------------- |
5527 | // Data members |
5528 | //----------------------------------------------------------- |
5529 | |
5530 | private: |
5531 | // The module that this Function is contained in. It maintains a strong reference to this object |
5532 | // and will neuter this object. |
5533 | CordbModule * m_pModule; |
5534 | |
5535 | // The Class that this function is contained in. |
5536 | CordbClass * m_pClass; |
5537 | |
5538 | // We only have 1 IL blob associated with a given Function object. |
5539 | RSSmartPtr<CordbILCode> m_pILCode; |
5540 | |
5541 | |
5542 | // Generics allow a single IL method to be instantiated to multiple native |
5543 | // code blobs. So CordbFunction : CordbNativeCode is 1:n. |
5544 | // This pointer is to arbitrary one of those n code bodies. |
5545 | // Someday we may need to get access to all N of them but not today |
5546 | RSSmartPtr<CordbNativeCode> m_nativeCode; |
5547 | |
5548 | // Metadata Token for the IL function. Scoped to m_module. |
5549 | mdMethodDef m_MDToken; |
5550 | |
5551 | // EnC version number of this instance |
5552 | SIZE_T m_dwEnCVersionNumber; |
5553 | |
5554 | // link to previous version of this function |
5555 | RSSmartPtr<CordbFunction> m_pPrevVersion; |
5556 | |
5557 | // Is the function implemented natively in the runtime?? (eg, it has no IL, may be an Ecall/fcall) |
5558 | ImplementationKind m_fIsNativeImpl; |
5559 | |
5560 | // True if method signature (argument) values are cached. |
5561 | BOOL m_fCachedMethodValuesValid; |
5562 | |
5563 | // Cached SigParser for this Function's argument signature. |
5564 | // Only valid if m_fCachedMethodValuesValid is set. |
5565 | SigParser m_methodSigParserCached; |
5566 | |
5567 | // Cached Count of arguments in the argument signature. |
5568 | // Only valid if m_fCachedMethodValuesValid is set. |
5569 | ULONG m_argCountCached; |
5570 | |
5571 | // Cached boolean if method is static or instance (part of the argument signature). |
5572 | // Only valid if m_fCachedMethodValuesValid is set. |
5573 | BOOL m_fIsStaticCached; |
5574 | |
5575 | // A collection, indexed by VMPTR_SharedReJitInfo, of IL code for rejit requests |
5576 | // The collection is filled lazily by LookupOrCreateReJitILCode |
5577 | CordbSafeHashTable<CordbReJitILCode> m_reJitILCodes; |
5578 | }; |
5579 | |
5580 | //----------------------------------------------------------------------------- |
5581 | // class CordbCode |
5582 | // Represents either IL or Native code blobs associated with a function. |
5583 | // |
5584 | // See the comments at the ICorDebugCode definition for invariants about Code objects. |
5585 | // |
5586 | //----------------------------------------------------------------------------- |
5587 | class CordbCode : public CordbBase, public ICorDebugCode |
5588 | { |
5589 | protected: |
5590 | CordbCode(CordbFunction * pFunction, UINT_PTR id, SIZE_T encVersion, BOOL fIsIL); |
5591 | |
5592 | public: |
5593 | virtual ~CordbCode(); |
5594 | virtual void Neuter(); |
5595 | |
5596 | #ifdef _DEBUG |
5597 | virtual const char * DbgGetName() = 0; |
5598 | #endif |
5599 | |
5600 | |
5601 | //----------------------------------------------------------- |
5602 | // IUnknown |
5603 | //----------------------------------------------------------- |
5604 | |
5605 | ULONG STDMETHODCALLTYPE AddRef() |
5606 | { |
5607 | return (BaseAddRef()); |
5608 | } |
5609 | ULONG STDMETHODCALLTYPE Release() |
5610 | { |
5611 | return (BaseRelease()); |
5612 | } |
5613 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
5614 | |
5615 | //----------------------------------------------------------- |
5616 | // ICorDebugCode |
5617 | //----------------------------------------------------------- |
5618 | |
5619 | COM_METHOD IsIL(BOOL * pbIL); |
5620 | COM_METHOD GetFunction(ICorDebugFunction ** ppFunction); |
5621 | COM_METHOD GetAddress(CORDB_ADDRESS * pStart) = 0; |
5622 | COM_METHOD GetSize(ULONG32 * pcBytes); |
5623 | COM_METHOD CreateBreakpoint(ULONG32 offset, |
5624 | ICorDebugFunctionBreakpoint ** ppBreakpoint); |
5625 | COM_METHOD GetCode(ULONG32 startOffset, ULONG32 endOffset, |
5626 | ULONG32 cBufferAlloc, |
5627 | BYTE buffer[], |
5628 | ULONG32 * pcBufferSize); |
5629 | COM_METHOD GetVersionNumber( ULONG32 * nVersion); |
5630 | COM_METHOD GetILToNativeMapping(ULONG32 cMap, |
5631 | ULONG32 * pcMap, |
5632 | COR_DEBUG_IL_TO_NATIVE_MAP map[]) = 0; |
5633 | COM_METHOD GetEnCRemapSequencePoints(ULONG32 cMap, |
5634 | ULONG32 * pcMap, |
5635 | ULONG32 offsets[]); |
5636 | |
5637 | //----------------------------------------------------------- |
5638 | // Accessors and convenience routines |
5639 | //----------------------------------------------------------- |
5640 | |
5641 | // get the CordbFunction instance for this code object |
5642 | CordbFunction * GetFunction(); |
5643 | |
5644 | // get the actual code bytes for this function |
5645 | virtual HRESULT ReadCodeBytes() = 0; |
5646 | |
5647 | // get the size in bytes of this function |
5648 | virtual ULONG32 GetSize() = 0; |
5649 | |
5650 | |
5651 | // get the metadata token for this code object |
5652 | mdMethodDef GetMetadataToken() |
5653 | { |
5654 | _ASSERTE(m_pFunction != NULL); |
5655 | return (m_pFunction->GetMetadataToken()); |
5656 | } |
5657 | |
5658 | // get the module this code object belongs to |
5659 | CordbModule * GetModule() |
5660 | { |
5661 | _ASSERTE(m_pFunction != NULL); |
5662 | return (m_pFunction->GetModule()); |
5663 | } |
5664 | |
5665 | // get the function signature for this code blob or throw on failure |
5666 | void GetSig(SigParser *pMethodSigParser, |
5667 | ULONG *pFunctionArgCount, |
5668 | BOOL *pFunctionIsStatic) |
5669 | { |
5670 | _ASSERTE(m_pFunction != NULL); |
5671 | IfFailThrow(m_pFunction->GetSig(pMethodSigParser, pFunctionArgCount, pFunctionIsStatic)); |
5672 | } |
5673 | |
5674 | // get the class to which this code blob belongs |
5675 | CordbClass * GetClass() |
5676 | { |
5677 | _ASSERTE(m_pFunction != NULL); |
5678 | return (m_pFunction->GetClass()); |
5679 | } |
5680 | |
5681 | // Quick helper to get the AppDomain that this code object lives in. |
5682 | CordbAppDomain *GetAppDomain() |
5683 | { |
5684 | _ASSERTE(m_pFunction != NULL); |
5685 | return (m_pFunction->GetAppDomain()); |
5686 | } |
5687 | |
5688 | // Get the EnC version of this blob |
5689 | SIZE_T GetVersion() { return m_nVersion; }; |
5690 | |
5691 | // Return true if this is an IL code blob. Else return false. |
5692 | BOOL IsIL() { return m_fIsIL; } |
5693 | |
5694 | // convert to CordbNativeCode as long as m_fIsIl is false. |
5695 | CordbNativeCode * AsNativeCode() |
5696 | { |
5697 | _ASSERTE(m_fIsIL == FALSE); |
5698 | return reinterpret_cast<CordbNativeCode *>(this); |
5699 | } |
5700 | |
5701 | // convert to CordbILCode as long as m_fIsIl is true. |
5702 | CordbILCode * AsILCode() |
5703 | { |
5704 | _ASSERTE(m_fIsIL == TRUE); |
5705 | return reinterpret_cast<CordbILCode *>(this); |
5706 | } |
5707 | |
5708 | //----------------------------------------------------------- |
5709 | // Data members |
5710 | //----------------------------------------------------------- |
5711 | |
5712 | private: |
5713 | UINT m_fIsIL : 1; |
5714 | |
5715 | // EnC version number. |
5716 | SIZE_T m_nVersion; |
5717 | |
5718 | protected: |
5719 | // Our local copy of the code. It will be GetSize() bytes long. |
5720 | BYTE * m_rgbCode; // will be NULL if we can't fit it into memory |
5721 | |
5722 | UINT m_continueCounterLastSync; |
5723 | |
5724 | // Owning Function associated with this code. |
5725 | CordbFunction * m_pFunction; |
5726 | }; //class CordbCode |
5727 | |
5728 | |
5729 | |
5730 | |
5731 | |
5732 | /* ------------------------------------------------------------------------- * |
5733 | * CordbILCode class |
5734 | * This class represents an IL code blob for a particular EnC version. Thus it is |
5735 | * 1:1 with a given instantiation of CordbFunction. Provided functionality includes |
5736 | * methods to get the starting address and size of an IL code blob and to read |
5737 | * the actual bytes of IL into a buffer. |
5738 | * ------------------------------------------------------------------------- */ |
5739 | |
5740 | class CordbILCode : public CordbCode |
5741 | { |
5742 | public: |
5743 | // Initialize a new CordbILCode instance |
5744 | CordbILCode(CordbFunction *pFunction, TargetBuffer codeRegionInfo, SIZE_T nVersion, mdSignature localVarSigToken, UINT_PTR id = 0); |
5745 | |
5746 | #ifdef _DEBUG |
5747 | const char * DbgGetName() { return "CordbILCode" ; }; |
5748 | #endif // _DEBUG |
5749 | |
5750 | COM_METHOD GetAddress(CORDB_ADDRESS * pStart); |
5751 | COM_METHOD GetILToNativeMapping(ULONG32 cMap, |
5752 | ULONG32 * pcMap, |
5753 | COR_DEBUG_IL_TO_NATIVE_MAP map[]); |
5754 | // Quick helper for internal access to: GetAddress(CORDB_ADDRESS *pStart); |
5755 | CORDB_ADDRESS GetAddress() { return m_codeRegionInfo.pAddress; } |
5756 | |
5757 | // get total size of the IL code |
5758 | ULONG32 GetSize() { return m_codeRegionInfo.cbSize; } |
5759 | |
5760 | #ifdef EnC_SUPPORTED |
5761 | void MakeOld(); |
5762 | #endif // EnC_SUPPORTED |
5763 | |
5764 | HRESULT GetLocalVarSig(SigParser *pLocalsSigParser, ULONG *pLocalVarCount); |
5765 | HRESULT GetLocalVariableType(DWORD dwIndex, const Instantiation * pInst, CordbType ** ppResultType); |
5766 | mdSignature GetLocalVarSigToken(); |
5767 | |
5768 | COM_METHOD CreateNativeBreakpoint(ICorDebugFunctionBreakpoint **ppBreakpoint); |
5769 | |
5770 | private: |
5771 | // Read the actual bytes of IL code into the data member m_rgbCode. |
5772 | // Helper routine for GetCode |
5773 | HRESULT ReadCodeBytes(); |
5774 | |
5775 | //----------------------------------------------------------- |
5776 | // Data members |
5777 | //----------------------------------------------------------- |
5778 | |
5779 | private: |
5780 | #ifdef EnC_SUPPORTED |
5781 | UINT m_fIsOld : 1; // marks this instance as an old EnC version |
5782 | bool m_encBreakpointsApplied; |
5783 | #endif |
5784 | |
5785 | // derived types can init this |
5786 | protected: |
5787 | TargetBuffer m_codeRegionInfo; // stores the starting address and size of the |
5788 | // IL code blob |
5789 | |
5790 | // Metadata token for local's signature. |
5791 | mdSignature m_localVarSigToken; |
5792 | |
5793 | }; // class CordbILCode |
5794 | |
5795 | /* ------------------------------------------------------------------------- * |
5796 | * CordbReJitILCode class |
5797 | * This class represents an IL code blob for a particular EnC version and |
5798 | * rejitID. Thus it is 1:N with a given instantiation of CordbFunction. |
5799 | * ------------------------------------------------------------------------- */ |
5800 | |
5801 | class CordbReJitILCode : public CordbILCode, |
5802 | public ICorDebugILCode, |
5803 | public ICorDebugILCode2 |
5804 | { |
5805 | public: |
5806 | // Initialize a new CordbILCode instance |
5807 | CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_ILCodeVersionNode vmILCodeVersionNode); |
5808 | |
5809 | //----------------------------------------------------------- |
5810 | // IUnknown |
5811 | //----------------------------------------------------------- |
5812 | ULONG STDMETHODCALLTYPE AddRef(); |
5813 | ULONG STDMETHODCALLTYPE Release(); |
5814 | COM_METHOD QueryInterface(REFIID riid, void** ppInterface); |
5815 | |
5816 | |
5817 | //----------------------------------------------------------- |
5818 | // ICorDebugILCode |
5819 | //----------------------------------------------------------- |
5820 | COM_METHOD GetEHClauses(ULONG32 cClauses, ULONG32 * pcClauses, CorDebugEHClause clauses[]); |
5821 | |
5822 | |
5823 | //----------------------------------------------------------- |
5824 | // ICorDebugILCode2 |
5825 | //----------------------------------------------------------- |
5826 | COM_METHOD GetLocalVarSigToken(mdSignature *pmdSig); |
5827 | COM_METHOD GetInstrumentedILMap(ULONG32 cMap, ULONG32 *pcMap, COR_IL_MAP map[]); |
5828 | |
5829 | private: |
5830 | HRESULT Init(DacSharedReJitInfo* pSharedReJitInfo); |
5831 | |
5832 | private: |
5833 | ULONG32 m_cClauses; |
5834 | NewArrayHolder<CorDebugEHClause> m_pClauses; |
5835 | ULONG32 m_cbLocalIL; |
5836 | NewArrayHolder<BYTE> m_pLocalIL; |
5837 | ULONG32 m_cILMap; |
5838 | NewArrayHolder<COR_IL_MAP> m_pILMap; |
5839 | }; |
5840 | |
5841 | /* ------------------------------------------------------------------------- * |
5842 | * CordbNativeCode class. These correspond to MethodDesc's on the left-side. |
5843 | * There may or may not be a DebuggerJitInfo associated with the MethodDesc. |
5844 | * At most one CordbNativeCode is created for each native code compilation of each method |
5845 | * that is seen by the right-side. Note that if each method were JITted only once |
5846 | * then this information could go in CordbFunction, however generics allow |
5847 | * methods to be compiled more than once. |
5848 | * |
5849 | * The purpose of this class is to encapsulate details about a blob of jitted/ngen'ed |
5850 | * code, including an optional set of mappings from IL to offsets in the native Code. |
5851 | * ------------------------------------------------------------------------- */ |
5852 | |
5853 | class CordbNativeCode : public CordbCode, |
5854 | public ICorDebugCode2, |
5855 | public ICorDebugCode3, |
5856 | public ICorDebugCode4 |
5857 | { |
5858 | public: |
5859 | CordbNativeCode(CordbFunction * pFunction, |
5860 | const NativeCodeFunctionData * pJitData, |
5861 | BOOL fIsInstantiatedGeneric); |
5862 | #ifdef _DEBUG |
5863 | const char * DbgGetName() { return "CordbNativeCode" ; }; |
5864 | #endif // _DEBUG |
5865 | |
5866 | ULONG STDMETHODCALLTYPE AddRef() |
5867 | { |
5868 | return (BaseAddRef()); |
5869 | } |
5870 | ULONG STDMETHODCALLTYPE Release() |
5871 | { |
5872 | return (BaseRelease()); |
5873 | } |
5874 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
5875 | |
5876 | //----------------------------------------------------------- |
5877 | // ICorDebugCode |
5878 | //----------------------------------------------------------- |
5879 | COM_METHOD GetAddress(CORDB_ADDRESS * pStart); |
5880 | COM_METHOD GetILToNativeMapping(ULONG32 cMap, |
5881 | ULONG32 * pcMap, |
5882 | COR_DEBUG_IL_TO_NATIVE_MAP map[]); |
5883 | //----------------------------------------------------------- |
5884 | // ICorDebugCode2 |
5885 | //----------------------------------------------------------- |
5886 | COM_METHOD GetCodeChunks(ULONG32 cbufSize, ULONG32 * pcnumChunks, CodeChunkInfo chunks[]); |
5887 | |
5888 | COM_METHOD GetCompilerFlags(DWORD * pdwFlags); |
5889 | |
5890 | //----------------------------------------------------------- |
5891 | // ICorDebugCode3 |
5892 | //----------------------------------------------------------- |
5893 | COM_METHOD GetReturnValueLiveOffset(ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets); |
5894 | |
5895 | |
5896 | //----------------------------------------------------------- |
5897 | // ICorDebugCode4 |
5898 | //----------------------------------------------------------- |
5899 | COM_METHOD EnumerateVariableHomes(ICorDebugVariableHomeEnum **ppEnum); |
5900 | |
5901 | //----------------------------------------------------------- |
5902 | // Internal members |
5903 | //----------------------------------------------------------- |
5904 | |
5905 | HRESULT ILVariableToNative(DWORD dwIndex, |
5906 | SIZE_T ip, |
5907 | const ICorDebugInfo::NativeVarInfo ** ppNativeInfo); |
5908 | void LoadNativeInfo(); |
5909 | |
5910 | //----------------------------------------------------------- |
5911 | // Accessors and convenience routines |
5912 | //----------------------------------------------------------- |
5913 | |
5914 | // get the argument type for a generic |
5915 | void GetArgumentType(DWORD dwIndex, |
5916 | const Instantiation * pInst, |
5917 | CordbType ** ppResultType) |
5918 | { |
5919 | CordbFunction * pFunction = GetFunction(); |
5920 | _ASSERTE(pFunction != NULL); |
5921 | IfFailThrow(pFunction->GetArgumentType(dwIndex, pInst, ppResultType)); |
5922 | } |
5923 | |
5924 | // Quick helper for internall access to: GetAddress(CORDB_ADDRESS *pStart); |
5925 | CORDB_ADDRESS GetAddress() { return m_rgCodeRegions[kHot].pAddress; }; |
5926 | |
5927 | VMPTR_MethodDesc GetVMNativeCodeMethodDescToken() { return m_vmNativeCodeMethodDescToken; }; |
5928 | |
5929 | // Worker function for GetReturnValueLiveOffset. |
5930 | HRESULT GetReturnValueLiveOffsetImpl(Instantiation *currentInstantiation, ULONG32 ILoffset, ULONG32 bufferSize, ULONG32 *pFetched, ULONG32 *pOffsets); |
5931 | |
5932 | // get total size of the code including both hot and cold regions |
5933 | ULONG32 GetSize(); |
5934 | |
5935 | // get the size of the cold region(s) only |
5936 | ULONG32 GetColdSize(); |
5937 | |
5938 | // Return true if the Code is split into hot + cold regions. |
5939 | bool HasColdRegion() { return m_rgCodeRegions[kCold].pAddress != NULL; } |
5940 | |
5941 | // Get the number of fixed arguments for this function (the "this" |
5942 | // but not varargs) |
5943 | unsigned int GetFixedArgCount() |
5944 | { |
5945 | return m_nativeVarData.GetFixedArgCount(); |
5946 | } |
5947 | |
5948 | // Get the number of all arguments for this function |
5949 | // ("this" pointer, fixed args and varargs) |
5950 | ULONG32 GetAllArgsCount() |
5951 | { |
5952 | return m_nativeVarData.GetAllArgsCount(); |
5953 | } |
5954 | |
5955 | void SetAllArgsCount(ULONG32 count) |
5956 | { |
5957 | m_nativeVarData.SetAllArgsCount(count); |
5958 | } |
5959 | |
5960 | // Determine whether this is an instantiation of a generic function |
5961 | BOOL IsInstantiatedGeneric() |
5962 | { |
5963 | return m_fIsInstantiatedGeneric != 0; |
5964 | } |
5965 | |
5966 | // Determine whether we have initialized the native variable and |
5967 | // sequence point offsets |
5968 | BOOL IsNativeCodeValid () |
5969 | { |
5970 | return ((m_nativeVarData.IsInitialized() != 0) && |
5971 | (m_sequencePoints.IsInitialized() != 0)); |
5972 | } |
5973 | |
5974 | SequencePoints * GetSequencePoints() |
5975 | { |
5976 | return &m_sequencePoints; |
5977 | } |
5978 | |
5979 | |
5980 | // Given an ILOffset in the current function, return the class token and function token of the IL call target at that |
5981 | // location. Also fill "methodSig" with the method's signature and "genericSig" with the method's generic signature. |
5982 | HRESULT GetCallSignature(ULONG32 ILOffset, mdToken *pClass, mdToken *pMDFunction, SigParser &methodSig, SigParser &genericSig); |
5983 | |
5984 | // Moves a method signature from the start of the signature to the location of the return value (passing out the |
5985 | // number of generic parameters in the method). |
5986 | static HRESULT SkipToReturn(SigParser &parser, ULONG *genArgCount = 0); |
5987 | |
5988 | private: |
5989 | // Read the actual bytes of native code into the data member m_rgbCode. |
5990 | // Helper routine for GetCode |
5991 | HRESULT ReadCodeBytes(); |
5992 | |
5993 | // Returns a failure HRESULT if we cannot handle the return value of the given |
5994 | // methodref, methoddef, or methodspec token, otherwise S_OK. Does NOT return S_FALSE; |
5995 | HRESULT EnsureReturnValueAllowed(Instantiation *currentInstantiation, mdToken targetClass, SigParser &parser, SigParser &methodGenerics); |
5996 | HRESULT EnsureReturnValueAllowedWorker(Instantiation *currentInstantiation, mdToken targetClass, SigParser &parser, SigParser &methodGenerics, ULONG genCount); |
5997 | |
5998 | // Grabs the appropriate signature parser for a methodref, methoddef, methodspec. |
5999 | HRESULT GetSigParserFromFunction(mdToken mdFunction, mdToken *pClass, SigParser &methodSig, SigParser &genericSig); |
6000 | |
6001 | int GetCallInstructionLength(BYTE *buffer, ULONG32 len); |
6002 | |
6003 | //----------------------------------------------------------- |
6004 | // Data members |
6005 | //----------------------------------------------------------- |
6006 | private: |
6007 | // offset of the beginning of the last sequence point in the sequence point map |
6008 | SIZE_T m_lastIL; |
6009 | |
6010 | // start address(es) and size(s) of hot and cold regions |
6011 | TargetBuffer m_rgCodeRegions[MAX_REGIONS]; |
6012 | |
6013 | // LS data structure--method desc for this instantiation. |
6014 | VMPTR_MethodDesc m_vmNativeCodeMethodDescToken; |
6015 | |
6016 | bool m_fCodeAvailable; // true iff the code has been jitted but not pitched |
6017 | |
6018 | bool m_fIsInstantiatedGeneric; // true iff this is an instantiated generic |
6019 | |
6020 | // information in the following two classes tracks native offsets and is initialized on demand. |
6021 | |
6022 | // location and ID information for local variables. See code:NativeVarData for details. |
6023 | NativeVarData m_nativeVarData; |
6024 | |
6025 | // mapping between IL and native code sequence points. |
6026 | SequencePoints m_sequencePoints; |
6027 | |
6028 | }; //class CordbNativeCode |
6029 | |
6030 | //--------------------------------------------------------------------------------------- |
6031 | // |
6032 | // GetActiveInternalFramesData is used to enumerate internal frames on a specific thread. |
6033 | // It is used in conjunction with code:CordbThread::GetActiveInternalFramesCallback. |
6034 | // We store each internal frame in ppInternalFrames as we enumerate them. |
6035 | // |
6036 | |
6037 | struct GetActiveInternalFramesData |
6038 | { |
6039 | public: |
6040 | // the thread we are walking |
6041 | CordbThread * pThis; |
6042 | |
6043 | // an array to store the internal frames |
6044 | RSPtrArray<CordbInternalFrame> pInternalFrames; |
6045 | |
6046 | // next element in the array to be filled |
6047 | ULONG32 uIndex; |
6048 | }; |
6049 | |
6050 | |
6051 | /* ------------------------------------------------------------------------- * |
6052 | * Thread classes |
6053 | * ------------------------------------------------------------------------- */ |
6054 | |
6055 | class CordbThread : public CordbBase, public ICorDebugThread, |
6056 | public ICorDebugThread2, |
6057 | public ICorDebugThread3, |
6058 | public ICorDebugThread4 |
6059 | { |
6060 | public: |
6061 | CordbThread(CordbProcess * pProcess, VMPTR_Thread); |
6062 | |
6063 | virtual ~CordbThread(); |
6064 | virtual void Neuter(); |
6065 | |
6066 | using CordbBase::GetProcess; |
6067 | |
6068 | #ifdef _DEBUG |
6069 | virtual const char * DbgGetName() { return "CordbThread" ; } |
6070 | #endif |
6071 | |
6072 | //----------------------------------------------------------- |
6073 | // IUnknown |
6074 | //----------------------------------------------------------- |
6075 | |
6076 | ULONG STDMETHODCALLTYPE AddRef() |
6077 | { |
6078 | // there's an external add ref from within RS in CordbEnumFilter |
6079 | return (BaseAddRef()); |
6080 | } |
6081 | ULONG STDMETHODCALLTYPE Release() |
6082 | { |
6083 | return (BaseRelease()); |
6084 | } |
6085 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6086 | |
6087 | //----------------------------------------------------------- |
6088 | // ICorDebugThread |
6089 | //----------------------------------------------------------- |
6090 | |
6091 | COM_METHOD GetProcess(ICorDebugProcess **ppProcess); |
6092 | COM_METHOD GetID(DWORD *pdwThreadId); |
6093 | COM_METHOD GetHandle(HANDLE * phThreadHandle); |
6094 | COM_METHOD GetAppDomain(ICorDebugAppDomain **ppAppDomain); |
6095 | COM_METHOD SetDebugState(CorDebugThreadState state); |
6096 | COM_METHOD GetDebugState(CorDebugThreadState *pState); |
6097 | COM_METHOD GetUserState(CorDebugUserState *pState); |
6098 | COM_METHOD GetCurrentException(ICorDebugValue ** ppExceptionObject); |
6099 | COM_METHOD ClearCurrentException(); |
6100 | COM_METHOD CreateStepper(ICorDebugStepper **ppStepper); |
6101 | COM_METHOD EnumerateChains(ICorDebugChainEnum **ppChains); |
6102 | COM_METHOD GetActiveChain(ICorDebugChain **ppChain); |
6103 | COM_METHOD GetActiveFrame(ICorDebugFrame **ppFrame); |
6104 | COM_METHOD GetRegisterSet(ICorDebugRegisterSet **ppRegisters); |
6105 | COM_METHOD CreateEval(ICorDebugEval **ppEval); |
6106 | COM_METHOD GetObject(ICorDebugValue ** ppObject); |
6107 | |
6108 | // ICorDebugThread2 |
6109 | COM_METHOD GetConnectionID(CONNID * pConnectionID); |
6110 | COM_METHOD GetTaskID(TASKID * pTaskID); |
6111 | COM_METHOD GetVolatileOSThreadID(DWORD * pdwTID); |
6112 | COM_METHOD GetActiveFunctions(ULONG32 cFunctions, ULONG32 * pcFunctions, COR_ACTIVE_FUNCTION pFunctions[]); |
6113 | // Intercept the current exception at the specified frame. pFrame must be a valid ICDFrame, possibly from |
6114 | // a previous stackwalk. |
6115 | COM_METHOD InterceptCurrentException(ICorDebugFrame * pFrame); |
6116 | |
6117 | |
6118 | |
6119 | // ICorDebugThread3 |
6120 | COM_METHOD CreateStackWalk(ICorDebugStackWalk **ppStackWalk); |
6121 | |
6122 | COM_METHOD GetActiveInternalFrames(ULONG32 cInternalFrames, |
6123 | ULONG32 * pcInternalFrames, |
6124 | ICorDebugInternalFrame2 * ppInternalFrames[]); |
6125 | |
6126 | // ICorDebugThread4 |
6127 | COM_METHOD HasUnhandledException(); |
6128 | |
6129 | COM_METHOD GetBlockingObjects(ICorDebugBlockingObjectEnum **ppBlockingObjectEnum); |
6130 | |
6131 | // Gets the current CustomNotification object from the thread or NULL if no such object exists |
6132 | COM_METHOD GetCurrentCustomDebuggerNotification(ICorDebugValue ** ppNotificationObject); |
6133 | //----------------------------------------------------------- |
6134 | // Internal members |
6135 | //----------------------------------------------------------- |
6136 | |
6137 | // callback used to enumerate the internal frames on a thread |
6138 | static void GetActiveInternalFramesCallback(const DebuggerIPCE_STRData * pFrameData, |
6139 | void * pUserData); |
6140 | |
6141 | CorDebugUserState GetUserState(); |
6142 | |
6143 | // Given a FramePointer, find the matching CordbFrame. |
6144 | HRESULT FindFrame(ICorDebugFrame ** ppFrame, FramePointer fp); |
6145 | |
6146 | // Get the task ID for this thread. |
6147 | TASKID GetTaskID(); |
6148 | |
6149 | void RefreshStack(); |
6150 | void CleanupStack(); |
6151 | void MarkStackFramesDirty(); |
6152 | |
6153 | |
6154 | #if defined(DBG_TARGET_X86) |
6155 | // Converts the values in the floating point register area of the context to real number values. |
6156 | void Get32bitFPRegisters(CONTEXT * pContext); |
6157 | |
6158 | #elif defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_ARM64) || defined(DBG_TARGET_ARM) |
6159 | // Converts the values in the floating point register area of the context to real number values. |
6160 | void Get64bitFPRegisters(FPRegister64 * rgContextFPRegisters, int start, int nRegisters); |
6161 | |
6162 | #endif // DBG_TARGET_X86 |
6163 | |
6164 | // Initializes the float state members of this instance of CordbThread. This function gets the context and |
6165 | // converts the floating point values from their context representation to real number values. |
6166 | void LoadFloatState(); |
6167 | |
6168 | |
6169 | HRESULT SetIP( bool fCanSetIPOnly, |
6170 | CordbNativeCode * pNativeCode, |
6171 | SIZE_T offset, |
6172 | bool fIsIL ); |
6173 | |
6174 | // Tells the LS to remap to the latest version of the function |
6175 | HRESULT SetRemapIP(SIZE_T offset); |
6176 | |
6177 | // Ask the left-side for the current (up-to-date) AppDomain of this thread's IP. |
6178 | // This should be preferred over using the cached value from GetAppDomain. |
6179 | HRESULT GetCurrentAppDomain(CordbAppDomain ** ppAppDomain); |
6180 | |
6181 | //----------------------------------------------------------- |
6182 | // Convenience routines |
6183 | //----------------------------------------------------------- |
6184 | |
6185 | // The last domain from which a debug event for this thread was sent. |
6186 | // This usually (but not always) the domain the thread is currently executing in. |
6187 | // Since this is a cache, it may sometimes be out-of-date. I believe all current |
6188 | // usage of this is OK (we pass AppDomains around a lot without really using them), |
6189 | // but no new code should rely on this value. |
6190 | // TODO: eliminate this and the m_pAppDomain field entirely |
6191 | CordbAppDomain *GetAppDomain() |
6192 | { |
6193 | return (m_pAppDomain); |
6194 | } |
6195 | |
6196 | DWORD GetVolatileOSThreadID(); |
6197 | |
6198 | ////////////////////////////////////////////////////////////////////////// |
6199 | // |
6200 | // Get Context |
6201 | // |
6202 | // <TODO>TODO: Since Thread will share the memory with RegisterSets, how |
6203 | // do we know that the RegisterSets have relinquished all pointers |
6204 | // to the m_pContext structure?</TODO> |
6205 | // |
6206 | // Returns: NULL if the thread's CONTEXT structure couldn't be obtained |
6207 | // A pointer to the CONTEXT otherwise. |
6208 | // |
6209 | // |
6210 | ////////////////////////////////////////////////////////////////////////// |
6211 | HRESULT GetManagedContext( DT_CONTEXT ** ppContext ); |
6212 | HRESULT SetManagedContext( DT_CONTEXT * pContext ); |
6213 | |
6214 | // API to retrieve the thread handle from the LS. |
6215 | void InternalGetHandle(HANDLE * phThread); |
6216 | void RefreshHandle(HANDLE * phThread); |
6217 | |
6218 | // NeuterList that's executed when this Thread's stack is refreshed. |
6219 | // Chain + Frame + some Value enums can be held on this. |
6220 | NeuterList * GetRefreshStackNeuterList() |
6221 | { |
6222 | return &m_RefreshStackNeuterList; |
6223 | } |
6224 | |
6225 | DWORD GetUniqueId(); |
6226 | |
6227 | |
6228 | // Hijack a thread at a 2nd-chance exception so that it can execute the CLR's UEF |
6229 | void HijackForUnhandledException(); |
6230 | |
6231 | // check whether the specified frame lives on the stack of the current thread |
6232 | bool OwnsFrame(CordbFrame *pFrame); |
6233 | |
6234 | // Specify that there's an outstanding exception on this thread. |
6235 | void SetExInfo(VMPTR_OBJECTHANDLE vmExcepObjHandle); |
6236 | |
6237 | VMPTR_OBJECTHANDLE GetThreadExceptionRawObjectHandle() { return m_vmExcepObjHandle; } |
6238 | bool HasException() { return m_fException; } |
6239 | |
6240 | void SetUnhandledNativeException(const EXCEPTION_RECORD * pExceptionRecord); |
6241 | bool HasUnhandledNativeException(); |
6242 | |
6243 | #ifdef _DEBUG |
6244 | // Helper to assert that this thread no longer appears in dac-dbi enumerations |
6245 | void DbgAssertThreadDeleted(); |
6246 | |
6247 | // Callback for DbgAssertThreadDeleted |
6248 | static void DbgAssertThreadDeletedCallback(VMPTR_Thread vmThread, void * pUserData); |
6249 | #endif // _DEBUG |
6250 | |
6251 | // Determine if the thread's current exception is managed or unmanaged. |
6252 | BOOL IsThreadExceptionManaged(); |
6253 | |
6254 | // This is a private hook for the shim to create a CordbRegisterSet for a ShimChain. |
6255 | void CreateCordbRegisterSet(DT_CONTEXT * pContext, |
6256 | BOOL fActive, |
6257 | CorDebugChainReason reason, |
6258 | ICorDebugRegisterSet ** ppRegSet); |
6259 | |
6260 | // This is a private hook for the shim to convert an ICDFrame into an ICDInternalFrame for a dynamic |
6261 | // method. Refer to the function header for more information. |
6262 | BOOL ConvertFrameForILMethodWithoutMetadata(ICorDebugFrame * pFrame, |
6263 | ICorDebugInternalFrame2 ** ppInternalFrame2); |
6264 | |
6265 | // Gets/sets m_fCreationEventQueued |
6266 | bool CreateEventWasQueued(); |
6267 | void SetCreateEventQueued(); |
6268 | |
6269 | //----------------------------------------------------------- |
6270 | // Data members |
6271 | //----------------------------------------------------------- |
6272 | |
6273 | public: |
6274 | // RS Cache for LS context. |
6275 | // NULL if we haven't allocated memory for a Right side context |
6276 | DT_CONTEXT * m_pContext; |
6277 | |
6278 | // Set to the CONTEXT pointer in the LS if this LS thread is |
6279 | // stopped in managed code. This may be either stopped for execution control |
6280 | // (breakpoint / single-step exception) or hijacked w/ a redirected frame because |
6281 | // another thread synced the LS. |
6282 | // This context is used by the RS to set enregistered vars. |
6283 | VMPTR_CONTEXT m_vmLeftSideContext; |
6284 | |
6285 | // indicates whether m_pContext is up-to-date |
6286 | bool m_fContextFresh; |
6287 | |
6288 | // last domain we've seen this thread. |
6289 | // If the appdomain exits, it will clear out this value. |
6290 | CordbAppDomain *m_pAppDomain; |
6291 | |
6292 | // Handle to VM's Thread* object. This is the primary key for a CordbThread object |
6293 | // @dbgtodo ICDThread - merge with m_id; |
6294 | VMPTR_Thread m_vmThreadToken; |
6295 | |
6296 | // Unique ID for this thread. See code:CordbThread::GetID for semantics of this field. |
6297 | DWORD m_dwUniqueID; |
6298 | |
6299 | CorDebugThreadState m_debugState; // Note that this is for resume |
6300 | // purposes, NOT the current state of |
6301 | // the thread. |
6302 | |
6303 | // The frames are all protected under the Stop-Go lock. |
6304 | // This field indicates whether the stack is valid (i.e. no update is necessary). |
6305 | bool m_fFramesFresh; |
6306 | |
6307 | // This is a cache of V3 ICDFrames. The cache is only used by two functions: |
6308 | // - code:CordbThread::GetActiveFunctions |
6309 | // - code:CordbThread::InterceptCurrentException. |
6310 | // |
6311 | // We don't clear the cache in CleanupStack() because we don't refresh the cache every time we stop. |
6312 | // Instead, we mark m_fFramesFresh in CleanupStack() and clear the cache only when it is used next time. |
6313 | CDynArray<CordbFrame *> m_stackFrames; |
6314 | |
6315 | bool m_fFloatStateValid; |
6316 | unsigned int m_floatStackTop; |
6317 | double m_floatValues[DebuggerIPCE_FloatCount]; |
6318 | |
6319 | private: |
6320 | // True for the window after an Exception callback, but before it's been continued. |
6321 | // We dispatch two exception events in a row (ICDManagedCallback::Exception and ICDManagedCallback2::Exception), |
6322 | // and a debugger may normally just skip the first one knowing it can stop on the 2nd once. |
6323 | // Both events will set this bit high. Be careful not to reset this bit inbetween them. |
6324 | bool m_fException; |
6325 | |
6326 | // True if a creation event has been queued for this thread |
6327 | // The event may or may not have been dispatched yet |
6328 | // Bugfix DevDiv2\DevDiv 77523 - this is only being set from ShimProcess::QueueFakeThreadAttachEventsNativeOrder |
6329 | bool m_fCreationEventQueued; |
6330 | |
6331 | // Object handle for Exception object in debuggee. |
6332 | VMPTR_OBJECTHANDLE m_vmExcepObjHandle; |
6333 | |
6334 | public: |
6335 | |
6336 | //Returns true if current user state of a thread is USER_WAIT_SLEEP_JOIN |
6337 | bool IsThreadWaitingOrSleeping(); |
6338 | |
6339 | // Returns true if the thread is dead. See function header for definition. |
6340 | bool IsThreadDead(); |
6341 | |
6342 | // Return CORDBG_E_BAD_THREAD_STATE if the thread is dead. |
6343 | HRESULT EnsureThreadIsAlive(); |
6344 | |
6345 | // On a RemapBreakpoint, the debugger will eventually call RemapFunction and |
6346 | // we need to communicate the IP back to LS. So we stash the address of where |
6347 | // to store the IP here and stuff it in on RemapFunction. |
6348 | // If we're not at an outstanding RemapOpportunity, this will be NULL |
6349 | REMOTE_PTR m_EnCRemapFunctionIP; |
6350 | |
6351 | private: |
6352 | void ClearStackFrameCache(); |
6353 | |
6354 | // True iff this thread has an unhandled exception on it. |
6355 | // Set high when Filter() gets noitifed of an unhandled exception. |
6356 | // Set Low if the thread is hijacked. |
6357 | bool m_fHasUnhandledException; |
6358 | |
6359 | // Exception record for last unhandled exception on this thread. |
6360 | // Lazily initialized. |
6361 | EXCEPTION_RECORD * m_pExceptionRecord; |
6362 | |
6363 | static const CorDebugUserState kInvalidUserState = CorDebugUserState(-1); |
6364 | CorDebugUserState m_userState; // This is the current state of the |
6365 | // thread, at the time that the |
6366 | // left side synchronized |
6367 | |
6368 | // NeuterList that's executed when this Thread's stack is refreshed. |
6369 | // This list is for everything related to stackwalking, i.e. everything which is invalidated |
6370 | // if the stack changes in any way. This list is cleared when any of the following is called: |
6371 | // 1) Continue() |
6372 | // 2) SetIP() |
6373 | // 3) RemapFunction() |
6374 | // 4) ICDProcess::SetThreadContext() |
6375 | NeuterList m_RefreshStackNeuterList; |
6376 | |
6377 | // The following two data members are used for caching thread handles. |
6378 | // @dbgtodo - Remove in V3 (can't have local handles with data-target abstraction); |
6379 | // offload to the shim to support V2 scenarios. |
6380 | HANDLE m_hCachedThread; |
6381 | HANDLE m_hCachedOutOfProcThread; |
6382 | }; |
6383 | |
6384 | /* ------------------------------------------------------------------------- * |
6385 | * StackWalk class |
6386 | * ------------------------------------------------------------------------- */ |
6387 | |
6388 | class CordbStackWalk : public CordbBase, public ICorDebugStackWalk |
6389 | { |
6390 | public: |
6391 | CordbStackWalk(CordbThread * pCordbThread); |
6392 | virtual ~CordbStackWalk(); |
6393 | virtual void Neuter(); |
6394 | |
6395 | // helper function for Neuter |
6396 | virtual void DeleteAll(); |
6397 | |
6398 | using CordbBase::GetProcess; |
6399 | |
6400 | #ifdef _DEBUG |
6401 | virtual const char * DbgGetName() { return "CordbStackWalk" ; } |
6402 | #endif |
6403 | |
6404 | //----------------------------------------------------------- |
6405 | // IUnknown |
6406 | //----------------------------------------------------------- |
6407 | |
6408 | ULONG STDMETHODCALLTYPE AddRef() |
6409 | { |
6410 | return (BaseAddRef()); |
6411 | } |
6412 | ULONG STDMETHODCALLTYPE Release() |
6413 | { |
6414 | return (BaseRelease()); |
6415 | } |
6416 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6417 | |
6418 | //----------------------------------------------------------- |
6419 | // ICorDebugStackWalk |
6420 | //----------------------------------------------------------- |
6421 | |
6422 | COM_METHOD GetContext(ULONG32 contextFlags, |
6423 | ULONG32 contextBufSize, |
6424 | ULONG32 * pContextSize, |
6425 | BYTE pbContextBuf[]); |
6426 | COM_METHOD SetContext(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]); |
6427 | COM_METHOD Next(); |
6428 | COM_METHOD GetFrame(ICorDebugFrame **ppFrame); |
6429 | |
6430 | //----------------------------------------------------------- |
6431 | // Internal members |
6432 | //----------------------------------------------------------- |
6433 | |
6434 | void SetContextWorker(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]); |
6435 | HRESULT GetFrameWorker(ICorDebugFrame **ppFrame); |
6436 | |
6437 | //----------------------------------------------------------- |
6438 | // Data members |
6439 | //----------------------------------------------------------- |
6440 | |
6441 | public: |
6442 | void Init(); |
6443 | |
6444 | private: |
6445 | // handle legacy V2 hijacking for unhandled hardware exceptions |
6446 | void CheckForLegacyHijackCase(); |
6447 | |
6448 | // refresh the data for this instance of CordbStackWalk if we have had an IPC event followed by a |
6449 | // continue since we got the information. |
6450 | void RefreshIfNeeded(); |
6451 | |
6452 | // unwind the frame and update m_context with the new context |
6453 | BOOL UnwindStackFrame(); |
6454 | |
6455 | // the thread on which this CordbStackWalk is created |
6456 | CordbThread * m_pCordbThread; |
6457 | |
6458 | // This is the same iterator used by the runtime itself. |
6459 | IDacDbiInterface::StackWalkHandle m_pSFIHandle; |
6460 | |
6461 | // buffers used for stackwalking |
6462 | DT_CONTEXT m_context; |
6463 | |
6464 | // Used to figure out if we have to refresh any reference objects |
6465 | // on the left side. We set it to CordbProcess::m_flushCounter on |
6466 | // creation and will check it against that value when we call GetFrame or Next. |
6467 | // If it doesn't match, an IPC event has occurred and the values will need to be |
6468 | // refreshed via the DAC. |
6469 | UINT m_lastSyncFlushCounter; |
6470 | |
6471 | // cached flag used for refreshing a CordbStackWalk |
6472 | CorDebugSetContextFlag m_cachedSetContextFlag; |
6473 | |
6474 | // We unwind one frame ahead of time to get the FramePointer on x86. |
6475 | // These fields are used for the bookkeeping. |
6476 | RSSmartPtr<CordbFrame> m_pCachedFrame; |
6477 | HRESULT m_cachedHR; |
6478 | bool m_fIsOneFrameAhead; |
6479 | }; |
6480 | |
6481 | |
6482 | class CordbContext : public CordbBase, public ICorDebugContext |
6483 | { |
6484 | public: |
6485 | |
6486 | CordbContext() : CordbBase(NULL, 0, enumCordbContext) {} |
6487 | |
6488 | |
6489 | |
6490 | #ifdef _DEBUG |
6491 | virtual const char * DbgGetName() { return "CordbContext" ; } |
6492 | #endif |
6493 | |
6494 | |
6495 | //----------------------------------------------------------- |
6496 | // IUnknown |
6497 | //----------------------------------------------------------- |
6498 | |
6499 | ULONG STDMETHODCALLTYPE AddRef() |
6500 | { |
6501 | return (BaseAddRef()); |
6502 | } |
6503 | ULONG STDMETHODCALLTYPE Release() |
6504 | { |
6505 | return (BaseRelease()); |
6506 | } |
6507 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6508 | |
6509 | //----------------------------------------------------------- |
6510 | // ICorDebugContext |
6511 | //----------------------------------------------------------- |
6512 | private: |
6513 | |
6514 | } ; |
6515 | |
6516 | |
6517 | /* ------------------------------------------------------------------------- * |
6518 | * Frame class |
6519 | * ------------------------------------------------------------------------- */ |
6520 | |
6521 | class CordbFrame : public CordbBase, public ICorDebugFrame |
6522 | { |
6523 | protected: |
6524 | // Ctor to provide dummy frame that just wraps a frame-pointer |
6525 | CordbFrame(CordbProcess * pProcess, FramePointer fp); |
6526 | |
6527 | public: |
6528 | CordbFrame(CordbThread * pThread, |
6529 | FramePointer fp, |
6530 | SIZE_T ip, |
6531 | CordbAppDomain * pCurrentAppDomain); |
6532 | |
6533 | virtual ~CordbFrame(); |
6534 | virtual void Neuter(); |
6535 | |
6536 | #ifdef _DEBUG |
6537 | virtual const char * DbgGetName() { return "CordbFrame" ; } |
6538 | #endif |
6539 | |
6540 | //----------------------------------------------------------- |
6541 | // IUnknown |
6542 | //----------------------------------------------------------- |
6543 | |
6544 | ULONG STDMETHODCALLTYPE AddRef() |
6545 | { |
6546 | return (BaseAddRef()); |
6547 | } |
6548 | ULONG STDMETHODCALLTYPE Release() |
6549 | { |
6550 | return (BaseRelease()); |
6551 | } |
6552 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6553 | |
6554 | //----------------------------------------------------------- |
6555 | // ICorDebugFrame |
6556 | //----------------------------------------------------------- |
6557 | |
6558 | COM_METHOD GetChain(ICorDebugChain **ppChain); |
6559 | |
6560 | // Derived versions of Frame will implement GetCode. |
6561 | COM_METHOD GetCode(ICorDebugCode **ppCode) = 0; |
6562 | |
6563 | COM_METHOD GetFunction(ICorDebugFunction **ppFunction); |
6564 | COM_METHOD GetFunctionToken(mdMethodDef *pToken); |
6565 | |
6566 | COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd); |
6567 | COM_METHOD GetCaller(ICorDebugFrame **ppFrame); |
6568 | COM_METHOD GetCallee(ICorDebugFrame **ppFrame); |
6569 | COM_METHOD CreateStepper(ICorDebugStepper **ppStepper); |
6570 | |
6571 | //----------------------------------------------------------- |
6572 | // Convenience routines |
6573 | //----------------------------------------------------------- |
6574 | |
6575 | CordbAppDomain *GetCurrentAppDomain() |
6576 | { |
6577 | return m_currentAppDomain; |
6578 | } |
6579 | |
6580 | // Internal helper to get a CordbFunction for this frame. |
6581 | virtual CordbFunction *GetFunction() = 0; |
6582 | |
6583 | FramePointer GetFramePointer() |
6584 | { |
6585 | return m_fp; |
6586 | } |
6587 | |
6588 | //----------------------------------------------------------- |
6589 | // Data members |
6590 | //----------------------------------------------------------- |
6591 | |
6592 | // Accessors to return NULL or typesafe cast to derived frame |
6593 | virtual CordbInternalFrame * GetAsInternalFrame() { return NULL; } |
6594 | virtual CordbNativeFrame * GetAsNativeFrame() { return NULL; } |
6595 | |
6596 | // determine if the frame pointer is in the stack range owned by the frame |
6597 | bool IsContainedInFrame(FramePointer fp); |
6598 | |
6599 | // This is basically a complicated cast function. We are casting from an ICorDebugFrame to a CordbFrame. |
6600 | static CordbFrame* GetCordbFrameFromInterface(ICorDebugFrame *pFrame); |
6601 | |
6602 | virtual const DT_CONTEXT * GetContext() const { return NULL; } |
6603 | |
6604 | public: |
6605 | // this represents the IL offset for a CordbJITILFrame, the native offset for a CordbNativeFrame, |
6606 | // and 0 for a CordbInternalFrame |
6607 | SIZE_T m_ip; |
6608 | |
6609 | CordbThread * m_pThread; |
6610 | |
6611 | CordbAppDomain *m_currentAppDomain; |
6612 | FramePointer m_fp; |
6613 | |
6614 | protected: |
6615 | // indicates whether this frame is the leaf frame; lazily initialized |
6616 | mutable Optional<bool> m_optfIsLeafFrame; |
6617 | |
6618 | private: |
6619 | #ifdef _DEBUG |
6620 | // For tracking down neutering bugs; |
6621 | UINT m_DbgContinueCounter; |
6622 | #endif |
6623 | }; |
6624 | |
6625 | // Dummy frame that just wraps a frame pointer. |
6626 | // This is used to pass a FramePointer back in the Exception2 callback. |
6627 | // Currently, the callback passes back an ICorDebugFrame as a way of exposing a cross-platform |
6628 | // frame pointer. However passing back an ICDFrame means we need to do a stackwalk, and |
6629 | // that may not be possible in V3: |
6630 | // - the stackwalk is very chatty, and may be too much work just to give an exception notification. |
6631 | // - in 64-bit, we may not even be able to do the stackwalk ourselves. |
6632 | // |
6633 | // The shim can take the framePointer and do the stackwalk and resolve it to a real frame, |
6634 | // so V2 emulation scenarios will continue to work. |
6635 | // @dbgtodo exception - resolve this when we iron out exceptions in V3. |
6636 | class CordbPlaceholderFrame : public CordbFrame |
6637 | { |
6638 | public: |
6639 | // Ctor to provide dummy frame that just wraps a frame-pointer |
6640 | CordbPlaceholderFrame(CordbProcess * pProcess, FramePointer fp) |
6641 | : CordbFrame(pProcess, fp) |
6642 | { |
6643 | } |
6644 | |
6645 | #ifdef _DEBUG |
6646 | virtual const char * DbgGetName() { return "CordbFrame" ; } |
6647 | #endif |
6648 | |
6649 | // Provide dummy implementation for some methods. These should never be called. |
6650 | COM_METHOD GetCode(ICorDebugCode **ppCode) |
6651 | { |
6652 | _ASSERTE(!"Don't call this" ); |
6653 | return E_NOTIMPL; |
6654 | } |
6655 | virtual CordbFunction *GetFunction() |
6656 | { |
6657 | _ASSERTE(!"Don't call this" ); |
6658 | return NULL; |
6659 | } |
6660 | }; |
6661 | |
6662 | class CordbInternalFrame : public CordbFrame, public ICorDebugInternalFrame, public ICorDebugInternalFrame2 |
6663 | { |
6664 | public: |
6665 | CordbInternalFrame(CordbThread * pThread, |
6666 | FramePointer fp, |
6667 | CordbAppDomain * pCurrentAppDomain, |
6668 | const DebuggerIPCE_STRData * pData); |
6669 | |
6670 | CordbInternalFrame(CordbThread * pThread, |
6671 | FramePointer fp, |
6672 | CordbAppDomain * pCurrentAppDomain, |
6673 | CorDebugInternalFrameType frameType, |
6674 | mdMethodDef funcMetadataToken, |
6675 | CordbFunction * pFunction, |
6676 | VMPTR_MethodDesc vmMethodDesc); |
6677 | |
6678 | virtual void Neuter(); |
6679 | |
6680 | #ifdef _DEBUG |
6681 | virtual const char * DbgGetName() { return "CordbInternalFrame" ; } |
6682 | #endif |
6683 | |
6684 | //----------------------------------------------------------- |
6685 | // IUnknown |
6686 | //----------------------------------------------------------- |
6687 | |
6688 | ULONG STDMETHODCALLTYPE AddRef() |
6689 | { |
6690 | return (BaseAddRef()); |
6691 | } |
6692 | ULONG STDMETHODCALLTYPE Release() |
6693 | { |
6694 | return (BaseRelease()); |
6695 | } |
6696 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6697 | |
6698 | //----------------------------------------------------------- |
6699 | // ICorDebugFrame |
6700 | //----------------------------------------------------------- |
6701 | |
6702 | COM_METHOD GetChain(ICorDebugChain **ppChain) |
6703 | { |
6704 | return (CordbFrame::GetChain(ppChain)); |
6705 | } |
6706 | |
6707 | // We don't expose a code-object for stubs. |
6708 | COM_METHOD GetCode(ICorDebugCode **ppCode) |
6709 | { |
6710 | return CORDBG_E_CODE_NOT_AVAILABLE; |
6711 | } |
6712 | |
6713 | COM_METHOD GetFunction(ICorDebugFunction **ppFunction) |
6714 | { |
6715 | return (CordbFrame::GetFunction(ppFunction)); |
6716 | } |
6717 | COM_METHOD GetFunctionToken(mdMethodDef *pToken) |
6718 | { |
6719 | return (CordbFrame::GetFunctionToken(pToken)); |
6720 | } |
6721 | |
6722 | COM_METHOD GetCaller(ICorDebugFrame **ppFrame) |
6723 | { |
6724 | return (CordbFrame::GetCaller(ppFrame)); |
6725 | } |
6726 | COM_METHOD GetCallee(ICorDebugFrame **ppFrame) |
6727 | { |
6728 | return (CordbFrame::GetCallee(ppFrame)); |
6729 | } |
6730 | COM_METHOD CreateStepper(ICorDebugStepper **ppStepper) |
6731 | { |
6732 | return E_NOTIMPL; |
6733 | } |
6734 | |
6735 | COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd); |
6736 | |
6737 | //----------------------------------------------------------- |
6738 | // ICorDebugInternalFrame |
6739 | //----------------------------------------------------------- |
6740 | |
6741 | // Get the type of internal frame. This will never be STUBFRAME_NONE. |
6742 | COM_METHOD GetFrameType(CorDebugInternalFrameType * pType) |
6743 | { |
6744 | VALIDATE_POINTER_TO_OBJECT(pType, CorDebugInternalFrameType) |
6745 | *pType = m_eFrameType; |
6746 | return S_OK; |
6747 | } |
6748 | |
6749 | //----------------------------------------------------------- |
6750 | // ICorDebugInternalFrame2 |
6751 | //----------------------------------------------------------- |
6752 | |
6753 | COM_METHOD GetAddress(CORDB_ADDRESS * pAddress); |
6754 | COM_METHOD IsCloserToLeaf(ICorDebugFrame * pFrameToCompare, |
6755 | BOOL * pIsCloser); |
6756 | |
6757 | BOOL IsCloserToLeafWorker(ICorDebugFrame * pFrameToCompare); |
6758 | |
6759 | //----------------------------------------------------------- |
6760 | // Non COM methods |
6761 | //----------------------------------------------------------- |
6762 | |
6763 | virtual CordbFunction *GetFunction(); |
6764 | |
6765 | |
6766 | // Accessors to return NULL or typesafe cast to derived frame |
6767 | virtual CordbInternalFrame * GetAsInternalFrame() { return this; } |
6768 | |
6769 | // accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata |
6770 | BOOL ConvertInternalFrameForILMethodWithoutMetadata(ICorDebugInternalFrame2 ** ppInternalFrame2); |
6771 | |
6772 | protected: |
6773 | // the frame type |
6774 | CorDebugInternalFrameType m_eFrameType; |
6775 | |
6776 | // the method token of the method (if any) associated with the internal frame |
6777 | mdMethodDef m_funcMetadataToken; |
6778 | |
6779 | // the method (if any) associated with the internal frame |
6780 | RSSmartPtr<CordbFunction> m_function; |
6781 | |
6782 | VMPTR_MethodDesc m_vmMethodDesc; |
6783 | }; |
6784 | |
6785 | //--------------------------------------------------------------------------------------- |
6786 | // |
6787 | // This class implements ICorDebugRuntimeUnwindableFrame. It is used to mark a native stack frame |
6788 | // which requires special unwinding and which doesn't correspond to any IL code. It is really |
6789 | // just a marker to tell the debugger to use the managed unwinder. The debugger is still responsible |
6790 | // to do all the inspection and symbol lookup. An example is the hijack stub. |
6791 | // |
6792 | |
6793 | class CordbRuntimeUnwindableFrame : public CordbFrame, public ICorDebugRuntimeUnwindableFrame |
6794 | { |
6795 | public: |
6796 | CordbRuntimeUnwindableFrame(CordbThread * pThread, |
6797 | FramePointer fp, |
6798 | CordbAppDomain * pCurrentAppDomain, |
6799 | DT_CONTEXT * pContext); |
6800 | |
6801 | virtual void Neuter(); |
6802 | |
6803 | #ifdef _DEBUG |
6804 | virtual const char * DbgGetName() { return "CordbRuntimeUnwindableFrame" ; } |
6805 | #endif |
6806 | |
6807 | //----------------------------------------------------------- |
6808 | // IUnknown |
6809 | //----------------------------------------------------------- |
6810 | |
6811 | ULONG STDMETHODCALLTYPE AddRef() |
6812 | { |
6813 | return (BaseAddRef()); |
6814 | } |
6815 | |
6816 | ULONG STDMETHODCALLTYPE Release() |
6817 | { |
6818 | return (BaseRelease()); |
6819 | } |
6820 | |
6821 | COM_METHOD QueryInterface(REFIID riid, void ** ppInterface); |
6822 | |
6823 | //----------------------------------------------------------- |
6824 | // ICorDebugFrame |
6825 | //----------------------------------------------------------- |
6826 | |
6827 | // |
6828 | // Just return E_NOTIMPL for everything. |
6829 | // See the class comment. |
6830 | // |
6831 | |
6832 | COM_METHOD GetChain(ICorDebugChain ** ppChain) |
6833 | { |
6834 | return E_NOTIMPL; |
6835 | } |
6836 | |
6837 | COM_METHOD GetCode(ICorDebugCode ** ppCode) |
6838 | { |
6839 | return E_NOTIMPL; |
6840 | } |
6841 | |
6842 | COM_METHOD GetFunction(ICorDebugFunction ** ppFunction) |
6843 | { |
6844 | return E_NOTIMPL; |
6845 | } |
6846 | |
6847 | COM_METHOD GetFunctionToken(mdMethodDef * pToken) |
6848 | { |
6849 | return E_NOTIMPL; |
6850 | } |
6851 | |
6852 | COM_METHOD GetCaller(ICorDebugFrame ** ppFrame) |
6853 | { |
6854 | return E_NOTIMPL; |
6855 | } |
6856 | |
6857 | COM_METHOD GetCallee(ICorDebugFrame ** ppFrame) |
6858 | { |
6859 | return E_NOTIMPL; |
6860 | } |
6861 | |
6862 | COM_METHOD CreateStepper(ICorDebugStepper ** ppStepper) |
6863 | { |
6864 | return E_NOTIMPL; |
6865 | } |
6866 | |
6867 | COM_METHOD GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd) |
6868 | { |
6869 | return E_NOTIMPL; |
6870 | } |
6871 | |
6872 | //----------------------------------------------------------- |
6873 | // Non COM methods |
6874 | //----------------------------------------------------------- |
6875 | |
6876 | virtual CordbFunction * GetFunction() |
6877 | { |
6878 | return NULL; |
6879 | } |
6880 | |
6881 | virtual const DT_CONTEXT * GetContext() const; |
6882 | |
6883 | private: |
6884 | DT_CONTEXT m_context; |
6885 | }; |
6886 | |
6887 | |
6888 | class CordbValueEnum : public CordbBase, public ICorDebugValueEnum |
6889 | { |
6890 | public: |
6891 | enum ValueEnumMode { |
6892 | LOCAL_VARS_ORIGINAL_IL, |
6893 | LOCAL_VARS_REJIT_IL, |
6894 | ARGS, |
6895 | } ; |
6896 | |
6897 | CordbValueEnum(CordbNativeFrame *frame, ValueEnumMode mode); |
6898 | HRESULT Init(); |
6899 | ~CordbValueEnum(); |
6900 | virtual void Neuter(); |
6901 | |
6902 | #ifdef _DEBUG |
6903 | virtual const char * DbgGetName() { return "CordbValueEnum" ; } |
6904 | #endif |
6905 | |
6906 | |
6907 | //----------------------------------------------------------- |
6908 | // IUnknown |
6909 | //----------------------------------------------------------- |
6910 | |
6911 | ULONG STDMETHODCALLTYPE AddRef() |
6912 | { |
6913 | return (BaseAddRef()); |
6914 | } |
6915 | ULONG STDMETHODCALLTYPE Release() |
6916 | { |
6917 | return (BaseRelease()); |
6918 | } |
6919 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
6920 | |
6921 | //----------------------------------------------------------- |
6922 | // ICorDebugEnum |
6923 | //----------------------------------------------------------- |
6924 | |
6925 | COM_METHOD Skip(ULONG celt); |
6926 | COM_METHOD Reset(); |
6927 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
6928 | COM_METHOD GetCount(ULONG *pcelt); |
6929 | |
6930 | //----------------------------------------------------------- |
6931 | // ICorDebugValueEnum |
6932 | //----------------------------------------------------------- |
6933 | |
6934 | COM_METHOD Next(ULONG celt, ICorDebugValue *values[], ULONG *pceltFetched); |
6935 | |
6936 | private: |
6937 | CordbNativeFrame* m_frame; |
6938 | ValueEnumMode m_mode; |
6939 | UINT m_iCurrent; |
6940 | UINT m_iMax; |
6941 | }; |
6942 | |
6943 | |
6944 | /* ------------------------------------------------------------------------- * |
6945 | * Misc Info for the Native Frame class |
6946 | * ------------------------------------------------------------------------- */ |
6947 | |
6948 | struct CordbMiscFrame |
6949 | { |
6950 | public: |
6951 | CordbMiscFrame(); |
6952 | |
6953 | // new-style constructor |
6954 | CordbMiscFrame(DebuggerIPCE_JITFuncData * pJITFuncData); |
6955 | |
6956 | #ifdef WIN64EXCEPTIONS |
6957 | SIZE_T parentIP; |
6958 | FramePointer fpParentOrSelf; |
6959 | bool fIsFilterFunclet; |
6960 | #endif // WIN64EXCEPTIONS |
6961 | }; |
6962 | |
6963 | |
6964 | /* ------------------------------------------------------------------------- * |
6965 | * Native Frame class |
6966 | * ------------------------------------------------------------------------- */ |
6967 | |
6968 | class CordbNativeFrame : public CordbFrame, public ICorDebugNativeFrame, public ICorDebugNativeFrame2 |
6969 | { |
6970 | public: |
6971 | CordbNativeFrame(CordbThread * pThread, |
6972 | FramePointer fp, |
6973 | CordbNativeCode * pNativeCode, |
6974 | SIZE_T ip, |
6975 | DebuggerREGDISPLAY * pDRD, |
6976 | TADDR addrAmbientESP, |
6977 | bool fQuicklyUnwound, |
6978 | CordbAppDomain * pCurrentAppDomain, |
6979 | CordbMiscFrame * pMisc = NULL, |
6980 | DT_CONTEXT * pContext = NULL); |
6981 | virtual ~CordbNativeFrame(); |
6982 | virtual void Neuter(); |
6983 | |
6984 | #ifdef _DEBUG |
6985 | virtual const char * DbgGetName() { return "CordbNativeFrame" ; } |
6986 | #endif |
6987 | |
6988 | //----------------------------------------------------------- |
6989 | // IUnknown |
6990 | //----------------------------------------------------------- |
6991 | |
6992 | ULONG STDMETHODCALLTYPE AddRef() |
6993 | { |
6994 | return (BaseAddRef()); |
6995 | } |
6996 | ULONG STDMETHODCALLTYPE Release() |
6997 | { |
6998 | return (BaseRelease()); |
6999 | } |
7000 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7001 | |
7002 | //----------------------------------------------------------- |
7003 | // ICorDebugFrame |
7004 | //----------------------------------------------------------- |
7005 | |
7006 | COM_METHOD GetChain(ICorDebugChain **ppChain) |
7007 | { |
7008 | return (CordbFrame::GetChain(ppChain)); |
7009 | } |
7010 | COM_METHOD GetCode(ICorDebugCode **ppCode); |
7011 | COM_METHOD GetFunction(ICorDebugFunction **ppFunction) |
7012 | { |
7013 | return (CordbFrame::GetFunction(ppFunction)); |
7014 | } |
7015 | COM_METHOD GetFunctionToken(mdMethodDef *pToken) |
7016 | { |
7017 | return (CordbFrame::GetFunctionToken(pToken)); |
7018 | } |
7019 | COM_METHOD GetCaller(ICorDebugFrame **ppFrame) |
7020 | { |
7021 | return (CordbFrame::GetCaller(ppFrame)); |
7022 | } |
7023 | COM_METHOD GetCallee(ICorDebugFrame **ppFrame) |
7024 | { |
7025 | return (CordbFrame::GetCallee(ppFrame)); |
7026 | } |
7027 | COM_METHOD CreateStepper(ICorDebugStepper **ppStepper) |
7028 | { |
7029 | return (CordbFrame::CreateStepper(ppStepper)); |
7030 | } |
7031 | |
7032 | COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd); |
7033 | |
7034 | //----------------------------------------------------------- |
7035 | // ICorDebugNativeFrame |
7036 | //----------------------------------------------------------- |
7037 | |
7038 | COM_METHOD GetIP(ULONG32* pnOffset); |
7039 | COM_METHOD SetIP(ULONG32 nOffset); |
7040 | COM_METHOD GetRegisterSet(ICorDebugRegisterSet **ppRegisters); |
7041 | COM_METHOD GetLocalRegisterValue(CorDebugRegister reg, |
7042 | ULONG cbSigBlob, |
7043 | PCCOR_SIGNATURE pvSigBlob, |
7044 | ICorDebugValue ** ppValue); |
7045 | |
7046 | COM_METHOD GetLocalDoubleRegisterValue(CorDebugRegister highWordReg, |
7047 | CorDebugRegister lowWordReg, |
7048 | ULONG cbSigBlob, |
7049 | PCCOR_SIGNATURE pvSigBlob, |
7050 | ICorDebugValue ** ppValue); |
7051 | |
7052 | COM_METHOD GetLocalMemoryValue(CORDB_ADDRESS address, |
7053 | ULONG cbSigBlob, |
7054 | PCCOR_SIGNATURE pvSigBlob, |
7055 | ICorDebugValue ** ppValue); |
7056 | |
7057 | COM_METHOD GetLocalRegisterMemoryValue(CorDebugRegister highWordReg, |
7058 | CORDB_ADDRESS lowWordAddress, |
7059 | ULONG cbSigBlob, |
7060 | PCCOR_SIGNATURE pvSigBlob, |
7061 | ICorDebugValue ** ppValue); |
7062 | |
7063 | COM_METHOD GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress, |
7064 | CorDebugRegister lowWordRegister, |
7065 | ULONG cbSigBlob, |
7066 | PCCOR_SIGNATURE pvSigBlob, |
7067 | ICorDebugValue ** ppValue); |
7068 | |
7069 | COM_METHOD CanSetIP(ULONG32 nOffset); |
7070 | |
7071 | //----------------------------------------------------------- |
7072 | // ICorDebugNativeFrame2 |
7073 | //----------------------------------------------------------- |
7074 | |
7075 | COM_METHOD IsChild(BOOL * pIsChild); |
7076 | |
7077 | COM_METHOD IsMatchingParentFrame(ICorDebugNativeFrame2 *pPotentialParentFrame, |
7078 | BOOL * pIsParent); |
7079 | |
7080 | COM_METHOD GetStackParameterSize(ULONG32 * pSize); |
7081 | |
7082 | //----------------------------------------------------------- |
7083 | // Non-COM members |
7084 | //----------------------------------------------------------- |
7085 | |
7086 | // Accessors to return NULL or typesafe cast to derived frame |
7087 | virtual CordbNativeFrame * GetAsNativeFrame() { return this; } |
7088 | |
7089 | CordbFunction * GetFunction(); |
7090 | CordbNativeCode * GetNativeCode(); |
7091 | virtual const DT_CONTEXT * GetContext() const; |
7092 | |
7093 | // Given the native variable information of a variable, return its value. |
7094 | // This function assumes that the value is either in a register or on the stack |
7095 | // (i.e. VLT_REG or VLT_STK). |
7096 | SIZE_T GetRegisterOrStackValue(const ICorDebugInfo::NativeVarInfo * pNativeVarInfo); |
7097 | |
7098 | HRESULT GetLocalRegisterValue(CorDebugRegister reg, |
7099 | CordbType * pType, |
7100 | ICorDebugValue **ppValue); |
7101 | HRESULT GetLocalDoubleRegisterValue(CorDebugRegister highWordReg, |
7102 | CorDebugRegister lowWordReg, |
7103 | CordbType * pType, |
7104 | ICorDebugValue **ppValue); |
7105 | HRESULT GetLocalMemoryValue(CORDB_ADDRESS address, |
7106 | CordbType * pType, |
7107 | ICorDebugValue **ppValue); |
7108 | HRESULT GetLocalByRefMemoryValue(CORDB_ADDRESS address, |
7109 | CordbType * pType, |
7110 | ICorDebugValue **ppValue); |
7111 | HRESULT GetLocalRegisterMemoryValue(CorDebugRegister highWordReg, |
7112 | CORDB_ADDRESS lowWordAddress, |
7113 | CordbType * pType, |
7114 | ICorDebugValue **ppValue); |
7115 | HRESULT GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress, |
7116 | CorDebugRegister lowWordRegister, |
7117 | CordbType * pType, |
7118 | ICorDebugValue **ppValue); |
7119 | UINT_PTR * GetAddressOfRegister(CorDebugRegister regNum) const; |
7120 | CORDB_ADDRESS GetLeftSideAddressOfRegister(CorDebugRegister regNum) const; |
7121 | HRESULT GetLocalFloatingPointValue(DWORD index, |
7122 | CordbType * pType, |
7123 | ICorDebugValue **ppValue); |
7124 | |
7125 | |
7126 | CORDB_ADDRESS GetLSStackAddress(ICorDebugInfo::RegNum regNum, signed offset); |
7127 | |
7128 | bool IsLeafFrame() const; |
7129 | |
7130 | // Return the offset used for inspection purposes. |
7131 | // Refer to the comment at the beginning of the function definition in RsThread.cpp for more information. |
7132 | SIZE_T GetInspectionIP(); |
7133 | |
7134 | ULONG32 GetIPOffset(); |
7135 | |
7136 | // whether this is a funclet frame |
7137 | bool IsFunclet(); |
7138 | bool IsFilterFunclet(); |
7139 | |
7140 | #ifdef WIN64EXCEPTIONS |
7141 | // return the offset of the parent method frame at which an exception occurs |
7142 | SIZE_T GetParentIP(); |
7143 | #endif // WIN64EXCEPTIONS |
7144 | |
7145 | TADDR GetAmbientESP() { return m_taAmbientESP; } |
7146 | TADDR GetReturnRegisterValue(); |
7147 | |
7148 | // accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata |
7149 | BOOL ConvertNativeFrameForILMethodWithoutMetadata(ICorDebugInternalFrame2 ** ppInternalFrame2); |
7150 | |
7151 | //----------------------------------------------------------- |
7152 | // Data members |
7153 | //----------------------------------------------------------- |
7154 | |
7155 | public: |
7156 | // the register set |
7157 | DebuggerREGDISPLAY m_rd; |
7158 | |
7159 | // This field is only true for Enter-Managed chain. It means that the register set is invalid. |
7160 | bool m_quicklyUnwound; |
7161 | |
7162 | // each CordbNativeFrame corresponds to exactly one CordbJITILFrame and one CordbNativeCode |
7163 | RSSmartPtr<CordbJITILFrame> m_JITILFrame; |
7164 | RSSmartPtr<CordbNativeCode> m_nativeCode; |
7165 | |
7166 | // auxiliary information only used on 64-bit to find the parent stack pointer and offset for funclets |
7167 | CordbMiscFrame m_misc; |
7168 | |
7169 | private: |
7170 | // the ambient SP value only used on x86 to retrieve sp-relative local variables |
7171 | // (most likely in a frameless method) |
7172 | TADDR m_taAmbientESP; |
7173 | |
7174 | // @dbgtodo inspection - When we DACize the various Cordb*Value classes, we should consider getting rid of the |
7175 | // DebuggerREGDISPLAY and just use the CONTEXT. A lot of simplification can be done here. |
7176 | DT_CONTEXT m_context; |
7177 | }; |
7178 | |
7179 | |
7180 | /* ------------------------------------------------------------------------- * |
7181 | * CordbRegisterSet class |
7182 | * |
7183 | * This can be obtained via GetRegisterSet from |
7184 | * CordbNativeFrame |
7185 | * CordbThread |
7186 | * |
7187 | * ------------------------------------------------------------------------- */ |
7188 | |
7189 | #define SETBITULONG64( x ) ( (ULONG64)1 << (x) ) |
7190 | #define SET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] |= (1 << ((_reg) & 7))) |
7191 | #define RESET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] &= ~(1 << ((_reg) & 7))) |
7192 | #define IS_SET_BIT_MASK(_mask, _reg) (_mask[(_reg) >> 3] & (1 << ((_reg) & 7))) |
7193 | |
7194 | |
7195 | class CordbRegisterSet : public CordbBase, public ICorDebugRegisterSet, public ICorDebugRegisterSet2 |
7196 | { |
7197 | public: |
7198 | CordbRegisterSet(DebuggerREGDISPLAY * pRegDisplay, |
7199 | CordbThread * pThread, |
7200 | bool fActive, |
7201 | bool fQuickUnwind, |
7202 | bool fTakeOwnershipOfDRD = false); |
7203 | |
7204 | |
7205 | ~CordbRegisterSet(); |
7206 | |
7207 | |
7208 | |
7209 | virtual void Neuter(); |
7210 | |
7211 | #ifdef _DEBUG |
7212 | virtual const char * DbgGetName() { return "CordbRegisterSet" ; } |
7213 | #endif |
7214 | |
7215 | //----------------------------------------------------------- |
7216 | // IUnknown |
7217 | //----------------------------------------------------------- |
7218 | |
7219 | ULONG STDMETHODCALLTYPE AddRef() |
7220 | { |
7221 | return (BaseAddRefEnforceExternal()); |
7222 | } |
7223 | ULONG STDMETHODCALLTYPE Release() |
7224 | { |
7225 | return (BaseReleaseEnforceExternal()); |
7226 | } |
7227 | |
7228 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7229 | |
7230 | |
7231 | |
7232 | //----------------------------------------------------------- |
7233 | // ICorDebugRegisterSet |
7234 | // More extensive explanation are in Src/inc/CorDebug.idl |
7235 | //----------------------------------------------------------- |
7236 | COM_METHOD GetRegistersAvailable(ULONG64 *pAvailable); |
7237 | |
7238 | COM_METHOD GetRegisters(ULONG64 mask, |
7239 | ULONG32 regCount, |
7240 | CORDB_REGISTER regBuffer[]); |
7241 | COM_METHOD SetRegisters( ULONG64 mask, |
7242 | ULONG32 regCount, |
7243 | CORDB_REGISTER regBuffer[]) |
7244 | { |
7245 | LIMITED_METHOD_CONTRACT; |
7246 | |
7247 | VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, |
7248 | regCount, true, true); |
7249 | |
7250 | return E_NOTIMPL; |
7251 | } |
7252 | |
7253 | COM_METHOD GetThreadContext(ULONG32 contextSize, BYTE context[]); |
7254 | |
7255 | // SetThreadContexthad a very problematic implementation in v1.1. |
7256 | // We've ripped it out in V2.0 and E_NOTIMPL it. See V1.1 sources for what it used to look like |
7257 | // in case we ever want to re-add it. |
7258 | // If we ever re-implement it consider the following: |
7259 | // - must fail on non-leaf frames (just check m_active). |
7260 | // - must make sure that GetThreadContext() is fully accurate. If we don't have SetThCtx, then |
7261 | // GetThreadCtx bugs are much more benign. |
7262 | // - be sure to update any shared reg displays (what if a frame + chain have the same rd) and |
7263 | // also update any cached contexts (such as CordbThread::m_context). |
7264 | // - be sure to honor the context flags and only setting what we can set. |
7265 | // |
7266 | // Friday, July 16, 2004. (This date will be useful for Source control history) |
7267 | COM_METHOD SetThreadContext(ULONG32 contextSize, BYTE context[]) |
7268 | { |
7269 | return E_NOTIMPL; |
7270 | } |
7271 | |
7272 | //----------------------------------------------------------- |
7273 | // ICorDebugRegisterSet2 |
7274 | // More extensive explanation are in Src/inc/CorDebug.idl |
7275 | //----------------------------------------------------------- |
7276 | COM_METHOD GetRegistersAvailable(ULONG32 regCount, |
7277 | BYTE pAvailable[]); |
7278 | |
7279 | COM_METHOD GetRegisters(ULONG32 maskCount, |
7280 | BYTE mask[], |
7281 | ULONG32 regCount, |
7282 | CORDB_REGISTER regBuffer[]); |
7283 | |
7284 | COM_METHOD SetRegisters(ULONG32 maskCount, |
7285 | BYTE mask[], |
7286 | ULONG32 regCount, |
7287 | CORDB_REGISTER regBuffer[]) |
7288 | { |
7289 | LIMITED_METHOD_CONTRACT; |
7290 | |
7291 | VALIDATE_POINTER_TO_OBJECT_ARRAY(regBuffer, CORDB_REGISTER, |
7292 | regCount, true, true); |
7293 | |
7294 | return E_NOTIMPL; |
7295 | } |
7296 | |
7297 | protected: |
7298 | // Platform specific helper for GetThreadContext. |
7299 | void InternalCopyRDToContext(DT_CONTEXT * pContext); |
7300 | |
7301 | // Adapters to impl v2.0 interfaces on top of v1.0 interfaces. |
7302 | HRESULT GetRegistersAvailableAdapter(ULONG32 regCount, BYTE pAvailable[]); |
7303 | HRESULT GetRegistersAdapter(ULONG32 maskCount, BYTE mask[], ULONG32 regCount, CORDB_REGISTER regBuffer[]); |
7304 | |
7305 | |
7306 | // This CordbRegisterSet is responsible to free this memory if m_fTakeOwnershipOfDRD is true. Otherwise, |
7307 | // this memory is freed by the CordbNativeFrame or CordbThread which creates this CordbRegisterSet. |
7308 | DebuggerREGDISPLAY *m_rd; |
7309 | CordbThread *m_thread; |
7310 | bool m_active; // true if we're the leafmost register set. |
7311 | bool m_quickUnwind; |
7312 | |
7313 | // true if the CordbRegisterSet owns the DebuggerREGDISPLAY pointer and needs to free the memory |
7314 | bool m_fTakeOwnershipOfDRD; |
7315 | } ; |
7316 | |
7317 | |
7318 | |
7319 | |
7320 | /* ------------------------------------------------------------------------- * |
7321 | * JIT-IL Frame class |
7322 | * ------------------------------------------------------------------------- */ |
7323 | |
7324 | class CordbJITILFrame : public CordbBase, public ICorDebugILFrame, public ICorDebugILFrame2, public ICorDebugILFrame3, public ICorDebugILFrame4 |
7325 | { |
7326 | public: |
7327 | CordbJITILFrame(CordbNativeFrame * pNativeFrame, |
7328 | CordbILCode * pCode, |
7329 | UINT_PTR ip, |
7330 | CorDebugMappingResult mapping, |
7331 | GENERICS_TYPE_TOKEN exactGenericArgsToken, |
7332 | DWORD dwExactGenericArgsTokenIndex, |
7333 | bool fVarArgFnx, |
7334 | CordbReJitILCode * pReJitCode); |
7335 | HRESULT Init(); |
7336 | virtual ~CordbJITILFrame(); |
7337 | virtual void Neuter(); |
7338 | |
7339 | |
7340 | #ifdef _DEBUG |
7341 | virtual const char * DbgGetName() { return "CordbJITILFrame" ; } |
7342 | #endif |
7343 | |
7344 | //----------------------------------------------------------- |
7345 | // IUnknown |
7346 | //----------------------------------------------------------- |
7347 | |
7348 | ULONG STDMETHODCALLTYPE AddRef() |
7349 | { |
7350 | return (BaseAddRef()); |
7351 | } |
7352 | ULONG STDMETHODCALLTYPE Release() |
7353 | { |
7354 | return (BaseRelease()); |
7355 | } |
7356 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7357 | |
7358 | //----------------------------------------------------------- |
7359 | // ICorDebugFrame |
7360 | //----------------------------------------------------------- |
7361 | |
7362 | COM_METHOD GetChain(ICorDebugChain **ppChain); |
7363 | COM_METHOD GetCode(ICorDebugCode **ppCode); |
7364 | COM_METHOD GetFunction(ICorDebugFunction **ppFunction); |
7365 | COM_METHOD GetFunctionToken(mdMethodDef *pToken); |
7366 | COM_METHOD GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd); |
7367 | COM_METHOD CreateStepper(ICorDebugStepper **ppStepper); |
7368 | COM_METHOD GetCaller(ICorDebugFrame **ppFrame); |
7369 | COM_METHOD GetCallee(ICorDebugFrame **ppFrame); |
7370 | |
7371 | //----------------------------------------------------------- |
7372 | // ICorDebugILFrame |
7373 | //----------------------------------------------------------- |
7374 | |
7375 | COM_METHOD GetIP(ULONG32* pnOffset, CorDebugMappingResult *pMappingResult); |
7376 | COM_METHOD SetIP(ULONG32 nOffset); |
7377 | COM_METHOD EnumerateLocalVariables(ICorDebugValueEnum **ppValueEnum); |
7378 | COM_METHOD GetLocalVariable(DWORD dwIndex, ICorDebugValue **ppValue); |
7379 | COM_METHOD EnumerateArguments(ICorDebugValueEnum **ppValueEnum); |
7380 | COM_METHOD GetArgument(DWORD dwIndex, ICorDebugValue ** ppValue); |
7381 | COM_METHOD GetStackDepth(ULONG32 *pDepth); |
7382 | COM_METHOD GetStackValue(DWORD dwIndex, ICorDebugValue **ppValue); |
7383 | COM_METHOD CanSetIP(ULONG32 nOffset); |
7384 | |
7385 | //----------------------------------------------------------- |
7386 | // ICorDebugILFrame2 |
7387 | //----------------------------------------------------------- |
7388 | |
7389 | // Called at an EnC remap opportunity to remap to the latest version of a function |
7390 | COM_METHOD RemapFunction(ULONG32 nOffset); |
7391 | |
7392 | COM_METHOD EnumerateTypeParameters(ICorDebugTypeEnum **ppTyParEnum); |
7393 | |
7394 | //----------------------------------------------------------- |
7395 | // ICorDebugILFrame3 |
7396 | //----------------------------------------------------------- |
7397 | |
7398 | COM_METHOD GetReturnValueForILOffset(ULONG32 ILoffset, ICorDebugValue** ppReturnValue); |
7399 | |
7400 | //----------------------------------------------------------- |
7401 | // ICorDebugILFrame4 |
7402 | //----------------------------------------------------------- |
7403 | |
7404 | COM_METHOD EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugValueEnum **ppValueEnum); |
7405 | COM_METHOD GetLocalVariableEx(ILCodeKind flags, DWORD dwIndex, ICorDebugValue **ppValue); |
7406 | COM_METHOD GetCodeEx(ILCodeKind flags, ICorDebugCode **ppCode); |
7407 | |
7408 | //----------------------------------------------------------- |
7409 | // Non-COM methods |
7410 | //----------------------------------------------------------- |
7411 | |
7412 | CordbModule *GetModule(); |
7413 | |
7414 | HRESULT GetNativeVariable(CordbType *type, |
7415 | const ICorDebugInfo::NativeVarInfo *pNativeVarInfo, |
7416 | ICorDebugValue **ppValue); |
7417 | |
7418 | CordbAppDomain *GetCurrentAppDomain(); |
7419 | |
7420 | CordbFunction *GetFunction(); |
7421 | |
7422 | // ILVariableToNative serves to let the frame intercept accesses |
7423 | // to var args variables. |
7424 | HRESULT ILVariableToNative(DWORD dwIndex, |
7425 | const ICorDebugInfo::NativeVarInfo ** ppNativeInfo); |
7426 | |
7427 | // Fills in our array of var args variables |
7428 | HRESULT FabricateNativeInfo(DWORD dwIndex, |
7429 | const ICorDebugInfo::NativeVarInfo ** ppNativeInfo); |
7430 | |
7431 | HRESULT GetArgumentType(DWORD dwIndex, |
7432 | CordbType ** ppResultType); |
7433 | |
7434 | // load the generics type and method arguments into a cache |
7435 | void LoadGenericArgs(); |
7436 | |
7437 | HRESULT QueryInterfaceInternal(REFIID id, void** pInterface); |
7438 | |
7439 | // Builds an generic Instaniation object from the mdClass and generic signature |
7440 | // for what we are calling into. |
7441 | static HRESULT BuildInstantiationForCallsite(CordbModule *pModule, NewArrayHolder<CordbType*> &types, Instantiation &inst, Instantiation *currentInstantiation, mdToken targetClass, SigParser funcGenerics); |
7442 | |
7443 | CordbILCode* GetOriginalILCode(); |
7444 | CordbReJitILCode* GetReJitILCode(); |
7445 | |
7446 | private: |
7447 | void RefreshCachedVarArgSigParserIfNeeded(); |
7448 | |
7449 | // Worker function for GetReturnValueForILOffset. |
7450 | HRESULT GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDebugValue** ppReturnValue); |
7451 | |
7452 | // Given pType, fills ppReturnValue with the correct value. |
7453 | HRESULT GetReturnValueForType(CordbType *pType, ICorDebugValue **ppReturnValue); |
7454 | |
7455 | //----------------------------------------------------------- |
7456 | // Data members |
7457 | //----------------------------------------------------------- |
7458 | |
7459 | public: |
7460 | // each CordbJITILFrame corresponds to exactly one CordbNativeFrame and one CordbILCode |
7461 | CordbNativeFrame * m_nativeFrame; |
7462 | CordbILCode * m_ilCode; |
7463 | |
7464 | // the IL offset and the mapping result for the offset |
7465 | UINT_PTR m_ip; |
7466 | CorDebugMappingResult m_mapping; |
7467 | |
7468 | // <vararg-specific fields> |
7469 | |
7470 | // whether this is a vararg function |
7471 | bool m_fVarArgFnx; |
7472 | |
7473 | // the number of arguments, including the var args |
7474 | ULONG m_allArgsCount; |
7475 | |
7476 | // This byte array is used to store the signature for vararg methods. |
7477 | // It points to the underlying memory used by m_sigParserCached, and it enables us to easily delete |
7478 | // the underlying memory when the CordbJITILFrame is neutered. |
7479 | BYTE * m_rgbSigParserBuf; |
7480 | |
7481 | // Do not mutate this, instead make copies of it and use the copies, that way we are guaranteed to |
7482 | // start at the correct position in the signature each time. |
7483 | // The underlying memory used for the signature in the SigParser must not be in the DAC cache. |
7484 | // Otherwise it may be flushed underneath us, and we would AV when we try to access it. |
7485 | SigParser m_sigParserCached; |
7486 | |
7487 | // the address of the first arg; only used for vararg functions |
7488 | CORDB_ADDRESS m_FirstArgAddr; |
7489 | |
7490 | // This is an array of variable information for the arguments. |
7491 | // The variable information is fabricated by the RS. |
7492 | ICorDebugInfo::NativeVarInfo * m_rgNVI; |
7493 | |
7494 | // </vararg-specific fields> |
7495 | |
7496 | Instantiation m_genericArgs; // the generics type arguments |
7497 | BOOL m_genericArgsLoaded; // whether we have loaded and cached the generics type arguments |
7498 | |
7499 | // An extra token to help fetch information about any generic |
7500 | // parameters passed to the method, perhaps dynamically. |
7501 | // This is the so-called generics type context/token. |
7502 | // |
7503 | // This token comes from the stackwalker and it may be NULL, in which case we need to retrieve the token |
7504 | // ourselves using m_dwFrameParamsTokenIndex and the variable lifetime information. |
7505 | GENERICS_TYPE_TOKEN m_frameParamsToken; |
7506 | |
7507 | // IL Variable index of the Generics Arg Token. |
7508 | DWORD m_dwFrameParamsTokenIndex; |
7509 | |
7510 | // if this frame is instrumented with rejit, this will point to the instrumented IL code |
7511 | RSSmartPtr<CordbReJitILCode> m_pReJitCode; |
7512 | }; |
7513 | |
7514 | /* ------------------------------------------------------------------------- * |
7515 | * Breakpoint class |
7516 | * ------------------------------------------------------------------------- */ |
7517 | |
7518 | enum CordbBreakpointType |
7519 | { |
7520 | CBT_FUNCTION, |
7521 | CBT_MODULE, |
7522 | CBT_VALUE |
7523 | }; |
7524 | |
7525 | class CordbBreakpoint : public CordbBase, public ICorDebugBreakpoint |
7526 | { |
7527 | public: |
7528 | CordbBreakpoint(CordbProcess * pProcess, CordbBreakpointType bpType); |
7529 | virtual void Neuter(); |
7530 | |
7531 | #ifdef _DEBUG |
7532 | virtual const char * DbgGetName() { return "CordbBreakpoint" ; } |
7533 | #endif |
7534 | |
7535 | //----------------------------------------------------------- |
7536 | // IUnknown |
7537 | //----------------------------------------------------------- |
7538 | |
7539 | ULONG STDMETHODCALLTYPE AddRef() |
7540 | { |
7541 | return (BaseAddRef()); |
7542 | } |
7543 | ULONG STDMETHODCALLTYPE Release() |
7544 | { |
7545 | return (BaseRelease()); |
7546 | } |
7547 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7548 | |
7549 | //----------------------------------------------------------- |
7550 | // ICorDebugBreakpoint |
7551 | //----------------------------------------------------------- |
7552 | |
7553 | COM_METHOD BaseIsActive(BOOL *pbActive); |
7554 | |
7555 | //----------------------------------------------------------- |
7556 | // Non-COM methods |
7557 | //----------------------------------------------------------- |
7558 | CordbBreakpointType GetBPType() |
7559 | { |
7560 | return m_type; |
7561 | } |
7562 | |
7563 | virtual void Disconnect() {} |
7564 | |
7565 | CordbAppDomain *GetAppDomain() |
7566 | { |
7567 | return m_pAppDomain; |
7568 | } |
7569 | //----------------------------------------------------------- |
7570 | // Data members |
7571 | //----------------------------------------------------------- |
7572 | |
7573 | public: |
7574 | bool m_active; |
7575 | CordbAppDomain *m_pAppDomain; |
7576 | CordbBreakpointType m_type; |
7577 | }; |
7578 | |
7579 | /* ------------------------------------------------------------------------- * |
7580 | * Function Breakpoint class |
7581 | * ------------------------------------------------------------------------- */ |
7582 | |
7583 | class CordbFunctionBreakpoint : public CordbBreakpoint, |
7584 | public ICorDebugFunctionBreakpoint |
7585 | { |
7586 | public: |
7587 | CordbFunctionBreakpoint(CordbCode *code, SIZE_T offset, BOOL offsetIsIl); |
7588 | ~CordbFunctionBreakpoint(); |
7589 | |
7590 | virtual void Neuter(); |
7591 | #ifdef _DEBUG |
7592 | virtual const char * DbgGetName() { return "CordbFunctionBreakpoint" ; } |
7593 | #endif |
7594 | |
7595 | |
7596 | //----------------------------------------------------------- |
7597 | // IUnknown |
7598 | //----------------------------------------------------------- |
7599 | |
7600 | ULONG STDMETHODCALLTYPE AddRef() |
7601 | { |
7602 | return (BaseAddRef()); |
7603 | } |
7604 | ULONG STDMETHODCALLTYPE Release() |
7605 | { |
7606 | return (BaseRelease()); |
7607 | } |
7608 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7609 | |
7610 | //----------------------------------------------------------- |
7611 | // ICorDebugBreakpoint |
7612 | //----------------------------------------------------------- |
7613 | |
7614 | COM_METHOD GetFunction(ICorDebugFunction **ppFunction); |
7615 | COM_METHOD GetOffset(ULONG32 *pnOffset); |
7616 | COM_METHOD Activate(BOOL bActive); |
7617 | COM_METHOD IsActive(BOOL *pbActive) |
7618 | { |
7619 | VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *); |
7620 | |
7621 | return BaseIsActive(pbActive); |
7622 | } |
7623 | |
7624 | //----------------------------------------------------------- |
7625 | // Non-COM methods |
7626 | //----------------------------------------------------------- |
7627 | |
7628 | void Disconnect(); |
7629 | |
7630 | //----------------------------------------------------------- |
7631 | // Convenience routines |
7632 | //----------------------------------------------------------- |
7633 | |
7634 | |
7635 | //----------------------------------------------------------- |
7636 | // Data members |
7637 | //----------------------------------------------------------- |
7638 | |
7639 | // Get a point to the LS BP object. |
7640 | LSPTR_BREAKPOINT GetLsPtrBP(); |
7641 | public: |
7642 | |
7643 | // We need to have a strong pointer because we may access the m_code object after we're neutered. |
7644 | // @todo - use external pointer b/c Breakpoints aren't yet rooted, and so this reference could be |
7645 | // leaked. |
7646 | RSExtSmartPtr<CordbCode> m_code; |
7647 | SIZE_T m_offset; |
7648 | BOOL m_offsetIsIl; |
7649 | }; |
7650 | |
7651 | /* ------------------------------------------------------------------------- * |
7652 | * Module Breakpoint class |
7653 | * ------------------------------------------------------------------------- */ |
7654 | |
7655 | class CordbModuleBreakpoint : public CordbBreakpoint, |
7656 | public ICorDebugModuleBreakpoint |
7657 | { |
7658 | public: |
7659 | CordbModuleBreakpoint(CordbModule *pModule); |
7660 | |
7661 | |
7662 | |
7663 | #ifdef _DEBUG |
7664 | virtual const char * DbgGetName() { return "CordbModuleBreakpoint" ; } |
7665 | #endif |
7666 | |
7667 | |
7668 | //----------------------------------------------------------- |
7669 | // IUnknown |
7670 | //----------------------------------------------------------- |
7671 | |
7672 | ULONG STDMETHODCALLTYPE AddRef() |
7673 | { |
7674 | return (BaseAddRef()); |
7675 | } |
7676 | ULONG STDMETHODCALLTYPE Release() |
7677 | { |
7678 | return (BaseRelease()); |
7679 | } |
7680 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7681 | |
7682 | //----------------------------------------------------------- |
7683 | // ICorDebugModuleBreakpoint |
7684 | //----------------------------------------------------------- |
7685 | |
7686 | COM_METHOD GetModule(ICorDebugModule **ppModule); |
7687 | COM_METHOD Activate(BOOL bActive); |
7688 | COM_METHOD IsActive(BOOL *pbActive) |
7689 | { |
7690 | VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *); |
7691 | |
7692 | return BaseIsActive(pbActive); |
7693 | } |
7694 | |
7695 | //----------------------------------------------------------- |
7696 | // Non-COM methods |
7697 | //----------------------------------------------------------- |
7698 | |
7699 | void Disconnect(); |
7700 | |
7701 | public: |
7702 | CordbModule *m_module; |
7703 | }; |
7704 | |
7705 | |
7706 | /* ------------------------------------------------------------------------- * |
7707 | * Stepper class |
7708 | * ------------------------------------------------------------------------- */ |
7709 | |
7710 | class CordbStepper : public CordbBase, public ICorDebugStepper, public ICorDebugStepper2 |
7711 | { |
7712 | public: |
7713 | CordbStepper(CordbThread *thread, CordbFrame *frame = NULL); |
7714 | |
7715 | |
7716 | |
7717 | #ifdef _DEBUG |
7718 | virtual const char * DbgGetName() { return "CordbStepper" ; } |
7719 | #endif |
7720 | |
7721 | |
7722 | //----------------------------------------------------------- |
7723 | // IUnknown |
7724 | //----------------------------------------------------------- |
7725 | |
7726 | ULONG STDMETHODCALLTYPE AddRef() |
7727 | { |
7728 | return (BaseAddRef()); |
7729 | } |
7730 | ULONG STDMETHODCALLTYPE Release() |
7731 | { |
7732 | return (BaseRelease()); |
7733 | } |
7734 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
7735 | |
7736 | //----------------------------------------------------------- |
7737 | // ICorDebugStepper |
7738 | //----------------------------------------------------------- |
7739 | |
7740 | COM_METHOD IsActive(BOOL *pbActive); |
7741 | COM_METHOD Deactivate(); |
7742 | COM_METHOD SetInterceptMask(CorDebugIntercept mask); |
7743 | COM_METHOD SetUnmappedStopMask(CorDebugUnmappedStop mask); |
7744 | COM_METHOD Step(BOOL bStepIn); |
7745 | COM_METHOD StepRange(BOOL bStepIn, |
7746 | COR_DEBUG_STEP_RANGE ranges[], |
7747 | ULONG32 cRangeCount); |
7748 | COM_METHOD StepOut(); |
7749 | COM_METHOD SetRangeIL(BOOL bIL); |
7750 | |
7751 | //----------------------------------------------------------- |
7752 | // ICorDebugStepper2 |
7753 | //----------------------------------------------------------- |
7754 | COM_METHOD SetJMC(BOOL fIsJMCStepper); |
7755 | |
7756 | //----------------------------------------------------------- |
7757 | // Convenience routines |
7758 | //----------------------------------------------------------- |
7759 | |
7760 | CordbAppDomain *GetAppDomain() |
7761 | { |
7762 | return (m_thread->GetAppDomain()); |
7763 | } |
7764 | |
7765 | LSPTR_STEPPER GetLsPtrStepper(); |
7766 | |
7767 | //----------------------------------------------------------- |
7768 | // Data members |
7769 | //----------------------------------------------------------- |
7770 | |
7771 | CordbThread *m_thread; |
7772 | CordbFrame *m_frame; |
7773 | REMOTE_PTR m_stepperToken; |
7774 | bool m_active; |
7775 | bool m_rangeIL; |
7776 | bool m_fIsJMCStepper; |
7777 | CorDebugUnmappedStop m_rgfMappingStop; |
7778 | CorDebugIntercept m_rgfInterceptStop; |
7779 | }; |
7780 | |
7781 | #define REG_SIZE sizeof(SIZE_T) |
7782 | |
7783 | // class RegisterInfo: encapsulates information necessary to identify and access a specific register in a |
7784 | // register display |
7785 | class RegisterInfo |
7786 | { |
7787 | public: |
7788 | // constructor for an instance of RegisterInfo |
7789 | // Arguments: |
7790 | // input: kNumber - value from CorDebugRegister to identify the register |
7791 | // addr - address in remote register display that holds the value |
7792 | // output: no out parameters, but this instance of RegisterInfo has been initialized |
7793 | RegisterInfo(const CorDebugRegister kNumber, CORDB_ADDRESS addr, SIZE_T value): |
7794 | m_kRegNumber((CorDebugRegister)kNumber), |
7795 | m_regAddr(addr), |
7796 | m_regValue(value) |
7797 | {}; |
7798 | |
7799 | |
7800 | // copy constructor |
7801 | // Arguments: |
7802 | // input: regInfo - register info from which the values for this instance will come |
7803 | // output: no out parameters, but this instance of RegisterInfo has been initialized |
7804 | RegisterInfo(const RegisterInfo * pRegInfo): |
7805 | m_kRegNumber(pRegInfo->m_kRegNumber), |
7806 | m_regAddr(pRegInfo->m_regAddr), |
7807 | m_regValue(pRegInfo->m_regValue) |
7808 | {}; |
7809 | |
7810 | |
7811 | //------------------------------------- |
7812 | // data members |
7813 | //------------------------------------- |
7814 | |
7815 | // enumeration value to identify the register, e.g., REGISTER_X86_EAX, or REGISTER_AMD64_XMM0 |
7816 | CorDebugRegister m_kRegNumber; |
7817 | |
7818 | // address in a context or frame register display of the register value |
7819 | CORDB_ADDRESS m_regAddr; |
7820 | |
7821 | // the actual value of the register |
7822 | SIZE_T m_regValue; |
7823 | }; // class RegisterInfo |
7824 | |
7825 | // class EnregisteredValueHome: abstract class to encapsulate basic information for a register value, and |
7826 | // serve as a base class for values residing in register-based locations, such as a single register, a |
7827 | // register pair, or a register and memory location. |
7828 | class EnregisteredValueHome |
7829 | { |
7830 | public: |
7831 | |
7832 | // constructor to initialize an instance of EnregisteredValueHome |
7833 | EnregisteredValueHome(const CordbNativeFrame * pFrame); |
7834 | |
7835 | virtual ~EnregisteredValueHome() {} |
7836 | |
7837 | // virtual "copy constructor" to make a copy of "this" to be owned by a different instance of |
7838 | // Cordb*Value. If an instance of CordbVCObjectValue represents an enregistered value class, it means |
7839 | // there is a single field. This implies that the register for the CordbVCObject instance is the same as |
7840 | // the register for its field. When we create a Cordb*Value to represent this field, we need to make a |
7841 | // copy of the EnregisteredValueHome belonging to the CordbVCObject instance to become the |
7842 | // EnregisteredValueHome of the Cord*Value representing the field. |
7843 | // returns: |
7844 | // a new cloned copy of this object, allocated on the heap. |
7845 | // Caller is responsible for deleting the memory (using the standard delete operator). |
7846 | // note: |
7847 | // C++ allows derived implementations to differ on return type, thus allowing |
7848 | // derived impls to return the cloned copy as its actual derived type, and not just as a base type. |
7849 | |
7850 | |
7851 | virtual |
7852 | EnregisteredValueHome * Clone() const = 0; |
7853 | |
7854 | // set a remote enregistered location to a new value |
7855 | // Arguments: |
7856 | // input: pNewValue - buffer containing the new value along with its size |
7857 | // pContext - context from which the value comes |
7858 | // fIsSigned - indicates whether the value is signed or not. The value provided may be smaller than |
7859 | // a register, in which case we'll need to extend it to a full register width. To do this |
7860 | // correctly, we need to know whether to sign extend or zero extend. Currently, only |
7861 | // the RegValueHome virtual function uses this, but we may need it if we introduce |
7862 | // types that don't completely occupy the size of two registers. |
7863 | // output: updates the remote enregistered value on success |
7864 | // Note: Throws E_FAIL for invalid input or various HRESULTs from an |
7865 | // unsuccessful call to WriteProcessMemory |
7866 | virtual |
7867 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned) = 0; |
7868 | |
7869 | // Gets an enregistered value and returns it to the caller |
7870 | // Arguments: |
7871 | // input: pValueOutBuffer - buffer in which to return the value, along with its size |
7872 | // output: pValueOutBuffer - filled with the value |
7873 | // Note: Throws E_NOTIMPL for attempts to get an enregistered value for a float register |
7874 | // (implementation for derived class FloatRegValueHome) |
7875 | virtual |
7876 | void GetEnregisteredValue(MemoryRange valueOutBuffer) = 0; |
7877 | |
7878 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
7879 | // instance of a derived class of EnregisteredValueHome |
7880 | // Arguments: input: none--uses fields of "this" |
7881 | // output: pRegAddr - address of an instance of RemoteAddress with field values set to corresponding |
7882 | // field values of "this" |
7883 | virtual |
7884 | void CopyToIPCEType(RemoteAddress * pRegAddr) = 0; |
7885 | |
7886 | // accessor |
7887 | const CordbNativeFrame * GetFrame() const { return m_pFrame; }; |
7888 | |
7889 | //------------------------------------- |
7890 | // data members |
7891 | //------------------------------------- |
7892 | protected: |
7893 | // The frame on which the value resides |
7894 | const CordbNativeFrame * m_pFrame; |
7895 | |
7896 | }; // class EnregisteredValueHome |
7897 | |
7898 | typedef NewHolder<EnregisteredValueHome> EnregisteredValueHomeHolder; |
7899 | |
7900 | // class RegValueHome: encapsulates basic information for a value that resides in a single register |
7901 | // and serves as a base class for values residing in a register pair. |
7902 | class RegValueHome: public EnregisteredValueHome |
7903 | { |
7904 | public: |
7905 | |
7906 | // initializing constructor |
7907 | // Arguments: |
7908 | // input: pFrame - frame to which the value belongs |
7909 | // regNum - enumeration value corresponding to the particular hardware register in |
7910 | // which the value resides |
7911 | // regAddr - remote address within a register display (in a context or frame) of the |
7912 | // register value |
7913 | // output: no out parameters, but the instance has been initialized |
7914 | RegValueHome(const CordbNativeFrame * pFrame, |
7915 | CorDebugRegister regNum): |
7916 | EnregisteredValueHome(pFrame), |
7917 | m_reg1Info(regNum, |
7918 | pFrame->GetLeftSideAddressOfRegister(regNum), |
7919 | *(pFrame->GetAddressOfRegister(regNum))) |
7920 | {}; |
7921 | |
7922 | // copy constructor |
7923 | // Arguments: |
7924 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
7925 | // instance will come |
7926 | // output: no out parameters, but the instance has been initialized |
7927 | RegValueHome(const RegValueHome * pRemoteRegAddr): |
7928 | EnregisteredValueHome(pRemoteRegAddr->m_pFrame), |
7929 | m_reg1Info(pRemoteRegAddr->m_reg1Info) |
7930 | {}; |
7931 | |
7932 | // make a copy of this instance of RegValueHome |
7933 | virtual |
7934 | RegValueHome * Clone() const { return new RegValueHome(*this); }; |
7935 | |
7936 | // updates a register in a given context, and in the regdisplay of a given frame. |
7937 | void SetContextRegister(DT_CONTEXT * pContext, |
7938 | CorDebugRegister regNum, |
7939 | SIZE_T newVal); |
7940 | |
7941 | // set the value of a remote enregistered value |
7942 | virtual |
7943 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); |
7944 | |
7945 | // Gets an enregistered value and returns it to the caller |
7946 | virtual |
7947 | void GetEnregisteredValue(MemoryRange valueOutBuffer); |
7948 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
7949 | // instance of a derived class of RegValueHome |
7950 | virtual |
7951 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
7952 | |
7953 | //------------------------------------- |
7954 | // data members |
7955 | //------------------------------------- |
7956 | protected: |
7957 | // The information for the register in which the value resides. |
7958 | const RegisterInfo m_reg1Info; |
7959 | }; // class RegValueHome |
7960 | |
7961 | // class RegRegValueHome |
7962 | // derived class to add a second register for values that live in a pair of registers |
7963 | class RegRegValueHome: public RegValueHome |
7964 | { |
7965 | public: |
7966 | // initializing constructor |
7967 | // Arguments: |
7968 | // input: pFrame - frame to which the value belongs |
7969 | // reg1Num - enumeration value corresponding to the first particular hardware register in |
7970 | // which the value resides |
7971 | // reg1Addr - remote address within a register display (in a context or frame) of the |
7972 | // first register |
7973 | // reg2Num - enumeration value corresponding to the second particular hardware register in |
7974 | // which the value resides |
7975 | // reg2Addr - remote address within a register display (in a context or frame) of the |
7976 | // second register |
7977 | // output: no out parameters, but the instance has been initialized |
7978 | RegRegValueHome(const CordbNativeFrame * pFrame, |
7979 | CorDebugRegister reg1Num, |
7980 | CorDebugRegister reg2Num): |
7981 | RegValueHome(pFrame, reg1Num), |
7982 | m_reg2Info(reg2Num, |
7983 | pFrame->GetLeftSideAddressOfRegister(reg2Num), |
7984 | *(pFrame->GetAddressOfRegister(reg2Num))) |
7985 | {}; |
7986 | |
7987 | // copy constructor |
7988 | // Arguments: |
7989 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
7990 | // instance will come |
7991 | // output: no out parameters, but the instance has been initialized |
7992 | RegRegValueHome(const RegRegValueHome * pRemoteRegAddr): |
7993 | RegValueHome(pRemoteRegAddr), |
7994 | m_reg2Info(pRemoteRegAddr->m_reg2Info) |
7995 | {}; |
7996 | |
7997 | // make a copy of this instance of RegRegValueHome |
7998 | virtual |
7999 | RegRegValueHome * Clone() const { return new RegRegValueHome(*this); }; |
8000 | |
8001 | // set the value of a remote enregistered value |
8002 | virtual |
8003 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); |
8004 | |
8005 | // Gets an enregistered value and returns it to the caller |
8006 | virtual |
8007 | void GetEnregisteredValue(MemoryRange valueOutBuffer); |
8008 | |
8009 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
8010 | // instance of a derived class of EnregisteredValueHome |
8011 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8012 | |
8013 | //------------------------------------- |
8014 | // data members |
8015 | //------------------------------------- |
8016 | |
8017 | protected: |
8018 | // The information for the second of two registers in which the value resides. |
8019 | const RegisterInfo m_reg2Info; |
8020 | }; // class RegRegValueHome |
8021 | |
8022 | // class RegAndMemBaseValueHome |
8023 | // derived from RegValueHome, this class is also a base class for RegMemValueHome |
8024 | // and MemRegValueHome, which add a memory location for reg-mem or mem-reg values |
8025 | class RegAndMemBaseValueHome: public RegValueHome |
8026 | { |
8027 | public: |
8028 | // initializing constructor |
8029 | // Arguments: |
8030 | // input: pFrame - frame to which the value belongs |
8031 | // reg1Num - enumeration value corresponding to the first particular hardware register in |
8032 | // which the value resides |
8033 | // reg1Addr - remote address within a register display (in a context or frame) of the |
8034 | // register component of the value |
8035 | // memAddr - remote address for the memory component of the value |
8036 | // output: no out parameters, but the instance has been initialized |
8037 | RegAndMemBaseValueHome(const CordbNativeFrame * pFrame, |
8038 | CorDebugRegister reg1Num, |
8039 | CORDB_ADDRESS memAddr): |
8040 | RegValueHome(pFrame, reg1Num), |
8041 | m_memAddr(memAddr) |
8042 | {}; |
8043 | |
8044 | // copy constructor |
8045 | // Arguments: |
8046 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
8047 | // instance will come |
8048 | // output: no out parameters, but the instance has been initialized |
8049 | RegAndMemBaseValueHome(const RegAndMemBaseValueHome * pRemoteRegAddr): |
8050 | RegValueHome(pRemoteRegAddr), |
8051 | m_memAddr() |
8052 | {}; |
8053 | |
8054 | // make a copy of this instance of RegRegValueHome |
8055 | virtual |
8056 | RegAndMemBaseValueHome * Clone() const = 0; |
8057 | |
8058 | // set the value of a remote enregistered value |
8059 | virtual |
8060 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * DT_pContext, bool fIsSigned) = 0; |
8061 | |
8062 | // Gets an enregistered value and returns it to the caller |
8063 | virtual |
8064 | void GetEnregisteredValue(MemoryRange valueOutBuffer) = 0; |
8065 | |
8066 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
8067 | // instance of a derived class of EnregisteredValueHome |
8068 | virtual |
8069 | void CopyToIPCEType(RemoteAddress * pRegAddr) = 0; |
8070 | |
8071 | //------------------------------------- |
8072 | // data members |
8073 | //------------------------------------- |
8074 | |
8075 | protected: |
8076 | // remote address for the memory component of the value |
8077 | CORDB_ADDRESS m_memAddr; |
8078 | |
8079 | }; // class RegAndMemBaseValueHome; |
8080 | |
8081 | // class RegMemValueHome |
8082 | // type derived from abstract class RegAndMemBaseValueHome to represent a Register/Memory location where the |
8083 | // high order part of the value is kept in a register, and the low order part is kept in memory |
8084 | class RegMemValueHome: public RegAndMemBaseValueHome |
8085 | { |
8086 | public: |
8087 | |
8088 | // initializing constructor |
8089 | // Arguments: |
8090 | // input: pFrame - frame to which the value belongs |
8091 | // reg1Num - enumeration value corresponding to the first particular hardware register in |
8092 | // which the value resides |
8093 | // reg1Addr - remote address within a register display (in a context or frame) of the |
8094 | // register component of the value |
8095 | // memAddr - remote address for the memory component of the value |
8096 | // output: no out parameters, but the instance has been initialized |
8097 | RegMemValueHome(const CordbNativeFrame * pFrame, |
8098 | CorDebugRegister reg1Num, |
8099 | CORDB_ADDRESS memAddr): |
8100 | RegAndMemBaseValueHome(pFrame, reg1Num, memAddr) |
8101 | {}; |
8102 | |
8103 | // copy constructor |
8104 | // Arguments: |
8105 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
8106 | // instance will come |
8107 | // output: no out parameters, but the instance has been initialized |
8108 | RegMemValueHome(const RegMemValueHome * pRemoteRegAddr): |
8109 | RegAndMemBaseValueHome(pRemoteRegAddr) |
8110 | {}; |
8111 | |
8112 | // make a copy of this instance of RegMemValueHome |
8113 | virtual |
8114 | RegMemValueHome * Clone() const { return new RegMemValueHome(*this); }; |
8115 | |
8116 | // set the value of a remote enregistered value |
8117 | virtual |
8118 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); |
8119 | |
8120 | // Gets an enregistered value and returns it to the caller |
8121 | virtual |
8122 | void GetEnregisteredValue(MemoryRange valueOutBuffer); |
8123 | |
8124 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
8125 | // instance of a derived class of EnregisteredValueHome |
8126 | virtual |
8127 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8128 | |
8129 | }; // class RegMemValueHome; |
8130 | |
8131 | // class MemRegValueHome |
8132 | // type derived from abstract class RegAndMemBaseValueHome to represent a Register/Memory location where the |
8133 | // low order part of the value is kept in a register, and the high order part is kept in memory |
8134 | class MemRegValueHome: public RegAndMemBaseValueHome |
8135 | { |
8136 | public: |
8137 | |
8138 | // initializing constructor |
8139 | // Arguments: |
8140 | // input: pFrame - frame to which the value belongs |
8141 | // reg1Num - enumeration value corresponding to the first particular hardware register in |
8142 | // which the value resides |
8143 | // reg1Addr - remote address within a register display (in a context or frame) of the |
8144 | // register component of the value |
8145 | // memAddr - remote address for the memory component of the value |
8146 | // output: no out parameters, but the instance has been initialized |
8147 | MemRegValueHome(const CordbNativeFrame * pFrame, |
8148 | CorDebugRegister reg1Num, |
8149 | CORDB_ADDRESS memAddr): |
8150 | RegAndMemBaseValueHome(pFrame, reg1Num, memAddr) |
8151 | {}; |
8152 | |
8153 | // copy constructor |
8154 | // Arguments: |
8155 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
8156 | // instance will come |
8157 | // output: no out parameters, but the instance has been initialized |
8158 | MemRegValueHome(const MemRegValueHome * pRemoteRegAddr): |
8159 | RegAndMemBaseValueHome(pRemoteRegAddr) |
8160 | {}; |
8161 | |
8162 | // make a copy of this instance of MemRegValueHome |
8163 | virtual |
8164 | MemRegValueHome * Clone() const { return new MemRegValueHome(*this); }; |
8165 | |
8166 | // set the value of a remote enregistered value |
8167 | virtual |
8168 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); |
8169 | |
8170 | // Gets an enregistered value and returns it to the caller |
8171 | virtual |
8172 | void GetEnregisteredValue(MemoryRange valueOutBuffer); |
8173 | |
8174 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
8175 | // instance of a derived class of EnregisteredValueHome |
8176 | virtual |
8177 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8178 | |
8179 | }; // class MemRegValueHome; |
8180 | |
8181 | // class FloatRegValueHome |
8182 | // derived class to add an index into the FP register stack for a floating point value |
8183 | class FloatRegValueHome: public EnregisteredValueHome |
8184 | { |
8185 | public: |
8186 | // initializing constructor |
8187 | // Arguments: |
8188 | // input: pFrame - frame to which the value belongs |
8189 | // index - index into the floating point stack where the value resides |
8190 | // output: no out parameters, but the instance has been initialized |
8191 | FloatRegValueHome(const CordbNativeFrame * pFrame, |
8192 | DWORD index): |
8193 | EnregisteredValueHome(pFrame), |
8194 | m_floatIndex(index) |
8195 | {}; |
8196 | |
8197 | // copy constructor |
8198 | // Arguments: |
8199 | // input: pRemoteRegAddr - instance of a remote register address from which the values for this |
8200 | // instance will come |
8201 | // output: no out parameters, but the instance has been initialized |
8202 | FloatRegValueHome(const FloatRegValueHome * pRemoteRegAddr): |
8203 | EnregisteredValueHome(pRemoteRegAddr->m_pFrame), |
8204 | m_floatIndex(pRemoteRegAddr->m_floatIndex) |
8205 | {}; |
8206 | |
8207 | // make a copy of this instance of FloatRegValueHome |
8208 | virtual |
8209 | FloatRegValueHome * Clone() const { return new FloatRegValueHome(*this); }; |
8210 | |
8211 | // set the value of a remote enregistered value |
8212 | virtual |
8213 | void SetEnregisteredValue(MemoryRange newValue, DT_CONTEXT * pContext, bool fIsSigned); |
8214 | |
8215 | // Gets an enregistered value and returns it to the caller |
8216 | virtual |
8217 | void GetEnregisteredValue(MemoryRange valueOutBuffer); |
8218 | |
8219 | // initialize an instance of RemoteAddress for use in an IPC event buffer with values from this |
8220 | // instance of a derived class of EnregisteredValueHome |
8221 | virtual |
8222 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8223 | |
8224 | //------------------------------------- |
8225 | // data members |
8226 | //------------------------------------- |
8227 | |
8228 | protected: |
8229 | // index into the FP registers for the register in which the floating point value resides |
8230 | const DWORD m_floatIndex; |
8231 | }; // class FloatRegValueHome |
8232 | |
8233 | // ---------------------------------------------------------------------------- |
8234 | // Type hierarchy for value locations |
8235 | // ValueHome |
8236 | // | | | |
8237 | // ------------------ | ------------------- |
8238 | // | | | |
8239 | // RemoteValueHome RegisterValueHome HandleValueHome |
8240 | // | | |
8241 | // -------- ------- |
8242 | // | | |
8243 | // VCRemoteValueHome RefRemoteValueHome |
8244 | // |
8245 | // ValueHome: abstract base class, provides remote read and write utilities |
8246 | // RemoteValueHome: used for CordbObjectValue, CordbArrayValue, and CordbBoxValue instances, |
8247 | // which have only remote locations, and for other ICDValues with a remote address |
8248 | // RegisterValueHome: used for CordbGenericValue and CordbReferenceValue instances with |
8249 | // only a register location |
8250 | // HandleValueHome: used for CordbReferenceValue instances with only an object handle |
8251 | // VCRemoteValueHome: used for CordbVCObjectValue instances to supply special operation CreateInternalValue for |
8252 | // value class objects with only a remote location |
8253 | // RefRemoteValueHome: used for CordbReferenceValue instances with only a remote location |
8254 | // |
8255 | // In addition, we have a special type for the ValueHome field for CordbReferenceValue instances: |
8256 | // RefValueHome. This will have a field of type ValueHome and will implement extra operations only relevant |
8257 | // for object references. |
8258 | // |
8259 | // ---------------------------------------------------------------------------- |
8260 | // |
8261 | class ValueHome |
8262 | { |
8263 | public: |
8264 | ValueHome(CordbProcess * pProcess): |
8265 | m_pProcess(pProcess) { _ASSERTE(pProcess != NULL); }; |
8266 | |
8267 | virtual |
8268 | ~ValueHome() {} |
8269 | |
8270 | // releases resources as necessary |
8271 | virtual |
8272 | void Clear() = 0; |
8273 | |
8274 | // gets the remote address for the value or returns NULL if none exists |
8275 | virtual |
8276 | CORDB_ADDRESS GetAddress() = 0; |
8277 | |
8278 | // Gets a value and returns it in dest |
8279 | // Argument: |
8280 | // input: none (uses fields of the instance) |
8281 | // output: dest - buffer containing the value retrieved as long as the returned HRESULT doesn't |
8282 | // indicate an error. |
8283 | // Note: Throws errors from read process memory operation or GetThreadContext operation |
8284 | virtual |
8285 | void GetValue(MemoryRange dest) = 0; |
8286 | |
8287 | // Sets a location to the value provided in src |
8288 | // Arguments: |
8289 | // input: src - buffer containing the new value to be set--memory for this buffer is owned by the caller |
8290 | // pType - type information about the value |
8291 | // output: none, but on success, changes m_remoteValue to hold the new value |
8292 | // Note: Throws errors from SafeWriteBuffer |
8293 | virtual |
8294 | void SetValue(MemoryRange src, CordbType * pType) = 0; |
8295 | |
8296 | // creates an ICDValue for a field or array element or for the value type of a boxed object |
8297 | // Arguments: |
8298 | // input: pType - type of the internal value |
8299 | // offset - offset to the internal value |
8300 | // localAddress - address of thelogical buffer within the parent class' local cached |
8301 | // copy that holds the internal element |
8302 | // size - size of the internal value |
8303 | // output: ppValue - the newly created ICDValue instance |
8304 | // Note: Throws for a variety of possible failures: OOM, E_FAIL, errors from |
8305 | // ReadProcessMemory. |
8306 | virtual |
8307 | void CreateInternalValue(CordbType * pType, |
8308 | SIZE_T offset, |
8309 | void * localAddress, |
8310 | ULONG32 size, |
8311 | ICorDebugValue ** ppValue) = 0; |
8312 | |
8313 | // Gets the value of a field or element of an existing ICDValue instance and returns it in dest |
8314 | // Arguments |
8315 | // input: offset - offset within the value to the internal field or element |
8316 | // output: dest - buffer to hold the value--memory for this buffer is owned by the caller |
8317 | // Note: Throws process memory write errors |
8318 | virtual |
8319 | void GetInternalValue(MemoryRange dest, SIZE_T offset) = 0; |
8320 | |
8321 | // copies register information from this to a RemoteAddress instance for FuncEval |
8322 | // Arguments: |
8323 | // output: pRegAddr - copy of information in m_pRemoteRegAddr, converted to |
8324 | // an instance of RemoteAddress |
8325 | virtual |
8326 | void CopyToIPCEType(RemoteAddress * pRegAddr) = 0; |
8327 | |
8328 | private: |
8329 | // unimplemented copy constructor to prevent passing by value |
8330 | ValueHome(ValueHome * pValHome); |
8331 | |
8332 | protected: |
8333 | // -------------- |
8334 | // data member |
8335 | // -------------- |
8336 | CordbProcess * m_pProcess; |
8337 | }; // class ValueHome |
8338 | |
8339 | // ============================================================================ |
8340 | // RemoteValueHome class |
8341 | // ============================================================================ |
8342 | // to be used for CordbObjectValue, CordbArrayValue, and CordbBoxValue, none of which ever have anything but |
8343 | // a remote address |
8344 | class RemoteValueHome: public ValueHome |
8345 | { |
8346 | public: |
8347 | // constructor |
8348 | // Note: It's possible that remoteValue.pAddress may be NULL--FuncEval makes |
8349 | // empty GenericValues for literals in which case we would have neither a remote address nor a |
8350 | // register address |
8351 | RemoteValueHome(CordbProcess * pProcess, TargetBuffer remoteValue); |
8352 | |
8353 | // gets the remote address for the value |
8354 | virtual |
8355 | CORDB_ADDRESS GetAddress() { return m_remoteValue.pAddress; }; |
8356 | |
8357 | // releases resources as necessary |
8358 | virtual |
8359 | void Clear() {}; |
8360 | |
8361 | // Gets a value and returns it in dest |
8362 | virtual |
8363 | void GetValue(MemoryRange dest); |
8364 | |
8365 | // Sets a location to the value provided in src |
8366 | virtual |
8367 | void SetValue(MemoryRange src, CordbType * pType); |
8368 | |
8369 | // creates an ICDValue for a field or array element or for the value type of a boxed object |
8370 | virtual |
8371 | void CreateInternalValue(CordbType * pType, |
8372 | SIZE_T offset, |
8373 | void * localAddress, |
8374 | ULONG32 size, |
8375 | ICorDebugValue ** ppValue); |
8376 | |
8377 | // Gets the value of a field or element of an existing ICDValue instance and returns it in dest |
8378 | virtual |
8379 | void GetInternalValue(MemoryRange dest, SIZE_T offset); |
8380 | |
8381 | // copies register information from this to a RemoteAddress instance for FuncEval |
8382 | virtual |
8383 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8384 | |
8385 | |
8386 | // ---------------- |
8387 | // data member |
8388 | // ---------------- |
8389 | |
8390 | protected: |
8391 | TargetBuffer m_remoteValue; |
8392 | }; // class RemoteValueHome |
8393 | |
8394 | // ============================================================================ |
8395 | // RegisterValueHome class |
8396 | // ============================================================================ |
8397 | // for values that may either have a remote location or be enregistered-- |
8398 | // to be used for CordbGenericValue, and as base for CordbVCObjectValue and CordbReferenceValue |
8399 | class RegisterValueHome: public ValueHome |
8400 | { |
8401 | public: |
8402 | // constructor |
8403 | RegisterValueHome(CordbProcess * pProcess, |
8404 | EnregisteredValueHomeHolder * ppRemoteRegAddr); |
8405 | |
8406 | // clean up resources |
8407 | virtual |
8408 | void Clear(); |
8409 | |
8410 | // gets the remote address for the value or returns NULL if none exists |
8411 | virtual |
8412 | CORDB_ADDRESS GetAddress() { return NULL; }; |
8413 | |
8414 | // Gets a value and returns it in dest |
8415 | virtual |
8416 | void GetValue(MemoryRange dest); |
8417 | |
8418 | // Sets a location to the value provided in src |
8419 | virtual |
8420 | void SetValue(MemoryRange src, CordbType * pType); |
8421 | |
8422 | // creates an ICDValue for a field or array element or for the value type of a boxed object |
8423 | virtual |
8424 | void CreateInternalValue(CordbType * pType, |
8425 | SIZE_T offset, |
8426 | void * localAddress, |
8427 | ULONG32 size, |
8428 | ICorDebugValue ** ppValue); |
8429 | |
8430 | // Gets the value of a field or element of an existing ICDValue instance and returns it in dest |
8431 | virtual |
8432 | void GetInternalValue(MemoryRange dest, SIZE_T offset); |
8433 | |
8434 | // copies the register information from this to a RemoteAddress instance |
8435 | virtual |
8436 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8437 | |
8438 | protected: |
8439 | |
8440 | // sets a remote enregistered location to a new value |
8441 | void SetEnregisteredValue(MemoryRange src, bool fIsSigned); |
8442 | |
8443 | // gets a value from an enregistered location |
8444 | void GetEnregisteredValue(MemoryRange dest); |
8445 | |
8446 | bool IsSigned(CorElementType elementType); |
8447 | |
8448 | // ---------------- |
8449 | // data member |
8450 | // ---------------- |
8451 | |
8452 | protected: |
8453 | // Left Side register location info for various kinds of (partly) enregistered values. |
8454 | EnregisteredValueHome * m_pRemoteRegAddr; |
8455 | |
8456 | }; // class RegisterValueHome |
8457 | |
8458 | // ============================================================================ |
8459 | // HandleValueHome class |
8460 | // ============================================================================ |
8461 | |
8462 | class HandleValueHome: public ValueHome |
8463 | { |
8464 | public: |
8465 | // constructor |
8466 | // Arguments: |
8467 | // input: pProcess - process to which the value belongs |
8468 | // vmObjHandle - objectHandle holding the object address |
8469 | HandleValueHome(CordbProcess * pProcess, VMPTR_OBJECTHANDLE vmObjHandle): |
8470 | ValueHome(pProcess), |
8471 | m_vmObjectHandle(vmObjHandle) {}; |
8472 | |
8473 | // releases resources as necessary |
8474 | virtual |
8475 | void Clear() {}; |
8476 | |
8477 | // gets the remote address for the value or returns NULL if none exists |
8478 | virtual |
8479 | CORDB_ADDRESS GetAddress(); |
8480 | |
8481 | // Gets a value and returns it in dest |
8482 | virtual |
8483 | void GetValue(MemoryRange dest); |
8484 | |
8485 | // Sets a location to the value provided in src |
8486 | virtual |
8487 | void SetValue(MemoryRange src, CordbType * pType); |
8488 | |
8489 | // creates an ICDValue for a field or array element or for the value type of a boxed object |
8490 | virtual |
8491 | void CreateInternalValue(CordbType * pType, |
8492 | SIZE_T offset, |
8493 | void * localAddress, |
8494 | ULONG32 size, |
8495 | ICorDebugValue ** ppValue); |
8496 | |
8497 | // Gets the value of a field or element of an existing ICDValue instance and returns it in dest |
8498 | virtual |
8499 | void GetInternalValue(MemoryRange dest, SIZE_T offset); |
8500 | |
8501 | // copies the register information from this to a RemoteAddress instance |
8502 | virtual |
8503 | void CopyToIPCEType(RemoteAddress * pRegAddr); |
8504 | |
8505 | // ---------------- |
8506 | // data member |
8507 | // ---------------- |
8508 | private: |
8509 | VMPTR_OBJECTHANDLE m_vmObjectHandle; |
8510 | }; // class HandleValueHome; |
8511 | |
8512 | // ============================================================================ |
8513 | // VCRemoteValueHome class |
8514 | // ============================================================================ |
8515 | // used only for CordbVCObjectValue |
8516 | class VCRemoteValueHome: public RemoteValueHome |
8517 | { |
8518 | public: |
8519 | // constructor |
8520 | VCRemoteValueHome(CordbProcess * pProcess, |
8521 | TargetBuffer remoteValue): |
8522 | RemoteValueHome(pProcess, remoteValue) {}; |
8523 | |
8524 | // Sets a location to the value provided in src |
8525 | virtual |
8526 | void SetValue(MemoryRange src, CordbType * pType); |
8527 | |
8528 | }; // class VCRemoteValueHome |
8529 | |
8530 | // ============================================================================ |
8531 | // RefRemoteValueHome class |
8532 | // ============================================================================ |
8533 | |
8534 | // used only for CordbReferenceValue |
8535 | class RefRemoteValueHome: public RemoteValueHome |
8536 | { |
8537 | public: |
8538 | // constructor |
8539 | // Arguments |
8540 | RefRemoteValueHome(CordbProcess * pProcess, |
8541 | TargetBuffer remoteValue); |
8542 | |
8543 | // Sets a location to the value provided in src |
8544 | virtual |
8545 | void SetValue(MemoryRange src, CordbType * pType); |
8546 | |
8547 | }; // class RefRemoteValueHome |
8548 | |
8549 | // ============================================================================ |
8550 | // RefValueHome class |
8551 | // ============================================================================ |
8552 | |
8553 | // abstract superclass for derivations RefRemoteValueHome and RefRegValueHome |
8554 | class RefValueHome |
8555 | { |
8556 | public: |
8557 | // constructor |
8558 | RefValueHome() { m_pHome = NULL; m_fNullObjHandle = true; }; |
8559 | |
8560 | // constructor |
8561 | RefValueHome(CordbProcess * pProcess, |
8562 | TargetBuffer remoteValue, |
8563 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
8564 | VMPTR_OBJECTHANDLE vmObjHandle); |
8565 | |
8566 | // indicates whether the object handle is null |
8567 | bool ObjHandleIsNull() { return m_fNullObjHandle; }; |
8568 | void SetObjHandleFlag(bool isNull) { m_fNullObjHandle = isNull; }; |
8569 | |
8570 | // ---------------- |
8571 | // data members |
8572 | // ---------------- |
8573 | // appropriate instantiation of ValueHome |
8574 | ValueHome * m_pHome; |
8575 | |
8576 | private: |
8577 | // true iff m_pHome is an instantiation of RemoteValueHome or RegisterValueHome |
8578 | bool m_fNullObjHandle; |
8579 | }; // class RefValueHome |
8580 | |
8581 | typedef enum {kUnboxed, kBoxed} BoxedValue; |
8582 | #define EMPTY_BUFFER TargetBuffer(PTR_TO_CORDB_ADDRESS((void *)NULL), 0) |
8583 | |
8584 | /* ------------------------------------------------------------------------- * |
8585 | * Variable Home class |
8586 | * ------------------------------------------------------------------------- */ |
8587 | class CordbVariableHome : public CordbBase, public ICorDebugVariableHome |
8588 | { |
8589 | public: |
8590 | CordbVariableHome(CordbNativeCode *pCode, |
8591 | const ICorDebugInfo::NativeVarInfo nativeVarInfo, |
8592 | BOOL isLoc, |
8593 | ULONG index); |
8594 | ~CordbVariableHome(); |
8595 | virtual void Neuter(); |
8596 | |
8597 | #ifdef _DEBUG |
8598 | virtual const char * DbgGetName() { return "CordbVariableHome" ; } |
8599 | #endif |
8600 | |
8601 | //----------------------------------------------------------- |
8602 | // IUnknown |
8603 | //----------------------------------------------------------- |
8604 | ULONG STDMETHODCALLTYPE AddRef() |
8605 | { |
8606 | return (BaseAddRef()); |
8607 | } |
8608 | ULONG STDMETHODCALLTYPE Release() |
8609 | { |
8610 | return (BaseRelease()); |
8611 | } |
8612 | |
8613 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
8614 | |
8615 | //----------------------------------------------------------- |
8616 | // ICorDebugVariableHome |
8617 | //----------------------------------------------------------- |
8618 | |
8619 | COM_METHOD GetCode(ICorDebugCode **ppCode); |
8620 | |
8621 | COM_METHOD GetSlotIndex(ULONG32 *pSlotIndex); |
8622 | |
8623 | COM_METHOD GetArgumentIndex(ULONG32* pArgumentIndex); |
8624 | |
8625 | COM_METHOD GetLiveRange(ULONG32* pStartOffset, |
8626 | ULONG32 *pEndOffset); |
8627 | |
8628 | COM_METHOD GetLocationType(VariableLocationType *pLocationType); |
8629 | |
8630 | COM_METHOD GetRegister(CorDebugRegister *pRegister); |
8631 | |
8632 | COM_METHOD GetOffset(LONG *pOffset); |
8633 | private: |
8634 | RSSmartPtr<CordbNativeCode> m_pCode; |
8635 | ICorDebugInfo::NativeVarInfo m_nativeVarInfo; |
8636 | BOOL m_isLocal; |
8637 | ULONG m_index; |
8638 | }; |
8639 | |
8640 | |
8641 | // for an inheritance graph of the ICDValue types, // See file:./ICorDebugValueTypes.vsd for a diagram of the types. |
8642 | /* ------------------------------------------------------------------------- * |
8643 | * Value class |
8644 | * ------------------------------------------------------------------------- */ |
8645 | |
8646 | class CordbValue : public CordbBase |
8647 | { |
8648 | public: |
8649 | //----------------------------------------------------------- |
8650 | // Constructor/destructor |
8651 | //----------------------------------------------------------- |
8652 | CordbValue(CordbAppDomain * appdomain, |
8653 | CordbType * type, |
8654 | CORDB_ADDRESS id, |
8655 | bool isLiteral, |
8656 | NeuterList * pList = NULL); |
8657 | |
8658 | virtual ~CordbValue(); |
8659 | virtual void Neuter(); |
8660 | |
8661 | //----------------------------------------------------------- |
8662 | // IUnknown |
8663 | //----------------------------------------------------------- |
8664 | |
8665 | ULONG STDMETHODCALLTYPE AddRef() |
8666 | { |
8667 | return (BaseAddRef()); |
8668 | } |
8669 | ULONG STDMETHODCALLTYPE Release() |
8670 | { |
8671 | return (BaseRelease()); |
8672 | } |
8673 | |
8674 | //----------------------------------------------------------- |
8675 | // ICorDebugValue |
8676 | //----------------------------------------------------------- |
8677 | |
8678 | COM_METHOD GetType(CorElementType *pType) |
8679 | { |
8680 | LIMITED_METHOD_CONTRACT; |
8681 | |
8682 | FAIL_IF_NEUTERED(this); |
8683 | VALIDATE_POINTER_TO_OBJECT(pType, CorElementType *); |
8684 | |
8685 | *pType = m_type->m_elementType; |
8686 | return (S_OK); |
8687 | } |
8688 | |
8689 | COM_METHOD GetSize(ULONG32 *pSize) |
8690 | { |
8691 | LIMITED_METHOD_CONTRACT; |
8692 | |
8693 | FAIL_IF_NEUTERED(this); |
8694 | VALIDATE_POINTER_TO_OBJECT(pSize, ULONG32 *); |
8695 | |
8696 | if (m_size > ULONG_MAX) |
8697 | { |
8698 | *pSize = ULONG_MAX; |
8699 | return (COR_E_OVERFLOW); |
8700 | } |
8701 | |
8702 | *pSize = (ULONG)m_size; |
8703 | return (S_OK); |
8704 | } |
8705 | |
8706 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint); |
8707 | |
8708 | //----------------------------------------------------------- |
8709 | // ICorDebugValue2 |
8710 | //----------------------------------------------------------- |
8711 | |
8712 | COM_METHOD GetExactType(ICorDebugType **ppType); |
8713 | |
8714 | //----------------------------------------------------------- |
8715 | // ICorDebugValue3 |
8716 | //----------------------------------------------------------- |
8717 | |
8718 | COM_METHOD GetSize64(ULONG64 *pSize) |
8719 | { |
8720 | LIMITED_METHOD_CONTRACT; |
8721 | |
8722 | FAIL_IF_NEUTERED(this); |
8723 | VALIDATE_POINTER_TO_OBJECT(pSize, ULONG64 *); |
8724 | |
8725 | *pSize = m_size; |
8726 | return (S_OK); |
8727 | } |
8728 | |
8729 | virtual HRESULT STDMETHODCALLTYPE GetAddress(CORDB_ADDRESS *pAddress) = 0; |
8730 | |
8731 | //----------------------------------------------------------- |
8732 | // Methods not exported through COM |
8733 | //----------------------------------------------------------- |
8734 | |
8735 | // Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbGenericValue |
8736 | static |
8737 | void CreateGenericValue(CordbAppDomain * pAppdomain, |
8738 | CordbType * pType, |
8739 | TargetBuffer remoteValue, |
8740 | MemoryRange localValue, |
8741 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
8742 | ICorDebugValue** ppValue); |
8743 | |
8744 | // Helper for code:CordbValue::CreateValueByType. Create a new instance of CordbVCObjectValue or |
8745 | // CordbReferenceValue |
8746 | static |
8747 | void CreateVCObjOrRefValue(CordbAppDomain * pAppdomain, |
8748 | CordbType * pType, |
8749 | bool boxed, |
8750 | TargetBuffer remoteValue, |
8751 | MemoryRange localValue, |
8752 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
8753 | ICorDebugValue** ppValue); |
8754 | |
8755 | // Create the proper ICDValue instance based on the given element type. |
8756 | static void CreateValueByType(CordbAppDomain * appdomain, |
8757 | CordbType * type, |
8758 | bool boxed, |
8759 | TargetBuffer remoteValue, |
8760 | MemoryRange localValue, |
8761 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
8762 | ICorDebugValue** ppValue); |
8763 | |
8764 | // Create the proper ICDValue instance based on the given remote heap object |
8765 | static ICorDebugValue* CreateHeapValue(CordbAppDomain* pAppDomain, |
8766 | VMPTR_Object vmObj); |
8767 | |
8768 | |
8769 | // Returns a pointer to the ValueHome field of this instance of CordbValue if one exists or NULL |
8770 | // otherwise. Therefore, this also tells us indirectly whether this instance of CordbValue is also an |
8771 | // instance of one of its derived types and thus has a ValueHome field. |
8772 | virtual |
8773 | ValueHome * GetValueHome() { return NULL; }; |
8774 | |
8775 | static ULONG32 GetSizeForType(CordbType * pType, BoxedValue boxing); |
8776 | |
8777 | virtual CordbAppDomain *GetAppDomain() |
8778 | { |
8779 | return m_appdomain; |
8780 | } |
8781 | |
8782 | HRESULT InternalCreateHandle( |
8783 | CorDebugHandleType handleType, |
8784 | ICorDebugHandleValue ** ppHandle); |
8785 | |
8786 | //----------------------------------------------------------- |
8787 | // Data members |
8788 | //----------------------------------------------------------- |
8789 | |
8790 | public: |
8791 | CordbAppDomain * m_appdomain; |
8792 | RSSmartPtr<CordbType> m_type; |
8793 | |
8794 | // size of the value |
8795 | SIZE_T m_size; |
8796 | |
8797 | // true if the value is a RS fabrication. |
8798 | bool m_isLiteral; |
8799 | |
8800 | }; |
8801 | |
8802 | /* ------------------------------------------------------------------------- * |
8803 | * Value Breakpoint class |
8804 | * ------------------------------------------------------------------------- */ |
8805 | |
8806 | class CordbValueBreakpoint : public CordbBreakpoint, |
8807 | public ICorDebugValueBreakpoint |
8808 | { |
8809 | public: |
8810 | CordbValueBreakpoint(CordbValue *pValue); |
8811 | |
8812 | |
8813 | #ifdef _DEBUG |
8814 | virtual const char * DbgGetName() { return "CordbValueBreakpoint" ; } |
8815 | #endif |
8816 | |
8817 | //----------------------------------------------------------- |
8818 | // IUnknown |
8819 | //----------------------------------------------------------- |
8820 | |
8821 | ULONG STDMETHODCALLTYPE AddRef() |
8822 | { |
8823 | return (BaseAddRef()); |
8824 | } |
8825 | ULONG STDMETHODCALLTYPE Release() |
8826 | { |
8827 | return (BaseRelease()); |
8828 | } |
8829 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
8830 | |
8831 | //----------------------------------------------------------- |
8832 | // ICorDebugValueBreakpoint |
8833 | //----------------------------------------------------------- |
8834 | |
8835 | COM_METHOD GetValue(ICorDebugValue **ppValue); |
8836 | COM_METHOD Activate(BOOL bActive); |
8837 | COM_METHOD IsActive(BOOL *pbActive) |
8838 | { |
8839 | VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *); |
8840 | |
8841 | return BaseIsActive(pbActive); |
8842 | } |
8843 | |
8844 | //----------------------------------------------------------- |
8845 | // Non-COM methods |
8846 | //----------------------------------------------------------- |
8847 | |
8848 | void Disconnect(); |
8849 | |
8850 | public: |
8851 | CordbValue * m_value; |
8852 | }; |
8853 | |
8854 | /* ------------------------------------------------------------------------- * |
8855 | * Generic Value class |
8856 | * ------------------------------------------------------------------------- */ |
8857 | |
8858 | class CordbGenericValue : public CordbValue, public ICorDebugGenericValue, public ICorDebugValue2, public ICorDebugValue3 |
8859 | { |
8860 | public: |
8861 | CordbGenericValue(CordbAppDomain * appdomain, |
8862 | CordbType * type, |
8863 | TargetBuffer remoteValue, |
8864 | EnregisteredValueHomeHolder * ppRemoteRegAddr); |
8865 | |
8866 | CordbGenericValue(CordbType * pType); |
8867 | // destructor |
8868 | ~CordbGenericValue(); |
8869 | |
8870 | #ifdef _DEBUG |
8871 | virtual const char * DbgGetName() { return "CordbGenericValue" ; } |
8872 | #endif |
8873 | |
8874 | |
8875 | //----------------------------------------------------------- |
8876 | // IUnknown |
8877 | //----------------------------------------------------------- |
8878 | |
8879 | ULONG STDMETHODCALLTYPE AddRef() |
8880 | { |
8881 | return (BaseAddRef()); |
8882 | } |
8883 | ULONG STDMETHODCALLTYPE Release() |
8884 | { |
8885 | return (BaseRelease()); |
8886 | } |
8887 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
8888 | |
8889 | //----------------------------------------------------------- |
8890 | // ICorDebugValue |
8891 | //----------------------------------------------------------- |
8892 | |
8893 | // gets the type of the value |
8894 | // Arguments: |
8895 | // output: pType - the type of the value. The caller must guarantee that pType is non-null. |
8896 | // Return Value: S_OK on success, E_INVALIDARG on failure |
8897 | COM_METHOD GetType(CorElementType *pType) |
8898 | { |
8899 | return (CordbValue::GetType(pType)); |
8900 | } |
8901 | |
8902 | // gets the size of the value |
8903 | // Arguments: |
8904 | // output: pSize - the size of the value. The caller must guarantee that pSize is non-null. |
8905 | // Return Value: S_OK on success, E_INVALIDARG on failure |
8906 | COM_METHOD GetSize(ULONG32 *pSize) |
8907 | { |
8908 | return (CordbValue::GetSize(pSize)); |
8909 | } |
8910 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
8911 | { |
8912 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
8913 | } |
8914 | |
8915 | // gets the remote (LS) address of the value. This may return NULL if the |
8916 | // value is a literal or resides in a register. |
8917 | // Arguments: |
8918 | // output: pAddress - the address of the value. The caller must guarantee is |
8919 | // non-Null |
8920 | // Return Value: S_OK on success or E_INVALIDARG if pAddress is null |
8921 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress) |
8922 | { |
8923 | LIMITED_METHOD_CONTRACT; |
8924 | |
8925 | FAIL_IF_NEUTERED(this); |
8926 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pAddress, CORDB_ADDRESS *); |
8927 | |
8928 | *pAddress = m_pValueHome ? m_pValueHome->GetAddress() : NULL; |
8929 | return (S_OK); |
8930 | } |
8931 | |
8932 | //----------------------------------------------------------- |
8933 | // ICorDebugValue2 |
8934 | //----------------------------------------------------------- |
8935 | |
8936 | COM_METHOD GetExactType(ICorDebugType **ppType) |
8937 | { |
8938 | return (CordbValue::GetExactType(ppType)); |
8939 | } |
8940 | |
8941 | //----------------------------------------------------------- |
8942 | // ICorDebugValue3 |
8943 | //----------------------------------------------------------- |
8944 | |
8945 | COM_METHOD GetSize64(ULONG64 *pSize) |
8946 | { |
8947 | return (CordbValue::GetSize64(pSize)); |
8948 | } |
8949 | |
8950 | //----------------------------------------------------------- |
8951 | // ICorDebugGenericValue |
8952 | //----------------------------------------------------------- |
8953 | |
8954 | COM_METHOD GetValue(void *pTo); |
8955 | COM_METHOD SetValue(void *pFrom); |
8956 | |
8957 | //----------------------------------------------------------- |
8958 | // Non-COM methods |
8959 | //----------------------------------------------------------- |
8960 | |
8961 | // initialize a generic value by copying the necessary data, either |
8962 | // from the remote process or from another value in this process. |
8963 | void Init(MemoryRange localValue); |
8964 | bool CopyLiteralData(BYTE *pBuffer); |
8965 | |
8966 | // Returns a pointer to the ValueHome field |
8967 | virtual |
8968 | ValueHome * GetValueHome() { return m_pValueHome; }; |
8969 | |
8970 | //----------------------------------------------------------- |
8971 | // Data members |
8972 | //----------------------------------------------------------- |
8973 | |
8974 | private: |
8975 | // hold copies of up to 64-bit values. |
8976 | BYTE m_pCopyOfData[8]; |
8977 | |
8978 | // location information--remote or register address |
8979 | ValueHome * m_pValueHome; |
8980 | }; |
8981 | |
8982 | |
8983 | /* ------------------------------------------------------------------------- * |
8984 | * Reference Value class |
8985 | * ------------------------------------------------------------------------- */ |
8986 | |
8987 | class CordbReferenceValue : public CordbValue, public ICorDebugReferenceValue, public ICorDebugValue2, public ICorDebugValue3 |
8988 | { |
8989 | public: |
8990 | CordbReferenceValue(CordbAppDomain * pAppdomain, |
8991 | CordbType * pType, |
8992 | MemoryRange localValue, |
8993 | TargetBuffer remoteValue, |
8994 | EnregisteredValueHomeHolder * ppRegAddr, |
8995 | VMPTR_OBJECTHANDLE vmObjectHandle); |
8996 | CordbReferenceValue(CordbType * pType); |
8997 | virtual ~CordbReferenceValue(); |
8998 | virtual void Neuter(); |
8999 | |
9000 | |
9001 | #ifdef _DEBUG |
9002 | virtual const char * DbgGetName() { return "CordbReferenceValue" ; } |
9003 | #endif |
9004 | |
9005 | //----------------------------------------------------------- |
9006 | // IUnknown |
9007 | //----------------------------------------------------------- |
9008 | |
9009 | ULONG STDMETHODCALLTYPE AddRef() |
9010 | { |
9011 | return (BaseAddRef()); |
9012 | } |
9013 | ULONG STDMETHODCALLTYPE Release() |
9014 | { |
9015 | return (BaseRelease()); |
9016 | } |
9017 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9018 | |
9019 | //----------------------------------------------------------- |
9020 | // ICorDebugValue |
9021 | //----------------------------------------------------------- |
9022 | |
9023 | COM_METHOD GetType(CorElementType *pType); |
9024 | |
9025 | // get the size of the reference |
9026 | // Arguments: |
9027 | // output: pSize - the size of the value--this must be non-NULL |
9028 | // Return Value: S_OK on success or E_INVALIDARG |
9029 | COM_METHOD GetSize(ULONG32 *pSize) |
9030 | { |
9031 | return (CordbValue::GetSize(pSize)); |
9032 | } |
9033 | |
9034 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress); |
9035 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
9036 | { |
9037 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
9038 | } |
9039 | |
9040 | //----------------------------------------------------------- |
9041 | // ICorDebugValue2 |
9042 | //----------------------------------------------------------- |
9043 | |
9044 | COM_METHOD GetExactType(ICorDebugType **ppType) |
9045 | { |
9046 | return (CordbValue::GetExactType(ppType)); |
9047 | } |
9048 | |
9049 | //----------------------------------------------------------- |
9050 | // ICorDebugValue3 |
9051 | //----------------------------------------------------------- |
9052 | |
9053 | COM_METHOD GetSize64(ULONG64 *pSize) |
9054 | { |
9055 | return (CordbValue::GetSize64(pSize)); |
9056 | } |
9057 | |
9058 | //----------------------------------------------------------- |
9059 | // ICorDebugReferenceValue |
9060 | //----------------------------------------------------------- |
9061 | |
9062 | COM_METHOD IsNull(BOOL * pfIsNull); |
9063 | COM_METHOD GetValue(CORDB_ADDRESS *pAddress); |
9064 | COM_METHOD SetValue(CORDB_ADDRESS address); |
9065 | COM_METHOD Dereference(ICorDebugValue **ppValue); |
9066 | COM_METHOD DereferenceStrong(ICorDebugValue **ppValue); |
9067 | |
9068 | //----------------------------------------------------------- |
9069 | // Non-COM methods |
9070 | //----------------------------------------------------------- |
9071 | |
9072 | // Helper function for SanityCheckPointer. Make an attempt to read memory at the address which is the |
9073 | // value of the reference. |
9074 | void TryDereferencingTarget(); |
9075 | |
9076 | // Do a sanity check on the pointer which is the value of the object reference. We can't efficiently |
9077 | // ensure that the pointer is really good, so we settle for a quick check just to make sure the memory at |
9078 | // the address is readable. We're actually just checking that we can dereference the pointer. |
9079 | // If the address is invalid, this will throw. |
9080 | void SanityCheckPointer (CorElementType type); |
9081 | |
9082 | // get information about the reference when it's not an object address but another kind of pointer type: |
9083 | // ELEMENT_TYPE_BYREF, ELEMENT_TYPE_PTR or ELEMENT_TYPE_FNPTR |
9084 | void GetPointerData(CorElementType type, MemoryRange localValue); |
9085 | |
9086 | // get basic object specific data when a reference points to an object, plus extra data if the object is |
9087 | // an array or string |
9088 | static |
9089 | void GetObjectData(CordbProcess * pProcess, |
9090 | void * objectAddress, |
9091 | CorElementType type, |
9092 | VMPTR_AppDomain vmAppdomain, |
9093 | DebuggerIPCE_ObjectData * pInfo); |
9094 | |
9095 | // get information about a TypedByRef object when the reference is the address of a TypedByRef structure. |
9096 | static |
9097 | void GetTypedByRefData(CordbProcess * pProcess, |
9098 | CORDB_ADDRESS pTypedByRef, |
9099 | CorElementType type, |
9100 | VMPTR_AppDomain vmAppDomain, |
9101 | DebuggerIPCE_ObjectData * pInfo); |
9102 | |
9103 | // get the address of the object referenced |
9104 | void * GetObjectAddress(MemoryRange localValue); |
9105 | |
9106 | // update type information after initializing -- when we initialize, we may get more exact type |
9107 | // information than we previously had |
9108 | void UpdateTypeInfo(); |
9109 | |
9110 | // Initialize this CordbReferenceValue. This may involve inspecting the LS to get information about the |
9111 | // referent. |
9112 | HRESULT InitRef(MemoryRange localValue); |
9113 | |
9114 | bool CopyLiteralData(BYTE *pBuffer); |
9115 | |
9116 | static HRESULT Build(CordbAppDomain * appdomain, |
9117 | CordbType * type, |
9118 | TargetBuffer remoteValue, |
9119 | MemoryRange localValue, |
9120 | VMPTR_OBJECTHANDLE vmObjectHandle, |
9121 | EnregisteredValueHomeHolder * ppRemoteRegAddr, |
9122 | CordbReferenceValue** ppValue); |
9123 | |
9124 | static HRESULT BuildFromGCHandle(CordbAppDomain *pAppDomain, VMPTR_OBJECTHANDLE gcHandle, ICorDebugReferenceValue ** pOutRef); |
9125 | |
9126 | // Common dereference routine shared by both CordbReferenceValue + CordbHandleValue |
9127 | static HRESULT DereferenceCommon(CordbAppDomain * pAppDomain, |
9128 | CordbType * pType, |
9129 | CordbType * pRealTypeOfTypedByref, |
9130 | DebuggerIPCE_ObjectData * m_pInfo, |
9131 | ICorDebugValue ** ppValue); |
9132 | |
9133 | // Returns a pointer to the ValueHome field |
9134 | virtual |
9135 | ValueHome * GetValueHome() { return m_valueHome.m_pHome; }; |
9136 | |
9137 | //----------------------------------------------------------- |
9138 | // Data members |
9139 | //----------------------------------------------------------- |
9140 | |
9141 | public: |
9142 | DebuggerIPCE_ObjectData m_info; |
9143 | CordbType * m_realTypeOfTypedByref; // weak ref |
9144 | |
9145 | RefValueHome m_valueHome; |
9146 | |
9147 | // Indicates when we last syncronized our stored data (m_info) from the left side |
9148 | UINT m_continueCounterLastSync; |
9149 | }; |
9150 | |
9151 | /* ------------------------------------------------------------------------- * |
9152 | * Object Value class |
9153 | * |
9154 | * Because of the oddness of string objects in the Runtime we have one |
9155 | * object that implements both ObjectValue and StringValue. There is a |
9156 | * definite string type, but its really just an object of the string |
9157 | * class. Furthermore, you can have a variable whose type is listed as |
9158 | * "class", but its an instance of the string class and therefore needs |
9159 | * to be treated like a string. |
9160 | * ------------------------------------------------------------------------- */ |
9161 | |
9162 | class CordbObjectValue : public CordbValue, |
9163 | public ICorDebugObjectValue, |
9164 | public ICorDebugObjectValue2, |
9165 | public ICorDebugGenericValue, |
9166 | public ICorDebugStringValue, |
9167 | public ICorDebugValue2, |
9168 | public ICorDebugValue3, |
9169 | public ICorDebugHeapValue2, |
9170 | public ICorDebugHeapValue3, |
9171 | public ICorDebugExceptionObjectValue, |
9172 | public ICorDebugComObjectValue |
9173 | { |
9174 | public: |
9175 | |
9176 | CordbObjectValue(CordbAppDomain * appdomain, |
9177 | CordbType * type, |
9178 | TargetBuffer remoteValue, |
9179 | DebuggerIPCE_ObjectData * pObjectData ); |
9180 | |
9181 | virtual ~CordbObjectValue(); |
9182 | |
9183 | |
9184 | virtual void Neuter(); |
9185 | #ifdef _DEBUG |
9186 | virtual const char * DbgGetName() { return "CordbObjectValue" ; } |
9187 | #endif |
9188 | |
9189 | //----------------------------------------------------------- |
9190 | // IUnknown |
9191 | //----------------------------------------------------------- |
9192 | |
9193 | ULONG STDMETHODCALLTYPE AddRef() |
9194 | { |
9195 | return (BaseAddRef()); |
9196 | } |
9197 | ULONG STDMETHODCALLTYPE Release() |
9198 | { |
9199 | return (BaseRelease()); |
9200 | } |
9201 | COM_METHOD QueryInterface(REFIID riid, void ** ppInterface); |
9202 | |
9203 | //----------------------------------------------------------- |
9204 | // ICorDebugValue |
9205 | //----------------------------------------------------------- |
9206 | |
9207 | COM_METHOD GetType(CorElementType * pType); |
9208 | COM_METHOD GetSize(ULONG32 * pSize); |
9209 | COM_METHOD GetAddress(CORDB_ADDRESS * pAddress); |
9210 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint); |
9211 | |
9212 | //----------------------------------------------------------- |
9213 | // ICorDebugValue2 |
9214 | //----------------------------------------------------------- |
9215 | |
9216 | COM_METHOD GetExactType(ICorDebugType ** ppType) |
9217 | { |
9218 | return (CordbValue::GetExactType(ppType)); |
9219 | } |
9220 | |
9221 | //----------------------------------------------------------- |
9222 | // ICorDebugValue3 |
9223 | //----------------------------------------------------------- |
9224 | |
9225 | COM_METHOD GetSize64(ULONG64 *pSize); |
9226 | |
9227 | //----------------------------------------------------------- |
9228 | // ICorDebugHeapValue |
9229 | //----------------------------------------------------------- |
9230 | |
9231 | COM_METHOD IsValid(BOOL * pfIsValid); |
9232 | COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint ** ppBreakpoint); |
9233 | |
9234 | //----------------------------------------------------------- |
9235 | // ICorDebugHeapValue2 |
9236 | //----------------------------------------------------------- |
9237 | COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle); |
9238 | |
9239 | //----------------------------------------------------------- |
9240 | // ICorDebugHeapValue3 |
9241 | //----------------------------------------------------------- |
9242 | COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount); |
9243 | COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum); |
9244 | |
9245 | //----------------------------------------------------------- |
9246 | // ICorDebugObjectValue |
9247 | //----------------------------------------------------------- |
9248 | |
9249 | COM_METHOD GetClass(ICorDebugClass ** ppClass); |
9250 | COM_METHOD GetFieldValue(ICorDebugClass * pClass, |
9251 | mdFieldDef fieldDef, |
9252 | ICorDebugValue ** ppValue); |
9253 | COM_METHOD GetVirtualMethod(mdMemberRef memberRef, |
9254 | ICorDebugFunction **ppFunction); |
9255 | COM_METHOD GetContext(ICorDebugContext ** ppContext); |
9256 | COM_METHOD IsValueClass(BOOL * pfIsValueClass); |
9257 | COM_METHOD GetManagedCopy(IUnknown ** ppObject); |
9258 | COM_METHOD SetFromManagedCopy(IUnknown * pObject); |
9259 | |
9260 | COM_METHOD GetFieldValueForType(ICorDebugType * pType, |
9261 | mdFieldDef fieldDef, |
9262 | ICorDebugValue ** ppValue); |
9263 | |
9264 | COM_METHOD GetVirtualMethodAndType(mdMemberRef memberRef, |
9265 | ICorDebugFunction ** ppFunction, |
9266 | ICorDebugType ** ppType); |
9267 | |
9268 | //----------------------------------------------------------- |
9269 | // ICorDebugGenericValue |
9270 | //----------------------------------------------------------- |
9271 | |
9272 | COM_METHOD GetValue(void * pTo); |
9273 | COM_METHOD SetValue(void * pFrom); |
9274 | |
9275 | //----------------------------------------------------------- |
9276 | // ICorDebugStringValue |
9277 | //----------------------------------------------------------- |
9278 | COM_METHOD GetLength(ULONG32 * pcchString); |
9279 | COM_METHOD GetString(ULONG32 cchString, |
9280 | ULONG32 * ppcchStrin, |
9281 | __out_ecount_opt(cchString) WCHAR szString[]); |
9282 | |
9283 | //----------------------------------------------------------- |
9284 | // ICorDebugExceptionObjectValue |
9285 | //----------------------------------------------------------- |
9286 | COM_METHOD EnumerateExceptionCallStack(ICorDebugExceptionObjectCallStackEnum** ppCallStackEnum); |
9287 | |
9288 | //----------------------------------------------------------- |
9289 | // ICorDebugComObjectValue |
9290 | //----------------------------------------------------------- |
9291 | COM_METHOD GetCachedInterfaceTypes(BOOL bIInspectableOnly, |
9292 | ICorDebugTypeEnum** ppInterfacesEnum); |
9293 | |
9294 | COM_METHOD GetCachedInterfacePointers(BOOL bIInspectableOnly, |
9295 | ULONG32 celt, |
9296 | ULONG32 *pcEltFetched, |
9297 | CORDB_ADDRESS * ptrs); |
9298 | |
9299 | //----------------------------------------------------------- |
9300 | // Non-COM methods |
9301 | //----------------------------------------------------------- |
9302 | |
9303 | HRESULT Init(); |
9304 | |
9305 | DebuggerIPCE_ObjectData GetInfo() { return m_info; } |
9306 | CordbHangingFieldTable * GetHangingFieldTable() { return &m_hangingFieldsInstance; } |
9307 | |
9308 | // Returns a pointer to the ValueHome field |
9309 | virtual |
9310 | RemoteValueHome * GetValueHome() { return &m_valueHome; }; |
9311 | |
9312 | protected: |
9313 | //----------------------------------------------------------- |
9314 | // Data members |
9315 | //----------------------------------------------------------- |
9316 | DebuggerIPCE_ObjectData m_info; |
9317 | BYTE * m_pObjectCopy; // local cached copy of the object |
9318 | BYTE * m_objectLocalVars; // var base in _this_ process |
9319 | // points _into_ m_pObjectCopy |
9320 | BYTE * m_stringBuffer; // points _into_ m_pObjectCopy |
9321 | |
9322 | // remote location information |
9323 | RemoteValueHome m_valueHome; |
9324 | |
9325 | // If instances fields are added by EnC, their storage will be off the objects |
9326 | // syncblock. Cache per-object information about such fields here. |
9327 | CordbHangingFieldTable m_hangingFieldsInstance; |
9328 | |
9329 | private: |
9330 | HRESULT IsExceptionObject(); |
9331 | |
9332 | BOOL m_fIsExceptionObject; |
9333 | |
9334 | HRESULT IsRcw(); |
9335 | |
9336 | BOOL m_fIsRcw; |
9337 | }; |
9338 | |
9339 | /* ------------------------------------------------------------------------- * |
9340 | * Value Class Object Value class |
9341 | * ------------------------------------------------------------------------- */ |
9342 | |
9343 | class CordbVCObjectValue : public CordbValue, |
9344 | public ICorDebugObjectValue, public ICorDebugObjectValue2, |
9345 | public ICorDebugGenericValue, public ICorDebugValue2, |
9346 | public ICorDebugValue3 |
9347 | { |
9348 | public: |
9349 | CordbVCObjectValue(CordbAppDomain * pAppdomain, |
9350 | CordbType * pType, |
9351 | TargetBuffer remoteValue, |
9352 | EnregisteredValueHomeHolder * ppRemoteRegAddr); |
9353 | virtual ~CordbVCObjectValue(); |
9354 | |
9355 | #ifdef _DEBUG |
9356 | virtual const char * DbgGetName() { return "CordbVCObjectValue" ; } |
9357 | #endif |
9358 | |
9359 | //----------------------------------------------------------- |
9360 | // IUnknown |
9361 | //----------------------------------------------------------- |
9362 | |
9363 | ULONG STDMETHODCALLTYPE AddRef() |
9364 | { |
9365 | return (BaseAddRef()); |
9366 | } |
9367 | ULONG STDMETHODCALLTYPE Release() |
9368 | { |
9369 | return (BaseRelease()); |
9370 | } |
9371 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9372 | |
9373 | //----------------------------------------------------------- |
9374 | // ICorDebugValue |
9375 | //----------------------------------------------------------- |
9376 | |
9377 | COM_METHOD GetType(CorElementType *pType); |
9378 | |
9379 | COM_METHOD GetSize(ULONG32 *pSize) |
9380 | { |
9381 | return (CordbValue::GetSize(pSize)); |
9382 | } |
9383 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
9384 | { |
9385 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
9386 | } |
9387 | |
9388 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress) |
9389 | { |
9390 | LIMITED_METHOD_CONTRACT; |
9391 | |
9392 | FAIL_IF_NEUTERED(this); |
9393 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
9394 | |
9395 | *pAddress = m_pValueHome->GetAddress(); |
9396 | return (S_OK); |
9397 | } |
9398 | |
9399 | //----------------------------------------------------------- |
9400 | // ICorDebugValue2 |
9401 | //----------------------------------------------------------- |
9402 | |
9403 | COM_METHOD GetExactType(ICorDebugType **ppType) |
9404 | { |
9405 | return (CordbValue::GetExactType(ppType)); |
9406 | } |
9407 | |
9408 | //----------------------------------------------------------- |
9409 | // ICorDebugValue3 |
9410 | //----------------------------------------------------------- |
9411 | |
9412 | COM_METHOD GetSize64(ULONG64 *pSize) |
9413 | { |
9414 | return (CordbValue::GetSize64(pSize)); |
9415 | } |
9416 | |
9417 | //----------------------------------------------------------- |
9418 | // ICorDebugObjectValue |
9419 | //----------------------------------------------------------- |
9420 | |
9421 | COM_METHOD GetClass(ICorDebugClass **ppClass); |
9422 | COM_METHOD GetFieldValue(ICorDebugClass *pClass, |
9423 | mdFieldDef fieldDef, |
9424 | ICorDebugValue **ppValue); |
9425 | COM_METHOD GetVirtualMethod(mdMemberRef memberRef, |
9426 | ICorDebugFunction **ppFunction); |
9427 | COM_METHOD GetContext(ICorDebugContext **ppContext); |
9428 | COM_METHOD IsValueClass(BOOL *pbIsValueClass); |
9429 | COM_METHOD GetManagedCopy(IUnknown **ppObject); |
9430 | COM_METHOD SetFromManagedCopy(IUnknown *pObject); |
9431 | COM_METHOD GetFieldValueForType(ICorDebugType * pType, |
9432 | mdFieldDef fieldDef, |
9433 | ICorDebugValue ** ppValue); |
9434 | COM_METHOD GetVirtualMethodAndType(mdMemberRef memberRef, |
9435 | ICorDebugFunction **ppFunction, |
9436 | ICorDebugType **ppType); |
9437 | |
9438 | //----------------------------------------------------------- |
9439 | // ICorDebugGenericValue |
9440 | //----------------------------------------------------------- |
9441 | |
9442 | COM_METHOD GetValue(void *pTo); |
9443 | COM_METHOD SetValue(void *pFrom); |
9444 | |
9445 | //----------------------------------------------------------- |
9446 | // Non-COM methods |
9447 | //----------------------------------------------------------- |
9448 | |
9449 | // Initializes the Right-Side's representation of a Value Class object. |
9450 | HRESULT Init(MemoryRange localValue); |
9451 | //HRESULT ResolveValueClass(); |
9452 | CordbClass *GetClass(); |
9453 | |
9454 | // Returns a pointer to the ValueHome field |
9455 | virtual |
9456 | ValueHome * GetValueHome() { return m_pValueHome; }; |
9457 | |
9458 | //----------------------------------------------------------- |
9459 | // Data members |
9460 | //----------------------------------------------------------- |
9461 | |
9462 | private: |
9463 | |
9464 | // local cached copy of the value class |
9465 | BYTE * m_pObjectCopy; |
9466 | |
9467 | // location information |
9468 | ValueHome * m_pValueHome; |
9469 | }; |
9470 | |
9471 | |
9472 | /* ------------------------------------------------------------------------- * |
9473 | * Box Value class |
9474 | * ------------------------------------------------------------------------- */ |
9475 | |
9476 | class CordbBoxValue : public CordbValue, |
9477 | public ICorDebugBoxValue, |
9478 | public ICorDebugGenericValue, |
9479 | public ICorDebugValue2, |
9480 | public ICorDebugValue3, |
9481 | public ICorDebugHeapValue2, |
9482 | public ICorDebugHeapValue3 |
9483 | { |
9484 | public: |
9485 | CordbBoxValue(CordbAppDomain * appdomain, |
9486 | CordbType * type, |
9487 | TargetBuffer remoteValue, |
9488 | ULONG32 size, |
9489 | SIZE_T offsetToVars); |
9490 | virtual ~CordbBoxValue(); |
9491 | |
9492 | #ifdef _DEBUG |
9493 | virtual const char * DbgGetName() { return "CordbBoxValue" ; } |
9494 | #endif |
9495 | |
9496 | //----------------------------------------------------------- |
9497 | // IUnknown |
9498 | //----------------------------------------------------------- |
9499 | |
9500 | ULONG STDMETHODCALLTYPE AddRef() |
9501 | { |
9502 | return (BaseAddRef()); |
9503 | } |
9504 | ULONG STDMETHODCALLTYPE Release() |
9505 | { |
9506 | return (BaseRelease()); |
9507 | } |
9508 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9509 | |
9510 | //----------------------------------------------------------- |
9511 | // ICorDebugValue |
9512 | //----------------------------------------------------------- |
9513 | |
9514 | COM_METHOD GetType(CorElementType *pType); |
9515 | |
9516 | COM_METHOD GetSize(ULONG32 *pSize) |
9517 | { |
9518 | return (CordbValue::GetSize(pSize)); |
9519 | } |
9520 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
9521 | { |
9522 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
9523 | } |
9524 | |
9525 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress) |
9526 | { |
9527 | LIMITED_METHOD_CONTRACT; |
9528 | |
9529 | FAIL_IF_NEUTERED(this); |
9530 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
9531 | |
9532 | *pAddress = m_valueHome.GetAddress(); |
9533 | return (S_OK); |
9534 | } |
9535 | |
9536 | //----------------------------------------------------------- |
9537 | // ICorDebugValue2 |
9538 | //----------------------------------------------------------- |
9539 | |
9540 | COM_METHOD GetExactType(ICorDebugType **ppType) |
9541 | { |
9542 | return (CordbValue::GetExactType(ppType)); |
9543 | } |
9544 | |
9545 | //----------------------------------------------------------- |
9546 | // ICorDebugValue3 |
9547 | //----------------------------------------------------------- |
9548 | |
9549 | COM_METHOD GetSize64(ULONG64 *pSize) |
9550 | { |
9551 | return (CordbValue::GetSize64(pSize)); |
9552 | } |
9553 | |
9554 | //----------------------------------------------------------- |
9555 | // ICorDebugHeapValue |
9556 | //----------------------------------------------------------- |
9557 | |
9558 | COM_METHOD IsValid(BOOL *pbValid); |
9559 | COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint); |
9560 | |
9561 | //----------------------------------------------------------- |
9562 | // ICorDebugHeapValue2 |
9563 | //----------------------------------------------------------- |
9564 | COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle); |
9565 | |
9566 | //----------------------------------------------------------- |
9567 | // ICorDebugHeapValue3 |
9568 | //----------------------------------------------------------- |
9569 | COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount); |
9570 | COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum); |
9571 | |
9572 | //----------------------------------------------------------- |
9573 | // ICorDebugGenericValue |
9574 | //----------------------------------------------------------- |
9575 | |
9576 | COM_METHOD GetValue(void *pTo); |
9577 | COM_METHOD SetValue(void *pFrom); |
9578 | |
9579 | //----------------------------------------------------------- |
9580 | // ICorDebugBoxValue |
9581 | //----------------------------------------------------------- |
9582 | COM_METHOD GetObject(ICorDebugObjectValue **ppObject); |
9583 | |
9584 | // Returns a pointer to the ValueHome field |
9585 | virtual |
9586 | RemoteValueHome * GetValueHome() { return &m_valueHome; }; |
9587 | |
9588 | //----------------------------------------------------------- |
9589 | // Data members |
9590 | //----------------------------------------------------------- |
9591 | |
9592 | private: |
9593 | SIZE_T m_offsetToVars; |
9594 | |
9595 | // remote location information |
9596 | RemoteValueHome m_valueHome; |
9597 | |
9598 | }; |
9599 | |
9600 | /* ------------------------------------------------------------------------- * |
9601 | * Array Value class |
9602 | * ------------------------------------------------------------------------- */ |
9603 | |
9604 | class CordbArrayValue : public CordbValue, |
9605 | public ICorDebugArrayValue, |
9606 | public ICorDebugGenericValue, |
9607 | public ICorDebugValue2, |
9608 | public ICorDebugValue3, |
9609 | public ICorDebugHeapValue2, |
9610 | public ICorDebugHeapValue3 |
9611 | { |
9612 | public: |
9613 | CordbArrayValue(CordbAppDomain * appdomain, |
9614 | CordbType * type, |
9615 | DebuggerIPCE_ObjectData * pObjectInfo, |
9616 | TargetBuffer remoteValue); |
9617 | virtual ~CordbArrayValue(); |
9618 | |
9619 | #ifdef _DEBUG |
9620 | virtual const char * DbgGetName() { return "CordbArrayValue" ; } |
9621 | #endif |
9622 | |
9623 | //----------------------------------------------------------- |
9624 | // IUnknown |
9625 | //----------------------------------------------------------- |
9626 | |
9627 | ULONG STDMETHODCALLTYPE AddRef() |
9628 | { |
9629 | return (BaseAddRef()); |
9630 | } |
9631 | ULONG STDMETHODCALLTYPE Release() |
9632 | { |
9633 | return (BaseRelease()); |
9634 | } |
9635 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9636 | |
9637 | //----------------------------------------------------------- |
9638 | // ICorDebugValue |
9639 | //----------------------------------------------------------- |
9640 | |
9641 | COM_METHOD GetType(CorElementType *pType) |
9642 | { |
9643 | return (CordbValue::GetType(pType)); |
9644 | } |
9645 | COM_METHOD GetSize(ULONG32 *pSize) |
9646 | { |
9647 | return (CordbValue::GetSize(pSize)); |
9648 | } |
9649 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress) |
9650 | { |
9651 | VALIDATE_POINTER_TO_OBJECT(pAddress, CORDB_ADDRESS *); |
9652 | *pAddress = m_valueHome.GetAddress(); |
9653 | return (S_OK); |
9654 | } |
9655 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) |
9656 | { |
9657 | return (CordbValue::CreateBreakpoint(ppBreakpoint)); |
9658 | } |
9659 | |
9660 | //----------------------------------------------------------- |
9661 | // ICorDebugValue2 |
9662 | //----------------------------------------------------------- |
9663 | |
9664 | COM_METHOD GetExactType(ICorDebugType **ppType) |
9665 | { |
9666 | return (CordbValue::GetExactType(ppType)); |
9667 | } |
9668 | |
9669 | //----------------------------------------------------------- |
9670 | // ICorDebugValue3 |
9671 | //----------------------------------------------------------- |
9672 | |
9673 | COM_METHOD GetSize64(ULONG64 *pSize) |
9674 | { |
9675 | return (CordbValue::GetSize64(pSize)); |
9676 | } |
9677 | |
9678 | //----------------------------------------------------------- |
9679 | // ICorDebugHeapValue |
9680 | //----------------------------------------------------------- |
9681 | |
9682 | COM_METHOD IsValid(BOOL *pbValid); |
9683 | COM_METHOD CreateRelocBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint); |
9684 | |
9685 | //----------------------------------------------------------- |
9686 | // ICorDebugHeapValue2 |
9687 | //----------------------------------------------------------- |
9688 | COM_METHOD CreateHandle(CorDebugHandleType type, ICorDebugHandleValue ** ppHandle); |
9689 | |
9690 | //----------------------------------------------------------- |
9691 | // ICorDebugHeapValue3 |
9692 | //----------------------------------------------------------- |
9693 | COM_METHOD GetThreadOwningMonitorLock(ICorDebugThread **ppThread, DWORD *pAcquisitionCount); |
9694 | COM_METHOD GetMonitorEventWaitList(ICorDebugThreadEnum **ppThreadEnum); |
9695 | |
9696 | //----------------------------------------------------------- |
9697 | // ICorDebugArrayValue |
9698 | //----------------------------------------------------------- |
9699 | |
9700 | COM_METHOD GetElementType(CorElementType * pType); |
9701 | COM_METHOD GetRank(ULONG32 * pnRank); |
9702 | COM_METHOD GetCount(ULONG32 * pnCount); |
9703 | COM_METHOD GetDimensions(ULONG32 cdim, ULONG32 dims[]); |
9704 | COM_METHOD HasBaseIndicies(BOOL * pbHasBaseIndices); |
9705 | COM_METHOD GetBaseIndicies(ULONG32 cdim, ULONG32 indices[]); |
9706 | COM_METHOD GetElement(ULONG32 cdim, ULONG32 indices[], ICorDebugValue ** ppValue); |
9707 | COM_METHOD GetElementAtPosition(ULONG32 nIndex, ICorDebugValue ** ppValue); |
9708 | |
9709 | //----------------------------------------------------------- |
9710 | // ICorDebugGenericValue |
9711 | //----------------------------------------------------------- |
9712 | |
9713 | COM_METHOD GetValue(void *pTo); |
9714 | COM_METHOD SetValue(void *pFrom); |
9715 | |
9716 | //----------------------------------------------------------- |
9717 | // Non-COM methods |
9718 | //----------------------------------------------------------- |
9719 | |
9720 | HRESULT Init(); |
9721 | |
9722 | // Returns a pointer to the ValueHome field |
9723 | virtual |
9724 | RemoteValueHome * GetValueHome() { return &m_valueHome; }; |
9725 | |
9726 | //----------------------------------------------------------- |
9727 | // Data members |
9728 | //----------------------------------------------------------- |
9729 | |
9730 | private: |
9731 | // contains information about the array, such as rank, number of elements, element size, etc. |
9732 | DebuggerIPCE_ObjectData m_info; |
9733 | |
9734 | // type of the elements |
9735 | CordbType *m_elemtype; |
9736 | |
9737 | // consists of three parts: a vector containing the lower bounds for each dimension, |
9738 | // a vector containing the upper bounds for each dimension, |
9739 | // a local cached copy of (part of) the array--initialized lazily when we |
9740 | // request a particular element. If the array is large, we will store only |
9741 | // part of it, swapping out the cached segment as necessary to retrieve |
9742 | // requested elements. |
9743 | BYTE * m_pObjectCopy; |
9744 | |
9745 | // points to the beginning of the vector containing the lower bounds for each dimension in m_pObjectCopy |
9746 | DWORD * m_arrayLowerBase; |
9747 | |
9748 | // points to the beginning of the vector containing the lower bounds for each dimension in m_pObjectCopy |
9749 | DWORD * m_arrayUpperBase; |
9750 | // index of lower bound of data currently stored in m_pObjectCopy |
9751 | SIZE_T m_idxLower; |
9752 | |
9753 | // index of upper bound of data currently stored in m_pObjectCopy |
9754 | SIZE_T m_idxUpper; |
9755 | |
9756 | // remote location information |
9757 | RemoteValueHome m_valueHome; |
9758 | |
9759 | }; |
9760 | |
9761 | class CordbHandleValue : public CordbValue, public ICorDebugHandleValue, public ICorDebugValue2, public ICorDebugValue3 |
9762 | { |
9763 | public: |
9764 | CordbHandleValue(CordbAppDomain *appdomain, |
9765 | CordbType *type, |
9766 | CorDebugHandleType handleType); |
9767 | HRESULT Init(VMPTR_OBJECTHANDLE pHandle); |
9768 | |
9769 | virtual ~CordbHandleValue(); |
9770 | |
9771 | virtual void Neuter(); |
9772 | virtual void NeuterLeftSideResources(); |
9773 | |
9774 | #ifdef _DEBUG |
9775 | virtual const char * DbgGetName() { return "CordbHandleValue" ; } |
9776 | #endif |
9777 | |
9778 | |
9779 | //----------------------------------------------------------- |
9780 | // IUnknown |
9781 | //----------------------------------------------------------- |
9782 | |
9783 | ULONG STDMETHODCALLTYPE AddRef() |
9784 | { |
9785 | return (BaseAddRef()); |
9786 | } |
9787 | ULONG STDMETHODCALLTYPE Release() |
9788 | { |
9789 | return (BaseRelease()); |
9790 | } |
9791 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9792 | |
9793 | //----------------------------------------------------------- |
9794 | // ICorDebugHandleValue interface |
9795 | //----------------------------------------------------------- |
9796 | COM_METHOD GetHandleType(CorDebugHandleType *pType); |
9797 | |
9798 | |
9799 | /* |
9800 | * The final release of the interface will also dispose of the handle. This |
9801 | * API provides the ability for client to early dispose the handle. |
9802 | * |
9803 | */ |
9804 | COM_METHOD Dispose(); |
9805 | |
9806 | //----------------------------------------------------------- |
9807 | // ICorDebugValue interface |
9808 | //----------------------------------------------------------- |
9809 | COM_METHOD GetType(CorElementType *pType); |
9810 | COM_METHOD GetSize(ULONG32 *pSize); |
9811 | COM_METHOD GetAddress(CORDB_ADDRESS *pAddress); |
9812 | COM_METHOD CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint); |
9813 | |
9814 | //----------------------------------------------------------- |
9815 | // ICorDebugValue2 |
9816 | //----------------------------------------------------------- |
9817 | |
9818 | COM_METHOD GetExactType(ICorDebugType **ppType) |
9819 | { |
9820 | FAIL_IF_NEUTERED(this); |
9821 | |
9822 | // If AppDomain is already unloaded, return error |
9823 | if (m_appdomain->IsNeutered() == TRUE) |
9824 | { |
9825 | return COR_E_APPDOMAINUNLOADED; |
9826 | } |
9827 | if (m_vmHandle.IsNull()) |
9828 | { |
9829 | return CORDBG_E_HANDLE_HAS_BEEN_DISPOSED; |
9830 | } |
9831 | |
9832 | return (CordbValue::GetExactType(ppType)); |
9833 | } |
9834 | |
9835 | //----------------------------------------------------------- |
9836 | // ICorDebugValue3 |
9837 | //----------------------------------------------------------- |
9838 | |
9839 | COM_METHOD GetSize64(ULONG64 *pSize); |
9840 | |
9841 | //----------------------------------------------------------- |
9842 | // ICorDebugReferenceValue interface |
9843 | //----------------------------------------------------------- |
9844 | |
9845 | COM_METHOD IsNull(BOOL *pbNull); |
9846 | COM_METHOD GetValue(CORDB_ADDRESS *pValue); |
9847 | COM_METHOD SetValue(CORDB_ADDRESS value); |
9848 | COM_METHOD Dereference(ICorDebugValue **ppValue); |
9849 | COM_METHOD DereferenceStrong(ICorDebugValue **ppValue); |
9850 | |
9851 | //----------------------------------------------------------- |
9852 | // Non-COM methods |
9853 | //----------------------------------------------------------- |
9854 | |
9855 | // Returns a pointer to the ValueHome field |
9856 | virtual |
9857 | RemoteValueHome * GetValueHome() { return NULL; }; |
9858 | |
9859 | private: |
9860 | //BOOL RefreshHandleValue(void **pObjectToken); |
9861 | HRESULT RefreshHandleValue(); |
9862 | |
9863 | // EE object handle pointer. Can be casted to OBJECTHANDLE when go to LS |
9864 | // This instance owns the handle object and must call into the VM to release |
9865 | // it. |
9866 | // If this is non-null, then we increment code:CordbProces::IncrementOutstandingHandles. |
9867 | // Once it goes null, we should decrement the count. |
9868 | // Use AssignHandle, ClearHandle to keep this in sync. |
9869 | VMPTR_OBJECTHANDLE m_vmHandle; |
9870 | |
9871 | |
9872 | void AssignHandle(VMPTR_OBJECTHANDLE handle); |
9873 | void ClearHandle(); |
9874 | |
9875 | BOOL m_fCanBeValid; // true if object "can" be valid. False when object is no longer valid. |
9876 | CorDebugHandleType m_handleType; // handle type can be strong or weak |
9877 | DebuggerIPCE_ObjectData m_info; |
9878 | ; // ICORDebugClass of this object when we create the handle |
9879 | }; |
9880 | |
9881 | // This class actually has the implementation for ICorDebugHeap3 interfaces. Any value which implements |
9882 | // the interface just delegates to these static calls. |
9883 | class CordbHeapValue3Impl |
9884 | { |
9885 | public: |
9886 | static HRESULT GetThreadOwningMonitorLock(CordbProcess* pProcess, |
9887 | CORDB_ADDRESS remoteObjAddress, |
9888 | ICorDebugThread **ppThread, |
9889 | DWORD *pAcquistionCount); |
9890 | static HRESULT GetMonitorEventWaitList(CordbProcess* pProcess, |
9891 | CORDB_ADDRESS remoteObjAddress, |
9892 | ICorDebugThreadEnum **ppThreadEnum); |
9893 | }; |
9894 | |
9895 | /* ------------------------------------------------------------------------- * |
9896 | * Eval class |
9897 | * ------------------------------------------------------------------------- */ |
9898 | |
9899 | class CordbEval : public CordbBase, public ICorDebugEval, public ICorDebugEval2 |
9900 | { |
9901 | public: |
9902 | CordbEval(CordbThread* pThread); |
9903 | virtual ~CordbEval(); |
9904 | |
9905 | #ifdef _DEBUG |
9906 | virtual const char * DbgGetName() { return "CordbEval" ; } |
9907 | #endif |
9908 | |
9909 | virtual void Neuter(); |
9910 | virtual void NeuterLeftSideResources(); |
9911 | |
9912 | //----------------------------------------------------------- |
9913 | // IUnknown |
9914 | //----------------------------------------------------------- |
9915 | |
9916 | ULONG STDMETHODCALLTYPE AddRef() |
9917 | { |
9918 | return (BaseAddRef()); |
9919 | } |
9920 | ULONG STDMETHODCALLTYPE Release() |
9921 | { |
9922 | return (BaseRelease()); |
9923 | } |
9924 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
9925 | |
9926 | //----------------------------------------------------------- |
9927 | // ICorDebugEval |
9928 | //----------------------------------------------------------- |
9929 | |
9930 | COM_METHOD CallFunction(ICorDebugFunction *pFunction, |
9931 | ULONG32 nArgs, |
9932 | ICorDebugValue *ppArgs[]); |
9933 | COM_METHOD NewObject(ICorDebugFunction *pConstructor, |
9934 | ULONG32 nArgs, |
9935 | ICorDebugValue *ppArgs[]); |
9936 | COM_METHOD NewObjectNoConstructor(ICorDebugClass *pClass); |
9937 | COM_METHOD NewString(LPCWSTR string); |
9938 | COM_METHOD NewArray(CorElementType elementType, |
9939 | ICorDebugClass *pElementClass, |
9940 | ULONG32 rank, |
9941 | ULONG32 dims[], |
9942 | ULONG32 lowBounds[]); |
9943 | COM_METHOD IsActive(BOOL *pbActive); |
9944 | COM_METHOD Abort(); |
9945 | COM_METHOD GetResult(ICorDebugValue **ppResult); |
9946 | COM_METHOD GetThread(ICorDebugThread **ppThread); |
9947 | COM_METHOD CreateValue(CorElementType elementType, |
9948 | ICorDebugClass *pElementClass, |
9949 | ICorDebugValue **ppValue); |
9950 | COM_METHOD NewStringWithLength(LPCWSTR wszString, UINT iLength); |
9951 | |
9952 | COM_METHOD CallParameterizedFunction(ICorDebugFunction * pFunction, |
9953 | ULONG32 nTypeArgs, |
9954 | ICorDebugType * rgpTypeArgs[], |
9955 | ULONG32 nArgs, |
9956 | ICorDebugValue * rgpArgs[]); |
9957 | |
9958 | COM_METHOD CreateValueForType(ICorDebugType *pType, |
9959 | ICorDebugValue **ppValue); |
9960 | |
9961 | COM_METHOD NewParameterizedObject(ICorDebugFunction * pConstructor, |
9962 | ULONG32 nTypeArgs, |
9963 | ICorDebugType * rgpTypeArgs[], |
9964 | ULONG32 nArgs, |
9965 | ICorDebugValue * rgpArgs[]); |
9966 | |
9967 | COM_METHOD NewParameterizedObjectNoConstructor(ICorDebugClass * pClass, |
9968 | ULONG32 nTypeArgs, |
9969 | ICorDebugType * rgpTypeArgs[]); |
9970 | |
9971 | COM_METHOD NewParameterizedArray(ICorDebugType * pElementType, |
9972 | ULONG32 rank, |
9973 | ULONG32 dims[], |
9974 | ULONG32 lowBounds[]); |
9975 | |
9976 | //----------------------------------------------------------- |
9977 | // ICorDebugEval2 |
9978 | //----------------------------------------------------------- |
9979 | |
9980 | COM_METHOD RudeAbort(); |
9981 | |
9982 | //----------------------------------------------------------- |
9983 | // Non-COM methods |
9984 | //----------------------------------------------------------- |
9985 | HRESULT GatherArgInfo(ICorDebugValue *pValue, |
9986 | DebuggerIPCE_FuncEvalArgData *argData); |
9987 | HRESULT SendCleanup(); |
9988 | |
9989 | // Create a RS literal for primitive type funceval result. In case the result is used as an argument for |
9990 | // another funceval, we need to make sure that we're not relying on the LS value, which will be freed and |
9991 | // thus unavailable. |
9992 | HRESULT CreatePrimitiveLiteral(CordbType * pType, |
9993 | ICorDebugValue ** ppValue); |
9994 | |
9995 | //----------------------------------------------------------- |
9996 | // Data members |
9997 | //----------------------------------------------------------- |
9998 | |
9999 | bool IsEvalDuringException() { return m_evalDuringException; } |
10000 | private: |
10001 | // We must keep a strong reference to the thread so we can properly fail out of SendCleanup if someone releases an |
10002 | // ICorDebugEval after the process has completely gone away. |
10003 | RSSmartPtr<CordbThread> m_thread; |
10004 | |
10005 | CordbFunction *m_function; |
10006 | CordbClass *m_class; |
10007 | DebuggerIPCE_FuncEvalType m_evalType; |
10008 | |
10009 | HRESULT SendFuncEval(unsigned int genericArgsCount, ICorDebugType *genericArgs[], void *argData1, unsigned int argData1Size, void *argData2, unsigned int argData2Size, DebuggerIPCEvent * event); |
10010 | HRESULT FilterHR(HRESULT hr); |
10011 | BOOL DoAppDomainsMatch( CordbAppDomain* pAppDomain, ULONG32 nTypes, ICorDebugType *pTypes[], ULONG32 nValues, ICorDebugValue *pValues[] ); |
10012 | |
10013 | public: |
10014 | bool m_complete; |
10015 | bool m_successful; |
10016 | bool m_aborted; |
10017 | void *m_resultAddr; |
10018 | |
10019 | // This is an OBJECTHANDLE on the LS if func-eval creates a strong handle. |
10020 | // This is a resource in the left-side and must be cleaned up in the left-side. |
10021 | // This gets handled off to a CordbHandleValue (m_pHandleValue) once code:CordbEval::GetResult |
10022 | // and then the CordbHandle is responsible for releasing it in the left-side. |
10023 | // Issue!! This will be leaked if nobody calls GetResult(). |
10024 | VMPTR_OBJECTHANDLE m_vmObjectHandle; |
10025 | |
10026 | // This is the corresponding cached CordbHandleValue for GetResult. |
10027 | // This takes ownership of the strong handle, m_objectHandle. |
10028 | // This is an External reference, which keeps the Value from being neutered |
10029 | // on a NeuterAtWill sweep. |
10030 | RSExtSmartPtr<CordbHandleValue> m_pHandleValue; |
10031 | |
10032 | DebuggerIPCE_ExpandedTypeData m_resultType; |
10033 | VMPTR_AppDomain m_resultAppDomainToken; |
10034 | |
10035 | // Left-side memory that needs to be freed. |
10036 | LSPTR_DEBUGGEREVAL m_debuggerEvalKey; |
10037 | |
10038 | |
10039 | // If we're evalling during a thread's exception, remember the info so that we can restore it when we're done. |
10040 | bool m_evalDuringException; // flag whether we're during the thread's exception. |
10041 | VMPTR_OBJECTHANDLE m_vmThreadOldExceptionHandle; // object handle for thread's managed exception object. |
10042 | |
10043 | #ifdef _DEBUG |
10044 | // Func-eval should perturb the the thread's current appdomain. So we remember it at start |
10045 | // and then ensure that the func-eval complete restores it. |
10046 | CordbAppDomain * m_DbgAppDomainStarted; |
10047 | #endif |
10048 | }; |
10049 | |
10050 | |
10051 | /* ------------------------------------------------------------------------- * |
10052 | * Win32 Event Thread class |
10053 | * ------------------------------------------------------------------------- */ |
10054 | const unsigned int CW32ET_UNKNOWN_PROCESS_SLOT = 0xFFffFFff; // it's a managed process, |
10055 | //but we don't know which slot it's in - for Detach. |
10056 | |
10057 | //--------------------------------------------------------------------------------------- |
10058 | // |
10059 | // Dedicated thread for win32 debugging operations. |
10060 | // |
10061 | // Notes: |
10062 | // This is owned by the ShimProcess object. That will both create this and destroy it. |
10063 | // OS restriction is that all win32 debugging APIs (CreateProcess, DebugActiveProcess, |
10064 | // DebugActiveProcessStop, WaitForDebugEvent, ContinueDebugEvent, etc) are on the same thread. |
10065 | // |
10066 | class CordbWin32EventThread |
10067 | { |
10068 | friend class CordbProcess; //so that Detach can call ExitProcess |
10069 | public: |
10070 | CordbWin32EventThread(Cordb * pCordb, ShimProcess * pShim); |
10071 | virtual ~CordbWin32EventThread(); |
10072 | |
10073 | // |
10074 | // You create a new instance of this class, call Init() to set it up, |
10075 | // then call Start() start processing events. Stop() terminates the |
10076 | // thread and deleting the instance cleans all the handles and such |
10077 | // up. |
10078 | // |
10079 | HRESULT Init(); |
10080 | HRESULT Start(); |
10081 | HRESULT Stop(); |
10082 | |
10083 | HRESULT SendCreateProcessEvent(MachineInfo machineInfo, |
10084 | LPCWSTR programName, |
10085 | __in_z LPWSTR programArgs, |
10086 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
10087 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
10088 | BOOL bInheritHandles, |
10089 | DWORD dwCreationFlags, |
10090 | PVOID lpEnvironment, |
10091 | LPCWSTR lpCurrentDirectory, |
10092 | LPSTARTUPINFOW lpStartupInfo, |
10093 | LPPROCESS_INFORMATION lpProcessInformation, |
10094 | CorDebugCreateProcessFlags corDebugFlags); |
10095 | |
10096 | HRESULT SendDebugActiveProcessEvent(MachineInfo machineInfo, |
10097 | const ProcessDescriptor *pProcessDescriptor, |
10098 | bool fWin32Attach, |
10099 | CordbProcess *pProcess); |
10100 | |
10101 | HRESULT SendDetachProcessEvent(CordbProcess *pProcess); |
10102 | |
10103 | #ifdef FEATURE_INTEROP_DEBUGGING |
10104 | HRESULT SendUnmanagedContinue(CordbProcess *pProcess, |
10105 | EUMContinueType eContType); |
10106 | HRESULT UnmanagedContinue(CordbProcess *pProcess, |
10107 | EUMContinueType eContType); |
10108 | void DoDbgContinue(CordbProcess * pProcess, |
10109 | CordbUnmanagedEvent * pUnmanagedEvent); |
10110 | void ForceDbgContinue(CordbProcess *pProcess, |
10111 | CordbUnmanagedThread *ut, |
10112 | DWORD contType, |
10113 | bool contProcess); |
10114 | |
10115 | #endif //FEATURE_INTEROP_DEBUGGING |
10116 | |
10117 | void LockSendToWin32EventThreadMutex() |
10118 | { |
10119 | LOG((LF_CORDB, LL_INFO10000, "W32ET::LockSendToWin32EventThreadMutex\n" )); |
10120 | m_sendToWin32EventThreadMutex.Lock(); |
10121 | } |
10122 | |
10123 | void UnlockSendToWin32EventThreadMutex() |
10124 | { |
10125 | m_sendToWin32EventThreadMutex.Unlock(); |
10126 | LOG((LF_CORDB, LL_INFO10000, "W32ET::UnlockSendToWin32EventThreadMutex\n" )); |
10127 | } |
10128 | |
10129 | bool IsWin32EventThread() |
10130 | { |
10131 | return (m_threadId == GetCurrentThreadId()); |
10132 | } |
10133 | |
10134 | void Win32EventLoop(); |
10135 | |
10136 | |
10137 | INativeEventPipeline * GetNativePipeline(); |
10138 | private: |
10139 | void ThreadProc(); |
10140 | static DWORD WINAPI ThreadProc(LPVOID parameter); |
10141 | |
10142 | void CreateProcess(); |
10143 | |
10144 | |
10145 | INativeEventPipeline * m_pNativePipeline; |
10146 | |
10147 | |
10148 | void AttachProcess(); |
10149 | |
10150 | void HandleUnmanagedContinue(); |
10151 | |
10152 | void ExitProcess(bool fDetach); |
10153 | |
10154 | private: |
10155 | RSSmartPtr<Cordb> m_cordb; |
10156 | |
10157 | HANDLE m_thread; |
10158 | DWORD m_threadId; |
10159 | HANDLE m_threadControlEvent; |
10160 | HANDLE m_actionTakenEvent; |
10161 | BOOL m_run; |
10162 | |
10163 | // The process that we're 1:1 with. |
10164 | // This is set when we get a Create / Attach event. |
10165 | // This is only used on the W32ET, which guarantees it will free of races. |
10166 | RSSmartPtr<CordbProcess> m_pProcess; |
10167 | |
10168 | |
10169 | ShimProcess * m_pShim; |
10170 | |
10171 | // @todo - convert this into Stop-Go lock? |
10172 | RSLock m_sendToWin32EventThreadMutex; |
10173 | |
10174 | unsigned int m_action; |
10175 | HRESULT m_actionResult; |
10176 | union |
10177 | { |
10178 | struct |
10179 | { |
10180 | MachineInfo machineInfo; |
10181 | LPCWSTR programName; |
10182 | LPWSTR programArgs; |
10183 | LPSECURITY_ATTRIBUTES lpProcessAttributes; |
10184 | LPSECURITY_ATTRIBUTES lpThreadAttributes; |
10185 | BOOL bInheritHandles; |
10186 | DWORD dwCreationFlags; |
10187 | PVOID lpEnvironment; |
10188 | LPCWSTR lpCurrentDirectory; |
10189 | LPSTARTUPINFOW lpStartupInfo; |
10190 | LPPROCESS_INFORMATION lpProcessInformation; |
10191 | CorDebugCreateProcessFlags corDebugFlags; |
10192 | } createData; |
10193 | |
10194 | struct |
10195 | { |
10196 | MachineInfo machineInfo; |
10197 | ProcessDescriptor processDescriptor; |
10198 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
10199 | bool fWin32Attach; |
10200 | #endif |
10201 | CordbProcess *pProcess; |
10202 | |
10203 | // Wrapper to determine if we're interop-debugging. |
10204 | bool IsInteropDebugging() |
10205 | { |
10206 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
10207 | return fWin32Attach; |
10208 | #else |
10209 | return false; |
10210 | #endif |
10211 | } |
10212 | } attachData; |
10213 | |
10214 | struct |
10215 | { |
10216 | CordbProcess *pProcess; |
10217 | } detachData; |
10218 | |
10219 | struct |
10220 | { |
10221 | CordbProcess *process; |
10222 | EUMContinueType eContType; |
10223 | } continueData; |
10224 | } m_actionData; |
10225 | }; |
10226 | |
10227 | |
10228 | // Thread-safe stack which. |
10229 | template <typename T> |
10230 | class InterlockedStack |
10231 | { |
10232 | public: |
10233 | InterlockedStack(); |
10234 | ~InterlockedStack(); |
10235 | |
10236 | // Thread safe pushes + pops. |
10237 | // Many threads can push simultaneously. |
10238 | // Only 1 thread can pop. |
10239 | void Push(T * pItem); |
10240 | T * Pop(); |
10241 | |
10242 | protected: |
10243 | T * m_pHead; |
10244 | }; |
10245 | |
10246 | //----------------------------------------------------------------------------- |
10247 | // Workitem to be placed on RCET worker queue. |
10248 | // There's 1 RCET for to be shared by all processes. |
10249 | //----------------------------------------------------------------------------- |
10250 | class RCETWorkItem |
10251 | { |
10252 | public: |
10253 | |
10254 | virtual ~RCETWorkItem() {} |
10255 | |
10256 | // Item is executed and then removed from the list and deleted. |
10257 | virtual void Do() = 0; |
10258 | |
10259 | CordbProcess * GetProcess() { return m_pProcess; } |
10260 | |
10261 | protected: |
10262 | RCETWorkItem(CordbProcess * pProcess) |
10263 | { |
10264 | m_pProcess.Assign(pProcess); |
10265 | m_next = NULL; |
10266 | } |
10267 | |
10268 | RSSmartPtr<CordbProcess> m_pProcess; |
10269 | |
10270 | // This field is accessed by the InterlockedStack. |
10271 | friend class InterlockedStack<RCETWorkItem>; |
10272 | RCETWorkItem * m_next; |
10273 | }; |
10274 | |
10275 | |
10276 | // Item to do Neutering work on ExitProcess. |
10277 | class ExitProcessWorkItem : public RCETWorkItem |
10278 | { |
10279 | public: |
10280 | ExitProcessWorkItem(CordbProcess * pProc) : RCETWorkItem(pProc) |
10281 | { |
10282 | } |
10283 | |
10284 | virtual void Do(); |
10285 | }; |
10286 | |
10287 | // Item to do send Attach event. |
10288 | class SendAttachProcessWorkItem : public RCETWorkItem |
10289 | { |
10290 | public: |
10291 | SendAttachProcessWorkItem(CordbProcess * pProc) : RCETWorkItem(pProc) |
10292 | { |
10293 | } |
10294 | |
10295 | virtual void Do(); |
10296 | }; |
10297 | |
10298 | |
10299 | /* ------------------------------------------------------------------------- * |
10300 | * Runtime Controller Event Thread class |
10301 | * ------------------------------------------------------------------------- */ |
10302 | |
10303 | class CordbRCEventThread |
10304 | { |
10305 | public: |
10306 | CordbRCEventThread(Cordb* cordb); |
10307 | virtual ~CordbRCEventThread(); |
10308 | |
10309 | // |
10310 | // You create a new instance of this class, call Init() to set it up, |
10311 | // then call Start() start processing events. Stop() terminates the |
10312 | // thread and deleting the instance cleans all the handles and such |
10313 | // up. |
10314 | // |
10315 | HRESULT Init(); |
10316 | HRESULT Start(); |
10317 | HRESULT Stop(); |
10318 | |
10319 | // RCET will take ownership of this item and delete it. |
10320 | void QueueAsyncWorkItem(RCETWorkItem * pItem); |
10321 | |
10322 | HRESULT SendIPCEvent(CordbProcess* process, |
10323 | DebuggerIPCEvent* event, |
10324 | SIZE_T eventSize); |
10325 | |
10326 | void ProcessStateChanged(); |
10327 | void FlushQueuedEvents(CordbProcess* process); |
10328 | |
10329 | HRESULT WaitForIPCEventFromProcess(CordbProcess* process, |
10330 | CordbAppDomain *pAppDomain, |
10331 | DebuggerIPCEvent* event); |
10332 | |
10333 | bool IsRCEventThread(); |
10334 | |
10335 | private: |
10336 | void DrainWorkerQueue(); |
10337 | |
10338 | void ThreadProc(); |
10339 | static DWORD WINAPI ThreadProc(LPVOID parameter); |
10340 | |
10341 | |
10342 | private: |
10343 | InterlockedStack<class RCETWorkItem> m_WorkerStack; |
10344 | |
10345 | RSSmartPtr<Cordb> m_cordb; |
10346 | HANDLE m_thread; |
10347 | DWORD m_threadId; |
10348 | BOOL m_run; |
10349 | HANDLE m_threadControlEvent; |
10350 | BOOL m_processStateChanged; |
10351 | }; |
10352 | |
10353 | #ifdef FEATURE_INTEROP_DEBUGGING |
10354 | /* ------------------------------------------------------------------------- * |
10355 | * Unmanaged Event struct |
10356 | * ------------------------------------------------------------------------- */ |
10357 | |
10358 | enum CordbUnmanagedEventState |
10359 | { |
10360 | |
10361 | // The continued flags get set in one of a few patterns. |
10362 | // 1) The event is continued having never been hijacked => |
10363 | // EventContinuedUnhijacked is set |
10364 | // 2) The event is continued having been hijacked and then the process terminates or |
10365 | // an error occurs before the hijack finishes => |
10366 | // EventContinuedHijacked is set |
10367 | // 3) The event is continued having been hijacked, then the hijack completes and |
10368 | // execution resumes in the debuggee |
10369 | // EventContinuedHijacked is set |
10370 | // EventContinuedUnhijacked is set |
10371 | |
10372 | CUES_None = 0x00, |
10373 | CUES_ExceptionCleared = 0x01, |
10374 | CUES_EventContinuedHijacked = 0x02, |
10375 | CUES_EventContinuedUnhijacked = 0x04, |
10376 | CUES_Dispatched = 0x08, |
10377 | CUES_ExceptionUnclearable = 0x10, |
10378 | |
10379 | // This is set when a user continues the event by calling |
10380 | // Continue() |
10381 | CUES_UserContinued = 0x20, |
10382 | // This is true if the event is an IB event |
10383 | CUES_IsIBEvent = 0x40, |
10384 | }; |
10385 | |
10386 | struct CordbUnmanagedEvent |
10387 | { |
10388 | public: |
10389 | BOOL IsExceptionCleared() { return m_state & CUES_ExceptionCleared; } |
10390 | BOOL IsEventContinuedHijacked() { return m_state & CUES_EventContinuedHijacked; } |
10391 | BOOL IsEventContinuedUnhijacked() { return m_state & CUES_EventContinuedUnhijacked; } |
10392 | BOOL IsEventUserContinued() { return m_state & CUES_UserContinued; } |
10393 | BOOL IsEventWaitingForContinue() |
10394 | { |
10395 | return (!IsEventContinuedHijacked() && !IsEventContinuedUnhijacked()); |
10396 | } |
10397 | BOOL IsDispatched() { return m_state & CUES_Dispatched; } |
10398 | BOOL IsExceptionUnclearable() { return m_state & CUES_ExceptionUnclearable; } |
10399 | BOOL IsIBEvent() { return m_state & CUES_IsIBEvent; } |
10400 | |
10401 | void SetState(CordbUnmanagedEventState state) { m_state = (CordbUnmanagedEventState)(m_state | state); } |
10402 | void ClearState(CordbUnmanagedEventState state) { m_state = (CordbUnmanagedEventState)(m_state & ~state); } |
10403 | |
10404 | CordbUnmanagedThread *m_owner; |
10405 | CordbUnmanagedEventState m_state; |
10406 | DEBUG_EVENT m_currentDebugEvent; |
10407 | CordbUnmanagedEvent *m_next; |
10408 | }; |
10409 | |
10410 | |
10411 | /* ------------------------------------------------------------------------- * |
10412 | * Unmanaged Thread class |
10413 | * ------------------------------------------------------------------------- */ |
10414 | |
10415 | enum CordbUnmanagedThreadState |
10416 | { |
10417 | CUTS_None = 0x0000, |
10418 | CUTS_Deleted = 0x0001, |
10419 | CUTS_FirstChanceHijacked = 0x0002, |
10420 | // Set when interop debugging needs the SS flag to be enabled |
10421 | // regardless of what the user wants it to be |
10422 | CUTS_IsSSFlagNeeded = 0x0004, |
10423 | CUTS_GenericHijacked = 0x0008, |
10424 | // when the m_raiseExceptionEntryContext is valid |
10425 | CUTS_HasRaiseExceptionEntryCtx = 0x0010, |
10426 | CUTS_BlockingForSync = 0x0020, |
10427 | CUTS_Suspended = 0x0040, |
10428 | CUTS_IsSpecialDebuggerThread = 0x0080, |
10429 | // when the thread is re-executing RaiseException to retrigger an exception |
10430 | CUTS_IsRaiseExceptionHijacked = 0x0100, |
10431 | CUTS_HasIBEvent = 0x0200, |
10432 | CUTS_HasOOBEvent = 0x0400, |
10433 | CUTS_HasSpecialStackOverflowCase = 0x0800, |
10434 | #ifdef _DEBUG |
10435 | CUTS_DEBUG_SingleStep = 0x1000, |
10436 | #endif |
10437 | CUTS_SkippingNativePatch = 0x2000, |
10438 | CUTS_HasContextSet = 0x4000, |
10439 | // Set when interop debugging is making use of the single step flag |
10440 | // but the user has not set it |
10441 | CUTS_IsSSFlagHidden = 0x8000 |
10442 | |
10443 | }; |
10444 | |
10445 | class CordbUnmanagedThread : public CordbBase |
10446 | { |
10447 | public: |
10448 | CordbUnmanagedThread(CordbProcess *pProcess, DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase); |
10449 | ~CordbUnmanagedThread(); |
10450 | |
10451 | using CordbBase::GetProcess; |
10452 | |
10453 | #ifdef _DEBUG |
10454 | virtual const char * DbgGetName() { return "CordbUnmanagedThread" ; } |
10455 | #endif |
10456 | |
10457 | // CordbUnmanagedThread is a purely internal object. It's not exposed via ICorDebug APIs and so |
10458 | // we should never use External AddRef. |
10459 | ULONG STDMETHODCALLTYPE AddRef() { _ASSERTE(!"Don't use external addref on a CordbUnmanagedThread" ); return (BaseAddRef());} |
10460 | ULONG STDMETHODCALLTYPE Release() { _ASSERTE(!"Don't use external release on a CordbUnmanagedThread" ); return (BaseRelease());} |
10461 | |
10462 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface) |
10463 | { |
10464 | _ASSERTE(!"Don't use QI on a CordbUnmanagedThread" ); |
10465 | // Not really used since we never expose this class. If we ever do expose this class via the ICorDebug API then |
10466 | // we should, of course, implement this. |
10467 | return E_NOINTERFACE; |
10468 | } |
10469 | |
10470 | HRESULT LoadTLSArrayPtr(); |
10471 | |
10472 | // Hijacks this thread to a hijack worker function which recieves the current |
10473 | // context and the provided exception record. The reason determines what code |
10474 | // the hijack worker executes |
10475 | HRESULT SetupFirstChanceHijack(EHijackReason::EHijackReason reason, const EXCEPTION_RECORD * pExceptionRecord); |
10476 | HRESULT SetupFirstChanceHijackForSync(); |
10477 | |
10478 | HRESULT SetupGenericHijack(DWORD eventCode, const EXCEPTION_RECORD * pRecord); |
10479 | HRESULT FixupFromGenericHijack(); |
10480 | |
10481 | HRESULT FixupAfterOOBException(CordbUnmanagedEvent * ue); |
10482 | |
10483 | void SetupForSkipBreakpoint(NativePatch * pNativePatch); |
10484 | void FixupForSkipBreakpoint(); |
10485 | bool IsCantStop(); |
10486 | |
10487 | // These are wrappers for the OS calls which hide |
10488 | // the effects of hijacking and internal SS flag usage |
10489 | HRESULT GetThreadContext(DT_CONTEXT * pContext); |
10490 | HRESULT SetThreadContext(DT_CONTEXT * pContext); |
10491 | |
10492 | // Turns on and off the internal usage of the SS flag |
10493 | VOID BeginStepping(); |
10494 | VOID EndStepping(); |
10495 | |
10496 | // An accessor for &m_context, this value generally stores |
10497 | // a context we may need to restore after a hijack completes |
10498 | DT_CONTEXT * GetHijackCtx(); |
10499 | |
10500 | private: |
10501 | CORDB_ADDRESS m_stackBase; |
10502 | CORDB_ADDRESS m_stackLimit; |
10503 | |
10504 | public: |
10505 | BOOL GetStackRange(CORDB_ADDRESS *pBase, CORDB_ADDRESS *pLimit); |
10506 | |
10507 | BOOL IsDeleted() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_Deleted; } |
10508 | BOOL IsFirstChanceHijacked() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_FirstChanceHijacked; } |
10509 | BOOL IsGenericHijacked() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_GenericHijacked; } |
10510 | BOOL IsBlockingForSync() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_BlockingForSync; } |
10511 | BOOL IsSuspended() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_Suspended; } |
10512 | BOOL IsSpecialDebuggerThread() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSpecialDebuggerThread; } |
10513 | BOOL HasIBEvent() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasIBEvent; } |
10514 | BOOL HasOOBEvent() { return m_state & CUTS_HasOOBEvent; } |
10515 | BOOL HasSpecialStackOverflowCase() {LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasSpecialStackOverflowCase; } |
10516 | #ifdef _DEBUG |
10517 | BOOL IsDEBUGTrace() { return m_state & CUTS_DEBUG_SingleStep; } |
10518 | #endif |
10519 | BOOL IsSkippingNativePatch() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_SkippingNativePatch; } |
10520 | BOOL IsContextSet() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasContextSet; } |
10521 | BOOL IsSSFlagNeeded() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSSFlagNeeded; } |
10522 | BOOL IsSSFlagHidden() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsSSFlagHidden; } |
10523 | BOOL HasRaiseExceptionEntryCtx() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_HasRaiseExceptionEntryCtx; } |
10524 | BOOL IsRaiseExceptionHijacked() { LIMITED_METHOD_CONTRACT; return m_state & CUTS_IsRaiseExceptionHijacked; } |
10525 | |
10526 | void SetState(CordbUnmanagedThreadState state) |
10527 | { |
10528 | LIMITED_METHOD_CONTRACT; |
10529 | m_state = (CordbUnmanagedThreadState)(m_state | state); |
10530 | _ASSERTE(!IsSuspended() || !IsBlockingForSync()); |
10531 | _ASSERTE(!IsSuspended() || !IsFirstChanceHijacked()); |
10532 | } |
10533 | void ClearState(CordbUnmanagedThreadState state) {LIMITED_METHOD_CONTRACT; m_state = (CordbUnmanagedThreadState)(m_state & ~state); } |
10534 | |
10535 | void HijackToRaiseException(); |
10536 | void RestoreFromRaiseExceptionHijack(); |
10537 | void SaveRaiseExceptionEntryContext(); |
10538 | void ClearRaiseExceptionEntryContext(); |
10539 | BOOL IsExceptionFromLastRaiseException(const EXCEPTION_RECORD* pExceptionRecord); |
10540 | |
10541 | CordbUnmanagedEvent *IBEvent() {LIMITED_METHOD_CONTRACT; return &m_IBEvent; } |
10542 | CordbUnmanagedEvent *IBEvent2() {LIMITED_METHOD_CONTRACT; return &m_IBEvent2; } |
10543 | CordbUnmanagedEvent *OOBEvent() { return &m_OOBEvent; } |
10544 | |
10545 | DWORD GetOSTid() |
10546 | { |
10547 | return (DWORD) this->m_id; |
10548 | } |
10549 | |
10550 | #ifdef DBG_TARGET_X86 |
10551 | // Stores the thread's current leaf SEH handler |
10552 | HRESULT SaveCurrentLeafSeh(); |
10553 | // Restores the thread's leaf SEH handler from the previously saved value |
10554 | HRESULT RestoreLeafSeh(); |
10555 | #endif |
10556 | |
10557 | // Logs basic data about a context to the debugging log |
10558 | static VOID LogContext(DT_CONTEXT* pContext); |
10559 | |
10560 | public: |
10561 | HANDLE m_handle; |
10562 | |
10563 | // @dbgtodo - the TLS reading is only used for interop hijacks; which goes away in Arrowhead. |
10564 | // Target address of the Thread Information Block (TIB). |
10565 | void *m_threadLocalBase; |
10566 | |
10567 | // Target address of the Thread Local Storage (TLS) array. This is for slots 0 -63. |
10568 | void *m_pTLSArray; |
10569 | |
10570 | // Target Address of extended Thread local Storage array. These are for slots about 63. |
10571 | // This may be NULL if extended storage is not yet allocated. |
10572 | void *m_pTLSExtendedArray; |
10573 | |
10574 | |
10575 | CordbUnmanagedThreadState m_state; |
10576 | |
10577 | CordbUnmanagedEvent m_IBEvent; |
10578 | CordbUnmanagedEvent m_IBEvent2; |
10579 | CordbUnmanagedEvent m_OOBEvent; |
10580 | |
10581 | LSPTR_CONTEXT m_pLeftSideContext; |
10582 | void *m_originalHandler; |
10583 | |
10584 | private: |
10585 | // Spare context used for various purposes. |
10586 | // See CordbUnmanagedThread::GetThreadContext for details |
10587 | DT_CONTEXT m_context; |
10588 | |
10589 | // The context of the thread the last time it called into kernel32!RaiseException |
10590 | DT_CONTEXT m_raiseExceptionEntryContext; |
10591 | |
10592 | DWORD m_raiseExceptionExceptionCode; |
10593 | DWORD m_raiseExceptionExceptionFlags; |
10594 | DWORD m_raiseExceptionNumberParameters; |
10595 | ULONG_PTR m_raiseExceptionExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; |
10596 | |
10597 | |
10598 | #ifdef DBG_TARGET_X86 |
10599 | // the SEH handler which was the leaf when SaveCurrentSeh was called (prior to hijack) |
10600 | REMOTE_PTR m_pSavedLeafSeh; |
10601 | #endif |
10602 | |
10603 | HRESULT EnableSSAfterBP(); |
10604 | bool GetEEThreadCantStopHelper(); |
10605 | |
10606 | HRESULT GetTlsSlot(DWORD slot, REMOTE_PTR *pValue); |
10607 | HRESULT SetTlsSlot(DWORD slot, REMOTE_PTR value); |
10608 | REMOTE_PTR GetPreDefTlsSlot(SIZE_T slot, bool * pRead); |
10609 | |
10610 | void * m_pPatchSkipAddress; |
10611 | |
10612 | UINT m_continueCountCached; |
10613 | |
10614 | DWORD_PTR GetEEThreadValue(); |
10615 | REMOTE_PTR GetEETlsDataBlock(); |
10616 | HRESULT GetClrModuleTlsDataAddress(REMOTE_PTR* pAddress); |
10617 | |
10618 | public: |
10619 | HRESULT GetEEDebuggerWord(REMOTE_PTR *pValue); |
10620 | HRESULT SetEEDebuggerWord(REMOTE_PTR value); |
10621 | HRESULT GetEEThreadPtr(REMOTE_PTR *ppEEThread); |
10622 | |
10623 | bool GetEEPGCDisabled(); |
10624 | void GetEEState(bool *threadStepping, bool *specialManagedException); |
10625 | bool GetEEFrame(); |
10626 | }; |
10627 | #endif // FEATURE_INTEROP_DEBUGGING |
10628 | |
10629 | |
10630 | //******************************************************************************** |
10631 | //**************** App Domain Publishing Service API ***************************** |
10632 | //******************************************************************************** |
10633 | |
10634 | |
10635 | class EnumElement |
10636 | { |
10637 | public: |
10638 | EnumElement() |
10639 | { |
10640 | m_pData = NULL; |
10641 | m_pNext = NULL; |
10642 | } |
10643 | |
10644 | void SetData (void *pData) { m_pData = pData;} |
10645 | void *GetData () { return m_pData;} |
10646 | void SetNext (EnumElement *pNext) { m_pNext = pNext;} |
10647 | EnumElement *GetNext () { return m_pNext;} |
10648 | |
10649 | private: |
10650 | void *m_pData; |
10651 | EnumElement *m_pNext; |
10652 | }; |
10653 | |
10654 | #if defined(FEATURE_DBG_PUBLISH) |
10655 | |
10656 | // Prototype of psapi!GetModuleFileNameEx. |
10657 | typedef DWORD FPGetModuleFileNameEx(HANDLE, HMODULE, LPTSTR, DWORD); |
10658 | |
10659 | |
10660 | class CorpubPublish : public CordbCommonBase, public ICorPublish |
10661 | { |
10662 | public: |
10663 | CorpubPublish(); |
10664 | virtual ~CorpubPublish(); |
10665 | |
10666 | #ifdef _DEBUG |
10667 | virtual const char * DbgGetName() { return "CordbPublish" ; } |
10668 | #endif |
10669 | |
10670 | //----------------------------------------------------------- |
10671 | // IUnknown |
10672 | //----------------------------------------------------------- |
10673 | |
10674 | ULONG STDMETHODCALLTYPE AddRef() |
10675 | { |
10676 | return (BaseAddRef()); |
10677 | } |
10678 | ULONG STDMETHODCALLTYPE Release() |
10679 | { |
10680 | return (BaseRelease()); |
10681 | } |
10682 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
10683 | |
10684 | //----------------------------------------------------------- |
10685 | // ICorPublish |
10686 | //----------------------------------------------------------- |
10687 | |
10688 | COM_METHOD EnumProcesses( |
10689 | COR_PUB_ENUMPROCESS Type, |
10690 | ICorPublishProcessEnum **ppIEnum); |
10691 | |
10692 | COM_METHOD GetProcess( |
10693 | unsigned pid, |
10694 | ICorPublishProcess **ppProcess); |
10695 | |
10696 | //----------------------------------------------------------- |
10697 | // CreateObject |
10698 | //----------------------------------------------------------- |
10699 | static COM_METHOD CreateObject(REFIID id, void **object) |
10700 | { |
10701 | *object = NULL; |
10702 | |
10703 | if (id != IID_IUnknown && id != IID_ICorPublish) |
10704 | return (E_NOINTERFACE); |
10705 | |
10706 | CorpubPublish *pCorPub = new (nothrow) CorpubPublish(); |
10707 | |
10708 | if (pCorPub == NULL) |
10709 | return (E_OUTOFMEMORY); |
10710 | |
10711 | *object = (ICorPublish*)pCorPub; |
10712 | pCorPub->AddRef(); |
10713 | |
10714 | return (S_OK); |
10715 | } |
10716 | |
10717 | private: |
10718 | HRESULT GetProcessInternal( unsigned pid, CorpubProcess **ppProcess ); |
10719 | |
10720 | // Cached information to get the process name. Not available on all platforms, so may be null. |
10721 | HModuleHolder m_hPSAPIdll; |
10722 | FPGetModuleFileNameEx * m_fpGetModuleFileNameEx; |
10723 | }; |
10724 | |
10725 | class CorpubProcess : public CordbCommonBase, public ICorPublishProcess |
10726 | { |
10727 | public: |
10728 | CorpubProcess(const ProcessDescriptor * pProcessDescriptor, |
10729 | bool fManaged, |
10730 | HANDLE hProcess, |
10731 | HANDLE hMutex, |
10732 | AppDomainEnumerationIPCBlock *pAD, |
10733 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
10734 | IPCReaderInterface *pIPCReader, |
10735 | #endif // !FEATURE_DBGIPC_TRANSPORT_DI |
10736 | FPGetModuleFileNameEx * fpGetModuleFileNameEx); |
10737 | virtual ~CorpubProcess(); |
10738 | |
10739 | #ifdef _DEBUG |
10740 | virtual const char * DbgGetName() { return "CorpubProcess" ; } |
10741 | #endif |
10742 | |
10743 | |
10744 | //----------------------------------------------------------- |
10745 | // IUnknown |
10746 | //----------------------------------------------------------- |
10747 | |
10748 | ULONG STDMETHODCALLTYPE AddRef() |
10749 | { |
10750 | return (BaseAddRef()); |
10751 | } |
10752 | ULONG STDMETHODCALLTYPE Release() |
10753 | { |
10754 | return (BaseRelease()); |
10755 | } |
10756 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
10757 | |
10758 | //----------------------------------------------------------- |
10759 | // ICorPublishProcess |
10760 | //----------------------------------------------------------- |
10761 | COM_METHOD IsManaged(BOOL *pbManaged); |
10762 | |
10763 | /* |
10764 | * Enumerate the list of known application domains in the target process. |
10765 | */ |
10766 | COM_METHOD EnumAppDomains(ICorPublishAppDomainEnum **ppEnum); |
10767 | |
10768 | /* |
10769 | * Returns the OS ID for the process in question. |
10770 | */ |
10771 | COM_METHOD GetProcessID(unsigned *pid); |
10772 | |
10773 | /* |
10774 | * Get the display name for a process. |
10775 | */ |
10776 | COM_METHOD GetDisplayName(ULONG32 cchName, |
10777 | ULONG32 *pcchName, |
10778 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
10779 | |
10780 | CorpubProcess *GetNextProcess () { return m_pNext;} |
10781 | void SetNext (CorpubProcess *pNext) { m_pNext = pNext;} |
10782 | |
10783 | // Helper to tell if this process has exited |
10784 | bool IsExited(); |
10785 | |
10786 | public: |
10787 | ProcessDescriptor m_processDescriptor; |
10788 | |
10789 | private: |
10790 | bool m_fIsManaged; |
10791 | HANDLE m_hProcess; |
10792 | HANDLE m_hMutex; |
10793 | AppDomainEnumerationIPCBlock *m_AppDomainCB; |
10794 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
10795 | IPCReaderInterface *m_pIPCReader; // controls the lifetime of the AppDomainEnumerationIPCBlock |
10796 | #endif // !FEATURE_DBGIPC_TRANSPORT_DI |
10797 | CorpubProcess *m_pNext; // pointer to the next process in the process list |
10798 | WCHAR *m_szProcessName; |
10799 | |
10800 | }; |
10801 | |
10802 | class CorpubAppDomain : public CordbCommonBase, public ICorPublishAppDomain |
10803 | { |
10804 | public: |
10805 | CorpubAppDomain (__in LPWSTR szAppDomainName, ULONG Id); |
10806 | virtual ~CorpubAppDomain(); |
10807 | |
10808 | #ifdef _DEBUG |
10809 | virtual const char * DbgGetName() { return "CorpubAppDomain" ; } |
10810 | #endif |
10811 | |
10812 | //----------------------------------------------------------- |
10813 | // IUnknown |
10814 | //----------------------------------------------------------- |
10815 | |
10816 | ULONG STDMETHODCALLTYPE AddRef() |
10817 | { |
10818 | return (BaseAddRef()); |
10819 | } |
10820 | ULONG STDMETHODCALLTYPE Release() |
10821 | { |
10822 | return (BaseRelease()); |
10823 | } |
10824 | COM_METHOD QueryInterface (REFIID riid, void **ppInterface); |
10825 | |
10826 | //----------------------------------------------------------- |
10827 | // ICorPublishAppDomain |
10828 | //----------------------------------------------------------- |
10829 | |
10830 | /* |
10831 | * Get the name and ID for an application domain. |
10832 | */ |
10833 | COM_METHOD GetID (ULONG32 *pId); |
10834 | |
10835 | /* |
10836 | * Get the name for an application domain. |
10837 | */ |
10838 | COM_METHOD GetName (ULONG32 cchName, |
10839 | ULONG32 *pcchName, |
10840 | __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
10841 | |
10842 | CorpubAppDomain *GetNextAppDomain () { return m_pNext;} |
10843 | void SetNext (CorpubAppDomain *pNext) { m_pNext = pNext;} |
10844 | |
10845 | private: |
10846 | CorpubAppDomain *m_pNext; |
10847 | WCHAR *m_szAppDomainName; |
10848 | ULONG m_id; |
10849 | |
10850 | }; |
10851 | |
10852 | class CorpubProcessEnum : public CordbCommonBase, public ICorPublishProcessEnum |
10853 | { |
10854 | public: |
10855 | CorpubProcessEnum(CorpubProcess *pFirst); |
10856 | virtual ~CorpubProcessEnum(); |
10857 | |
10858 | #ifdef _DEBUG |
10859 | virtual const char * DbgGetName() { return "CorpubProcessEnum" ; } |
10860 | #endif |
10861 | |
10862 | |
10863 | //----------------------------------------------------------- |
10864 | // IUnknown |
10865 | //----------------------------------------------------------- |
10866 | |
10867 | ULONG STDMETHODCALLTYPE AddRef() |
10868 | { |
10869 | return (BaseAddRef()); |
10870 | } |
10871 | ULONG STDMETHODCALLTYPE Release() |
10872 | { |
10873 | return (BaseRelease()); |
10874 | } |
10875 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
10876 | |
10877 | //----------------------------------------------------------- |
10878 | // ICorPublishProcessEnum |
10879 | //----------------------------------------------------------- |
10880 | |
10881 | COM_METHOD Skip(ULONG celt); |
10882 | COM_METHOD Reset(); |
10883 | COM_METHOD Clone(ICorPublishEnum **ppEnum); |
10884 | COM_METHOD GetCount(ULONG *pcelt); |
10885 | COM_METHOD Next(ULONG celt, |
10886 | ICorPublishProcess *objects[], |
10887 | ULONG *pceltFetched); |
10888 | |
10889 | private: |
10890 | CorpubProcess *m_pFirst; |
10891 | CorpubProcess *m_pCurrent; |
10892 | |
10893 | }; |
10894 | |
10895 | class CorpubAppDomainEnum : public CordbCommonBase, public ICorPublishAppDomainEnum |
10896 | { |
10897 | public: |
10898 | CorpubAppDomainEnum(CorpubAppDomain *pFirst); |
10899 | virtual ~CorpubAppDomainEnum(); |
10900 | |
10901 | |
10902 | #ifdef _DEBUG |
10903 | virtual const char * DbgGetName() { return "CordbAppDomainEnum" ; } |
10904 | #endif |
10905 | |
10906 | |
10907 | //----------------------------------------------------------- |
10908 | // IUnknown |
10909 | //----------------------------------------------------------- |
10910 | |
10911 | ULONG STDMETHODCALLTYPE AddRef() |
10912 | { |
10913 | return (BaseAddRef()); |
10914 | } |
10915 | ULONG STDMETHODCALLTYPE Release() |
10916 | { |
10917 | return (BaseRelease()); |
10918 | } |
10919 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
10920 | |
10921 | //----------------------------------------------------------- |
10922 | // ICorPublishAppDomainEnum |
10923 | //----------------------------------------------------------- |
10924 | COM_METHOD Skip(ULONG celt); |
10925 | COM_METHOD Reset(); |
10926 | COM_METHOD Clone(ICorPublishEnum **ppEnum); |
10927 | COM_METHOD GetCount(ULONG *pcelt); |
10928 | |
10929 | COM_METHOD Next(ULONG celt, |
10930 | ICorPublishAppDomain *objects[], |
10931 | ULONG *pceltFetched); |
10932 | |
10933 | private: |
10934 | CorpubAppDomain *m_pFirst; |
10935 | CorpubAppDomain *m_pCurrent; |
10936 | |
10937 | }; |
10938 | |
10939 | #endif // defined(FEATURE_DBG_PUBLISH) |
10940 | |
10941 | class CordbHeapEnum : public CordbBase, public ICorDebugHeapEnum |
10942 | { |
10943 | public: |
10944 | CordbHeapEnum(CordbProcess *proc); |
10945 | |
10946 | #ifdef _DEBUG |
10947 | virtual const char * DbgGetName() { return "CordbHeapEnum" ; } |
10948 | #endif |
10949 | |
10950 | //----------------------------------------------------------- |
10951 | // IUnknown |
10952 | //----------------------------------------------------------- |
10953 | ULONG STDMETHODCALLTYPE AddRef() |
10954 | { |
10955 | return (BaseAddRef()); |
10956 | } |
10957 | ULONG STDMETHODCALLTYPE Release() |
10958 | { |
10959 | return (BaseRelease()); |
10960 | } |
10961 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
10962 | |
10963 | COM_METHOD Skip(ULONG celt); |
10964 | COM_METHOD Reset(); |
10965 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
10966 | COM_METHOD GetCount(ULONG *pcelt); |
10967 | |
10968 | COM_METHOD Next(ULONG celt, |
10969 | COR_HEAPOBJECT objects[], |
10970 | ULONG *pceltFetched); |
10971 | |
10972 | virtual void Neuter() |
10973 | { |
10974 | Clear(); |
10975 | CordbBase::Neuter(); |
10976 | } |
10977 | private: |
10978 | void Clear(); |
10979 | |
10980 | private: |
10981 | IDacDbiInterface::HeapWalkHandle mHeapHandle; |
10982 | }; |
10983 | |
10984 | |
10985 | class CordbRefEnum : public CordbBase, public ICorDebugGCReferenceEnum |
10986 | { |
10987 | public: |
10988 | CordbRefEnum(CordbProcess *proc, BOOL walkWeakRefs); |
10989 | CordbRefEnum(CordbProcess *proc, CorGCReferenceType types); |
10990 | |
10991 | #ifdef _DEBUG |
10992 | virtual const char * DbgGetName() { return "CordbHeapEnum" ; } |
10993 | #endif |
10994 | |
10995 | //----------------------------------------------------------- |
10996 | // IUnknown |
10997 | //----------------------------------------------------------- |
10998 | ULONG STDMETHODCALLTYPE AddRef() |
10999 | { |
11000 | return (BaseAddRef()); |
11001 | } |
11002 | ULONG STDMETHODCALLTYPE Release() |
11003 | { |
11004 | return (BaseRelease()); |
11005 | } |
11006 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
11007 | |
11008 | COM_METHOD Skip(ULONG celt); |
11009 | COM_METHOD Reset(); |
11010 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
11011 | COM_METHOD GetCount(ULONG *pcelt); |
11012 | |
11013 | COM_METHOD Next(ULONG celt, |
11014 | COR_GC_REFERENCE refs[], |
11015 | ULONG *pceltFetched); |
11016 | |
11017 | virtual void Neuter(); |
11018 | |
11019 | private: |
11020 | RefWalkHandle mRefHandle; |
11021 | BOOL ; |
11022 | UINT32 mHandleMask; |
11023 | }; |
11024 | |
11025 | // Since the hash table of modules is per app domain (and |
11026 | // threads is per process) (for fast lookup from the appdomain/process), |
11027 | // we need this wrapper |
11028 | // here which allows us to iterate through an assembly's |
11029 | // modules. Is basically filters out modules/threads that aren't |
11030 | // in the assembly/appdomain. This slow & awkward for assemblies, but fast |
11031 | // for the common case - appdomain lookup. |
11032 | class CordbEnumFilter : public CordbBase, |
11033 | public ICorDebugThreadEnum, |
11034 | public ICorDebugModuleEnum |
11035 | { |
11036 | public: |
11037 | CordbEnumFilter(CordbBase * pOwnerObj, NeuterList * pOwnerList); |
11038 | CordbEnumFilter(CordbEnumFilter*src); |
11039 | virtual ~CordbEnumFilter(); |
11040 | |
11041 | virtual void Neuter(); |
11042 | |
11043 | |
11044 | #ifdef _DEBUG |
11045 | virtual const char * DbgGetName() { return "CordbEnumFilter" ; } |
11046 | #endif |
11047 | |
11048 | |
11049 | //----------------------------------------------------------- |
11050 | // IUnknown |
11051 | //----------------------------------------------------------- |
11052 | |
11053 | ULONG STDMETHODCALLTYPE AddRef() |
11054 | { |
11055 | return (BaseAddRef()); |
11056 | } |
11057 | ULONG STDMETHODCALLTYPE Release() |
11058 | { |
11059 | return (BaseRelease()); |
11060 | } |
11061 | COM_METHOD QueryInterface(REFIID riid, void **ppInterface); |
11062 | |
11063 | //----------------------------------------------------------- |
11064 | // Common methods |
11065 | //----------------------------------------------------------- |
11066 | COM_METHOD Skip(ULONG celt); |
11067 | COM_METHOD Reset(); |
11068 | COM_METHOD Clone(ICorDebugEnum **ppEnum); |
11069 | COM_METHOD GetCount(ULONG *pcelt); |
11070 | //----------------------------------------------------------- |
11071 | // ICorDebugModuleEnum |
11072 | //----------------------------------------------------------- |
11073 | COM_METHOD Next(ULONG celt, |
11074 | ICorDebugModule *objects[], |
11075 | ULONG *pceltFetched); |
11076 | |
11077 | //----------------------------------------------------------- |
11078 | // ICorDebugThreadEnum |
11079 | //----------------------------------------------------------- |
11080 | COM_METHOD Next(ULONG celt, |
11081 | ICorDebugThread *objects[], |
11082 | ULONG *pceltFetched); |
11083 | |
11084 | HRESULT Init (ICorDebugModuleEnum *pModEnum, CordbAssembly *pAssembly); |
11085 | HRESULT Init (ICorDebugThreadEnum *pThreadEnum, CordbAppDomain *pAppDomain); |
11086 | |
11087 | |
11088 | private: |
11089 | HRESULT NextWorker(ULONG celt, ICorDebugModule *objects[], ULONG *pceltFetched); |
11090 | HRESULT NextWorker(ULONG celt,ICorDebugThread *objects[], ULONG *pceltFetched); |
11091 | |
11092 | // Owning object is our link to the CordbProcess* tree. Never null until we're neutered. |
11093 | // NeuterList is related to the owning object. Need to cache it so that we can pass it on |
11094 | // to our clones. |
11095 | CordbBase * m_pOwnerObj; // provides us w/ a CordbProcess* |
11096 | NeuterList * m_pOwnerNeuterList; |
11097 | |
11098 | |
11099 | EnumElement *m_pFirst; |
11100 | EnumElement *m_pCurrent; |
11101 | int m_iCount; |
11102 | }; |
11103 | |
11104 | // Helpers to double-check the RS results against DAC. |
11105 | #if defined(_DEBUG) |
11106 | void CheckAgainstDAC(CordbFunction * pFunc, void * pIP, mdMethodDef mdExpected); |
11107 | #endif |
11108 | |
11109 | HRESULT CopyOutString(const WCHAR * pInputString, ULONG32 cchName, ULONG32 * pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]); |
11110 | |
11111 | |
11112 | |
11113 | inline UINT AllocCookieCordbEval(CordbProcess *pProc, CordbEval* p) |
11114 | { |
11115 | _ASSERTE(pProc->GetProcessLock()->HasLock()); |
11116 | return pProc->m_EvalTable.Add(p); |
11117 | } |
11118 | inline CordbEval * UnwrapCookieCordbEval(CordbProcess *pProc, UINT cookie) |
11119 | { |
11120 | _ASSERTE(pProc->GetProcessLock()->HasLock()); |
11121 | return pProc->m_EvalTable.LookupAndRemove(cookie); |
11122 | } |
11123 | |
11124 | |
11125 | // We defined this at the top of the file - undef it now so that we don't pollute other files. |
11126 | #undef CRITICAL_SECTION |
11127 | |
11128 | |
11129 | #ifdef RSCONTRACTS |
11130 | |
11131 | //----------------------------------------------------------------------------- |
11132 | // For debug builds, we maintain some thread-state to track debug bits |
11133 | // to help us do some more aggressive asserts. |
11134 | //----------------------------------------------------------------------------- |
11135 | |
11136 | class PublicAPIHolder; |
11137 | class PublicReentrantAPIHolder; |
11138 | class PublicCallbackHolder; |
11139 | class PublicDebuggerErrorCallbackHolder; |
11140 | |
11141 | class DbgRSThread |
11142 | { |
11143 | public: |
11144 | friend class PublicAPIHolder; |
11145 | friend class PublicReentrantAPIHolder; |
11146 | friend class PublicCallbackHolder; |
11147 | friend class PublicDebuggerErrorCallbackHolder; |
11148 | friend class PrivateShimCallbackHolder; |
11149 | |
11150 | DbgRSThread(); |
11151 | |
11152 | // The TLS slot that we'll put this thread object in. |
11153 | static DWORD s_TlsSlot; |
11154 | |
11155 | static LONG s_Total; // Total count of thread objects |
11156 | |
11157 | // Get a thread object for the current thread via a TLS lookup. |
11158 | static DbgRSThread * GetThread(); |
11159 | |
11160 | // Call during DllMain to release this. |
11161 | static DbgRSThread * Create() |
11162 | { |
11163 | InterlockedIncrement(&s_Total); |
11164 | |
11165 | DbgRSThread * p = new (nothrow) DbgRSThread(); |
11166 | BOOL f = TlsSetValue(s_TlsSlot, p); |
11167 | _ASSERT(f); |
11168 | return p; |
11169 | } |
11170 | |
11171 | void Destroy() |
11172 | { |
11173 | InterlockedDecrement(&s_Total); |
11174 | |
11175 | BOOL f = TlsSetValue(s_TlsSlot, NULL); |
11176 | _ASSERT(f); |
11177 | |
11178 | delete this; |
11179 | } |
11180 | |
11181 | // Return true if this thread is inside the RS. |
11182 | bool IsInRS() { return m_cInsideRS > 0; } |
11183 | |
11184 | // Locking API.. |
11185 | // These will assert if the operation is unsafe. |
11186 | void NotifyTakeLock(RSLock * pLock); |
11187 | void NotifyReleaseLock(RSLock * pLock); |
11188 | |
11189 | // Used to map other resources (like thread access) into the lock hierachy. |
11190 | // Note this only effects lock leveling checks and doesn't effect HoldsAnyLock(). |
11191 | void TakeVirtualLock(RSLock::ERSLockLevel level); |
11192 | void ReleaseVirtualLock(RSLock::ERSLockLevel level); |
11193 | |
11194 | // return true if this thread is holding any RS locks. Useful to check on Public API transition boundaries. |
11195 | bool HoldsAnyDbgApiLocks() { return m_cTotalDbgApiLocks > 0; } |
11196 | |
11197 | enum EThreadType |
11198 | { |
11199 | cOther, |
11200 | cW32ET |
11201 | }; |
11202 | void SetThreadType(EThreadType e) { m_eThreadType = e; } |
11203 | |
11204 | bool IsWin32EventThread() { return m_eThreadType == cW32ET; } |
11205 | |
11206 | void SetUnrecoverableCallback(bool fIsUnrecoverableErrorCallback) |
11207 | { |
11208 | // Not reentrant. |
11209 | _ASSERTE(m_fIsUnrecoverableErrorCallback != fIsUnrecoverableErrorCallback); |
11210 | |
11211 | m_fIsUnrecoverableErrorCallback = fIsUnrecoverableErrorCallback; |
11212 | } |
11213 | |
11214 | inline void AssertThreadIsLockFree() |
11215 | { |
11216 | // If we're in an unrecoverable callback, we may hold locks. |
11217 | _ASSERTE(m_fIsUnrecoverableErrorCallback |
11218 | || !HoldsAnyDbgApiLocks() || |
11219 | !"Thread should not have locks on public/internal transition" ); |
11220 | } |
11221 | |
11222 | protected: |
11223 | EThreadType m_eThreadType; |
11224 | |
11225 | // More debugging tidbits - tid that we're on, and a sanity checking cookie. |
11226 | DWORD m_tid; |
11227 | DWORD m_Cookie; |
11228 | |
11229 | enum ECookie |
11230 | { |
11231 | COOKIE_VALUE = 0x12345678 |
11232 | }; |
11233 | |
11234 | |
11235 | // This tells us if the thread is currently in the scope of a PublicAPIHolder. |
11236 | int m_cInsideRS; |
11237 | |
11238 | // This tells us if a thread is currently being dispatched via a callback. |
11239 | bool m_fIsInCallback; |
11240 | |
11241 | // We explicitly track if this thread is in an unrecoverable error callback |
11242 | // b/c that will weaken some other asserts. |
11243 | // It would be nice to clean up the unrecoverable error callback and have it |
11244 | // behave like all the other callbacks. Then we can remove this. |
11245 | bool m_fIsUnrecoverableErrorCallback; |
11246 | |
11247 | // Locking context. Used to tell what levels of locks we hold so we can determine if a lock is safe to take. |
11248 | int m_cLocks[RSLock::LL_MAX]; |
11249 | int m_cTotalDbgApiLocks; |
11250 | }; |
11251 | |
11252 | //----------------------------------------------------------------------------- |
11253 | // Mark when we enter / exit public APIs |
11254 | //----------------------------------------------------------------------------- |
11255 | |
11256 | // Holder for Non-reentrant Public API (this is the vast majority) |
11257 | class PublicAPIHolder |
11258 | { |
11259 | public: |
11260 | PublicAPIHolder() |
11261 | { |
11262 | // on entry |
11263 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11264 | pThread->m_cInsideRS++; |
11265 | _ASSERTE(pThread->m_cInsideRS == 1 || !"Non-reentrant API being called re-entrantly" ); |
11266 | |
11267 | // Should never be in public w/ these locks |
11268 | pThread->AssertThreadIsLockFree(); |
11269 | } |
11270 | ~PublicAPIHolder() { |
11271 | // On exit. |
11272 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11273 | pThread->m_cInsideRS--; |
11274 | _ASSERTE(!pThread->IsInRS()); |
11275 | |
11276 | // Should never be in public w/ these locks. If we assert here, |
11277 | // then we're leaking locks. |
11278 | pThread->AssertThreadIsLockFree(); |
11279 | } |
11280 | }; |
11281 | |
11282 | // Holder for reentrant public API |
11283 | class PublicReentrantAPIHolder |
11284 | { |
11285 | public: |
11286 | PublicReentrantAPIHolder() |
11287 | { |
11288 | // on entry |
11289 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11290 | pThread->m_cInsideRS++; |
11291 | |
11292 | // Cache count now so that we can calidate it in the dtor. |
11293 | m_oldCount = pThread->m_cInsideRS; |
11294 | // Since a we may have been called from within the RS, we may hold locks |
11295 | } |
11296 | ~PublicReentrantAPIHolder() |
11297 | { |
11298 | |
11299 | // On exit. |
11300 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11301 | |
11302 | // Ensure that our children were balanced |
11303 | _ASSERTE(pThread->m_cInsideRS == m_oldCount); |
11304 | |
11305 | pThread->m_cInsideRS--; |
11306 | _ASSERTE(pThread->m_cInsideRS >= 0); |
11307 | |
11308 | // Since a we may have been called from within the RS, we may hold locks |
11309 | } |
11310 | private: |
11311 | int m_oldCount; |
11312 | }; |
11313 | |
11314 | // Special holder for DebuggerError callback. This adjusts InsideRS count w/o |
11315 | // verifying locks. This is very dangerous. We allow this b/c the Debugger Error callback can come at any time. |
11316 | class PublicDebuggerErrorCallbackHolder |
11317 | { |
11318 | public: |
11319 | PublicDebuggerErrorCallbackHolder() |
11320 | { |
11321 | // Exiting from RS; entering Cordbg via a callback |
11322 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11323 | |
11324 | // This callback is called from within the RS |
11325 | _ASSERTE(pThread->IsInRS()); |
11326 | |
11327 | // Debugger error callback may be called from deep within the RS (after many nestings). |
11328 | // So immediately jump to outside. We'll restore this in dtor. |
11329 | m_oldCount = pThread->m_cInsideRS; |
11330 | pThread->m_cInsideRS = 0; |
11331 | |
11332 | _ASSERTE(!pThread->IsInRS()); |
11333 | |
11334 | // We may be leaking locks for the unrecoverable callback. We mark that so that |
11335 | // the asserts about locking can be relaxed. |
11336 | pThread->SetUnrecoverableCallback(true); |
11337 | } |
11338 | |
11339 | ~PublicDebuggerErrorCallbackHolder() |
11340 | { |
11341 | // Re-entering RS from after a callback. |
11342 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11343 | |
11344 | pThread->SetUnrecoverableCallback(false); |
11345 | pThread->m_cInsideRS = m_oldCount; |
11346 | |
11347 | // Our status of being "Inside the RS" is now restored. |
11348 | _ASSERTE(pThread->IsInRS()); |
11349 | } |
11350 | private: |
11351 | int m_oldCount; |
11352 | }; |
11353 | |
11354 | //--------------------------------------------------------------------------------------- |
11355 | // |
11356 | // This is the same as the PublicCallbackHolder, except that this class doesn't assert that we are not holding |
11357 | // any locks when we call out to the shim. |
11358 | // |
11359 | // Notes: |
11360 | // @dbgtodo shim, synchronization - We need to settle on one consistent relationshipo between the RS |
11361 | // and the shim. Then we can clean up the sychronization story. Right now some code considers the shim |
11362 | // to be outside of the RS, and so we cannot hold any locks when we call out to the shim. However, there |
11363 | // are cases where we must hold a lock when we call out to the shim. For example, when we call out to the |
11364 | // shim to do a V2-style stackwalk, we need to be holding the stop-go lock so that another thread can't |
11365 | // come in and call Continue(). Finally, when we fix this, we should fix |
11366 | // PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM() as well. |
11367 | // |
11368 | |
11369 | class PrivateShimCallbackHolder |
11370 | { |
11371 | public: |
11372 | PrivateShimCallbackHolder() |
11373 | { |
11374 | // Exiting from RS; entering Cordbg via a callback |
11375 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11376 | |
11377 | // This callback is called from within the RS |
11378 | _ASSERTE(pThread->IsInRS()); |
11379 | |
11380 | // Debugger error callback may be called from deep within the RS (after many nestings). |
11381 | // So immediately jump to outside. We'll restore this in dtor. |
11382 | m_oldCount = pThread->m_cInsideRS; |
11383 | pThread->m_cInsideRS = 0; |
11384 | |
11385 | _ASSERTE(!pThread->IsInRS()); |
11386 | } |
11387 | |
11388 | ~PrivateShimCallbackHolder() |
11389 | { |
11390 | // Re-entering RS from after a callback. |
11391 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11392 | |
11393 | pThread->m_cInsideRS = m_oldCount; |
11394 | |
11395 | // Our status of being "Inside the RS" is now restored. |
11396 | _ASSERTE(pThread->IsInRS()); |
11397 | } |
11398 | private: |
11399 | int m_oldCount; |
11400 | }; |
11401 | |
11402 | class InternalAPIHolder |
11403 | { |
11404 | public: |
11405 | InternalAPIHolder() |
11406 | { |
11407 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11408 | |
11409 | // Internal APIs should already be inside the RS. |
11410 | _ASSERTE(pThread->IsInRS() ||!"Internal API being called directly from outside (there should be a public API on the stack)" ); |
11411 | } |
11412 | void dummy() {} |
11413 | }; |
11414 | |
11415 | //--------------------------------------------------------------------------------------- |
11416 | // |
11417 | // This is a simple holder to assert that the current thread is holding the process lock. The purpose of |
11418 | // having this holder is to enforce a lock ordering between the process lock in the RS and the DD lock in DAC. |
11419 | // If a thread needs to take the process lock, it must do so BEFORE taking the DD lock. Otherwise we could have |
11420 | // a deadlock between the process lock and the DD lock. |
11421 | // |
11422 | // Normally we take the process lock before calling out to DAC, and every DAC API takes the DD lock on entry. |
11423 | // Moreover, normally DAC doesn't call back into the RS. The exceptions we currently have are: |
11424 | // 1) enumeration callbacks (e.g. code:CordbProcess::AppDomainEnumerationCallback) |
11425 | // 2) code:IDacDbiInterface::IMetaDataLookup |
11426 | // 3) code:IDacDbiInterface::IAllocator |
11427 | // 4) code:IStringHolder |
11428 | // |
11429 | // Note that the last two are fine because they don't need to take the process lock. The first two categories |
11430 | // need to take the process lock before calling into DAC to avoid potential deadlocks. |
11431 | // |
11432 | |
11433 | class InternalDacCallbackHolder |
11434 | { |
11435 | public: |
11436 | InternalDacCallbackHolder(CordbProcess * pProcess) |
11437 | { |
11438 | _ASSERTE(pProcess->ThreadHoldsProcessLock()); |
11439 | } |
11440 | }; |
11441 | |
11442 | // cotract that occurs at public builds. |
11443 | #define PUBLIC_CONTRACT \ |
11444 | CONTRACTL { NOTHROW; } CONTRACTL_END; |
11445 | |
11446 | |
11447 | // Private hook for Shim to call into DBI. |
11448 | // Since Shim is considered outside DBI, we need to mark that we've re-entered. |
11449 | // Big difference is that we can throw across this boundary. |
11450 | // @dbgtodo private shim hook - Eventually, these will all go away since the shim will be fully public. |
11451 | #define PUBLIC_API_ENTRY_FOR_SHIM(_pThis) \ |
11452 | PublicAPIHolder __pah; |
11453 | |
11454 | |
11455 | #define PUBLIC_API_UNSAFE_ENTRY_FOR_SHIM(_pThis) \ |
11456 | PublicDebuggerErrorCallbackHolder __pahCallback; |
11457 | |
11458 | // @dbgtodo shim, synchronization - Because of the problem mentioned in the comments for |
11459 | // PrivateShimCallbackHolder, we need this macro so that we don't hit an assertion when we come back into |
11460 | // the RS from the shim. |
11461 | #define PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(_pThis) \ |
11462 | PublicReentrantAPIHolder __pah; |
11463 | |
11464 | //----------------------------------------------------------------------------- |
11465 | // Declare whether an API is public or internal |
11466 | // Public APIs have the following: |
11467 | // - We may be called concurrently from multiple threads (ie, not thread safe) |
11468 | // - This thread does not hold any RS Locks while entering or leaving this function. |
11469 | // - May or May-not be reentrant. |
11470 | // Internal APIs: |
11471 | // - let us specifically mark that we're not a public API, and |
11472 | // - we're only being called through a public API. |
11473 | //----------------------------------------------------------------------------- |
11474 | #define PUBLIC_API_ENTRY(_pThis) \ |
11475 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "[Public API '%s', this=0x%p]\n", __FUNCTION__, _pThis); \ |
11476 | PUBLIC_CONTRACT; \ |
11477 | PublicAPIHolder __pah; |
11478 | |
11479 | // Mark public APIs that are re-entrant. |
11480 | // Very few of our APIs should be re-entrant. Even for field access APIs (like GetXXX), the |
11481 | // public version is heavier (eg, checking the HRESULT) so we benefit from having a fast |
11482 | // internal version and calling that directly. |
11483 | #define PUBLIC_REENTRANT_API_ENTRY(_pThis) \ |
11484 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "[Public API (re) '%s', this=0x%p]\n", __FUNCTION__, _pThis); \ |
11485 | PUBLIC_CONTRACT; \ |
11486 | PublicReentrantAPIHolder __pah; |
11487 | |
11488 | |
11489 | |
11490 | // Mark internal APIs. |
11491 | // All internal APIs are reentrant (duh) |
11492 | #define INTERNAL_API_ENTRY(_pThis) InternalAPIHolder __pah; __pah.dummy(); |
11493 | |
11494 | // Mark an internal API from ATT_REQUIRE_STOP / ATT_ALLOW_LIVE_DO_STOP_GO. |
11495 | // This can assert that we're safe to send IPC events (that we're stopped and hold the SG lock) |
11496 | // @dbgtodo synchronization - in V2, this would assert that we were synced. |
11497 | // In V3, our definition of Sync is in flux. Need to resolve this with the synchronization feature crew. |
11498 | #define INTERNAL_SYNC_API_ENTRY(pProc) \ |
11499 | CordbProcess * __pProc = (pProc); \ |
11500 | _ASSERTE(__pProc->GetStopGoLock()->HasLock() || !"Must have stop go lock for internal-sync-api"); \ |
11501 | InternalAPIHolder __pah; __pah.dummy(); |
11502 | |
11503 | |
11504 | |
11505 | // Mark that a thread is owned by us. Thus the thread's "Inside RS" count > 0. |
11506 | #define INTERNAL_THREAD_ENTRY(_pThis) \ |
11507 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[Internal thread started, this=0x%p]\n", _pThis); \ |
11508 | PUBLIC_CONTRACT; \ |
11509 | PublicAPIHolder __pah; |
11510 | |
11511 | // @dbgtodo unrecoverable error - This sould be deprecated once we deprecate UnrecoverableError. |
11512 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE_DEBUGGERERROR(_pThis) \ |
11513 | PublicDebuggerErrorCallbackHolder __pahCallback; |
11514 | |
11515 | #define PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(_pThis) \ |
11516 | PrivateShimCallbackHolder __pahCallback; |
11517 | |
11518 | // Mark places where DAC may call back into DBI. We need to assert that we are holding the process lock in |
11519 | // these places, since otherwise we could deadlock between the DD lock and the process lock. |
11520 | #define INTERNAL_DAC_CALLBACK(__pProcess) \ |
11521 | InternalDacCallbackHolder __idch(__pProcess); |
11522 | |
11523 | |
11524 | // Helper to log debug events. |
11525 | inline void StressLogNativeDebugEvent(const DEBUG_EVENT * pDebugEvent, bool fOOB) |
11526 | { |
11527 | if ((pDebugEvent)->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) |
11528 | { |
11529 | STRESS_LOG4(LF_CORDB, LL_EVERYTHING, "[Dispatching Win32 code=1 (EXCEPTION_DEBUG_EVENT, tid=%x, oob=%d, code=0x%x, 1st=%d]\n" , |
11530 | pDebugEvent->dwThreadId, |
11531 | fOOB, |
11532 | pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode, |
11533 | pDebugEvent->u.Exception.dwFirstChance); |
11534 | } |
11535 | else |
11536 | { |
11537 | STRESS_LOG3(LF_CORDB, LL_EVERYTHING, "[Dispatching Win32 code=%d, tid=%x, oob=%d.]\n" , |
11538 | pDebugEvent->dwDebugEventCode, pDebugEvent->dwThreadId, fOOB); |
11539 | } |
11540 | |
11541 | } |
11542 | |
11543 | #define PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(_pThis, _pDebugEvent, _fOOB) \ |
11544 | StressLogNativeDebugEvent(_pDebugEvent, _fOOB); \ |
11545 | PublicCallbackHolder __pahCallback(DB_IPCE_INVALID_EVENT); |
11546 | |
11547 | // Visisbility spec for dtors. |
11548 | // Currently, dtors are like public methods b/c they can be called from Release. |
11549 | // But they're also reentrant since they may be called from an internal-release. |
11550 | // @todo - we'd like to get all "useful" work out of the dtor; in which case we may |
11551 | // be able to change this to something more aggressive. |
11552 | #define DTOR_ENTRY(_pThis) PUBLIC_REENTRANT_API_ENTRY(_pThis) |
11553 | |
11554 | |
11555 | //----------------------------------------------------------------------------- |
11556 | // Typesafe bool for thread safety. This typesafety forces us to use |
11557 | // an specific reason for thread-safety, taken from a well-known list. |
11558 | // This is mostly concerned w/ being serialized. |
11559 | // Note that this assertion must be done on a per function basis and we |
11560 | // can't have any sort of 'ThreadSafetyReason CallerIsSafe()' b/c we can't |
11561 | // enforce that all of our callers are thread safe (only that our current caller is safe). |
11562 | //----------------------------------------------------------------------------- |
11563 | struct ThreadSafetyReason |
11564 | { |
11565 | public: |
11566 | ThreadSafetyReason(bool f) { fIsSafe = f; } |
11567 | |
11568 | bool fIsSafe; |
11569 | }; |
11570 | |
11571 | // Different valid reasons that we may be threads safe. |
11572 | inline ThreadSafetyReason HoldsLock(RSLock * pLock) |
11573 | { |
11574 | _ASSERTE(pLock != NULL); |
11575 | return ThreadSafetyReason(pLock->HasLock()); |
11576 | } |
11577 | inline ThreadSafetyReason OnW32ET(CordbProcess * pProc) |
11578 | { |
11579 | return ThreadSafetyReason(IsWin32EventThread(pProc)); |
11580 | } |
11581 | |
11582 | inline ThreadSafetyReason OnRCET(Cordb *pCordb) |
11583 | { |
11584 | return ThreadSafetyReason (IsRCEventThread(pCordb)); |
11585 | } |
11586 | |
11587 | // We use this when we assume that a function is thread-safe (b/c it's serialized). |
11588 | // The reason also lets us assert that our assumption is true. |
11589 | // By using a function, we enforce typesafety and thus require a valid reason |
11590 | // (as opposed to an arbitrary bool) |
11591 | inline void AssertThreadSafeHelper(ThreadSafetyReason r) { |
11592 | _ASSERTE(r.fIsSafe); |
11593 | } |
11594 | |
11595 | //----------------------------------------------------------------------------- |
11596 | // Assert that the given scope is always called on a single thread b/c of |
11597 | // xReason. Common reasons may be b/c we hold a lock or we're always |
11598 | // called on a specific thread (Eg w32et). |
11599 | // The only valid reasons are of type ThreadSafetyReason (thus forcing us to |
11600 | // choose from a well-known list of valid reasons). |
11601 | //----------------------------------------------------------------------------- |
11602 | #define ASSERT_SINGLE_THREAD_ONLY(xReason) \ |
11603 | AssertThreadSafeHelper(xReason); |
11604 | |
11605 | #else |
11606 | |
11607 | //----------------------------------------------------------------------------- |
11608 | // Retail versions just nop. See the debug implementation for these |
11609 | // for their semantics. |
11610 | //----------------------------------------------------------------------------- |
11611 | |
11612 | #define PUBLIC_CONTRACT |
11613 | #define PUBLIC_API_ENTRY_FOR_SHIM(_pThis) |
11614 | #define PUBLIC_API_UNSAFE_ENTRY_FOR_SHIM(_pThis) |
11615 | #define PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(_pThis) |
11616 | #define PUBLIC_API_ENTRY(_pThis) |
11617 | #define PUBLIC_REENTRANT_API_ENTRY(_pThis) |
11618 | #define INTERNAL_API_ENTRY(_pThis) |
11619 | #define INTERNAL_SYNC_API_ENTRY(pProc) |
11620 | #define INTERNAL_THREAD_ENTRY(_pThis) |
11621 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE_DEBUGGERERROR(_pThis) |
11622 | #define PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(_pThis) |
11623 | #define INTERNAL_DAC_CALLBACK(__pProcess) |
11624 | #define PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(_pThis, _pDebugEvent, _fOOB) |
11625 | #define DTOR_ENTRY(_pThis) |
11626 | |
11627 | |
11628 | #define ASSERT_SINGLE_THREAD_ONLY(x) |
11629 | |
11630 | #endif // #if RSCONTRACTS |
11631 | |
11632 | |
11633 | class PublicCallbackHolder |
11634 | { |
11635 | public: |
11636 | PublicCallbackHolder(RSLockHolder * pHolder, DebuggerIPCEventType type) |
11637 | { |
11638 | m_pHolder = pHolder; |
11639 | _ASSERTE(!pHolder->IsNull()); // acquired |
11640 | |
11641 | // Release the lock. We'll reacquire it at the dtor. |
11642 | m_pHolder->Release(); |
11643 | |
11644 | Init(type); |
11645 | } |
11646 | |
11647 | PublicCallbackHolder(DebuggerIPCEventType type) |
11648 | { |
11649 | m_pHolder = NULL; |
11650 | Init(type); |
11651 | } |
11652 | |
11653 | void Init(DebuggerIPCEventType type) |
11654 | { |
11655 | m_type = type; |
11656 | |
11657 | #if defined(RSCONTRACTS) |
11658 | // Exiting from RS; entering Cordbg via a callback |
11659 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11660 | |
11661 | // m_cInsideRS may be arbitrarily large if we're called from a PUBLIC_REENTRANT_API, |
11662 | // so just remember the current count and blast it back to 0. |
11663 | m_oldCount = pThread->m_cInsideRS; |
11664 | pThread->m_cInsideRS = 0; |
11665 | |
11666 | _ASSERTE(!pThread->IsInRS()); |
11667 | |
11668 | // Should never be in public w/ these locks. (Even if we're re-entrant.) |
11669 | pThread->AssertThreadIsLockFree(); |
11670 | #endif // RSCONTRACTS |
11671 | } |
11672 | |
11673 | ~PublicCallbackHolder() |
11674 | { |
11675 | #if defined(RSCONTRACTS) |
11676 | // Re-entering RS from after a callback. |
11677 | DbgRSThread * pThread = DbgRSThread::GetThread(); |
11678 | _ASSERTE(!pThread->IsInRS()); |
11679 | |
11680 | pThread->m_cInsideRS = m_oldCount; |
11681 | |
11682 | // Should never be in public w/ these locks. (Even if we're re-entrant.) |
11683 | pThread->AssertThreadIsLockFree(); |
11684 | #endif // RSCONTRACTS |
11685 | |
11686 | // Reacquire the lock |
11687 | if (m_pHolder != NULL) |
11688 | { |
11689 | m_pHolder->Acquire(); |
11690 | } |
11691 | } |
11692 | protected: |
11693 | int m_oldCount; |
11694 | DebuggerIPCEventType m_type; |
11695 | RSLockHolder * m_pHolder; |
11696 | }; |
11697 | |
11698 | |
11699 | // Mark that a thread is calling out via a callback. This will adjust the "Inside RS" counter. |
11700 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE(_pThis, pLockHolder, event) \ |
11701 | STRESS_LOG1(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s']\n", IPCENames::GetName((event)->type)); \ |
11702 | PublicCallbackHolder __pahCallback(pLockHolder, (event)->type); |
11703 | |
11704 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE1(_pThis, pLockHolder, event, formatLiteralString, arg0) \ |
11705 | STRESS_LOG2(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0); \ |
11706 | PublicCallbackHolder __pahCallback(pLockHolder, (event)->type); |
11707 | |
11708 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE2(_pThis, pLockHolder, event, formatLiteralString, arg0, arg1) \ |
11709 | STRESS_LOG3(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0, arg1); \ |
11710 | PublicCallbackHolder __pahCallback(pLockHolder, (event)->type); |
11711 | |
11712 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE3(_pThis, pLockHolder, event, formatLiteralString, arg0, arg1, arg2) \ |
11713 | STRESS_LOG4(LF_CORDB, LL_EVERYTHING, "[Dispatching '%s' " formatLiteralString "]\n", IPCENames::GetName((event)->type), arg0, arg1, arg2); \ |
11714 | PublicCallbackHolder __pahCallback(pLockHolder, (event)->type); |
11715 | |
11716 | |
11717 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(_pThis) \ |
11718 | PublicCallbackHolder __pahCallback(DB_IPCE_INVALID_EVENT); |
11719 | |
11720 | #define PUBLIC_CALLBACK_IN_THIS_SCOPE0(_pThis, pLockHolder) \ |
11721 | PublicCallbackHolder __pahCallback(pLockHolder, DB_IPCE_INVALID_EVENT); |
11722 | |
11723 | |
11724 | //----------------------------------------------------------------------------- |
11725 | // Helpers |
11726 | inline void ValidateOrThrow(const void * p) |
11727 | { |
11728 | if (p == NULL) |
11729 | { |
11730 | ThrowHR(E_INVALIDARG); |
11731 | } |
11732 | } |
11733 | |
11734 | // aligns argBase on platforms that require it else it's a no-op |
11735 | inline void AlignAddressForType(CordbType* pArgType, CORDB_ADDRESS& argBase) |
11736 | { |
11737 | #ifdef DBG_TARGET_ARM |
11738 | // TODO: review the following |
11739 | #ifdef FEATURE_64BIT_ALIGNMENT |
11740 | BOOL align = FALSE; |
11741 | HRESULT hr = pArgType->RequiresAlign8(&align); |
11742 | _ASSERTE(SUCCEEDED(hr)); |
11743 | |
11744 | if (align) |
11745 | argBase = ALIGN_ADDRESS(argBase, 8); |
11746 | #endif // FEATURE_64BIT_ALIGNMENT |
11747 | #endif // DBG_TARGET_ARM |
11748 | } |
11749 | |
11750 | //----------------------------------------------------------------------------- |
11751 | // Macros to mark public ICorDebug functions |
11752 | // Usage: |
11753 | // |
11754 | // HRESULT CordbXYZ:Function(...) |
11755 | // { |
11756 | // HRESULT hr = S_OK; |
11757 | // PUBLIC_API_BEGIN(this); |
11758 | // // body, may throw |
11759 | // PUBLIC_API_END(hr); |
11760 | // return hr; |
11761 | // } |
11762 | #define PUBLIC_API_BEGIN(__this) \ |
11763 | CordbBase * __pThis = (__this); \ |
11764 | PUBLIC_API_ENTRY(__pThis); \ |
11765 | EX_TRY { \ |
11766 | RSLockHolder __lockHolder(__pThis->GetProcess()->GetProcessLock()); \ |
11767 | THROW_IF_NEUTERED(__pThis); \ |
11768 | |
11769 | // You should not use this in general. We're adding it as a temporary workaround for a |
11770 | // particular scenario until we do the synchronization feature crew |
11771 | #define PUBLIC_API_NO_LOCK_BEGIN(__this) \ |
11772 | CordbBase * __pThis = (__this); \ |
11773 | PUBLIC_API_ENTRY(__pThis); \ |
11774 | EX_TRY { \ |
11775 | THROW_IF_NEUTERED(__pThis); \ |
11776 | |
11777 | // Some APIs (that invoke callbacks), need to toggle the lock. |
11778 | #define GET_PUBLIC_LOCK_HOLDER() (&__lockHolder) |
11779 | |
11780 | #define PUBLIC_API_END(__hr) \ |
11781 | } EX_CATCH_HRESULT(__hr); \ |
11782 | |
11783 | // @todo: clean up API constracts. Should we really be taking the Process lock for |
11784 | // reentrant APIS?? |
11785 | #define PUBLIC_REENTRANT_API_BEGIN(__this) \ |
11786 | CordbBase * __pThis = (__this); \ |
11787 | PUBLIC_REENTRANT_API_ENTRY(__pThis); \ |
11788 | EX_TRY { \ |
11789 | RSLockHolder __lockHolder(__pThis->GetProcess()->GetProcessLock()); \ |
11790 | THROW_IF_NEUTERED(__pThis); \ |
11791 | |
11792 | #define PUBLIC_REENTRANT_API_END(__hr) \ |
11793 | } EX_CATCH_HRESULT(__hr); \ |
11794 | |
11795 | // If an API needs to take the stop/go lock as well as the process lock, the |
11796 | // stop/go lock has to be taken first. This is an alternative to PUBLIC_REENTRANT_API_BEGIN |
11797 | // that allows this, since it doesn't take the process lock. It should be closed with |
11798 | // PUBLIC_REENTRANT_API_END |
11799 | #define PUBLIC_REENTRANT_API_NO_LOCK_BEGIN(__this) \ |
11800 | CordbBase * __pThis = (__this); \ |
11801 | PUBLIC_REENTRANT_API_ENTRY(__pThis); \ |
11802 | EX_TRY { \ |
11803 | THROW_IF_NEUTERED(__pThis); \ |
11804 | |
11805 | |
11806 | //----------------------------------------------------------------------------- |
11807 | // For debugging ease, cache some global values. |
11808 | // Include these in retail & free because that's where we need them the most!! |
11809 | // Optimized builds may not let us view locals & parameters. So Having these |
11810 | // cached as global values should let us inspect almost all of |
11811 | // the interesting parts of the RS even in a Retail build! |
11812 | //----------------------------------------------------------------------------- |
11813 | struct RSDebuggingInfo |
11814 | { |
11815 | // There should only be 1 global Cordb object. Store it here. |
11816 | Cordb * m_Cordb; |
11817 | |
11818 | // We have lots of processes. Keep a pointer to the most recently touched |
11819 | // (subjective) process, as a hint about what our "current" process is. |
11820 | // If we're only debugging 1 process, this will be sufficient. |
11821 | CordbProcess * m_MRUprocess; |
11822 | |
11823 | CordbRCEventThread * m_RCET; |
11824 | }; |
11825 | |
11826 | #include "rspriv.inl" |
11827 | |
11828 | #endif // #if RSPRIV_H |
11829 | |
11830 | |
11831 | |