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 | // File: DacDbiImpl.cpp |
6 | // |
7 | |
8 | // |
9 | // Implement DAC/DBI interface |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | |
14 | #include "stdafx.h" |
15 | |
16 | #include "dacdbiinterface.h" |
17 | |
18 | #include "typestring.h" |
19 | #include "holder.h" |
20 | #include "debuginfostore.h" |
21 | #include "peimagelayout.inl" |
22 | #include "encee.h" |
23 | #include "switches.h" |
24 | #include "generics.h" |
25 | #include "stackwalk.h" |
26 | |
27 | #include "dacdbiimpl.h" |
28 | |
29 | #ifdef FEATURE_COMINTEROP |
30 | #include "runtimecallablewrapper.h" |
31 | #include "comcallablewrapper.h" |
32 | #endif // FEATURE_COMINTEROP |
33 | |
34 | #include "request_common.h" |
35 | |
36 | //----------------------------------------------------------------------------- |
37 | // Have standard enter and leave macros at the DacDbi boundary to enforce |
38 | // standard behavior. |
39 | // 1. catch exceptions and convert them at the boundary. |
40 | // 2. provide a space to hook logging and transitions. |
41 | // 3. provide a hook to verify return values. |
42 | // |
43 | // Usage notes: |
44 | // - use this at the DacDbi boundary; but not at internal functions |
45 | // - it's ok to Return from the middle. |
46 | // |
47 | // Expected usage is: |
48 | // Foo() |
49 | // { |
50 | // DD_ENTER_MAY_THROW |
51 | // ... |
52 | // if (...) { ThrowHr(E_SOME_FAILURE); } |
53 | // ... |
54 | // if (...) { return; } // early success case |
55 | // ... |
56 | // } |
57 | //----------------------------------------------------------------------------- |
58 | |
59 | |
60 | |
61 | |
62 | // Global allocator for DD. Access is protected under the g_dacCritSec lock. |
63 | IDacDbiInterface::IAllocator * g_pAllocator = NULL; |
64 | |
65 | //--------------------------------------------------------------------------------------- |
66 | // |
67 | // Extra sugar for wrapping IAllocator under friendly New/Delete operators. |
68 | // |
69 | // Sample usage: |
70 | // void Foo(TestClass ** ppOut) |
71 | // { |
72 | // *ppOut = NULL; |
73 | // TestClass * p = new (forDbi) TestClass(); |
74 | // ... |
75 | // if (ok) |
76 | // { |
77 | // *ppOut = p; |
78 | // return; // DBI will then free this memory. |
79 | // } |
80 | // ... |
81 | // DeleteDbiMemory(p); |
82 | // } |
83 | // |
84 | // Be very careful when using this on classes since Dbi and DAC may be in |
85 | // separate dlls. This is best used when operating on blittable data-structures. |
86 | // (no ctor/dtor, plain data fields) to guarantee the proper DLL isolation. |
87 | // You don't want to call the ctor in DAC's context and the dtor in DBI's context |
88 | // unless you really know what you're doing and that it's safe. |
89 | // |
90 | |
91 | // Need a class to serve as a tag that we can use to overload New/Delete. |
92 | forDbiWorker forDbi; |
93 | |
94 | void * operator new(size_t lenBytes, const forDbiWorker &) |
95 | { |
96 | _ASSERTE(g_pAllocator != NULL); |
97 | void *result = g_pAllocator->Alloc(lenBytes); |
98 | if (result == NULL) |
99 | { |
100 | ThrowOutOfMemory(); |
101 | } |
102 | return result; |
103 | } |
104 | |
105 | void * operator new[](size_t lenBytes, const forDbiWorker &) |
106 | { |
107 | _ASSERTE(g_pAllocator != NULL); |
108 | void *result = g_pAllocator->Alloc(lenBytes); |
109 | if (result == NULL) |
110 | { |
111 | ThrowOutOfMemory(); |
112 | } |
113 | return result; |
114 | } |
115 | |
116 | // Note: there is no C++ syntax for manually invoking this, but if a constructor throws an exception I understand that |
117 | // this delete operator will be invoked automatically to destroy the object. |
118 | void operator delete(void *p, const forDbiWorker &) |
119 | { |
120 | if (p == NULL) |
121 | { |
122 | return; |
123 | } |
124 | |
125 | _ASSERTE(g_pAllocator != NULL); |
126 | g_pAllocator->Free((BYTE*) p); |
127 | |
128 | } |
129 | |
130 | // Note: there is no C++ syntax for manually invoking this, but if a constructor throws an exception I understand that |
131 | // this delete operator will be invoked automatically to destroy the object. |
132 | void operator delete[](void *p, const forDbiWorker &) |
133 | { |
134 | if (p == NULL) |
135 | { |
136 | return; |
137 | } |
138 | |
139 | _ASSERTE(g_pAllocator != NULL); |
140 | g_pAllocator->Free((BYTE*) p); |
141 | } |
142 | |
143 | // @dbgtodo dac support: determine how to handle an array of class instances to ensure the dtors get |
144 | // called correctly or document that they won't |
145 | // Delete memory and invoke dtor for memory allocated with 'operator (forDbi) new' |
146 | template<class T> void DeleteDbiMemory(T *p) |
147 | { |
148 | if (p == NULL) |
149 | { |
150 | return; |
151 | } |
152 | p->~T(); |
153 | |
154 | _ASSERTE(g_pAllocator != NULL); |
155 | g_pAllocator->Free((BYTE*) p); |
156 | } |
157 | |
158 | |
159 | //--------------------------------------------------------------------------------------- |
160 | // Creates the DacDbiInterface object, used by Dbi. |
161 | // |
162 | // Arguments: |
163 | // pTarget - pointer to a Data-Target |
164 | // baseAddress - non-zero base address of mscorwks in target to debug. |
165 | // pAllocator - pointer to client allocator object. This lets DD allocate objects and |
166 | // pass them out back to the client, which can then delete them. |
167 | // DD takes a weak ref to this, so client must keep it alive until it |
168 | // calls Destroy. |
169 | // pMetadataLookup - callback interface to do internal metadata lookup. This is because |
170 | // metadata is not dac-ized. |
171 | // ppInterface - mandatory out-parameter |
172 | // |
173 | // Return Value: |
174 | // S_OK on success. |
175 | // |
176 | // |
177 | // Notes: |
178 | // On Windows, this is public function that can be retrieved by GetProcAddress. |
179 | |
180 | // On Mac, this is used internally by DacDbiMarshalStubInstance below |
181 | // This will yield an IDacDbiInterface to provide structured access to the |
182 | // data-target. |
183 | // |
184 | // Must call Destroy to on interface to free its resources. |
185 | // |
186 | //--------------------------------------------------------------------------------------- |
187 | STDAPI |
188 | DacDbiInterfaceInstance( |
189 | ICorDebugDataTarget * pTarget, |
190 | CORDB_ADDRESS baseAddress, |
191 | IDacDbiInterface::IAllocator * pAllocator, |
192 | IDacDbiInterface::IMetaDataLookup * pMetaDataLookup, |
193 | IDacDbiInterface ** ppInterface) |
194 | { |
195 | // No marshalling is done by the instantiationf function - we just need to setup the infrastructure. |
196 | // We don't want to warn if this involves creating and accessing undacized data structures, |
197 | // because it's for the infrastructure, not DACized code itself. |
198 | SUPPORTS_DAC_HOST_ONLY; |
199 | |
200 | // Since this is public, verify it. |
201 | if ((ppInterface == NULL) || (pTarget == NULL) || (baseAddress == 0)) |
202 | { |
203 | return E_INVALIDARG; |
204 | } |
205 | |
206 | *ppInterface = NULL; |
207 | |
208 | // |
209 | // Actually allocate the real object and initialize it. |
210 | // |
211 | DacDbiInterfaceImpl * pDac = new (nothrow) DacDbiInterfaceImpl(pTarget, baseAddress, pAllocator, pMetaDataLookup); |
212 | if (!pDac) |
213 | { |
214 | return E_OUTOFMEMORY; |
215 | } |
216 | |
217 | HRESULT hrStatus = pDac->Initialize(); |
218 | |
219 | if (SUCCEEDED(hrStatus)) |
220 | { |
221 | *ppInterface = pDac; |
222 | } |
223 | else |
224 | { |
225 | delete pDac; |
226 | } |
227 | return hrStatus; |
228 | } |
229 | |
230 | |
231 | //--------------------------------------------------------------------------------------- |
232 | // Constructor. Instantiates a DAC/DBI interface around a DataTarget. |
233 | // |
234 | // Arguments: |
235 | // pTarget - pointer to a Data-Target |
236 | // baseAddress - non-zero base address of mscorwks in target to debug. |
237 | // pAllocator - pointer to client allocator object. This lets DD allocate objects and |
238 | // pass them out back to the client, which can then delete them. |
239 | // DD takes a weak ref to this, so client must keep it alive until it |
240 | // calls Destroy. |
241 | // pMetadataLookup - callback interface to do internal metadata lookup. This is because |
242 | // metadata is not dac-ized. |
243 | // |
244 | // Notes: |
245 | // pAllocator is a weak reference. |
246 | //--------------------------------------------------------------------------------------- |
247 | DacDbiInterfaceImpl::DacDbiInterfaceImpl( |
248 | ICorDebugDataTarget* pTarget, |
249 | CORDB_ADDRESS baseAddress, |
250 | IAllocator * pAllocator, |
251 | IMetaDataLookup * pMetaDataLookup |
252 | ) : ClrDataAccess(pTarget), |
253 | m_pAllocator(pAllocator), |
254 | m_pMetaDataLookup(pMetaDataLookup), |
255 | m_pCachedPEFile(VMPTR_PEFile::NullPtr()), |
256 | m_pCachedImporter(NULL), |
257 | m_isCachedHijackFunctionValid(FALSE) |
258 | { |
259 | _ASSERTE(baseAddress != NULL); |
260 | m_globalBase = CORDB_ADDRESS_TO_TADDR(baseAddress); |
261 | |
262 | _ASSERTE(pMetaDataLookup != NULL); |
263 | _ASSERTE(pAllocator != NULL); |
264 | _ASSERTE(pTarget != NULL); |
265 | |
266 | #ifdef _DEBUG |
267 | // Enable verification asserts in ICorDebug scenarios. ICorDebug never guesses at the DAC path, so any |
268 | // mismatch should be fatal, and so always of interest to the user. |
269 | // This overrides the assignment in the base class ctor (which runs first). |
270 | m_fEnableDllVerificationAsserts = true; |
271 | #endif |
272 | } |
273 | |
274 | //----------------------------------------------------------------------------- |
275 | // Destructor. |
276 | // |
277 | // Notes: |
278 | // This gets invoked after Destroy(). |
279 | //----------------------------------------------------------------------------- |
280 | DacDbiInterfaceImpl::~DacDbiInterfaceImpl() |
281 | { |
282 | SUPPORTS_DAC_HOST_ONLY; |
283 | // This will automatically chain to the base class dtor |
284 | } |
285 | |
286 | //----------------------------------------------------------------------------- |
287 | // Called from DAC-ized code to get a IMDInternalImport |
288 | // |
289 | // Arguments: |
290 | // pPEFile - PE file for which to get importer for |
291 | // fThrowEx - if true, throw instead of returning NULL. |
292 | // |
293 | // Returns: |
294 | // an Internal importer object for this file. |
295 | // May return NULL or throw (depending on fThrowEx). |
296 | // May throw in exceptional circumstances (eg, corrupt debuggee). |
297 | // |
298 | // Assumptions: |
299 | // This is called from DAC-ized code within the VM, which |
300 | // was in turn called from some DD primitive. The returned importer will |
301 | // be used by the DAC-ized code in the callstack, but it won't be cached. |
302 | // |
303 | // Notes: |
304 | // This is an Internal importer, not a public Metadata importer. |
305 | // |
306 | interface IMDInternalImport* DacDbiInterfaceImpl::GetMDImport( |
307 | const PEFile* pPEFile, |
308 | const ReflectionModule * pReflectionModule, |
309 | bool fThrowEx) |
310 | { |
311 | // Since this is called from an existing DAC-primitive, we already hold the g_dacCritSec lock. |
312 | // The lock conveniently protects our cache. |
313 | SUPPORTS_DAC; |
314 | |
315 | IDacDbiInterface::IMetaDataLookup * pLookup = m_pMetaDataLookup; |
316 | _ASSERTE(pLookup != NULL); |
317 | |
318 | VMPTR_PEFile vmPEFile = VMPTR_PEFile::NullPtr(); |
319 | |
320 | if (pPEFile != NULL) |
321 | { |
322 | vmPEFile.SetHostPtr(pPEFile); |
323 | } |
324 | else if (pReflectionModule != NULL) |
325 | { |
326 | // SOS and ClrDataAccess rely on special logic to find the metadata for methods in dynamic modules. |
327 | // We don't need to. The RS has already taken care of the special logic for us. |
328 | // So here we just grab the PEFile off of the ReflectionModule and continue down the normal |
329 | // code path. See code:ClrDataAccess::GetMDImport for comparison. |
330 | vmPEFile.SetHostPtr(pReflectionModule->GetFile()); |
331 | } |
332 | |
333 | // Optimize for the case where the VM queries the same Importer many times in a row. |
334 | if (m_pCachedPEFile == vmPEFile) |
335 | { |
336 | return m_pCachedImporter; |
337 | } |
338 | |
339 | // Go to DBI to find the metadata. |
340 | IMDInternalImport * pInternal = NULL; |
341 | bool isILMetaDataForNI = false; |
342 | EX_TRY |
343 | { |
344 | // If test needs it in the future, prop isILMetaDataForNI back up to |
345 | // ClrDataAccess.m_mdImports.Add() call. |
346 | // example in code:ClrDataAccess::GetMDImport |
347 | // CordbModule::GetMetaDataInterface also looks up MetaData and would need attention. |
348 | |
349 | // This is the new codepath that uses ICorDebugMetaDataLookup. |
350 | // To get the old codepath that uses the v2 metadata lookup methods, |
351 | // you'd have to load DAC only and then you'll get ClrDataAccess's implementation |
352 | // of this function. |
353 | pInternal = pLookup->LookupMetaData(vmPEFile, isILMetaDataForNI); |
354 | } |
355 | EX_CATCH |
356 | { |
357 | // Any expected error we should ignore. |
358 | if ((GET_EXCEPTION()->GetHR() != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && |
359 | (GET_EXCEPTION()->GetHR() != CORDBG_E_READVIRTUAL_FAILURE) && |
360 | (GET_EXCEPTION()->GetHR() != CORDBG_E_SYMBOLS_NOT_AVAILABLE) && |
361 | (GET_EXCEPTION()->GetHR() != CORDBG_E_MODULE_LOADED_FROM_DISK)) |
362 | { |
363 | EX_RETHROW; |
364 | } |
365 | } |
366 | EX_END_CATCH(SwallowAllExceptions) |
367 | |
368 | if (pInternal == NULL) |
369 | { |
370 | SIMPLIFYING_ASSUMPTION(!"MD lookup failed" ); |
371 | if (fThrowEx) |
372 | { |
373 | ThrowHR(E_FAIL); |
374 | } |
375 | return NULL; |
376 | } |
377 | else |
378 | { |
379 | // Cache it such that it we look for the exact same Importer again, we'll return it. |
380 | m_pCachedPEFile = vmPEFile; |
381 | m_pCachedImporter = pInternal; |
382 | } |
383 | |
384 | return pInternal; |
385 | } |
386 | |
387 | //----------------------------------------------------------------------------- |
388 | // Implementation of IDacDbiInterface |
389 | // See DacDbiInterface.h for full descriptions of all of these functions |
390 | //----------------------------------------------------------------------------- |
391 | |
392 | // Destroy the connection, freeing up any resources. |
393 | void DacDbiInterfaceImpl::Destroy() |
394 | { |
395 | m_pAllocator = NULL; |
396 | |
397 | this->Release(); |
398 | // Memory is deleted, don't access this object any more |
399 | } |
400 | |
401 | // Check whether the version of the DBI matches the version of the runtime. |
402 | // See code:CordbProcess::CordbProcess#DBIVersionChecking for more information regarding version checking. |
403 | HRESULT DacDbiInterfaceImpl::CheckDbiVersion(const DbiVersion * pVersion) |
404 | { |
405 | DD_ENTER_MAY_THROW; |
406 | |
407 | if (pVersion->m_dwFormat != kCurrentDbiVersionFormat) |
408 | { |
409 | return CORDBG_E_INCOMPATIBLE_PROTOCOL; |
410 | } |
411 | |
412 | if ((pVersion->m_dwProtocolBreakingChangeCounter != kCurrentDacDbiProtocolBreakingChangeCounter) || |
413 | (pVersion->m_dwReservedMustBeZero1 != 0)) |
414 | { |
415 | return CORDBG_E_INCOMPATIBLE_PROTOCOL; |
416 | } |
417 | |
418 | return S_OK; |
419 | } |
420 | |
421 | // Flush the DAC cache. This should be called when target memory changes. |
422 | HRESULT DacDbiInterfaceImpl::FlushCache() |
423 | { |
424 | // Non-reentrant. We don't want to flush cached instances from a callback. |
425 | // That would remove host DAC instances while they're being used. |
426 | DD_NON_REENTRANT_MAY_THROW; |
427 | |
428 | m_pCachedPEFile = VMPTR_PEFile::NullPtr(); |
429 | m_pCachedImporter = NULL; |
430 | m_isCachedHijackFunctionValid = FALSE; |
431 | |
432 | HRESULT hr = ClrDataAccess::Flush(); |
433 | |
434 | // Current impl of Flush() should always succeed. If it ever fails, we want to know. |
435 | _ASSERTE(SUCCEEDED(hr)); |
436 | return hr; |
437 | } |
438 | |
439 | // enable or disable DAC target consistency checks |
440 | void DacDbiInterfaceImpl::DacSetTargetConsistencyChecks(bool fEnableAsserts) |
441 | { |
442 | // forward on to our ClrDataAccess base class |
443 | ClrDataAccess::SetTargetConsistencyChecks(fEnableAsserts); |
444 | } |
445 | |
446 | // Query if Left-side is started up? |
447 | BOOL DacDbiInterfaceImpl::IsLeftSideInitialized() |
448 | { |
449 | DD_ENTER_MAY_THROW; |
450 | |
451 | if (g_pDebugger != NULL) |
452 | { |
453 | // This check is "safe". |
454 | // The initialize order in the left-side is: |
455 | // 1) g_pDebugger is an RVA based global initialized to NULL when the module is loaded. |
456 | // 2) Allocate a "Debugger" object. |
457 | // 3) run the ctor, which will set m_fLeftSideInitialized = FALSE. |
458 | // 4) assign the object to g_pDebugger. |
459 | // 5) later, LS initialization code will assign g_pDebugger->m_fLeftSideInitialized = TRUE. |
460 | // |
461 | // The memory write in #5 is atomic. There is no window where we're reading unitialized data. |
462 | |
463 | return (g_pDebugger->m_fLeftSideInitialized != 0); |
464 | } |
465 | |
466 | return FALSE; |
467 | } |
468 | |
469 | |
470 | // Determines if a given adddress is a CLR stub. |
471 | BOOL DacDbiInterfaceImpl::IsTransitionStub(CORDB_ADDRESS address) |
472 | { |
473 | DD_ENTER_MAY_THROW; |
474 | |
475 | BOOL fIsStub = FALSE; |
476 | |
477 | #if defined(FEATURE_PAL) |
478 | // Currently IsIPInModule() is not implemented in the PAL. Rather than skipping the check, we should |
479 | // either E_NOTIMPL this API or implement IsIPInModule() in the PAL. Since ICDProcess::IsTransitionStub() |
480 | // is only called by VS in mixed-mode debugging scenarios, and mixed-mode debugging is not supported on |
481 | // POSIX systems, there is really no incentive to implement this API at this point. |
482 | ThrowHR(E_NOTIMPL); |
483 | |
484 | #else // !FEATURE_PAL |
485 | |
486 | TADDR ip = (TADDR)address; |
487 | |
488 | if (ip == NULL) |
489 | { |
490 | fIsStub = FALSE; |
491 | } |
492 | else |
493 | { |
494 | fIsStub = StubManager::IsStub(ip); |
495 | } |
496 | |
497 | // If it's in Mscorwks, count that as a stub too. |
498 | if (fIsStub == FALSE) |
499 | { |
500 | fIsStub = IsIPInModule(m_globalBase, ip); |
501 | } |
502 | |
503 | #endif // FEATURE_PAL |
504 | |
505 | return fIsStub; |
506 | } |
507 | |
508 | // Gets the type of 'address'. |
509 | IDacDbiInterface::AddressType DacDbiInterfaceImpl::GetAddressType(CORDB_ADDRESS address) |
510 | { |
511 | DD_ENTER_MAY_THROW; |
512 | TADDR taAddr = CORDB_ADDRESS_TO_TADDR(address); |
513 | |
514 | if (IsPossibleCodeAddress(taAddr) == S_OK) |
515 | { |
516 | if (ExecutionManager::IsManagedCode(taAddr)) |
517 | { |
518 | return kAddressManagedMethod; |
519 | } |
520 | |
521 | if (StubManager::IsStub(taAddr)) |
522 | { |
523 | return kAddressRuntimeUnmanagedStub; |
524 | } |
525 | } |
526 | |
527 | return kAddressUnrecognized; |
528 | } |
529 | |
530 | |
531 | // Get a VM appdomain pointer that matches the appdomain ID |
532 | VMPTR_AppDomain DacDbiInterfaceImpl::GetAppDomainFromId(ULONG appdomainId) |
533 | { |
534 | DD_ENTER_MAY_THROW; |
535 | |
536 | VMPTR_AppDomain vmAppDomain; |
537 | |
538 | // @dbgtodo dac support - We would like to wean ourselves off the IXClrData interfaces. |
539 | IXCLRDataProcess * pDAC = this; |
540 | ReleaseHolder<IXCLRDataAppDomain> pDacAppDomain; |
541 | |
542 | HRESULT hrStatus = pDAC->GetAppDomainByUniqueID(appdomainId, &pDacAppDomain); |
543 | IfFailThrow(hrStatus); |
544 | |
545 | IXCLRDataAppDomain * pIAppDomain = pDacAppDomain; |
546 | AppDomain * pAppDomain = (static_cast<ClrDataAppDomain *> (pIAppDomain))->GetAppDomain(); |
547 | SIMPLIFYING_ASSUMPTION(pAppDomain != NULL); |
548 | if (pAppDomain == NULL) |
549 | { |
550 | ThrowHR(E_FAIL); // corrupted left-side? |
551 | } |
552 | |
553 | TADDR addrAppDomain = PTR_HOST_TO_TADDR(pAppDomain); |
554 | vmAppDomain.SetDacTargetPtr(addrAppDomain); |
555 | |
556 | return vmAppDomain; |
557 | } |
558 | |
559 | |
560 | // Get the AppDomain ID for an AppDomain. |
561 | ULONG DacDbiInterfaceImpl::GetAppDomainId(VMPTR_AppDomain vmAppDomain) |
562 | { |
563 | DD_ENTER_MAY_THROW; |
564 | |
565 | if (vmAppDomain.IsNull()) |
566 | { |
567 | return 0; |
568 | } |
569 | else |
570 | { |
571 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
572 | return pAppDomain->GetId().m_dwId; |
573 | } |
574 | } |
575 | |
576 | // Get the managed AppDomain object for an AppDomain. |
577 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetAppDomainObject(VMPTR_AppDomain vmAppDomain) |
578 | { |
579 | DD_ENTER_MAY_THROW; |
580 | |
581 | AppDomain* pAppDomain = vmAppDomain.GetDacPtr(); |
582 | OBJECTHANDLE hAppDomainManagedObject = pAppDomain->GetRawExposedObjectHandleForDebugger(); |
583 | VMPTR_OBJECTHANDLE vmObj = VMPTR_OBJECTHANDLE::NullPtr(); |
584 | vmObj.SetDacTargetPtr(hAppDomainManagedObject); |
585 | return vmObj; |
586 | |
587 | } |
588 | |
589 | // Determine if the specified AppDomain is the default domain |
590 | BOOL DacDbiInterfaceImpl::IsDefaultDomain(VMPTR_AppDomain vmAppDomain) |
591 | { |
592 | DD_ENTER_MAY_THROW; |
593 | |
594 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
595 | BOOL fDefaultDomain = pAppDomain->IsDefaultDomain(); |
596 | |
597 | return fDefaultDomain; |
598 | } |
599 | |
600 | |
601 | // Get the full AD friendly name for the given EE AppDomain. |
602 | void DacDbiInterfaceImpl::GetAppDomainFullName( |
603 | VMPTR_AppDomain vmAppDomain, |
604 | IStringHolder * pStrName ) |
605 | { |
606 | DD_ENTER_MAY_THROW; |
607 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
608 | |
609 | // Get the AppDomain name from the VM without changing anything |
610 | // We might be able to simplify this, eg. by returning an SString. |
611 | bool fIsUtf8; |
612 | PVOID pRawName = pAppDomain->GetFriendlyNameNoSet(&fIsUtf8); |
613 | |
614 | if (!pRawName) |
615 | { |
616 | ThrowHR(E_NOINTERFACE); |
617 | } |
618 | |
619 | HRESULT hrStatus = S_OK; |
620 | if (fIsUtf8) |
621 | { |
622 | // we have to allocate a temporary string |
623 | // we could avoid this by adding a version of IStringHolder::AssignCopy that takes a UTF8 string |
624 | // We should also probably check to see when fIsUtf8 is ever true (it looks like it should normally be false). |
625 | ULONG32 dwNameLen = 0; |
626 | hrStatus = ConvertUtf8((LPCUTF8)pRawName, 0, &dwNameLen, NULL); |
627 | if (SUCCEEDED( hrStatus )) |
628 | { |
629 | NewArrayHolder<WCHAR> pwszName(new WCHAR[dwNameLen]); |
630 | hrStatus = ConvertUtf8((LPCUTF8)pRawName, dwNameLen, &dwNameLen, pwszName ); |
631 | IfFailThrow(hrStatus); |
632 | |
633 | hrStatus = pStrName->AssignCopy(pwszName); |
634 | } |
635 | } |
636 | else |
637 | { |
638 | hrStatus = pStrName->AssignCopy(static_cast<PCWSTR>(pRawName)); |
639 | } |
640 | |
641 | // Very important that this either sets pStrName or Throws. |
642 | // Don't set it and then then throw. |
643 | IfFailThrow(hrStatus); |
644 | } |
645 | |
646 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
647 | // JIT Compiler Flags |
648 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
649 | |
650 | // Get the values of the JIT Optimization and EnC flags. |
651 | void DacDbiInterfaceImpl::GetCompilerFlags ( |
652 | VMPTR_DomainFile vmDomainFile, |
653 | BOOL *pfAllowJITOpts, |
654 | BOOL *pfEnableEnC) |
655 | { |
656 | DD_ENTER_MAY_THROW; |
657 | |
658 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
659 | |
660 | if (pDomainFile == NULL) |
661 | { |
662 | ThrowHR(E_FAIL); |
663 | } |
664 | |
665 | // Get the underlying module - none of this is AppDomain specific |
666 | Module * pModule = pDomainFile->GetModule(); |
667 | DWORD dwBits = pModule->GetDebuggerInfoBits(); |
668 | *pfAllowJITOpts = !CORDisableJITOptimizations(dwBits); |
669 | *pfEnableEnC = pModule->IsEditAndContinueEnabled(); |
670 | |
671 | |
672 | } //GetCompilerFlags |
673 | |
674 | //----------------------------------------------------------------------------- |
675 | // Helper function for SetCompilerFlags to set EnC status. |
676 | // Arguments: |
677 | // Input: |
678 | // pModule - The runtime module for which flags are being set. |
679 | // |
680 | // Return value: |
681 | // true if the Enc bits can be set on this module |
682 | //----------------------------------------------------------------------------- |
683 | |
684 | bool DacDbiInterfaceImpl::CanSetEnCBits(Module * pModule) |
685 | { |
686 | _ASSERTE(pModule != NULL); |
687 | #ifdef EnC_SUPPORTED |
688 | // If we're using explicit sequence points (from the PDB), then we can't do EnC |
689 | // because EnC won't get updated pdbs and so the sequence points will be wrong. |
690 | bool fIgnorePdbs = ((pModule->GetDebuggerInfoBits() & DACF_IGNORE_PDBS) != 0); |
691 | |
692 | bool fAllowEnc = pModule->IsEditAndContinueCapable() && |
693 | |
694 | #ifdef PROFILING_SUPPORTED_DATA |
695 | !CORProfilerPresent() && // this queries target |
696 | #endif |
697 | fIgnorePdbs; |
698 | #else // ! EnC_SUPPORTED |
699 | // Enc not supported on any other platforms. |
700 | bool fAllowEnc = false; |
701 | #endif |
702 | |
703 | return fAllowEnc; |
704 | } // DacDbiInterfaceImpl::SetEnCBits |
705 | |
706 | // Set the values of the JIT optimization and EnC flags. |
707 | HRESULT DacDbiInterfaceImpl::SetCompilerFlags(VMPTR_DomainFile vmDomainFile, |
708 | BOOL fAllowJitOpts, |
709 | BOOL fEnableEnC) |
710 | { |
711 | DD_ENTER_MAY_THROW; |
712 | |
713 | DWORD dwBits = 0; |
714 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
715 | Module * pModule = pDomainFile->GetCurrentModule(); |
716 | HRESULT hr = S_OK; |
717 | |
718 | |
719 | #ifdef FEATURE_PREJIT |
720 | if (pModule->HasNativeImage()) |
721 | { |
722 | ThrowHR(CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE); |
723 | } |
724 | #endif |
725 | _ASSERTE(pModule != NULL); |
726 | |
727 | // Initialize dwBits. |
728 | dwBits = (pModule->GetDebuggerInfoBits() & ~(DACF_ALLOW_JIT_OPTS | DACF_ENC_ENABLED)); |
729 | dwBits &= DACF_CONTROL_FLAGS_MASK; |
730 | |
731 | if (fAllowJitOpts) |
732 | { |
733 | dwBits |= DACF_ALLOW_JIT_OPTS; |
734 | } |
735 | if (fEnableEnC) |
736 | { |
737 | if (CanSetEnCBits(pModule)) |
738 | { |
739 | dwBits |= DACF_ENC_ENABLED; |
740 | } |
741 | else |
742 | { |
743 | hr = CORDBG_S_NOT_ALL_BITS_SET; |
744 | } |
745 | } |
746 | // Settings from the debugger take precedence over all other settings. |
747 | dwBits |= DACF_USER_OVERRIDE; |
748 | |
749 | // set flags. This will write back to the target |
750 | pModule->SetDebuggerInfoBits((DebuggerAssemblyControlFlags)dwBits); |
751 | |
752 | |
753 | LOG((LF_CORDB, LL_INFO100, "D::HIPCE, Changed Jit-Debug-Info: fOpt=%d, fEnableEnC=%d, new bits=0x%08x\n" , |
754 | (dwBits & DACF_ALLOW_JIT_OPTS) != 0, |
755 | (dwBits & DACF_ENC_ENABLED) != 0, |
756 | dwBits)); |
757 | |
758 | _ASSERTE(SUCCEEDED(hr)); |
759 | return hr; |
760 | |
761 | } // DacDbiInterfaceImpl::SetCompilerFlags |
762 | |
763 | |
764 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
765 | // sequence points and var info |
766 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
767 | |
768 | // Initialize the native/IL sequence points and native var info for a function. |
769 | void DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, |
770 | CORDB_ADDRESS startAddr, |
771 | BOOL fCodeAvailable, |
772 | NativeVarData * pNativeVarData, |
773 | SequencePoints * pSequencePoints) |
774 | { |
775 | DD_ENTER_MAY_THROW; |
776 | |
777 | _ASSERTE(!vmMethodDesc.IsNull()); |
778 | |
779 | MethodDesc * pMD = vmMethodDesc.GetDacPtr(); |
780 | |
781 | _ASSERTE(fCodeAvailable != 0); |
782 | |
783 | // get information about the locations of arguments and local variables |
784 | GetNativeVarData(pMD, startAddr, GetArgCount(pMD), pNativeVarData); |
785 | |
786 | // get the sequence points |
787 | GetSequencePoints(pMD, startAddr, pSequencePoints); |
788 | |
789 | } // GetNativeCodeSequencePointsAndVarInfo |
790 | |
791 | //----------------------------------------------------------------------------- |
792 | // Get the number of fixed arguments to a function, i.e., the explicit args and the "this" pointer. |
793 | // This does not include other implicit arguments or varargs. This is used to compute a variable ID |
794 | // (see comment in CordbJITILFrame::ILVariableToNative for more detail) |
795 | // Arguments: |
796 | // input: pMD pointer to the method desc for the function |
797 | // output: none |
798 | // Return value: |
799 | // the number of fixed arguments to the function |
800 | //----------------------------------------------------------------------------- |
801 | SIZE_T DacDbiInterfaceImpl::GetArgCount(MethodDesc * pMD) |
802 | { |
803 | |
804 | // Create a MetaSig for the given method's sig. (Easier than |
805 | // picking the sig apart ourselves.) |
806 | PCCOR_SIGNATURE pCallSig; |
807 | DWORD cbCallSigSize; |
808 | |
809 | pMD->GetSig(&pCallSig, &cbCallSigSize); |
810 | |
811 | if (pCallSig == NULL) |
812 | { |
813 | // Sig should only be null if the image is corrupted. (Even for lightweight-codegen) |
814 | // We expect the jit+verifier to catch this, so that we never land here. |
815 | // But just in case ... |
816 | CONSISTENCY_CHECK_MSGF(false, ("Corrupted image, null sig.(%s::%s)" , |
817 | pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); |
818 | return 0; |
819 | } |
820 | |
821 | MetaSig msig(pCallSig, cbCallSigSize, pMD->GetModule(), NULL, MetaSig::sigMember); |
822 | |
823 | // Get the arg count. |
824 | UINT32 NumArguments = msig.NumFixedArgs(); |
825 | |
826 | // Account for the 'this' argument. |
827 | if (!pMD->IsStatic()) |
828 | { |
829 | NumArguments++; |
830 | } |
831 | /* |
832 | SigParser sigParser(pCallSig, cbCallSigSize); |
833 | sigParser.SkipMethodHeaderSignature(&m_allArgsCount); |
834 | */ |
835 | return NumArguments; |
836 | } //GetArgCount |
837 | |
838 | // Allocator to pass to DebugInfoStores, allocating forDBI |
839 | BYTE* InfoStoreForDbiNew(void * pData, size_t cBytes) |
840 | { |
841 | return new(forDbi) BYTE[cBytes]; |
842 | } |
843 | |
844 | // Allocator to pass to the debug-info-stores... |
845 | BYTE* InfoStoreNew(void * pData, size_t cBytes) |
846 | { |
847 | return new BYTE[cBytes]; |
848 | } |
849 | |
850 | //----------------------------------------------------------------------------- |
851 | // Get locations and code offsets for local variables and arguments in a function |
852 | // This information is used to find the location of a value at a given IP. |
853 | // Arguments: |
854 | // input: |
855 | // pMethodDesc pointer to the method desc for the function |
856 | // startAddr starting address of the function--used to differentiate |
857 | // EnC versions |
858 | // fixedArgCount number of fixed arguments to the function |
859 | // output: |
860 | // pVarInfo data structure containing a list of variable and |
861 | // argument locations by range of IP offsets |
862 | // Note: this function may throw |
863 | //----------------------------------------------------------------------------- |
864 | void DacDbiInterfaceImpl::GetNativeVarData(MethodDesc * pMethodDesc, |
865 | CORDB_ADDRESS startAddr, |
866 | SIZE_T fixedArgCount, |
867 | NativeVarData * pVarInfo) |
868 | { |
869 | // make sure we haven't done this already |
870 | if (pVarInfo->IsInitialized()) |
871 | { |
872 | return; |
873 | } |
874 | |
875 | NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL); |
876 | |
877 | DebugInfoRequest request; |
878 | request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); |
879 | |
880 | ULONG32 entryCount; |
881 | |
882 | BOOL success = DebugInfoManager::GetBoundariesAndVars(request, |
883 | InfoStoreNew, NULL, // allocator |
884 | NULL, NULL, |
885 | &entryCount, &nativeVars); |
886 | |
887 | if (!success) |
888 | ThrowHR(E_FAIL); |
889 | |
890 | // set key fields of pVarInfo |
891 | pVarInfo->InitVarDataList(nativeVars, (int)fixedArgCount, (int)entryCount); |
892 | } // GetNativeVarData |
893 | |
894 | |
895 | //----------------------------------------------------------------------------- |
896 | // Given a instrumented IL map from the profiler that maps: |
897 | // Original offset IL_A -> Instrumentend offset IL_B |
898 | // And a native mapping from the JIT that maps: |
899 | // Instrumented offset IL_B -> native offset Native_C |
900 | // This function merges the two maps and stores the result back into the nativeMap. |
901 | // The nativeMap now maps: |
902 | // Original offset IL_A -> native offset Native_C |
903 | // pEntryCount is the number of valid entries in nativeMap, and it may be adjusted downwards |
904 | // as part of the composition. |
905 | //----------------------------------------------------------------------------- |
906 | void DacDbiInterfaceImpl::ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount) |
907 | { |
908 | // Translate the IL offset if the profiler has provided us with a mapping. |
909 | // The ICD public API should always expose the original IL offsets, but GetBoundaries() |
910 | // directly accesses the debug info, which stores the instrumented IL offsets. |
911 | |
912 | ULONG32 entryCount = *pEntryCount; |
913 | // The map pointer could be NULL or there could be no entries in the map, in either case no work to do |
914 | if (pProfilerILMap && !pProfilerILMap->IsNull()) |
915 | { |
916 | // If we did instrument, then we can't have any sequence points that |
917 | // are "in-between" the old-->new map that the profiler gave us. |
918 | // Ex, if map is: |
919 | // (6 old -> 36 new) |
920 | // (8 old -> 50 new) |
921 | // And the jit gives us an entry for 44 new, that will map back to 6 old. |
922 | // Since the map can only have one entry for 6 old, we remove 44 new. |
923 | |
924 | // First Pass: invalidate all the duplicate entries by setting their IL offset to MAX_ILNUM |
925 | ULONG32 cDuplicate = 0; |
926 | ULONG32 prevILOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM); |
927 | for (ULONG32 i = 0; i < entryCount; i++) |
928 | { |
929 | ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, pProfilerILMap); |
930 | |
931 | if (origILOffset == prevILOffset) |
932 | { |
933 | // mark this sequence point as invalid; refer to the comment above |
934 | nativeMap[i].ilOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM); |
935 | cDuplicate += 1; |
936 | } |
937 | else |
938 | { |
939 | // overwrite the instrumented IL offset with the original IL offset |
940 | nativeMap[i].ilOffset = origILOffset; |
941 | prevILOffset = origILOffset; |
942 | } |
943 | } |
944 | |
945 | // Second Pass: move all the valid entries up front |
946 | ULONG32 realIndex = 0; |
947 | for (ULONG32 curIndex = 0; curIndex < entryCount; curIndex++) |
948 | { |
949 | if (nativeMap[curIndex].ilOffset != (ULONG32)(ICorDebugInfo::MAX_ILNUM)) |
950 | { |
951 | // This is a valid entry. Move it up front. |
952 | nativeMap[realIndex] = nativeMap[curIndex]; |
953 | realIndex += 1; |
954 | } |
955 | } |
956 | |
957 | // make sure we have done the bookkeeping correctly |
958 | _ASSERTE((realIndex + cDuplicate) == entryCount); |
959 | |
960 | // Final Pass: derecement entryCount |
961 | entryCount -= cDuplicate; |
962 | *pEntryCount = entryCount; |
963 | } |
964 | } |
965 | |
966 | |
967 | //----------------------------------------------------------------------------- |
968 | // Get the native/IL sequence points for a function |
969 | // Arguments: |
970 | // input: |
971 | // pMethodDesc pointer to the method desc for the function |
972 | // startAddr starting address of the function--used to differentiate |
973 | // output: |
974 | // pNativeMap data structure containing a list of sequence points |
975 | // Note: this function may throw |
976 | //----------------------------------------------------------------------------- |
977 | void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, |
978 | CORDB_ADDRESS startAddr, |
979 | SequencePoints * pSeqPoints) |
980 | { |
981 | |
982 | // make sure we haven't done this already |
983 | if (pSeqPoints->IsInitialized()) |
984 | { |
985 | return; |
986 | } |
987 | |
988 | // Use the DebugInfoStore to get IL->Native maps. |
989 | // It doesn't matter whether we're jitted, ngenned etc. |
990 | DebugInfoRequest request; |
991 | request.InitFromStartingAddr(pMethodDesc, CORDB_ADDRESS_TO_TADDR(startAddr)); |
992 | |
993 | |
994 | // Bounds info. |
995 | NewArrayHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL); |
996 | |
997 | ULONG32 entryCount; |
998 | BOOL success = DebugInfoManager::GetBoundariesAndVars(request, |
999 | InfoStoreNew, NULL, // allocator |
1000 | &entryCount, &mapCopy, |
1001 | NULL, NULL); |
1002 | if (!success) |
1003 | ThrowHR(E_FAIL); |
1004 | |
1005 | // if there is a rejit IL map for this function, apply that in preference to load-time mapping |
1006 | #ifdef FEATURE_REJIT |
1007 | CodeVersionManager * pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); |
1008 | NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(dac_cast<PTR_MethodDesc>(pMethodDesc), (PCODE)startAddr); |
1009 | if (!nativeCodeVersion.IsNull()) |
1010 | { |
1011 | const InstrumentedILOffsetMapping * pRejitMapping = nativeCodeVersion.GetILCodeVersion().GetInstrumentedILMap(); |
1012 | ComposeMapping(pRejitMapping, mapCopy, &entryCount); |
1013 | } |
1014 | else |
1015 | { |
1016 | #endif |
1017 | // if there is a profiler load-time mapping and not a rejit mapping, apply that instead |
1018 | InstrumentedILOffsetMapping loadTimeMapping = |
1019 | pMethodDesc->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef()); |
1020 | ComposeMapping(&loadTimeMapping, mapCopy, &entryCount); |
1021 | #ifdef FEATURE_REJIT |
1022 | } |
1023 | #endif |
1024 | |
1025 | pSeqPoints->InitSequencePoints(entryCount); |
1026 | |
1027 | // mapCopy and pSeqPoints have elements of different types. Thus, we |
1028 | // need to copy the individual members from the elements of mapCopy to the |
1029 | // elements of pSeqPoints. Once we're done, we can release mapCopy |
1030 | pSeqPoints->CopyAndSortSequencePoints(mapCopy); |
1031 | |
1032 | } // GetSequencePoints |
1033 | |
1034 | // ---------------------------------------------------------------------------- |
1035 | // DacDbiInterfaceImpl::TranslateInstrumentedILOffsetToOriginal |
1036 | // |
1037 | // Description: |
1038 | // Helper function to convert an instrumented IL offset to the corresponding original IL offset. |
1039 | // |
1040 | // Arguments: |
1041 | // * ilOffset - offset to be translated |
1042 | // * pMapping - the profiler-provided mapping between original IL offsets and instrumented IL offsets |
1043 | // |
1044 | // Return Value: |
1045 | // Return the translated offset. |
1046 | // |
1047 | |
1048 | ULONG DacDbiInterfaceImpl::TranslateInstrumentedILOffsetToOriginal(ULONG ilOffset, |
1049 | const InstrumentedILOffsetMapping * pMapping) |
1050 | { |
1051 | SIZE_T cMap = pMapping->GetCount(); |
1052 | ARRAY_PTR_COR_IL_MAP rgMap = pMapping->GetOffsets(); |
1053 | |
1054 | _ASSERTE((cMap == 0) == (rgMap == NULL)); |
1055 | |
1056 | // Early out if there is no mapping, or if we are dealing with a special IL offset such as |
1057 | // prolog, epilog, etc. |
1058 | if ((cMap == 0) || ((int)ilOffset < 0)) |
1059 | { |
1060 | return ilOffset; |
1061 | } |
1062 | |
1063 | SIZE_T i = 0; |
1064 | for (i = 1; i < cMap; i++) |
1065 | { |
1066 | if (ilOffset < rgMap[i].newOffset) |
1067 | { |
1068 | return rgMap[i - 1].oldOffset; |
1069 | } |
1070 | } |
1071 | return rgMap[i - 1].oldOffset; |
1072 | } |
1073 | |
1074 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1075 | // Function Data |
1076 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1077 | |
1078 | |
1079 | // GetILCodeAndSig returns the function's ILCode and SigToken given |
1080 | // a module and a token. The info will come from a MethodDesc, if |
1081 | // one exists or from metadata. |
1082 | // |
1083 | void DacDbiInterfaceImpl::GetILCodeAndSig(VMPTR_DomainFile vmDomainFile, |
1084 | mdToken functionToken, |
1085 | TargetBuffer * pCodeInfo, |
1086 | mdToken * pLocalSigToken) |
1087 | { |
1088 | DD_ENTER_MAY_THROW; |
1089 | |
1090 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
1091 | Module * pModule = pDomainFile->GetCurrentModule(); |
1092 | RVA methodRVA = 0; |
1093 | DWORD implFlags; |
1094 | |
1095 | // preinitialize out params |
1096 | pCodeInfo->Clear(); |
1097 | *pLocalSigToken = mdSignatureNil; |
1098 | |
1099 | // Get the RVA and impl flags for this method. |
1100 | IfFailThrow(pModule->GetMDImport()->GetMethodImplProps(functionToken, |
1101 | &methodRVA, |
1102 | &implFlags)); |
1103 | |
1104 | MethodDesc* pMethodDesc = |
1105 | FindLoadedMethodRefOrDef(pModule, functionToken); |
1106 | |
1107 | // If the RVA is 0 or it's native, then the method is not IL |
1108 | if (methodRVA == 0) |
1109 | { |
1110 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Function is not IL - methodRVA == NULL!\n" )); |
1111 | // return (CORDBG_E_FUNCTION_NOT_IL); |
1112 | // Sanity check this.... |
1113 | |
1114 | if(!pMethodDesc || !pMethodDesc->IsIL()) |
1115 | { |
1116 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: And the MD agrees..\n" )); |
1117 | ThrowHR(CORDBG_E_FUNCTION_NOT_IL); |
1118 | } |
1119 | else |
1120 | { |
1121 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: But the MD says it's IL..\n" )); |
1122 | } |
1123 | |
1124 | if (pMethodDesc != NULL && pMethodDesc->GetRVA() == 0) |
1125 | { |
1126 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Actually, MD says RVA is 0 too - keep going...!\n" )); |
1127 | } |
1128 | } |
1129 | if (IsMiNative(implFlags)) |
1130 | { |
1131 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: Function is not IL - IsMiNative!\n" )); |
1132 | ThrowHR(CORDBG_E_FUNCTION_NOT_IL); |
1133 | } |
1134 | |
1135 | *pLocalSigToken = GetILCodeAndSigHelper(pModule, pMethodDesc, functionToken, methodRVA, pCodeInfo); |
1136 | |
1137 | #ifdef LOGGING |
1138 | else |
1139 | { |
1140 | LOG((LF_CORDB,LL_INFO100000, "DDI::GICAS: GetMethodImplProps failed!\n" )); |
1141 | } |
1142 | #endif |
1143 | } // GetILCodeAndSig |
1144 | |
1145 | //--------------------------------------------------------------------------------------- |
1146 | // |
1147 | // This is just a worker function for GetILCodeAndSig. It returns the function's ILCode and SigToken |
1148 | // given a module, a token, and the RVA. If a MethodDesc is provided, it has to be consistent with |
1149 | // the token and the RVA. |
1150 | // |
1151 | // Arguments: |
1152 | // pModule - the Module containing the specified method |
1153 | // pMD - the specified method; can be NULL |
1154 | // mdMethodToken - the MethodDef token of the specified method |
1155 | // methodRVA - the RVA of the IL for the specified method |
1156 | // pIL - out parameter; return the target address and size of the IL of the specified method |
1157 | // |
1158 | // Return Value: |
1159 | // Return the local variable signature token of the specified method. Can be mdSignatureNil. |
1160 | // |
1161 | |
1162 | mdSignature DacDbiInterfaceImpl::GetILCodeAndSigHelper(Module * pModule, |
1163 | MethodDesc * pMD, |
1164 | mdMethodDef mdMethodToken, |
1165 | RVA methodRVA, |
1166 | TargetBuffer * pIL) |
1167 | { |
1168 | _ASSERTE(pModule != NULL); |
1169 | |
1170 | // If a MethodDesc is provided, it has to be consistent with the MethodDef token and the RVA. |
1171 | _ASSERTE((pMD == NULL) || ((pMD->GetMemberDef() == mdMethodToken) && (pMD->GetRVA() == methodRVA))); |
1172 | |
1173 | TADDR pTargetIL; // target address of start of IL blob |
1174 | |
1175 | // This works for methods in dynamic modules, and methods overriden by a profiler. |
1176 | pTargetIL = pModule->GetDynamicIL(mdMethodToken, TRUE); |
1177 | |
1178 | // Method not overriden - get the original copy of the IL by going to the PE file/RVA |
1179 | // If this is in a dynamic module then don't even attempt this since ReflectionModule::GetIL isn't |
1180 | // implemend for DAC. |
1181 | if (pTargetIL == 0 && !pModule->IsReflection()) |
1182 | { |
1183 | pTargetIL = (TADDR)pModule->GetIL(methodRVA); |
1184 | } |
1185 | |
1186 | mdSignature mdSig = mdSignatureNil; |
1187 | if (pTargetIL == 0) |
1188 | { |
1189 | // Currently this should only happen for LCG methods (including IL stubs). |
1190 | // LCG methods have a 0 RVA, and so we don't currently have any way to get the IL here. |
1191 | _ASSERTE(pMD->IsDynamicMethod()); |
1192 | _ASSERTE(pMD->AsDynamicMethodDesc()->IsLCGMethod()|| |
1193 | pMD->AsDynamicMethodDesc()->IsILStub()); |
1194 | |
1195 | // Clear the buffer. |
1196 | pIL->Clear(); |
1197 | } |
1198 | else |
1199 | { |
1200 | // Now we have the target address of the IL blob, we need to bring it over to the host. |
1201 | // DacGetILMethod will copy the COR_ILMETHOD information that we need |
1202 | COR_ILMETHOD * pHostIL = DacGetIlMethod(pTargetIL); // host address of start of IL blob |
1203 | COR_ILMETHOD_DECODER (pHostIL); // host address of header |
1204 | |
1205 | |
1206 | // Get the IL code info. We need the address of the IL itself, which will be beyond the header |
1207 | // at the beginning of the blob. We ultimately need the target address. To get this, we take |
1208 | // target address of the target IL blob and add the offset from the beginning of the host IL blob |
1209 | // (the header) to the beginning of the IL itself (we get this information from the header). |
1210 | pIL->pAddress = pTargetIL + ((SIZE_T)(header.Code) - (SIZE_T)pHostIL); |
1211 | pIL->cbSize = header.GetCodeSize(); |
1212 | |
1213 | // Now we get the signature token |
1214 | if (header.LocalVarSigTok != NULL) |
1215 | { |
1216 | mdSig = header.GetLocalVarSigTok(); |
1217 | } |
1218 | else |
1219 | { |
1220 | mdSig = mdSignatureNil; |
1221 | } |
1222 | } |
1223 | |
1224 | return mdSig; |
1225 | } |
1226 | |
1227 | |
1228 | bool DacDbiInterfaceImpl::GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, |
1229 | DWORD &dwTimeStamp, |
1230 | DWORD &dwSize, |
1231 | bool &isNGEN, |
1232 | IStringHolder* pStrFilename) |
1233 | { |
1234 | #if !defined(FEATURE_PREJIT) |
1235 | |
1236 | return false; |
1237 | |
1238 | #else // defined(FEATURE_PREJIT) |
1239 | |
1240 | DD_ENTER_MAY_THROW; |
1241 | |
1242 | DWORD dwDataSize; |
1243 | DWORD dwRvaHint; |
1244 | PEFile * pPEFile = vmPEFile.GetDacPtr(); |
1245 | _ASSERTE(pPEFile != NULL); |
1246 | if (pPEFile == NULL) |
1247 | return false; |
1248 | |
1249 | WCHAR wszFilePath[MAX_LONGPATH] = {0}; |
1250 | DWORD cchFilePath = MAX_LONGPATH; |
1251 | bool ret = ClrDataAccess::GetMetaDataFileInfoFromPEFile(pPEFile, |
1252 | dwTimeStamp, |
1253 | dwSize, |
1254 | dwDataSize, |
1255 | dwRvaHint, |
1256 | isNGEN, |
1257 | wszFilePath, |
1258 | cchFilePath); |
1259 | |
1260 | pStrFilename->AssignCopy(wszFilePath); |
1261 | return ret; |
1262 | #endif // !defined(FEATURE_PREJIT) |
1263 | } |
1264 | |
1265 | |
1266 | bool DacDbiInterfaceImpl::GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, |
1267 | DWORD &dwTimeStamp, |
1268 | DWORD &dwSize, |
1269 | IStringHolder* pStrFilename) |
1270 | { |
1271 | #if !defined(FEATURE_PREJIT) |
1272 | |
1273 | return false; |
1274 | |
1275 | #else // defined(FEATURE_PREJIT) |
1276 | |
1277 | DD_ENTER_MAY_THROW; |
1278 | |
1279 | PEFile * pPEFile = vmPEFile.GetDacPtr(); |
1280 | _ASSERTE(pPEFile != NULL); |
1281 | if (pPEFile == NULL) |
1282 | { |
1283 | return false; |
1284 | } |
1285 | |
1286 | WCHAR wszFilePath[MAX_LONGPATH] = {0}; |
1287 | DWORD cchFilePath = MAX_LONGPATH; |
1288 | bool ret = ClrDataAccess::GetILImageInfoFromNgenPEFile(pPEFile, |
1289 | dwTimeStamp, |
1290 | dwSize, |
1291 | wszFilePath, |
1292 | cchFilePath); |
1293 | |
1294 | pStrFilename->AssignCopy(wszFilePath); |
1295 | return ret; |
1296 | #endif // !defined(FEATURE_PREJIT) |
1297 | } |
1298 | |
1299 | // Get start addresses and sizes for hot and cold regions for a native code blob. |
1300 | // Arguments: |
1301 | // Input: |
1302 | // pMethodDesc - method desc for the function we are inspecting |
1303 | // Output (required): |
1304 | // pCodeInfo - initializes the m_rgCodeRegions field of this structure |
1305 | // if the native code is available. Otherwise, |
1306 | // pCodeInfo->IsValid() is false. |
1307 | |
1308 | void DacDbiInterfaceImpl::GetMethodRegionInfo(MethodDesc * pMethodDesc, |
1309 | NativeCodeFunctionData * pCodeInfo) |
1310 | { |
1311 | CONTRACTL |
1312 | { |
1313 | SO_INTOLERANT; |
1314 | GC_NOTRIGGER; |
1315 | PRECONDITION(CheckPointer(pCodeInfo)); |
1316 | } |
1317 | CONTRACTL_END; |
1318 | |
1319 | IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; |
1320 | PCODE functionAddress = pMethodDesc->GetNativeCode(); |
1321 | |
1322 | // get the start address of the hot region and initialize the jit manager |
1323 | pCodeInfo->m_rgCodeRegions[kHot].pAddress = CORDB_ADDRESS(PCODEToPINSTR(functionAddress)); |
1324 | |
1325 | // if the start address is NULL, the code isn't available yet, so just return |
1326 | if (functionAddress != NULL) |
1327 | { |
1328 | EECodeInfo codeInfo(functionAddress); |
1329 | _ASSERTE(codeInfo.IsValid()); |
1330 | |
1331 | codeInfo.GetMethodRegionInfo(&methodRegionInfo); |
1332 | |
1333 | // now get the rest of the region information |
1334 | pCodeInfo->m_rgCodeRegions[kHot].cbSize = (ULONG)methodRegionInfo.hotSize; |
1335 | pCodeInfo->m_rgCodeRegions[kCold].Init(PCODEToPINSTR(methodRegionInfo.coldStartAddress), |
1336 | (ULONG)methodRegionInfo.coldSize); |
1337 | _ASSERTE(pCodeInfo->IsValid()); |
1338 | } |
1339 | else |
1340 | { |
1341 | _ASSERTE(!pCodeInfo->IsValid()); |
1342 | } |
1343 | } // GetMethodRegionInfo |
1344 | |
1345 | |
1346 | // Gets the following information about a native code blob: |
1347 | // - its method desc |
1348 | // - whether it's an instantiated generic |
1349 | // - its EnC version number |
1350 | // - hot and cold region information. |
1351 | // If the hot region start address is NULL at the end, it means the native code |
1352 | // isn't currently available. In this case, all values in pCodeInfo will be |
1353 | // cleared. |
1354 | |
1355 | void DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainFile vmDomainFile, |
1356 | mdToken functionToken, |
1357 | NativeCodeFunctionData * pCodeInfo) |
1358 | { |
1359 | DD_ENTER_MAY_THROW; |
1360 | |
1361 | _ASSERTE(pCodeInfo != NULL); |
1362 | |
1363 | // pre-initialize: |
1364 | pCodeInfo->Clear(); |
1365 | |
1366 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
1367 | Module * pModule = pDomainFile->GetCurrentModule(); |
1368 | |
1369 | MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); |
1370 | pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); |
1371 | |
1372 | // if we are loading a module and trying to bind a previously set breakpoint, we may not have |
1373 | // a method desc yet, so check for that situation |
1374 | if(pMethodDesc != NULL) |
1375 | { |
1376 | GetMethodRegionInfo(pMethodDesc, pCodeInfo); |
1377 | if (pCodeInfo->m_rgCodeRegions[kHot].pAddress != NULL) |
1378 | { |
1379 | pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation(); |
1380 | LookupEnCVersions(pModule, |
1381 | pCodeInfo->vmNativeCodeMethodDescToken, |
1382 | functionToken, |
1383 | pCodeInfo->m_rgCodeRegions[kHot].pAddress, |
1384 | &(pCodeInfo->encVersion)); |
1385 | } |
1386 | } |
1387 | } // GetNativeCodeInfo |
1388 | |
1389 | // Gets the following information about a native code blob: |
1390 | // - its method desc |
1391 | // - whether it's an instantiated generic |
1392 | // - its EnC version number |
1393 | // - hot and cold region information. |
1394 | void DacDbiInterfaceImpl::GetNativeCodeInfoForAddr(VMPTR_MethodDesc vmMethodDesc, |
1395 | CORDB_ADDRESS hotCodeStartAddr, |
1396 | NativeCodeFunctionData * pCodeInfo) |
1397 | { |
1398 | DD_ENTER_MAY_THROW; |
1399 | |
1400 | _ASSERTE(pCodeInfo != NULL); |
1401 | |
1402 | if (hotCodeStartAddr == NULL) |
1403 | { |
1404 | // if the start address is NULL, the code isn't available yet, so just return |
1405 | _ASSERTE(!pCodeInfo->IsValid()); |
1406 | return; |
1407 | } |
1408 | |
1409 | IJitManager::MethodRegionInfo methodRegionInfo = {NULL, 0, NULL, 0}; |
1410 | TADDR codeAddr = CORDB_ADDRESS_TO_TADDR(hotCodeStartAddr); |
1411 | |
1412 | #ifdef _TARGET_ARM_ |
1413 | // TADDR should not have the thumb code bit set. |
1414 | _ASSERTE((codeAddr & THUMB_CODE) == 0); |
1415 | codeAddr &= ~THUMB_CODE; |
1416 | #endif |
1417 | |
1418 | EECodeInfo codeInfo(codeAddr); |
1419 | _ASSERTE(codeInfo.IsValid()); |
1420 | |
1421 | // We may not have the memory for the cold code region in a minidump. |
1422 | // Do not fail stackwalking because of this. |
1423 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY |
1424 | { |
1425 | codeInfo.GetMethodRegionInfo(&methodRegionInfo); |
1426 | } |
1427 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; |
1428 | |
1429 | // Even if GetMethodRegionInfo() fails to retrieve the cold code region info, |
1430 | // we should still be able to get the hot code region info. We are counting on this for |
1431 | // stackwalking to work in dump debugging scenarios. |
1432 | _ASSERTE(methodRegionInfo.hotStartAddress == codeAddr); |
1433 | |
1434 | // now get the rest of the region information |
1435 | pCodeInfo->m_rgCodeRegions[kHot].Init(PCODEToPINSTR(methodRegionInfo.hotStartAddress), |
1436 | (ULONG)methodRegionInfo.hotSize); |
1437 | pCodeInfo->m_rgCodeRegions[kCold].Init(PCODEToPINSTR(methodRegionInfo.coldStartAddress), |
1438 | (ULONG)methodRegionInfo.coldSize); |
1439 | _ASSERTE(pCodeInfo->IsValid()); |
1440 | |
1441 | MethodDesc* pMethodDesc = vmMethodDesc.GetDacPtr(); |
1442 | pCodeInfo->isInstantiatedGeneric = pMethodDesc->HasClassOrMethodInstantiation(); |
1443 | pCodeInfo->vmNativeCodeMethodDescToken = vmMethodDesc; |
1444 | |
1445 | SIZE_T unusedLatestEncVersion; |
1446 | Module * pModule = pMethodDesc->GetModule(); |
1447 | _ASSERTE(pModule != NULL); |
1448 | LookupEnCVersions(pModule, |
1449 | vmMethodDesc, |
1450 | pMethodDesc->GetMemberDef(), |
1451 | codeAddr, |
1452 | &unusedLatestEncVersion, //unused by caller |
1453 | &(pCodeInfo->encVersion)); |
1454 | |
1455 | } // GetNativeCodeInfo |
1456 | |
1457 | |
1458 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1459 | // |
1460 | // Functions to get Type and Class information |
1461 | // |
1462 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1463 | //----------------------------------------------------------------------------- |
1464 | //DacDbiInterfaceImpl::GetTypeHandles |
1465 | // Get the approximate and exact type handles for a type |
1466 | // Arguments: |
1467 | // input: |
1468 | // vmThExact - VMPTR of the exact type handle. If this method is called |
1469 | // to get information for a new generic instantiation, this will already |
1470 | // be initialized. If it's called to get type information for an arbitrary |
1471 | // type (i.e., called to initialize an instance of CordbClass), it will be NULL |
1472 | // vmThApprox - VMPTR of the approximate type handle. If this method is called |
1473 | // to get information for a new generic instantiation, this will already |
1474 | // be initialized. If it's called to get type information for an arbitrary |
1475 | // type (i.e., called to initialize an instance of CordbClass), it will be NULL |
1476 | // output: |
1477 | // pThExact - handle for exact type information for a generic instantiation |
1478 | // pThApprox - handle for type information |
1479 | // Notes: |
1480 | // pThExact and pTHApprox must be pointers to existing memory. |
1481 | //----------------------------------------------------------------------------- |
1482 | void DacDbiInterfaceImpl::GetTypeHandles(VMPTR_TypeHandle vmThExact, |
1483 | VMPTR_TypeHandle vmThApprox, |
1484 | TypeHandle * pThExact, |
1485 | TypeHandle * pThApprox) |
1486 | { |
1487 | _ASSERTE((pThExact != NULL) && (pThApprox != NULL)); |
1488 | |
1489 | *pThExact = TypeHandle::FromPtr(vmThExact.GetDacPtr()); |
1490 | *pThApprox = TypeHandle::FromPtr(vmThApprox.GetDacPtr()); |
1491 | |
1492 | // If we can't find the class, return the proper HR to the right side. Note: if the class is not a value class and |
1493 | // the class is also not restored, then we must pretend that the class is still not loaded. We are gonna let |
1494 | // unrestored value classes slide, though, and special case access to the class's parent below. |
1495 | if ((pThApprox->IsNull()) || ((!pThApprox->IsValueType()) && (!pThApprox->IsRestored()))) |
1496 | { |
1497 | LOG((LF_CORDB, LL_INFO10000, "D::GASCI: class isn't loaded.\n" )); |
1498 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
1499 | } |
1500 | // If the exact type handle is not restored ignore it. |
1501 | if (!pThExact->IsNull() && !pThExact->IsRestored()) |
1502 | { |
1503 | *pThExact = TypeHandle(); |
1504 | } |
1505 | } // DacDbiInterfaceImpl::GetTypeHandles |
1506 | |
1507 | //----------------------------------------------------------------------------- |
1508 | // DacDbiInterfaceImpl::GetTotalFieldCount |
1509 | // Gets the total number of fields for a type. |
1510 | // Input Argument: thApprox - type handle used to determine the number of fields |
1511 | // Return Value: count of the total fields of the type. |
1512 | //----------------------------------------------------------------------------- |
1513 | unsigned int DacDbiInterfaceImpl::GetTotalFieldCount(TypeHandle thApprox) |
1514 | { |
1515 | MethodTable *pMT = thApprox.GetMethodTable(); |
1516 | |
1517 | // Count the instance and static fields for this class (not including parent). |
1518 | // This will not include any newly added EnC fields. |
1519 | unsigned int IFCount = pMT->GetNumIntroducedInstanceFields(); |
1520 | unsigned int SFCount = pMT->GetNumStaticFields(); |
1521 | |
1522 | #ifdef EnC_SUPPORTED |
1523 | PTR_Module pModule = pMT->GetModule(); |
1524 | |
1525 | // Stats above don't include EnC fields. So add them now. |
1526 | if (pModule->IsEditAndContinueEnabled()) |
1527 | { |
1528 | PTR_EnCEEClassData pEncData = |
1529 | (dac_cast<PTR_EditAndContinueModule>(pModule))->GetEnCEEClassData(pMT, TRUE); |
1530 | |
1531 | if (pEncData != NULL) |
1532 | { |
1533 | _ASSERTE(pEncData->GetMethodTable() == pMT); |
1534 | |
1535 | // EnC only adds fields, never removes them. |
1536 | IFCount += pEncData->GetAddedInstanceFields(); |
1537 | SFCount += pEncData->GetAddedStaticFields(); |
1538 | } |
1539 | } |
1540 | #endif |
1541 | return IFCount + SFCount; |
1542 | } // DacDbiInterfaceImpl::GetTotalFieldCount |
1543 | |
1544 | //----------------------------------------------------------------------------- |
1545 | // DacDbiInterfaceImpl::InitClassData |
1546 | // initializes various values of the ClassInfo data structure, including the |
1547 | // field count, generic args count, size and value class flag |
1548 | // Arguments: |
1549 | // input: thApprox - used to get access to all the necessary values |
1550 | // fIsInstantiatedType - used to determine how to compute the size |
1551 | // output: pData - contains fields to be initialized |
1552 | //----------------------------------------------------------------------------- |
1553 | void DacDbiInterfaceImpl::InitClassData(TypeHandle thApprox, |
1554 | BOOL fIsInstantiatedType, |
1555 | ClassInfo * pData) |
1556 | { |
1557 | pData->m_fieldList.Alloc(GetTotalFieldCount(thApprox)); |
1558 | |
1559 | // For Generic classes you must get the object size via the type handle, which |
1560 | // will get you to the right information for the particular instantiation |
1561 | // you're working with... |
1562 | pData->m_objectSize = 0; |
1563 | if ((!thApprox.GetNumGenericArgs()) || fIsInstantiatedType) |
1564 | { |
1565 | pData->m_objectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes(); |
1566 | } |
1567 | |
1568 | } // DacDbiInterfaceImpl::InitClassData |
1569 | |
1570 | //----------------------------------------------------------------------------- |
1571 | // DacDbiInterfaceImpl::GetStaticsBases |
1572 | // Gets the base table addresses for both GC and non-GC statics |
1573 | // Arguments: |
1574 | // input: thExact - exact type handle for the class |
1575 | // pAppDomain - AppDomain in which the class is loaded |
1576 | // output: ppGCStaticsBase - base pointer for GC statics |
1577 | // ppNonGCStaticsBase - base pointer for non GC statics |
1578 | // Notes: |
1579 | // If this is a non-generic type, or an instantiated type, then we'll be able to get the static var bases |
1580 | // If the typeHandle represents a generic type constructor (i.e. an uninstantiated generic class), then |
1581 | // the static bases will be null (since statics are per-instantiation). |
1582 | //----------------------------------------------------------------------------- |
1583 | void DacDbiInterfaceImpl::GetStaticsBases(TypeHandle thExact, |
1584 | AppDomain * pAppDomain, |
1585 | PTR_BYTE * ppGCStaticsBase, |
1586 | PTR_BYTE * ppNonGCStaticsBase) |
1587 | { |
1588 | MethodTable * pMT = thExact.GetMethodTable(); |
1589 | Module * pModuleForStatics = pMT->GetModuleForStatics(); |
1590 | if (pModuleForStatics != NULL) |
1591 | { |
1592 | PTR_DomainLocalModule pLocalModule = pModuleForStatics->GetDomainLocalModule(pAppDomain); |
1593 | if (pLocalModule != NULL) |
1594 | { |
1595 | *ppGCStaticsBase = pLocalModule->GetGCStaticsBasePointer(pMT); |
1596 | *ppNonGCStaticsBase = pLocalModule->GetNonGCStaticsBasePointer(pMT); |
1597 | } |
1598 | } |
1599 | } // DacDbiInterfaceImpl::GetStaticsBases |
1600 | |
1601 | //----------------------------------------------------------------------------- |
1602 | // DacDbiInterfaceImpl::ComputeFieldData |
1603 | // Computes the field info for pFD and stores it in pcurrentFieldData |
1604 | // Arguments: |
1605 | // input: pFD - FieldDesc used to get necessary information |
1606 | // pGCStaticsBase - base table address for GC statics |
1607 | // pNonGCStaticsBase - base table address for non-GC statics |
1608 | // output: pCurrentFieldData - contains fields to be initialized |
1609 | //----------------------------------------------------------------------------- |
1610 | void DacDbiInterfaceImpl::ComputeFieldData(PTR_FieldDesc pFD, |
1611 | PTR_BYTE pGCStaticsBase, |
1612 | PTR_BYTE pNonGCStaticsBase, |
1613 | FieldData * pCurrentFieldData) |
1614 | { |
1615 | pCurrentFieldData->Initialize(pFD->IsStatic(), pFD->IsPrimitive(), pFD->GetMemberDef()); |
1616 | |
1617 | #ifdef EnC_SUPPORTED |
1618 | // If the field was newly introduced via EnC, and hasn't yet |
1619 | // been fixed up, then we'll send back a marker indicating |
1620 | // that it isn't yet available. |
1621 | if (pFD->IsEnCNew()) |
1622 | { |
1623 | // @dbgtodo Microsoft inspection: eliminate the debugger token when ICDClass and ICDType are |
1624 | // completely DACized |
1625 | pCurrentFieldData->m_vmFieldDesc.SetHostPtr(pFD); |
1626 | pCurrentFieldData->m_fFldStorageAvailable = FALSE; |
1627 | pCurrentFieldData->m_fFldIsTLS = FALSE; |
1628 | pCurrentFieldData->m_fFldIsRVA = FALSE; |
1629 | pCurrentFieldData->m_fFldIsCollectibleStatic = FALSE; |
1630 | } |
1631 | else |
1632 | #endif // EnC_SUPPORTED |
1633 | { |
1634 | // Otherwise, we'll compute the info & send it back. |
1635 | pCurrentFieldData->m_fFldStorageAvailable = TRUE; |
1636 | // @dbgtodo Microsoft inspection: eliminate the debugger token when ICDClass and ICDType are |
1637 | // completely DACized |
1638 | pCurrentFieldData->m_vmFieldDesc.SetHostPtr(pFD); |
1639 | pCurrentFieldData->m_fFldIsTLS = (pFD->IsThreadStatic() == TRUE); |
1640 | pCurrentFieldData->m_fFldIsRVA = (pFD->IsRVA() == TRUE); |
1641 | pCurrentFieldData->m_fFldIsCollectibleStatic = (pFD->IsStatic() == TRUE && |
1642 | pFD->GetEnclosingMethodTable()->Collectible()); |
1643 | |
1644 | // Compute the address of the field |
1645 | if (pFD->IsStatic()) |
1646 | { |
1647 | // statics are addressed using an absolute address. |
1648 | if (pFD->IsRVA()) |
1649 | { |
1650 | // RVA statics are relative to a base module address |
1651 | DWORD offset = pFD->GetOffset(); |
1652 | PTR_VOID addr = pFD->GetModule()->GetRvaField(offset, pFD->IsZapped()); |
1653 | if (pCurrentFieldData->OkToGetOrSetStaticAddress()) |
1654 | { |
1655 | pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(addr)); |
1656 | } |
1657 | } |
1658 | else if (pFD->IsThreadStatic() || |
1659 | pCurrentFieldData->m_fFldIsCollectibleStatic) |
1660 | { |
1661 | // this is a special type of static that must be queried using DB_IPCE_GET_SPECIAL_STATIC |
1662 | } |
1663 | else |
1664 | { |
1665 | // This is a normal static variable in the GC or Non-GC static base table |
1666 | PTR_BYTE base = pFD->IsPrimitive() ? pNonGCStaticsBase : pGCStaticsBase; |
1667 | if (base == NULL) |
1668 | { |
1669 | // static var not available. This may be an open generic class (not an instantiated type), |
1670 | // or we might only have approximate type information because the type hasn't been |
1671 | // initialized yet. |
1672 | |
1673 | if (pCurrentFieldData->OkToGetOrSetStaticAddress()) |
1674 | { |
1675 | pCurrentFieldData->SetStaticAddress(NULL); |
1676 | } |
1677 | } |
1678 | else |
1679 | { |
1680 | if (pCurrentFieldData->OkToGetOrSetStaticAddress()) |
1681 | { |
1682 | // calculate the absolute address using the base and the offset from the base |
1683 | pCurrentFieldData->SetStaticAddress(PTR_TO_TADDR(base) + pFD->GetOffset()); |
1684 | } |
1685 | } |
1686 | } |
1687 | } |
1688 | else |
1689 | { |
1690 | // instance variables are addressed using an offset within the instance |
1691 | if (pCurrentFieldData->OkToGetOrSetInstanceOffset()) |
1692 | { |
1693 | pCurrentFieldData->SetInstanceOffset(pFD->GetOffset()); |
1694 | } |
1695 | } |
1696 | } |
1697 | |
1698 | } // DacDbiInterfaceImpl::ComputeFieldData |
1699 | |
1700 | //----------------------------------------------------------------------------- |
1701 | // DacDbiInterfaceImpl::CollectFields |
1702 | // Gets information for all the fields for a given type |
1703 | // Arguments: |
1704 | // input: thExact - used to determine whether we need to get statics base tables |
1705 | // thApprox - used to get the field desc iterator |
1706 | // pAppDomain - used to get statics base tables |
1707 | // output: |
1708 | // pFieldList - contains fields to be initialized |
1709 | // Note: the caller must ensure that *ppFields is NULL (i.e., any previously allocated memory |
1710 | // must have been deallocated. |
1711 | //----------------------------------------------------------------------------- |
1712 | void DacDbiInterfaceImpl::CollectFields(TypeHandle thExact, |
1713 | TypeHandle thApprox, |
1714 | AppDomain * pAppDomain, |
1715 | DacDbiArrayList<FieldData> * pFieldList) |
1716 | { |
1717 | PTR_BYTE pGCStaticsBase = NULL; |
1718 | PTR_BYTE pNonGCStaticsBase = NULL; |
1719 | if (!thExact.IsNull() && !thExact.GetMethodTable()->Collectible()) |
1720 | { |
1721 | // get base tables for static fields |
1722 | GetStaticsBases(thExact, pAppDomain, &pGCStaticsBase, &pNonGCStaticsBase); |
1723 | } |
1724 | |
1725 | unsigned int fieldCount = 0; |
1726 | |
1727 | // <TODO> we are losing exact type information for static fields in generic types. We have |
1728 | // field desc iterators only for approximate types, but statics are per instantiation, so we |
1729 | // need an exact type to be able to handle these correctly. We need to use |
1730 | // FieldDesc::GetExactDeclaringType to get at the correct field. This requires the exact |
1731 | // TypeHandle. </TODO> |
1732 | EncApproxFieldDescIterator fdIterator(thApprox.GetMethodTable(), |
1733 | ApproxFieldDescIterator::ALL_FIELDS, |
1734 | FALSE); // don't fixup EnC (we can't, we're stopped) |
1735 | |
1736 | PTR_FieldDesc pCurrentFD; |
1737 | int index = 0; |
1738 | while (((pCurrentFD = fdIterator.Next()) != NULL) && (index < pFieldList->Count())) |
1739 | { |
1740 | // fill in the pCurrentEntry structure |
1741 | ComputeFieldData(pCurrentFD, pGCStaticsBase, pNonGCStaticsBase, &((*pFieldList)[index])); |
1742 | |
1743 | // Bump our counts and pointers. |
1744 | fieldCount++; |
1745 | index++; |
1746 | } |
1747 | _ASSERTE(fieldCount == (unsigned int)pFieldList->Count()); |
1748 | |
1749 | } // DacDbiInterfaceImpl::CollectFields |
1750 | |
1751 | |
1752 | // Determine if a type is a ValueType |
1753 | BOOL DacDbiInterfaceImpl::IsValueType (VMPTR_TypeHandle vmTypeHandle) |
1754 | { |
1755 | DD_ENTER_MAY_THROW; |
1756 | |
1757 | TypeHandle th = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); |
1758 | return th.IsValueType(); |
1759 | } |
1760 | |
1761 | // Determine if a type has generic parameters |
1762 | BOOL DacDbiInterfaceImpl::HasTypeParams (VMPTR_TypeHandle vmTypeHandle) |
1763 | { |
1764 | DD_ENTER_MAY_THROW; |
1765 | |
1766 | TypeHandle th = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); |
1767 | return th.ContainsGenericVariables(); |
1768 | } |
1769 | |
1770 | // DacDbi API: Get type information for a class |
1771 | void DacDbiInterfaceImpl::GetClassInfo(VMPTR_AppDomain vmAppDomain, |
1772 | VMPTR_TypeHandle vmThExact, |
1773 | ClassInfo * pData) |
1774 | { |
1775 | DD_ENTER_MAY_THROW; |
1776 | |
1777 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
1778 | |
1779 | TypeHandle thExact; |
1780 | TypeHandle thApprox; |
1781 | |
1782 | GetTypeHandles(vmThExact, vmThExact, &thExact, &thApprox); |
1783 | |
1784 | // initialize field count, generic args count, size and value class flag |
1785 | InitClassData(thApprox, false, pData); |
1786 | |
1787 | if (pAppDomain != NULL) |
1788 | CollectFields(thExact, thApprox, pAppDomain, &(pData->m_fieldList)); |
1789 | } // DacDbiInterfaceImpl::GetClassInfo |
1790 | |
1791 | // DacDbi API: Get field information and object size for an instantiated generic type |
1792 | void DacDbiInterfaceImpl::GetInstantiationFieldInfo (VMPTR_DomainFile vmDomainFile, |
1793 | VMPTR_TypeHandle vmThExact, |
1794 | VMPTR_TypeHandle vmThApprox, |
1795 | DacDbiArrayList<FieldData> * pFieldList, |
1796 | SIZE_T * pObjectSize) |
1797 | { |
1798 | DD_ENTER_MAY_THROW; |
1799 | |
1800 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
1801 | _ASSERTE(pDomainFile != NULL); |
1802 | AppDomain * pAppDomain = pDomainFile->GetAppDomain(); |
1803 | TypeHandle thExact; |
1804 | TypeHandle thApprox; |
1805 | |
1806 | GetTypeHandles(vmThExact, vmThApprox, &thExact, &thApprox); |
1807 | |
1808 | *pObjectSize = thApprox.GetMethodTable()->GetNumInstanceFieldBytes(); |
1809 | |
1810 | pFieldList->Alloc(GetTotalFieldCount(thApprox)); |
1811 | |
1812 | CollectFields(thExact, thApprox, pAppDomain, pFieldList); |
1813 | |
1814 | } // DacDbiInterfaceImpl::GetInstantiationFieldInfo |
1815 | |
1816 | //----------------------------------------------------------------------------------- |
1817 | // DacDbiInterfaceImpl::TypeDataWalk member functions |
1818 | //----------------------------------------------------------------------------------- |
1819 | |
1820 | //----------------------------------------------------------------------------- |
1821 | // TypeDataWalk constructor--initialize the buffer and number of remaining items from input data |
1822 | // Arguments: pData - pointer to a list of records containing information about type parameters for an |
1823 | // instantiated type |
1824 | // nData - number of entries in pData |
1825 | //----------------------------------------------------------------------------- |
1826 | DacDbiInterfaceImpl::TypeDataWalk::TypeDataWalk(DebuggerIPCE_TypeArgData * pData, unsigned int nData) |
1827 | { |
1828 | m_pCurrentData = pData; |
1829 | m_nRemaining = nData; |
1830 | } // DacDbiInterfaceImpl::TypeDataWalk::TypeDataWalk |
1831 | |
1832 | //----------------------------------------------------------------------------- |
1833 | // DacDbiInterfaceImpl::TypeDataWalk::ReadOne |
1834 | // read and return a single node from the list of type parameters |
1835 | // Arguments: none (uses internal state) |
1836 | // Return value: information about the next type parameter in m_pCurrentData |
1837 | //----------------------------------------------------------------------------- |
1838 | DebuggerIPCE_TypeArgData * DacDbiInterfaceImpl::TypeDataWalk::ReadOne() |
1839 | { |
1840 | LIMITED_METHOD_CONTRACT; |
1841 | if (m_nRemaining) |
1842 | { |
1843 | m_nRemaining--; |
1844 | return m_pCurrentData++; |
1845 | } |
1846 | else |
1847 | { |
1848 | return NULL; |
1849 | } |
1850 | } // DacDbiInterfaceImpl::TypeDataWalk::ReadOne |
1851 | |
1852 | //----------------------------------------------------------------------------- |
1853 | // DacDbiInterfaceImpl::TypeDataWalk::Skip |
1854 | // Skip a single node from the list of type handles along with any children it might have |
1855 | // Arguments: none (uses internal state) |
1856 | // Return value: none (updates internal state) |
1857 | //----------------------------------------------------------------------------- |
1858 | void DacDbiInterfaceImpl::TypeDataWalk::Skip() |
1859 | { |
1860 | LIMITED_METHOD_CONTRACT; |
1861 | |
1862 | DebuggerIPCE_TypeArgData * pData = ReadOne(); |
1863 | if (pData) |
1864 | { |
1865 | for (unsigned int i = 0; i < pData->numTypeArgs; i++) |
1866 | { |
1867 | Skip(); |
1868 | } |
1869 | } |
1870 | } // DacDbiInterfaceImpl::TypeDataWalk::Skip |
1871 | |
1872 | //----------------------------------------------------------------------------- |
1873 | // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg |
1874 | // Read a type handle when it is used in the position of a generic argument or |
1875 | // argument of an array or address type. Take into account generic code sharing if we |
1876 | // have been requested to find the canonical representation amongst a set of shared- |
1877 | // code generic types. That is, if generics code sharing is enabled then return "Object" |
1878 | // for all reference types, and canonicalize underneath value types, e.g. V<string> --> V<object>. |
1879 | // Return TypeHandle() if any of the type handles are not loaded. |
1880 | // |
1881 | // Arguments: retrieveWhich - indicates whether to retrieve a canonical representation or |
1882 | // an exact representation |
1883 | // Return value: the type handle for the type parameter |
1884 | //----------------------------------------------------------------------------- |
1885 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg(TypeHandleReadType retrieveWhich) |
1886 | { |
1887 | CONTRACTL |
1888 | { |
1889 | NOTHROW; |
1890 | GC_NOTRIGGER; |
1891 | } |
1892 | CONTRACTL_END; |
1893 | |
1894 | #if !defined(FEATURE_SHARE_GENERIC_CODE) |
1895 | return ReadLoadedTypeHandle(kGetExact); |
1896 | #else |
1897 | |
1898 | if (retrieveWhich == kGetExact) |
1899 | return ReadLoadedTypeHandle(kGetExact); |
1900 | |
1901 | // This nasty bit of code works out what the "canonicalization" of a |
1902 | // parameter to a generic is once we take into account generics code sharing. |
1903 | // |
1904 | // This logic is somewhat a duplication of logic in vm\typehandle.cpp, though |
1905 | // that logic operates on a TypeHandle format, i.e. assumes we're finding the |
1906 | // canonical form of a type that has already been loaded. Here we are finding |
1907 | // the canonical form of a type that may not have been loaded (but where we expect |
1908 | // its canonical form to have been loaded). |
1909 | // |
1910 | // Ideally this logic would not be duplicated in this way, but it is difficult |
1911 | // to arrange for that. |
1912 | DebuggerIPCE_TypeArgData * pData = ReadOne(); |
1913 | if (!pData) |
1914 | return TypeHandle(); |
1915 | |
1916 | // If we have code sharing then the process of canonicalizing is trickier. |
1917 | // unfortunately we have to include the exact specification of compatibility at |
1918 | // this point. |
1919 | CorElementType elementType = pData->data.elementType; |
1920 | |
1921 | switch (elementType) |
1922 | { |
1923 | case ELEMENT_TYPE_PTR: |
1924 | _ASSERTE(pData->numTypeArgs == 1); |
1925 | return PtrOrByRefTypeArg(pData, retrieveWhich); |
1926 | break; |
1927 | |
1928 | case ELEMENT_TYPE_CLASS: |
1929 | case ELEMENT_TYPE_VALUETYPE: |
1930 | return ClassTypeArg(pData, retrieveWhich); |
1931 | break; |
1932 | |
1933 | case ELEMENT_TYPE_FNPTR: |
1934 | return FnPtrTypeArg(pData, retrieveWhich); |
1935 | break; |
1936 | |
1937 | default: |
1938 | return ObjRefOrPrimitiveTypeArg(pData, elementType); |
1939 | break; |
1940 | } |
1941 | |
1942 | #endif // FEATURE_SHARE_GENERIC_CODE |
1943 | } // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeArg |
1944 | |
1945 | //----------------------------------------------------------------------------- |
1946 | // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles |
1947 | // Iterate through the type argument data, creating type handles as we go. |
1948 | // |
1949 | // Arguments: |
1950 | // input: retrieveWhich - indicates whether we can return a canonical type handle |
1951 | // or we must return an exact type handle |
1952 | // nTypeArgs - number of type arguments to be read |
1953 | // output: ppResults - pointer to a list of TypeHandles that will hold the type handles |
1954 | // for each type parameter |
1955 | // |
1956 | // Return Value: FALSE iff any of the type handles are not loaded. |
1957 | //----------------------------------------------------------------------------- |
1958 | BOOL DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles(TypeHandleReadType retrieveWhich, |
1959 | unsigned int nTypeArgs, |
1960 | TypeHandle * ppResults) |
1961 | { |
1962 | WRAPPER_NO_CONTRACT; |
1963 | |
1964 | BOOL allOK = true; |
1965 | for (unsigned int i = 0; i < nTypeArgs; i++) |
1966 | { |
1967 | ppResults[i] = ReadLoadedTypeArg(retrieveWhich); |
1968 | allOK &= !ppResults[i].IsNull(); |
1969 | } |
1970 | return allOK; |
1971 | } // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandles |
1972 | |
1973 | //----------------------------------------------------------------------------- |
1974 | // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation |
1975 | // Read an instantiation of a generic type if it has already been created. |
1976 | // |
1977 | // Arguments: |
1978 | // input: retrieveWhich - indicates whether we can return a canonical type handle |
1979 | // or we must return an exact type handle |
1980 | // pModule - module in which the instantiated type is loaded |
1981 | // mdToken - metadata token for the type |
1982 | // nTypeArgs - number of type arguments to be read |
1983 | // Return value: the type handle for the instantiated type |
1984 | //----------------------------------------------------------------------------- |
1985 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation(TypeHandleReadType retrieveWhich, |
1986 | Module * pModule, |
1987 | mdTypeDef mdToken, |
1988 | unsigned int nTypeArgs) |
1989 | { |
1990 | WRAPPER_NO_CONTRACT; |
1991 | |
1992 | NewHolder<TypeHandle> pInst(new TypeHandle[nTypeArgs]); |
1993 | |
1994 | // get the type handle for each of the type parameters |
1995 | if (!ReadLoadedTypeHandles(retrieveWhich, nTypeArgs, pInst)) |
1996 | { |
1997 | return TypeHandle(); |
1998 | } |
1999 | |
2000 | // get the type handle for the particular instantiation that corresponds to |
2001 | // the given type parameters |
2002 | return FindLoadedInstantiation(pModule, mdToken, nTypeArgs, pInst); |
2003 | |
2004 | } // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedInstantiation |
2005 | |
2006 | |
2007 | //----------------------------------------------------------------------------- |
2008 | // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle |
2009 | // |
2010 | // Compute the type handle for a given type. |
2011 | // This is the top-level function that will return the type handle for an |
2012 | // arbitrary type. It uses mutual recursion with ReadLoadedTypeArg to get |
2013 | // the type handle for a (possibly parameterized) type. Note that the referent of |
2014 | // address types or the element type of an array type are viewed as type parameters. |
2015 | // |
2016 | // For example, assume that we are retrieving only exact types, and we have as our |
2017 | // top level type an array defined as int [][]. |
2018 | // We start by noting that the type is an array type, so we call ReadLoadedTypeArg to |
2019 | // get the element type. We find that the element type is also an array:int []. |
2020 | // ReadLoadedTypeArg will call ReadLoadedTypeHandle with this type information. |
2021 | // Again, we determine that the top-level type is an array, so we call ReadLoadedTypeArg |
2022 | // to get the element type, int. ReadLoadedTypeArg will again call ReadLoadedTypeHandle |
2023 | // which will find that this time, the top-level type is a primitive type. It will request |
2024 | // the loaded type handle from the loader and return it. On return, we get the type handle |
2025 | // for an array of int from the loader. We return again and request the type handle for an |
2026 | // array of arrays of int. This is the type handle we will return. |
2027 | // |
2028 | // Arguments: |
2029 | // input: retrieveWhich - determines whether we can return the type handle for |
2030 | // a canonical type or only for an exact type |
2031 | // we use the list of type data stored in the TypeDataWalk data members |
2032 | // for other input information |
2033 | // Return value: type handle for the current type. |
2034 | //----------------------------------------------------------------------------- |
2035 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle(TypeHandleReadType retrieveWhich) |
2036 | { |
2037 | CONTRACTL |
2038 | { |
2039 | NOTHROW; |
2040 | GC_NOTRIGGER; |
2041 | } |
2042 | CONTRACTL_END; |
2043 | |
2044 | // get the type information at the head of the list m_pCurrentData |
2045 | DebuggerIPCE_TypeArgData * pData = ReadOne(); |
2046 | if (!pData) |
2047 | return TypeHandle(); |
2048 | |
2049 | // get the type handle that corresponds to its elementType |
2050 | TypeHandle typeHandle; |
2051 | switch (pData->data.elementType) |
2052 | { |
2053 | case ELEMENT_TYPE_ARRAY: |
2054 | case ELEMENT_TYPE_SZARRAY: |
2055 | typeHandle = ArrayTypeArg(pData, retrieveWhich); |
2056 | break; |
2057 | |
2058 | case ELEMENT_TYPE_PTR: |
2059 | case ELEMENT_TYPE_BYREF: |
2060 | typeHandle = PtrOrByRefTypeArg(pData, retrieveWhich); |
2061 | break; |
2062 | case ELEMENT_TYPE_CLASS: |
2063 | case ELEMENT_TYPE_VALUETYPE: |
2064 | { |
2065 | Module * pModule = pData->data.ClassTypeData.vmModule.GetDacPtr(); |
2066 | typeHandle = ReadLoadedInstantiation(retrieveWhich, |
2067 | pModule, |
2068 | pData->data.ClassTypeData.metadataToken, |
2069 | pData->numTypeArgs); |
2070 | } |
2071 | break; |
2072 | |
2073 | case ELEMENT_TYPE_FNPTR: |
2074 | { |
2075 | typeHandle = FnPtrTypeArg(pData, retrieveWhich); |
2076 | } |
2077 | break; |
2078 | |
2079 | default: |
2080 | typeHandle = FindLoadedElementType(pData->data.elementType); |
2081 | break; |
2082 | } |
2083 | return typeHandle; |
2084 | } // DacDbiInterfaceImpl::TypeDataWalk::ReadLoadedTypeHandle |
2085 | |
2086 | //----------------------------------------------------------------------------- |
2087 | // DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg |
2088 | // get a loaded type handle for an array type (E_T_ARRAY or E_T_SZARRAY) |
2089 | // |
2090 | // Arguments: |
2091 | // input: pArrayTypeInfo - type information for an array type |
2092 | // Although this is in fact a pointer (in)to a list, we treat it here |
2093 | // simply as a pointer to a single instance of DebuggerIPCE_TypeArgData |
2094 | // which holds type information for an array. |
2095 | // This is the most recent type node (for an array type) retrieved |
2096 | // by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeArg will |
2097 | // result in call(s) to ReadOne to retrieve one or more type nodes |
2098 | // that are needed to compute the type handle for the |
2099 | // element type of the array. When we return from that call, we pass |
2100 | // pArrayTypeInfo along with arrayElementTypeArg to FindLoadedArrayType |
2101 | // to get the type handle for this particular array type. |
2102 | // Note: |
2103 | // On entry, we know that pArrayTypeInfo is the same as m_pCurrentData - 1, |
2104 | // but by the time we need to use it, this is no longer true. Because |
2105 | // we can't predict how many nodes will be consumed by the call to |
2106 | // ReadLoadedTypeArg, we can't compute this value from the member fields |
2107 | // of TypeDataWalk and therefore pass it as a parameter. |
2108 | // retrieveWhich - determines whether we can return the type handle for |
2109 | // a canonical type or only for an exact type |
2110 | // Return value: the type handle corresponding to the array type |
2111 | //----------------------------------------------------------------------------- |
2112 | |
2113 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg(DebuggerIPCE_TypeArgData * pArrayTypeInfo, |
2114 | TypeHandleReadType retrieveWhich) |
2115 | { |
2116 | TypeHandle arrayElementTypeArg = ReadLoadedTypeArg(retrieveWhich); |
2117 | if (!arrayElementTypeArg.IsNull()) |
2118 | { |
2119 | return FindLoadedArrayType(pArrayTypeInfo->data.elementType, |
2120 | arrayElementTypeArg, |
2121 | pArrayTypeInfo->data.ArrayTypeData.arrayRank); |
2122 | } |
2123 | return TypeHandle(); |
2124 | } // DacDbiInterfaceImpl::TypeDataWalk::ArrayTypeArg |
2125 | |
2126 | |
2127 | //----------------------------------------------------------------------------- |
2128 | // DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg |
2129 | // get a loaded type handle for an address type (E_T_PTR or E_T_BYREF) |
2130 | // |
2131 | // Arguments: |
2132 | // input: pPtrOrByRefTypeInfo - type information for a pointer or byref type |
2133 | // Although this is in fact a pointer (in)to a list, we treat it here |
2134 | // simply as a pointer to a single instance of DebuggerIPCE_TypeArgData |
2135 | // which holds type information for a pointer or byref type. |
2136 | // This is the most recent type node (for a pointer or byref type) retrieved |
2137 | // by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeArg will |
2138 | // result in call(s) to ReadOne to retrieve one or more type nodes |
2139 | // that are needed to compute the type handle for the |
2140 | // referent type of the pointer. When we return from that call, we pass |
2141 | // pPtrOrByRefTypeInfo along with referentTypeArg to FindLoadedPointerOrByrefType |
2142 | // to get the type handle for this particular pointer or byref type. |
2143 | // Note: |
2144 | // On entry, we know that pPtrOrByRefTypeInfo is the same as m_pCurrentData - 1, |
2145 | // but by the time we need to use it, this is no longer true. Because |
2146 | // we can't predict how many nodes will be consumed by the call to |
2147 | // ReadLoadedTypeArg, we can't compute this value from the member fields |
2148 | // of TypeDataWalk and therefore pass it as a parameter. |
2149 | // retrieveWhich - determines whether we can return the type handle for |
2150 | // a canonical type or only for an exact type |
2151 | // Return value: the type handle corresponding to the address type |
2152 | //----------------------------------------------------------------------------- |
2153 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg(DebuggerIPCE_TypeArgData * pPtrOrByRefTypeInfo, |
2154 | TypeHandleReadType retrieveWhich) |
2155 | { |
2156 | TypeHandle referentTypeArg = ReadLoadedTypeArg(retrieveWhich); |
2157 | if (!referentTypeArg.IsNull()) |
2158 | { |
2159 | return FindLoadedPointerOrByrefType(pPtrOrByRefTypeInfo->data.elementType, referentTypeArg); |
2160 | } |
2161 | |
2162 | return TypeHandle(); |
2163 | |
2164 | } // DacDbiInterfaceImpl::TypeDataWalk::PtrOrByRefTypeArg |
2165 | |
2166 | //----------------------------------------------------------------------------- |
2167 | // DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg |
2168 | // get a loaded type handle for a class type (E_T_CLASS or E_T_VALUETYPE) |
2169 | // |
2170 | // Arguments: |
2171 | // input: pClassTypeInfo - type information for a class type |
2172 | // Although this is in fact a pointer (in)to a list, we treat it here |
2173 | // simply as a pointer to a single instance of DebuggerIPCE_TypeArgData |
2174 | // which holds type information for a pointer or byref type. |
2175 | // This is the most recent type node (for a pointer or byref type) retrieved |
2176 | // by TypeDataWalk::ReadOne(). The call to ReadLoadedInstantiation will |
2177 | // result in call(s) to ReadOne to retrieve one or more type nodes |
2178 | // that are needed to compute the type handle for the type parameters |
2179 | // for the class. If we can't find an exact loaded type for the class, we will |
2180 | // instead return a canonical method table. In this case, we need to skip |
2181 | // the type parameter information for each actual parameter to the class. |
2182 | // This is necessary because we may be getting a type handle for a class which is |
2183 | // in turn an argument to a parent type. If the parent type has more arguments, we |
2184 | // need to be at the right place in the list when we return. We use |
2185 | // pClassTypeInfo to get the number of type arguments that we need to skip. |
2186 | // retrieveWhich - determines whether we can return the type handle for |
2187 | // a canonical type or only for an exact type |
2188 | // Return value: the type handle corresponding to the class type |
2189 | //----------------------------------------------------------------------------- |
2190 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg(DebuggerIPCE_TypeArgData * pClassTypeInfo, |
2191 | TypeHandleReadType retrieveWhich) |
2192 | { |
2193 | Module * pModule = pClassTypeInfo->data.ClassTypeData.vmModule.GetDacPtr(); |
2194 | TypeHandle typeDef = ClassLoader::LookupTypeDefOrRefInModule(pModule, |
2195 | pClassTypeInfo->data.ClassTypeData.metadataToken); |
2196 | |
2197 | if ((!typeDef.IsNull() && typeDef.IsValueType()) || (pClassTypeInfo->data.elementType == ELEMENT_TYPE_VALUETYPE)) |
2198 | { |
2199 | return ReadLoadedInstantiation(retrieveWhich, |
2200 | pModule, |
2201 | pClassTypeInfo->data.ClassTypeData.metadataToken, |
2202 | pClassTypeInfo->numTypeArgs); |
2203 | } |
2204 | else |
2205 | { |
2206 | _ASSERTE(retrieveWhich == kGetCanonical); |
2207 | // skip the instantiation - no need to look at it since the type canonicalizes to "Object" |
2208 | for (unsigned int i = 0; i < pClassTypeInfo->numTypeArgs; i++) |
2209 | { |
2210 | Skip(); |
2211 | } |
2212 | return TypeHandle(g_pCanonMethodTableClass); |
2213 | } |
2214 | }// DacDbiInterfaceImpl::TypeDataWalk::ClassTypeArg |
2215 | |
2216 | //----------------------------------------------------------------------------- |
2217 | // DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg |
2218 | // get a loaded type handle for a function pointer type (E_T_FNPTR) |
2219 | // |
2220 | // Arguments: |
2221 | // input: pFnPtrTypeInfo - type information for a pointer or byref type |
2222 | // Although this is in fact a pointer (in)to a list, we treat it here |
2223 | // simply as a pointer to a single instance of DebuggerIPCE_TypeArgData |
2224 | // which holds type information for a function pointer type. |
2225 | // This is the most recent type node (for a function pointer type) retrieved |
2226 | // by TypeDataWalk::ReadOne(). The call to ReadLoadedTypeHandles will |
2227 | // result in call(s) to ReadOne to retrieve one or more type nodes |
2228 | // that are needed to compute the type handle for the return type and |
2229 | // parameter types of the function. When we return from that call, we pass |
2230 | // pFnPtrTypeInfo along with pInst to FindLoadedFnptrType |
2231 | // to get the type handle for this particular function pointer type. |
2232 | // retrieveWhich - determines whether we can return the type handle for |
2233 | // a canonical type or only for an exact type |
2234 | // Return value: the type handle corresponding to the function pointer type |
2235 | //----------------------------------------------------------------------------- |
2236 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg(DebuggerIPCE_TypeArgData * pFnPtrTypeInfo, |
2237 | TypeHandleReadType retrieveWhich) |
2238 | { |
2239 | // allocate space to store a list of type handles, one for the return type and one for each |
2240 | // of the parameter types of the function to which the FnPtr type refers. |
2241 | NewHolder<TypeHandle> pInst(new TypeHandle[sizeof(TypeHandle) * pFnPtrTypeInfo->numTypeArgs]); |
2242 | |
2243 | if (ReadLoadedTypeHandles(retrieveWhich, pFnPtrTypeInfo->numTypeArgs, pInst)) |
2244 | { |
2245 | return FindLoadedFnptrType(pFnPtrTypeInfo->numTypeArgs, pInst); |
2246 | } |
2247 | |
2248 | return TypeHandle(); |
2249 | |
2250 | } // DacDbiInterfaceImpl::TypeDataWalk::FnPtrTypeArg |
2251 | |
2252 | //----------------------------------------------------------------------------- |
2253 | // DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg |
2254 | // get a loaded type handle for a primitive type or ObjRef |
2255 | // |
2256 | // Arguments: |
2257 | // input: pArgInfo - type information for an objref or primitive type. |
2258 | // This is called only when the objref or primitive type |
2259 | // is a type argument for a parent type. In this case, |
2260 | // we treat all objrefs the same, that is, we don't care |
2261 | // about type parameters for the referent. Instead, we will |
2262 | // simply return the canonical object type handle as the type |
2263 | // of the referent. <@dbgtodo Microsoft: why is this?> |
2264 | // If this is a primitive type, we'll simply get the |
2265 | // type handle for that type. |
2266 | // elementType - type of the argument |
2267 | // Return value: the type handle corresponding to the elementType |
2268 | //----------------------------------------------------------------------------- |
2269 | TypeHandle DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg(DebuggerIPCE_TypeArgData * pArgInfo, |
2270 | CorElementType elementType) |
2271 | { |
2272 | // If there are any type args (e.g. for arrays) they can be skipped. The thing |
2273 | // is a reference type anyway. |
2274 | for (unsigned int i = 0; i < pArgInfo->numTypeArgs; i++) |
2275 | { |
2276 | Skip(); |
2277 | } |
2278 | |
2279 | // for an ObjRef, just return the CLASS____CANON type handle |
2280 | if (CorTypeInfo::IsObjRef_NoThrow(elementType)) |
2281 | { |
2282 | return TypeHandle(g_pCanonMethodTableClass); |
2283 | } |
2284 | else |
2285 | { |
2286 | return FindLoadedElementType(elementType); |
2287 | } |
2288 | } // DacDbiInterfaceImpl::TypeDataWalk::ObjRefOrPrimitiveTypeArg |
2289 | |
2290 | |
2291 | //------------------------------------------------------------------------- |
2292 | // end of TypeDataWalk implementations |
2293 | //------------------------------------------------------------------------- |
2294 | //------------------------------------------------------------------------- |
2295 | // functions to use loader to get type handles |
2296 | // ------------------------------------------------------------------------ |
2297 | |
2298 | // Note, in these functions, the use of ClassLoader::DontLoadTypes was chosen |
2299 | // instead of FailIfNotLoaded because, although we may want to debug unrestored |
2300 | // VCs, we can't do it because the debug API is not set up to handle them. |
2301 | // |
2302 | //----------------------------------------------------------------------------- |
2303 | // DacDbiInterfaceImpl::FindLoadedArrayType |
2304 | // Use ClassLoader to find a loaded type handle for an array type (E_T_ARRAY or E_T_SZARRAY) |
2305 | // Arguments: |
2306 | // input: arrayType - type of the array |
2307 | // TypeArg - type handle for the base type |
2308 | // rank - array rank |
2309 | // Return Value: type handle for the array type |
2310 | //----------------------------------------------------------------------------- |
2311 | // static |
2312 | TypeHandle DacDbiInterfaceImpl::FindLoadedArrayType(CorElementType arrayType, |
2313 | TypeHandle typeArg, |
2314 | unsigned rank) |
2315 | { |
2316 | // Lookup operations run the class loader in non-load mode. |
2317 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
2318 | |
2319 | if (typeArg.IsNull()) |
2320 | { |
2321 | return TypeHandle(); |
2322 | } |
2323 | else |
2324 | { |
2325 | return ClassLoader::LoadArrayTypeThrowing(typeArg, |
2326 | arrayType, |
2327 | rank, |
2328 | ClassLoader::DontLoadTypes ); |
2329 | } |
2330 | } // DacDbiInterfaceImpl::FindLoadedArrayType; |
2331 | |
2332 | //----------------------------------------------------------------------------- |
2333 | // DacDbiInterfaceImpl::FindLoadedPointerOrByrefType |
2334 | // Use ClassLoader to find a loaded type handle for an address type (E_T_PTR or E_T_BYREF) |
2335 | // Arguments: |
2336 | // input: addressType - type of the address type |
2337 | // TypeArg - type handle for the base type |
2338 | // Return Value: type handle for the address type |
2339 | //----------------------------------------------------------------------------- |
2340 | // static |
2341 | TypeHandle DacDbiInterfaceImpl::FindLoadedPointerOrByrefType(CorElementType addressType, TypeHandle typeArg) |
2342 | { |
2343 | // Lookup operations run the class loader in non-load mode. |
2344 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
2345 | |
2346 | return ClassLoader::LoadPointerOrByrefTypeThrowing(addressType, |
2347 | typeArg, |
2348 | ClassLoader::DontLoadTypes); |
2349 | } // DacDbiInterfaceImpl::FindLoadedPointerOrByrefType |
2350 | |
2351 | //----------------------------------------------------------------------------- |
2352 | // DacDbiInterfaceImpl::FindLoadedFnptrType |
2353 | // Use ClassLoader to find a loaded type handle for a function pointer type (E_T_FNPTR) |
2354 | // Arguments: |
2355 | // input: pInst - type handles of the function's return value and arguments |
2356 | // numTypeArgs - number of type handles in pInst |
2357 | // Return Value: type handle for the function pointer type |
2358 | //----------------------------------------------------------------------------- |
2359 | // static |
2360 | TypeHandle DacDbiInterfaceImpl::FindLoadedFnptrType(DWORD numTypeArgs, TypeHandle * pInst) |
2361 | { |
2362 | // Lookup operations run the class loader in non-load mode. |
2363 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
2364 | |
2365 | // @dbgtodo : Do we need to worry about calling convention here? |
2366 | // LoadFnptrTypeThrowing expects the count of arguments, not |
2367 | // including return value, so we subtract 1 from numTypeArgs. |
2368 | return ClassLoader::LoadFnptrTypeThrowing(0, |
2369 | numTypeArgs - 1, |
2370 | pInst, |
2371 | ClassLoader::DontLoadTypes); |
2372 | } // DacDbiInterfaceImpl::FindLoadedFnptrType |
2373 | |
2374 | //----------------------------------------------------------------------------- |
2375 | // DacDbiInterfaceImpl::FindLoadedInstantiation |
2376 | // Use ClassLoader to find a loaded type handle for a particular instantiation of a |
2377 | // class type (E_T_CLASS or E_T_VALUECLASS) |
2378 | // |
2379 | // Arguments: |
2380 | // input: pModule - module in which the type is loaded |
2381 | // mdToken - metadata token for the type |
2382 | // nTypeArgs - number of type arguments in pInst |
2383 | // pInst - list of type handles for the type parameters |
2384 | // Return value: type handle for the instantiated class type |
2385 | //----------------------------------------------------------------------------- |
2386 | // static |
2387 | TypeHandle DacDbiInterfaceImpl::FindLoadedInstantiation(Module * pModule, |
2388 | mdTypeDef mdToken, |
2389 | DWORD nTypeArgs, |
2390 | TypeHandle * pInst) |
2391 | { |
2392 | // Lookup operations run the class loader in non-load mode. |
2393 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
2394 | |
2395 | return ClassLoader::LoadGenericInstantiationThrowing(pModule, |
2396 | mdToken, |
2397 | Instantiation(pInst,nTypeArgs), |
2398 | ClassLoader::DontLoadTypes); |
2399 | |
2400 | } // DacDbiInterfaceImpl::FindLoadedInstantiation |
2401 | |
2402 | //----------------------------------------------------------------------------- |
2403 | // DacDbiInterfaceImpl::FindLoadedElementType |
2404 | // Get the type handle for a primitive type |
2405 | // Arguments: |
2406 | // input: elementType - type of the primitive type |
2407 | // Return Value: Type handle for the primitive type |
2408 | //----------------------------------------------------------------------------- |
2409 | // static |
2410 | TypeHandle DacDbiInterfaceImpl::FindLoadedElementType(CorElementType elementType) |
2411 | { |
2412 | // Lookup operations run the class loader in non-load mode. |
2413 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
2414 | |
2415 | MethodTable * pMethodTable = (&g_Mscorlib)->GetElementType(elementType); |
2416 | |
2417 | return TypeHandle(pMethodTable); |
2418 | } // DacDbiInterfaceImpl::FindLoadedElementType |
2419 | |
2420 | |
2421 | //----------------------------------------------------------------------------- |
2422 | // DacDbiInterfaceImpl::GetArrayTypeInfo |
2423 | // Gets additional information to convert a type handle to an instance of CordbType if the type is E_T_ARRAY. |
2424 | // Specifically, we get the rank and the type of the array elements |
2425 | // |
2426 | // Arguments: |
2427 | // input: typeHandle - type handle for the array type |
2428 | // pAppDomain - AppDomain into which the type is loaded |
2429 | // output: pTypeInfo - information for the array rank and element type |
2430 | // |
2431 | //----------------------------------------------------------------------------- |
2432 | void DacDbiInterfaceImpl::GetArrayTypeInfo(TypeHandle typeHandle, |
2433 | DebuggerIPCE_ExpandedTypeData * pTypeInfo, |
2434 | AppDomain * pAppDomain) |
2435 | { |
2436 | _ASSERTE(typeHandle.IsArray()); |
2437 | pTypeInfo->ArrayTypeData.arrayRank = typeHandle.AsArray()->GetRank(); |
2438 | TypeHandleToBasicTypeInfo(typeHandle.AsArray()->GetArrayElementTypeHandle(), |
2439 | &(pTypeInfo->ArrayTypeData.arrayTypeArg), |
2440 | pAppDomain); |
2441 | } // DacDbiInterfaceImpl::GetArrayTypeInfo |
2442 | |
2443 | //----------------------------------------------------------------------------- |
2444 | // DacDbiInterfaceImpl::GetPtrTypeInfo |
2445 | // Gets additional information to convert a type handle to an instance of CordbType if the type is |
2446 | // E_T_PTR or E_T_BYREF. Specifically, we get the type for the referent of the address type |
2447 | // |
2448 | // Arguments: |
2449 | // input: boxed - indicates what, if anything, is boxed (see code:AreValueTypesBoxed for |
2450 | // more specific information) |
2451 | // typeHandle - type handle for the address type |
2452 | // pAppDomain - AppDomain into which the type is loaded |
2453 | // output: pTypeInfo - information for the referent type |
2454 | // |
2455 | //----------------------------------------------------------------------------- |
2456 | void DacDbiInterfaceImpl::GetPtrTypeInfo(AreValueTypesBoxed boxed, |
2457 | TypeHandle typeHandle, |
2458 | DebuggerIPCE_ExpandedTypeData * pTypeInfo, |
2459 | AppDomain * pAppDomain) |
2460 | { |
2461 | if (boxed == AllBoxed) |
2462 | { |
2463 | GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2464 | } |
2465 | else |
2466 | { |
2467 | _ASSERTE(typeHandle.IsTypeDesc()); |
2468 | TypeHandleToBasicTypeInfo(typeHandle.AsTypeDesc()->GetTypeParam(), |
2469 | &(pTypeInfo->UnaryTypeData.unaryTypeArg), |
2470 | pAppDomain); |
2471 | } |
2472 | } // DacDbiInterfaceImpl::GetPtrTypeInfo |
2473 | |
2474 | //----------------------------------------------------------------------------- |
2475 | // DacDbiInterfaceImpl::GetFnPtrTypeInfo |
2476 | // Gets additional information to convert a type handle to an instance of CordbType if the type is |
2477 | // E_T_FNPTR, specifically the typehandle for the referent. |
2478 | // |
2479 | // Arguments |
2480 | // input: boxed - indicates what, if anything, is boxed (see code:AreValueTypesBoxed for |
2481 | // more specific information) |
2482 | // typeHandle - type handle for the address type |
2483 | // pAppDomain - AppDomain into which the type is loaded |
2484 | // output: pTypeInfo - information for the referent type |
2485 | // |
2486 | //----------------------------------------------------------------------------- |
2487 | void DacDbiInterfaceImpl::GetFnPtrTypeInfo(AreValueTypesBoxed boxed, |
2488 | TypeHandle typeHandle, |
2489 | DebuggerIPCE_ExpandedTypeData * pTypeInfo, |
2490 | AppDomain * pAppDomain) |
2491 | { |
2492 | if (boxed == AllBoxed) |
2493 | { |
2494 | GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2495 | } |
2496 | else |
2497 | { |
2498 | pTypeInfo->NaryTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2499 | } |
2500 | } // DacDbiInterfaceImpl::GetFnPtrTypeInfo |
2501 | |
2502 | |
2503 | //----------------------------------------------------------------------------- |
2504 | // DacDbiInterfaceImpl::GetClassTypeInfo |
2505 | // Gets additional information to convert a type handle to an instance of CordbType if the type is |
2506 | // E_T_CLASS or E_T_VALUETYPE |
2507 | // |
2508 | // Arguments |
2509 | // input: typeHandle - type handle for the address type |
2510 | // pAppDomain - AppDomain into which the type is loaded |
2511 | // output: pTypeInfo - information for the referent type |
2512 | // |
2513 | //----------------------------------------------------------------------------- |
2514 | void DacDbiInterfaceImpl::GetClassTypeInfo(TypeHandle typeHandle, |
2515 | DebuggerIPCE_ExpandedTypeData * pTypeInfo, |
2516 | AppDomain * pAppDomain) |
2517 | { |
2518 | Module * pModule = typeHandle.GetModule(); |
2519 | |
2520 | if (typeHandle.HasInstantiation()) // the type handle represents a generic instantiation |
2521 | { |
2522 | pTypeInfo->ClassTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2523 | } |
2524 | else // non-generic |
2525 | { |
2526 | pTypeInfo->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); |
2527 | } |
2528 | |
2529 | pTypeInfo->ClassTypeData.metadataToken = typeHandle.GetCl(); |
2530 | |
2531 | _ASSERTE(pModule); |
2532 | pTypeInfo->ClassTypeData.vmModule.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule)); |
2533 | if (pAppDomain) |
2534 | { |
2535 | pTypeInfo->ClassTypeData.vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule->GetDomainFile(pAppDomain))); |
2536 | } |
2537 | else |
2538 | { |
2539 | pTypeInfo->ClassTypeData.vmDomainFile = VMPTR_DomainFile::NullPtr(); |
2540 | } |
2541 | } // DacDbiInterfaceImpl::GetClassTypeInfo |
2542 | |
2543 | //----------------------------------------------------------------------------- |
2544 | // DacDbiInterfaceImpl::GetElementType |
2545 | // Gets the correct CorElementType value from a type handle |
2546 | // |
2547 | // Arguments |
2548 | // input: typeHandle - type handle for the address type |
2549 | // Return Value: the CorElementType enum value for the type handle |
2550 | //----------------------------------------------------------------------------- |
2551 | CorElementType DacDbiInterfaceImpl::GetElementType (TypeHandle typeHandle) |
2552 | { |
2553 | if (typeHandle.IsNull()) |
2554 | { |
2555 | return ELEMENT_TYPE_VOID; |
2556 | } |
2557 | else if (typeHandle.GetMethodTable() == g_pObjectClass) |
2558 | { |
2559 | return ELEMENT_TYPE_OBJECT; |
2560 | } |
2561 | else if (typeHandle.GetMethodTable() == g_pStringClass) |
2562 | { |
2563 | return ELEMENT_TYPE_STRING; |
2564 | } |
2565 | else |
2566 | { |
2567 | // GetSignatureCorElementType returns E_T_CLASS for E_T_STRING... :-( |
2568 | return typeHandle.GetSignatureCorElementType(); |
2569 | } |
2570 | |
2571 | } // DacDbiInterfaceImpl::GetElementType |
2572 | |
2573 | //----------------------------------------------------------------------------- |
2574 | // DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo |
2575 | // Gets additional information to convert a type handle to an instance of CordbType for the referent of an |
2576 | // E_T_BYREF or E_T_PTR or for the element type of an E_T_ARRAY or E_T_SZARRAY |
2577 | // |
2578 | // Arguments: |
2579 | // input: typeHandle - type handle for the address type |
2580 | // pAppDomain - AppDomain into which the type is loaded |
2581 | // output: pTypeInfo - information for the referent type |
2582 | // |
2583 | //----------------------------------------------------------------------------- |
2584 | void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle typeHandle, |
2585 | DebuggerIPCE_BasicTypeData * pTypeInfo, |
2586 | AppDomain * pAppDomain) |
2587 | { |
2588 | pTypeInfo->elementType = GetElementType(typeHandle); |
2589 | |
2590 | switch (pTypeInfo->elementType) |
2591 | { |
2592 | case ELEMENT_TYPE_ARRAY: |
2593 | case ELEMENT_TYPE_SZARRAY: |
2594 | case ELEMENT_TYPE_FNPTR: |
2595 | case ELEMENT_TYPE_PTR: |
2596 | case ELEMENT_TYPE_BYREF: |
2597 | pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2598 | pTypeInfo->metadataToken = mdTokenNil; |
2599 | pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); |
2600 | break; |
2601 | |
2602 | case ELEMENT_TYPE_CLASS: |
2603 | case ELEMENT_TYPE_VALUETYPE: |
2604 | { |
2605 | Module * pModule = typeHandle.GetModule(); |
2606 | |
2607 | if (typeHandle.HasInstantiation()) // only set if instantiated |
2608 | { |
2609 | pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2610 | } |
2611 | else |
2612 | { |
2613 | pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
2614 | } |
2615 | |
2616 | pTypeInfo->metadataToken = typeHandle.GetCl(); |
2617 | _ASSERTE(pModule); |
2618 | |
2619 | pTypeInfo->vmModule.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule)); |
2620 | if (pAppDomain) |
2621 | { |
2622 | pTypeInfo->vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pModule->GetDomainFile(pAppDomain))); |
2623 | } |
2624 | else |
2625 | { |
2626 | pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); |
2627 | } |
2628 | break; |
2629 | } |
2630 | |
2631 | default: |
2632 | pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
2633 | pTypeInfo->metadataToken = mdTokenNil; |
2634 | pTypeInfo->vmDomainFile = VMPTR_DomainFile::NullPtr(); |
2635 | break; |
2636 | } |
2637 | return; |
2638 | } // DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo |
2639 | |
2640 | |
2641 | void DacDbiInterfaceImpl::GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, |
2642 | VMPTR_AppDomain vmAppDomain, |
2643 | COR_TYPEID id, |
2644 | DebuggerIPCE_ExpandedTypeData *pTypeInfo) |
2645 | { |
2646 | DD_ENTER_MAY_THROW; |
2647 | |
2648 | PTR_MethodTable pMT(TO_TADDR(id.token1)); |
2649 | |
2650 | if (pMT->IsArray()) |
2651 | { |
2652 | // ArrayBase::GetTypeHandle() may return a NULL handle in corner case scenarios. This check prevents |
2653 | // us from an AV but doesn't actually fix the problem. See DevDiv 653441 for more info. |
2654 | TypeHandle arrayHandle = ArrayBase::GetTypeHandle(pMT); |
2655 | if (arrayHandle.IsNull()) |
2656 | { |
2657 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
2658 | } |
2659 | |
2660 | TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, arrayHandle, pTypeInfo); |
2661 | } |
2662 | else |
2663 | { |
2664 | TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, TypeHandle::FromPtr(TO_TADDR(id.token1)), pTypeInfo); |
2665 | } |
2666 | } |
2667 | |
2668 | void DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, |
2669 | VMPTR_AppDomain vmAppDomain, |
2670 | CORDB_ADDRESS addr, |
2671 | DebuggerIPCE_ExpandedTypeData *pTypeInfo) |
2672 | { |
2673 | DD_ENTER_MAY_THROW; |
2674 | |
2675 | PTR_Object obj(TO_TADDR(addr)); |
2676 | TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, obj->GetGCSafeTypeHandle(), pTypeInfo); |
2677 | } |
2678 | |
2679 | // DacDbi API: use a type handle to get the information needed to create the corresponding RS CordbType instance |
2680 | void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, |
2681 | VMPTR_AppDomain vmAppDomain, |
2682 | VMPTR_TypeHandle vmTypeHandle, |
2683 | DebuggerIPCE_ExpandedTypeData * pTypeInfo) |
2684 | { |
2685 | DD_ENTER_MAY_THROW; |
2686 | |
2687 | |
2688 | TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); |
2689 | TypeHandleToExpandedTypeInfoImpl(boxed, vmAppDomain, typeHandle, pTypeInfo); |
2690 | } |
2691 | |
2692 | |
2693 | void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed boxed, |
2694 | VMPTR_AppDomain vmAppDomain, |
2695 | TypeHandle typeHandle, |
2696 | DebuggerIPCE_ExpandedTypeData * pTypeInfo) |
2697 | { |
2698 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
2699 | pTypeInfo->elementType = GetElementType(typeHandle); |
2700 | |
2701 | switch (pTypeInfo->elementType) |
2702 | { |
2703 | case ELEMENT_TYPE_ARRAY: |
2704 | case ELEMENT_TYPE_SZARRAY: |
2705 | GetArrayTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2706 | break; |
2707 | |
2708 | case ELEMENT_TYPE_PTR: |
2709 | case ELEMENT_TYPE_BYREF: |
2710 | GetPtrTypeInfo(boxed, typeHandle, pTypeInfo, pAppDomain); |
2711 | break; |
2712 | |
2713 | case ELEMENT_TYPE_VALUETYPE: |
2714 | if (boxed == OnlyPrimitivesUnboxed || boxed == AllBoxed) |
2715 | { |
2716 | pTypeInfo->elementType = ELEMENT_TYPE_CLASS; |
2717 | } |
2718 | GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2719 | break; |
2720 | |
2721 | case ELEMENT_TYPE_CLASS: |
2722 | GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2723 | break; |
2724 | |
2725 | case ELEMENT_TYPE_FNPTR: |
2726 | GetFnPtrTypeInfo(boxed, typeHandle, pTypeInfo, pAppDomain); |
2727 | break; |
2728 | default: |
2729 | if (boxed == AllBoxed) |
2730 | { |
2731 | pTypeInfo->elementType = ELEMENT_TYPE_CLASS; |
2732 | GetClassTypeInfo(typeHandle, pTypeInfo, pAppDomain); |
2733 | } |
2734 | // else the element type is sufficient |
2735 | break; |
2736 | } |
2737 | LOG((LF_CORDB, LL_INFO10000, "D::THTETI: converted left-side type handle to expanded right-side type info, pTypeInfo->ClassTypeData.typeHandle = 0x%08x.\n" , pTypeInfo->ClassTypeData.typeHandle.GetRawPtr())); |
2738 | return; |
2739 | } // DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo |
2740 | |
2741 | // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. |
2742 | VMPTR_TypeHandle DacDbiInterfaceImpl::GetTypeHandle(VMPTR_Module vmModule, |
2743 | mdTypeDef metadataToken) |
2744 | { |
2745 | DD_ENTER_MAY_THROW; |
2746 | Module* pModule = vmModule.GetDacPtr(); |
2747 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
2748 | |
2749 | TypeHandle th = ClassLoader::LookupTypeDefOrRefInModule(pModule, metadataToken); |
2750 | if (th.IsNull()) |
2751 | { |
2752 | LOG((LF_CORDB, LL_INFO10000, "D::GTH: class isn't loaded.\n" )); |
2753 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
2754 | } |
2755 | |
2756 | vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); |
2757 | return vmTypeHandle; |
2758 | } |
2759 | |
2760 | // DacDbi API: GetAndSendApproxTypeHandle finds the type handle for the layout of the instance fields of an |
2761 | // instantiated type if it is available. |
2762 | VMPTR_TypeHandle DacDbiInterfaceImpl::GetApproxTypeHandle(TypeInfoList * pTypeData) |
2763 | { |
2764 | DD_ENTER_MAY_THROW; |
2765 | |
2766 | LOG((LF_CORDB, LL_INFO10000, "D::GATH: getting info.\n" )); |
2767 | |
2768 | |
2769 | TypeDataWalk walk(&((*pTypeData)[0]), pTypeData->Count()); |
2770 | TypeHandle typeHandle = walk.ReadLoadedTypeHandle(TypeDataWalk::kGetCanonical); |
2771 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
2772 | |
2773 | vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2774 | if (!typeHandle.IsNull()) |
2775 | { |
2776 | vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2777 | } |
2778 | else |
2779 | { |
2780 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
2781 | } |
2782 | |
2783 | LOG((LF_CORDB, LL_INFO10000, |
2784 | "D::GATH: sending result, result = 0x%0x8\n" , |
2785 | typeHandle)); |
2786 | return vmTypeHandle; |
2787 | } // DacDbiInterfaceImpl::GetApproxTypeHandle |
2788 | |
2789 | // DacDbiInterface API: Get the exact type handle from type data |
2790 | HRESULT DacDbiInterfaceImpl::GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData * pTypeData, |
2791 | ArgInfoList * pArgInfo, |
2792 | VMPTR_TypeHandle& vmTypeHandle) |
2793 | { |
2794 | DD_ENTER_MAY_THROW; |
2795 | |
2796 | LOG((LF_CORDB, LL_INFO10000, "D::GETH: getting info.\n" )); |
2797 | |
2798 | HRESULT hr = S_OK; |
2799 | |
2800 | EX_TRY |
2801 | { |
2802 | vmTypeHandle = vmTypeHandle.NullPtr(); |
2803 | |
2804 | // convert the type information to a type handle |
2805 | TypeHandle typeHandle = ExpandedTypeInfoToTypeHandle(pTypeData, pArgInfo); |
2806 | _ASSERTE(!typeHandle.IsNull()); |
2807 | vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); |
2808 | } |
2809 | EX_CATCH_HRESULT(hr); |
2810 | |
2811 | return hr; |
2812 | } // DacDbiInterfaceImpl::GetExactTypeHandle |
2813 | |
2814 | // Retrieve the generic type params for a given MethodDesc. This function is specifically |
2815 | // for stackwalking because it requires the generic type token on the stack. |
2816 | void DacDbiInterfaceImpl::GetMethodDescParams( |
2817 | VMPTR_AppDomain vmAppDomain, |
2818 | VMPTR_MethodDesc vmMethodDesc, |
2819 | GENERICS_TYPE_TOKEN genericsToken, |
2820 | UINT32 * pcGenericClassTypeParams, |
2821 | TypeParamsList * pGenericTypeParams) |
2822 | { |
2823 | DD_ENTER_MAY_THROW; |
2824 | |
2825 | if (vmAppDomain.IsNull() || vmMethodDesc.IsNull()) |
2826 | { |
2827 | ThrowHR(E_INVALIDARG); |
2828 | } |
2829 | |
2830 | _ASSERTE((pcGenericClassTypeParams != NULL) && (pGenericTypeParams != NULL)); |
2831 | |
2832 | MethodDesc * pMD = vmMethodDesc.GetDacPtr(); |
2833 | |
2834 | // Retrieve the number of type parameters for the class and |
2835 | // the number of type parameters for the method itself. |
2836 | // For example, the method Foo<T, U>::Bar<V>() has 2 class type parameters and 1 method type parameters. |
2837 | UINT32 cGenericClassTypeParams = pMD->GetNumGenericClassArgs(); |
2838 | UINT32 cGenericMethodTypeParams = pMD->GetNumGenericMethodArgs(); |
2839 | UINT32 cTotalGenericTypeParams = cGenericClassTypeParams + cGenericMethodTypeParams; |
2840 | |
2841 | // Set the out parameter. |
2842 | *pcGenericClassTypeParams = cGenericClassTypeParams; |
2843 | |
2844 | TypeHandle thSpecificClass; |
2845 | MethodDesc * pSpecificMethod; |
2846 | |
2847 | // Try to retrieve a more specific MethodDesc and TypeHandle via the generics type token. |
2848 | // The generics token is not always guaranteed to be available. |
2849 | // For example, it may be unavailable in prologs and epilogs. |
2850 | // In dumps, not available can also mean a thrown exception for missing memory. |
2851 | BOOL fExact = FALSE; |
2852 | ALLOW_DATATARGET_MISSING_MEMORY( |
2853 | fExact = Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation( |
2854 | pMD, |
2855 | PTR_VOID((TADDR)genericsToken), |
2856 | &thSpecificClass, |
2857 | &pSpecificMethod); |
2858 | ); |
2859 | if (!fExact || |
2860 | !thSpecificClass.GetMethodTable()->SanityCheck() || |
2861 | !pSpecificMethod->GetMethodTable()->SanityCheck()) |
2862 | { |
2863 | // Use the canonical MethodTable and MethodDesc if the exact generics token is not available. |
2864 | thSpecificClass = TypeHandle(pMD->GetMethodTable()); |
2865 | pSpecificMethod = pMD; |
2866 | } |
2867 | |
2868 | // Retrieve the array of class type parameters and the array of method type parameters. |
2869 | Instantiation classInst = pSpecificMethod->GetExactClassInstantiation(thSpecificClass); |
2870 | Instantiation methodInst = pSpecificMethod->GetMethodInstantiation(); |
2871 | |
2872 | _ASSERTE((classInst.IsEmpty()) == (cGenericClassTypeParams == 0)); |
2873 | _ASSERTE((methodInst.IsEmpty()) == (cGenericMethodTypeParams == 0)); |
2874 | |
2875 | // allocate memory for the return array |
2876 | pGenericTypeParams->Alloc(cTotalGenericTypeParams); |
2877 | |
2878 | for (UINT32 i = 0; i < cTotalGenericTypeParams; i++) |
2879 | { |
2880 | // Retrieve the current type parameter depending on the index. |
2881 | TypeHandle thCurrent; |
2882 | if (i < cGenericClassTypeParams) |
2883 | { |
2884 | thCurrent = classInst[i]; |
2885 | } |
2886 | else |
2887 | { |
2888 | thCurrent = methodInst[i - cGenericClassTypeParams]; |
2889 | } |
2890 | |
2891 | // There is the possiblity that we'll get this far with a dump and not fail, but still |
2892 | // not be able to get full info for a particular param. |
2893 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
2894 | { |
2895 | // Fill in the struct using the TypeHandle of the current type parameter if we can. |
2896 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
2897 | vmTypeHandle.SetDacTargetPtr(thCurrent.AsTAddr()); |
2898 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
2899 | vmAppDomain, |
2900 | vmTypeHandle, |
2901 | &((*pGenericTypeParams)[i])); |
2902 | } |
2903 | EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
2904 | { |
2905 | // On failure for a particular type, default it back to System.__Canon. |
2906 | VMPTR_TypeHandle vmTHCanon = VMPTR_TypeHandle::NullPtr(); |
2907 | TypeHandle thCanon = TypeHandle(g_pCanonMethodTableClass); |
2908 | vmTHCanon.SetDacTargetPtr(thCanon.AsTAddr()); |
2909 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
2910 | vmAppDomain, |
2911 | vmTHCanon, |
2912 | &((*pGenericTypeParams)[i])); |
2913 | } |
2914 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
2915 | } |
2916 | } |
2917 | |
2918 | //----------------------------------------------------------------------------- |
2919 | // DacDbiInterfaceImpl::GetClassOrValueTypeHandle |
2920 | // get a typehandle for a class or valuetype from basic type data (metadata token |
2921 | // and domain file). |
2922 | // Arguments: |
2923 | // input: pData - contains the metadata token and domain file |
2924 | // Return value: the type handle for the corresponding type |
2925 | //----------------------------------------------------------------------------- |
2926 | TypeHandle DacDbiInterfaceImpl::GetClassOrValueTypeHandle(DebuggerIPCE_BasicTypeData * pData) |
2927 | { |
2928 | TypeHandle typeHandle; |
2929 | |
2930 | // if we already have a type handle, just return it |
2931 | if (!pData->vmTypeHandle.IsNull()) |
2932 | { |
2933 | typeHandle = TypeHandle::FromPtr(pData->vmTypeHandle.GetDacPtr()); |
2934 | } |
2935 | // otherwise, have the loader look it up using the metadata token and domain file |
2936 | else |
2937 | { |
2938 | DomainFile * pDomainFile = pData->vmDomainFile.GetDacPtr(); |
2939 | Module * pModule = pDomainFile->GetModule(); |
2940 | |
2941 | typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, pData->metadataToken); |
2942 | if (typeHandle.IsNull()) |
2943 | { |
2944 | LOG((LF_CORDB, LL_INFO10000, "D::BTITTH: class isn't loaded.\n" )); |
2945 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
2946 | } |
2947 | |
2948 | _ASSERTE(typeHandle.GetNumGenericArgs() == 0); |
2949 | } |
2950 | |
2951 | return typeHandle; |
2952 | |
2953 | } // DacDbiInterfaceImpl::GetClassOrValueTypeHandle |
2954 | |
2955 | //----------------------------------------------------------------------------- |
2956 | // DacDbiInterfaceImpl::GetExactArrayTypeHandle |
2957 | // get an exact type handle for an array type |
2958 | // Arguments: |
2959 | // input: pTopLevelTypeData - type information for a top-level array type |
2960 | // pArgInfo - contains the following information: |
2961 | // m_genericArgsCount - number of generic parameters for the element type--this should be 1 |
2962 | // m_pGenericArgs - pointer to the generic parameter for the element type--this is |
2963 | // effectively a one-element list. These are the actual parameters |
2964 | // Return Value: the exact type handle for the type |
2965 | //----------------------------------------------------------------------------- |
2966 | TypeHandle DacDbiInterfaceImpl::GetExactArrayTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, |
2967 | ArgInfoList * pArgInfo) |
2968 | { |
2969 | TypeHandle typeArg; |
2970 | |
2971 | _ASSERTE(pArgInfo->Count() == 1); |
2972 | |
2973 | // get the type handle for the element type |
2974 | typeArg = BasicTypeInfoToTypeHandle(&((*pArgInfo)[0])); |
2975 | |
2976 | // get the exact type handle for the array type |
2977 | return FindLoadedArrayType(pTopLevelTypeData->elementType, |
2978 | typeArg, |
2979 | pTopLevelTypeData->ArrayTypeData.arrayRank); |
2980 | |
2981 | } // DacDbiInterfaceImpl::GetExactArrayTypeHandle |
2982 | |
2983 | //----------------------------------------------------------------------------- |
2984 | // DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle |
2985 | // get an exact type handle for a PTR or BYREF type |
2986 | // Arguments: |
2987 | // input: pTopLevelTypeData - type information for the PTR or BYREF type |
2988 | // pArgInfo - contains the following information: |
2989 | // m_genericArgsCount - number of generic parameters for the element type--this should be 1 |
2990 | // m_pGenericArgs - pointer to the generic parameter for the element type--this is |
2991 | // effectively a one-element list. These are the actual parameters |
2992 | // Return Value: the exact type handle for the type |
2993 | //----------------------------------------------------------------------------- |
2994 | TypeHandle DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, |
2995 | ArgInfoList * pArgInfo) |
2996 | { |
2997 | TypeHandle typeArg; |
2998 | _ASSERTE(pArgInfo->Count() == 1); |
2999 | |
3000 | // get the type handle for the referent |
3001 | typeArg = BasicTypeInfoToTypeHandle(&((*pArgInfo)[0])); |
3002 | |
3003 | // get the exact type handle for the PTR or BYREF type |
3004 | return FindLoadedPointerOrByrefType(pTopLevelTypeData->elementType, typeArg); |
3005 | |
3006 | } // DacDbiInterfaceImpl::GetExactPtrOrByRefTypeHandle |
3007 | |
3008 | //----------------------------------------------------------------------------- |
3009 | // DacDbiInterfaceImpl::GetExactClassTypeHandle |
3010 | // get an exact type handle for a CLASS or VALUETYPE type |
3011 | // Arguments: |
3012 | // input: pTopLevelTypeData - type information for the CLASS or VALUETYPE type |
3013 | // pArgInfo - contains the following information: |
3014 | // m_genericArgsCount - number of generic parameters for the class |
3015 | // m_pGenericArgs - list of generic parameters for the class--these |
3016 | // are the actual parameters |
3017 | // Return Value: the exact type handle for the type |
3018 | //----------------------------------------------------------------------------- |
3019 | TypeHandle DacDbiInterfaceImpl::GetExactClassTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, |
3020 | ArgInfoList * pArgInfo) |
3021 | { |
3022 | Module * pModule = pTopLevelTypeData->ClassTypeData.vmModule.GetDacPtr(); |
3023 | int argCount = pArgInfo->Count(); |
3024 | |
3025 | TypeHandle typeConstructor = |
3026 | ClassLoader::LookupTypeDefOrRefInModule(pModule, pTopLevelTypeData->ClassTypeData.metadataToken); |
3027 | |
3028 | // If we can't find the class, throw the appropriate HR. Note: if the class is not a value class and |
3029 | // the class is also not restored, then we must pretend that the class is still not loaded. We are gonna let |
3030 | // unrestored value classes slide, though, and special case access to the class's parent below. |
3031 | if (typeConstructor.IsNull()) |
3032 | { |
3033 | LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: class isn't loaded.\n" )); |
3034 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3035 | } |
3036 | |
3037 | // if there are no generic parameters, we already have the correct type handle |
3038 | if (argCount == 0) |
3039 | { |
3040 | return typeConstructor; |
3041 | } |
3042 | |
3043 | // we have generic parameters--first validate we have a number consistent with the list |
3044 | // of parameters we received |
3045 | if ((unsigned int)argCount != typeConstructor.GetNumGenericArgs()) |
3046 | { |
3047 | LOG((LF_CORDB, LL_INFO10000, |
3048 | "D::ETITTH: wrong number of type parameters, %d given, %d expected\n" , |
3049 | argCount, typeConstructor.GetNumGenericArgs())); |
3050 | _ASSERTE((unsigned int)argCount == typeConstructor.GetNumGenericArgs()); |
3051 | ThrowHR(E_FAIL); |
3052 | } |
3053 | |
3054 | // now we allocate a list to store the type handles for each parameter |
3055 | S_UINT32 allocSize = S_UINT32(argCount) * S_UINT32(sizeof(TypeHandle)); |
3056 | if (allocSize.IsOverflow()) |
3057 | { |
3058 | ThrowHR(E_OUTOFMEMORY); |
3059 | } |
3060 | |
3061 | NewHolder<TypeHandle> pInst(new TypeHandle[allocSize.Value()]); |
3062 | |
3063 | // convert the type information for each parameter to its corresponding type handle |
3064 | // and store it in the list |
3065 | for (unsigned int i = 0; i < (unsigned int)argCount; i++) |
3066 | { |
3067 | pInst[i] = BasicTypeInfoToTypeHandle(&((*pArgInfo)[i])); |
3068 | } |
3069 | |
3070 | // Finally, we find the type handle corresponding to this particular instantiation |
3071 | return FindLoadedInstantiation(typeConstructor.GetModule(), |
3072 | typeConstructor.GetCl(), |
3073 | argCount, |
3074 | pInst); |
3075 | |
3076 | } // DacDbiInterfaceImpl::GetExactClassTypeHandle |
3077 | |
3078 | //----------------------------------------------------------------------------- |
3079 | // DacDbiInterfaceImpl::GetExactFnPtrTypeHandle |
3080 | // get an exact type handle for a FNPTR type |
3081 | // Arguments: |
3082 | // input: pArgInfo - Contains the following information: |
3083 | // m_genericArgsCount - number of generic parameters for the referent |
3084 | // m_pGenericArgs - list of generic parameters for the referent--these |
3085 | // are the actual parameters for the function signature |
3086 | // Return Value: the exact type handle for the type |
3087 | //----------------------------------------------------------------------------- |
3088 | TypeHandle DacDbiInterfaceImpl::GetExactFnPtrTypeHandle(ArgInfoList * pArgInfo) |
3089 | { |
3090 | // allocate a list to store the type handles for each parameter |
3091 | S_UINT32 allocSize = S_UINT32(pArgInfo->Count()) * S_UINT32(sizeof(TypeHandle)); |
3092 | if( allocSize.IsOverflow() ) |
3093 | { |
3094 | ThrowHR(E_OUTOFMEMORY); |
3095 | } |
3096 | NewHolder<TypeHandle> pInst(new TypeHandle[allocSize.Value()]); |
3097 | |
3098 | // convert the type information for each parameter to its corresponding type handle |
3099 | // and store it in the list |
3100 | for (int i = 0; i < pArgInfo->Count(); i++) |
3101 | { |
3102 | pInst[i] = BasicTypeInfoToTypeHandle(&((*pArgInfo)[i])); |
3103 | } |
3104 | |
3105 | // find the type handle corresponding to this particular FNPTR |
3106 | return FindLoadedFnptrType(pArgInfo->Count(), pInst); |
3107 | } // DacDbiInterfaceImpl::GetExactFnPtrTypeHandle |
3108 | |
3109 | //----------------------------------------------------------------------------- |
3110 | // DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle |
3111 | // Convert basic type info for a type parameter that came from a top-level type to |
3112 | // the corresponding type handle. If the type parameter is an array or pointer |
3113 | // type, we simply extract the LS type handle from the VMPTR_TypeHandle that is |
3114 | // part of the type information. If the type parameter is a class or value type, |
3115 | // we use the metadata token and domain file in the type info to look up the |
3116 | // appropriate type handle. If the type parameter is any other types, we get the |
3117 | // type handle by having the loader look up the type handle for the element type. |
3118 | // Arguments: |
3119 | // input: pArgTypeData - basic type information for the type. |
3120 | // Return Value: the type handle for the type. |
3121 | //----------------------------------------------------------------------------- |
3122 | TypeHandle DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle(DebuggerIPCE_BasicTypeData * pArgTypeData) |
3123 | { |
3124 | LOG((LF_CORDB, LL_INFO10000, |
3125 | "D::BTITTH: expanding basic right-side type to left-side type, ELEMENT_TYPE: %d.\n" , |
3126 | pArgTypeData->elementType)); |
3127 | TypeHandle typeHandle = TypeHandle(); |
3128 | |
3129 | switch (pArgTypeData->elementType) |
3130 | { |
3131 | case ELEMENT_TYPE_ARRAY: |
3132 | case ELEMENT_TYPE_SZARRAY: |
3133 | case ELEMENT_TYPE_PTR: |
3134 | case ELEMENT_TYPE_BYREF: |
3135 | case ELEMENT_TYPE_FNPTR: |
3136 | _ASSERTE(!pArgTypeData->vmTypeHandle.IsNull()); |
3137 | typeHandle = TypeHandle::FromPtr(pArgTypeData->vmTypeHandle.GetDacPtr()); |
3138 | break; |
3139 | |
3140 | case ELEMENT_TYPE_CLASS: |
3141 | case ELEMENT_TYPE_VALUETYPE: |
3142 | typeHandle = GetClassOrValueTypeHandle(pArgTypeData); |
3143 | break; |
3144 | |
3145 | default: |
3146 | typeHandle = FindLoadedElementType(pArgTypeData->elementType); |
3147 | break; |
3148 | } |
3149 | if (typeHandle.IsNull()) |
3150 | { |
3151 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3152 | } |
3153 | return typeHandle; |
3154 | } // DacDbiInterfaceImpl::BasicTypeInfoToTypeHandle |
3155 | |
3156 | |
3157 | //----------------------------------------------------------------------------- |
3158 | // DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle |
3159 | // Convert type information for a top-level type to an exact type handle. This |
3160 | // information includes information about the element type if the top-level type is |
3161 | // an array type, the referent if the top-level type is a pointer type, or actual |
3162 | // parameters if the top-level type is a generic class or value type. |
3163 | // Arguments: |
3164 | // input: pTopLevelTypeData - type information for the top-level type |
3165 | // pArgInfo - contains the following information: |
3166 | // m_genericArtsCount - number of parameters |
3167 | // m_pGenericArgs - list of actual parameters |
3168 | // Return Value: the exact type handle corresponding to the type represented by |
3169 | // pTopLevelTypeData |
3170 | //----------------------------------------------------------------------------- |
3171 | TypeHandle DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle(DebuggerIPCE_ExpandedTypeData * pTopLevelTypeData, |
3172 | ArgInfoList * pArgInfo) |
3173 | { |
3174 | WRAPPER_NO_CONTRACT; |
3175 | |
3176 | LOG((LF_CORDB, LL_INFO10000, |
3177 | "D::ETITTH: expanding right-side type to left-side type, ELEMENT_TYPE: %d.\n" , |
3178 | pData->elementType)); |
3179 | |
3180 | TypeHandle typeHandle = TypeHandle(); |
3181 | // depending on the top-level type, get the type handle incorporating information about any type arguments |
3182 | switch (pTopLevelTypeData->elementType) |
3183 | { |
3184 | case ELEMENT_TYPE_ARRAY: |
3185 | case ELEMENT_TYPE_SZARRAY: |
3186 | typeHandle = GetExactArrayTypeHandle(pTopLevelTypeData, pArgInfo); |
3187 | break; |
3188 | |
3189 | case ELEMENT_TYPE_PTR: |
3190 | case ELEMENT_TYPE_BYREF: |
3191 | typeHandle = GetExactPtrOrByRefTypeHandle(pTopLevelTypeData, pArgInfo); |
3192 | break; |
3193 | |
3194 | case ELEMENT_TYPE_CLASS: |
3195 | case ELEMENT_TYPE_VALUETYPE: |
3196 | typeHandle = GetExactClassTypeHandle(pTopLevelTypeData, pArgInfo); |
3197 | break; |
3198 | case ELEMENT_TYPE_FNPTR: |
3199 | typeHandle = GetExactFnPtrTypeHandle(pArgInfo); |
3200 | break; |
3201 | default: |
3202 | typeHandle = FindLoadedElementType(pTopLevelTypeData->elementType); |
3203 | break; |
3204 | } // end switch (pData->elementType) |
3205 | |
3206 | if (typeHandle.IsNull()) |
3207 | { |
3208 | // This may fail because there are cases when a type can be used (and so visible to the |
3209 | // debugger), but not yet loaded to the point of being available in the EETypeHashTable. |
3210 | // For example, generic value types (without explicit constructors) may not need their |
3211 | // exact instantiation type to be loaded in order to be used as a field of an object |
3212 | // created on the heap |
3213 | LOG((LF_CORDB, LL_INFO10000, "D::ETITTH: type isn't loaded.\n" )); |
3214 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3215 | } |
3216 | return typeHandle; |
3217 | } // DacDbiInterfaceImpl::ExpandedTypeInfoToTypeHandle |
3218 | |
3219 | // ---------------------------------------------------------------------------- |
3220 | // DacDbi API: GetThreadStaticAddress |
3221 | // Get the target field address of a thread local static. |
3222 | // |
3223 | // Notes: |
3224 | // The address is constant and could be cached. |
3225 | // |
3226 | // This can commonly fail, in which case, it will return NULL. |
3227 | // ---------------------------------------------------------------------------- |
3228 | CORDB_ADDRESS DacDbiInterfaceImpl::GetThreadStaticAddress(VMPTR_FieldDesc vmField, |
3229 | VMPTR_Thread vmRuntimeThread) |
3230 | { |
3231 | DD_ENTER_MAY_THROW; |
3232 | |
3233 | Thread * pRuntimeThread = vmRuntimeThread.GetDacPtr(); |
3234 | PTR_FieldDesc pFieldDesc = vmField.GetDacPtr(); |
3235 | TADDR fieldAddress = NULL; |
3236 | |
3237 | _ASSERTE(pRuntimeThread != NULL); |
3238 | |
3239 | // Find out whether the field is thread local and get its address. |
3240 | if (pFieldDesc->IsThreadStatic()) |
3241 | { |
3242 | fieldAddress = pRuntimeThread->GetStaticFieldAddrNoCreate(pFieldDesc); |
3243 | } |
3244 | else |
3245 | { |
3246 | // In case we have more special cases added later, this will allow us to notice the need to |
3247 | // update this function. |
3248 | ThrowHR(E_NOTIMPL); |
3249 | } |
3250 | return fieldAddress; |
3251 | |
3252 | } // DacDbiInterfaceImpl::GetThreadStaticAddress |
3253 | |
3254 | // Get the target field address of a collectible types static. |
3255 | CORDB_ADDRESS DacDbiInterfaceImpl::GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField, |
3256 | VMPTR_AppDomain vmAppDomain) |
3257 | { |
3258 | DD_ENTER_MAY_THROW; |
3259 | |
3260 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
3261 | PTR_FieldDesc pFieldDesc = vmField.GetDacPtr(); |
3262 | _ASSERTE(pAppDomain != NULL); |
3263 | |
3264 | // |
3265 | // Verify this field is of the right type |
3266 | // |
3267 | if(!pFieldDesc->IsStatic() || |
3268 | pFieldDesc->IsSpecialStatic()) |
3269 | { |
3270 | _ASSERTE(!"BUG: Unsupported static field type for collectible types" ); |
3271 | } |
3272 | |
3273 | // |
3274 | // Check that the data is available |
3275 | // |
3276 | /* TODO: Ideally we should be checking if the class is allocated first, however |
3277 | we don't appear to be doing this even for non-collectible statics and |
3278 | we have never seen an issue. |
3279 | */ |
3280 | |
3281 | // |
3282 | // Get the address |
3283 | // |
3284 | PTR_VOID base = pFieldDesc->GetBaseInDomain(pAppDomain); |
3285 | if (base == PTR_NULL) |
3286 | { |
3287 | return PTR_HOST_TO_TADDR(NULL); |
3288 | } |
3289 | |
3290 | // |
3291 | // Store the result and return |
3292 | // |
3293 | PTR_VOID addr = pFieldDesc->GetStaticAddressHandle(base); |
3294 | return PTR_TO_TADDR(addr); |
3295 | |
3296 | } // DacDbiInterfaceImpl::GetCollectibleTypeStaticAddress |
3297 | |
3298 | // DacDbi API: GetTypeHandleParams |
3299 | // - gets the necessary data for a type handle, i.e. its type parameters, e.g. "String" and "List<int>" from the type handle |
3300 | // for "Dict<String,List<int>>", and sends it back to the right side. |
3301 | // - pParams is allocated and initialized by this function |
3302 | // - This should not fail except for OOM |
3303 | void DacDbiInterfaceImpl::GetTypeHandleParams(VMPTR_AppDomain vmAppDomain, |
3304 | VMPTR_TypeHandle vmTypeHandle, |
3305 | TypeParamsList * pParams) |
3306 | { |
3307 | DD_ENTER_MAY_THROW |
3308 | |
3309 | TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); |
3310 | LOG((LF_CORDB, LL_INFO10000, "D::GTHP: getting type parameters for 0x%08x 0x%0x8.\n" , |
3311 | vmAppDomain.GetDacPtr(), typeHandle.AsPtr())); |
3312 | |
3313 | |
3314 | // Find the class given its type handle. |
3315 | _ASSERTE(pParams->IsEmpty()); |
3316 | pParams->Alloc(typeHandle.GetNumGenericArgs()); |
3317 | |
3318 | // collect type information for each type parameter |
3319 | for (int i = 0; i < pParams->Count(); ++i) |
3320 | { |
3321 | VMPTR_TypeHandle thInst = VMPTR_TypeHandle::NullPtr(); |
3322 | thInst.SetDacTargetPtr(typeHandle.GetInstantiation()[i].AsTAddr()); |
3323 | |
3324 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
3325 | vmAppDomain, |
3326 | thInst, |
3327 | &((*pParams)[i])); |
3328 | } |
3329 | |
3330 | LOG((LF_CORDB, LL_INFO10000, "D::GTHP: sending result" )); |
3331 | } // DacDbiInterfaceImpl::GetTypeHandleParams |
3332 | |
3333 | //----------------------------------------------------------------------------- |
3334 | // DacDbi API: GetSimpleType |
3335 | // gets the metadata token and domain file corresponding to a simple type |
3336 | //----------------------------------------------------------------------------- |
3337 | void DacDbiInterfaceImpl::GetSimpleType(VMPTR_AppDomain vmAppDomain, |
3338 | CorElementType simpleType, |
3339 | mdTypeDef *pMetadataToken, |
3340 | VMPTR_Module *pVmModule, |
3341 | VMPTR_DomainFile *pVmDomainFile) |
3342 | { |
3343 | DD_ENTER_MAY_THROW; |
3344 | |
3345 | AppDomain *pAppDomain = vmAppDomain.GetDacPtr(); |
3346 | |
3347 | // if we fail to get either a valid type handle or module, we will want to send back |
3348 | // a NULL domain file too, so we'll to preinitialize this here. |
3349 | _ASSERTE(pVmDomainFile != NULL); |
3350 | *pVmDomainFile = VMPTR_DomainFile::NullPtr(); |
3351 | // FindLoadedElementType will return NULL if the type hasn't been loaded yet. |
3352 | TypeHandle typeHandle = FindLoadedElementType(simpleType); |
3353 | |
3354 | if (typeHandle.IsNull()) |
3355 | { |
3356 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3357 | } |
3358 | else |
3359 | { |
3360 | _ASSERTE(pMetadataToken != NULL); |
3361 | *pMetadataToken = typeHandle.GetCl(); |
3362 | |
3363 | Module * pModule = typeHandle.GetModule(); |
3364 | if (pModule == NULL) |
3365 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
3366 | |
3367 | pVmModule->SetHostPtr(pModule); |
3368 | |
3369 | if (pAppDomain) |
3370 | { |
3371 | pVmDomainFile->SetHostPtr(pModule->GetDomainFile(pAppDomain)); |
3372 | if (pVmDomainFile->IsNull()) |
3373 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
3374 | } |
3375 | } |
3376 | |
3377 | LOG((LF_CORDB, LL_INFO10000, "D::STI: sending result.\n" )); |
3378 | } // DacDbiInterfaceImpl::GetSimpleType |
3379 | |
3380 | BOOL DacDbiInterfaceImpl::IsExceptionObject(VMPTR_Object vmObject) |
3381 | { |
3382 | DD_ENTER_MAY_THROW; |
3383 | |
3384 | Object* objPtr = vmObject.GetDacPtr(); |
3385 | MethodTable* pMT = objPtr->GetMethodTable(); |
3386 | |
3387 | return IsExceptionObject(pMT); |
3388 | } |
3389 | |
3390 | BOOL DacDbiInterfaceImpl::IsExceptionObject(MethodTable* pMT) |
3391 | { |
3392 | PTR_MethodTable pExMT = g_pExceptionClass; |
3393 | |
3394 | TADDR targetMT = dac_cast<TADDR>(pMT); |
3395 | TADDR exceptionMT = dac_cast<TADDR>(pExMT); |
3396 | |
3397 | do |
3398 | { |
3399 | if (targetMT == exceptionMT) |
3400 | return TRUE; |
3401 | |
3402 | pMT = pMT->GetParentMethodTable(); |
3403 | targetMT = dac_cast<TADDR>(pMT); |
3404 | } while (pMT); |
3405 | |
3406 | return FALSE; |
3407 | } |
3408 | |
3409 | void DacDbiInterfaceImpl::GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames) |
3410 | { |
3411 | DD_ENTER_MAY_THROW; |
3412 | |
3413 | PTR_Object objPtr = vmObject.GetDacPtr(); |
3414 | |
3415 | #ifdef _DEBUG |
3416 | // ensure we have an Exception object |
3417 | MethodTable* pMT = objPtr->GetMethodTable(); |
3418 | _ASSERTE(IsExceptionObject(pMT)); |
3419 | #endif |
3420 | |
3421 | OBJECTREF objRef = ObjectToOBJECTREF(objPtr); |
3422 | |
3423 | DebugStackTrace::GetStackFramesData stackFramesData; |
3424 | |
3425 | stackFramesData.pDomain = NULL; |
3426 | stackFramesData.skip = 0; |
3427 | stackFramesData.NumFramesRequested = 0; |
3428 | |
3429 | DebugStackTrace::GetStackFramesFromException(&objRef, &stackFramesData); |
3430 | |
3431 | INT32 dacStackFramesLength = stackFramesData.cElements; |
3432 | |
3433 | if (dacStackFramesLength > 0) |
3434 | { |
3435 | dacStackFrames.Alloc(dacStackFramesLength); |
3436 | |
3437 | for (INT32 index = 0; index < dacStackFramesLength; ++index) |
3438 | { |
3439 | DebugStackTrace::DebugStackTraceElement const& currentElement = stackFramesData.pElements[index]; |
3440 | DacExceptionCallStackData& currentFrame = dacStackFrames[index]; |
3441 | |
3442 | Module* pModule = currentElement.pFunc->GetModule(); |
3443 | BaseDomain* pBaseDomain = currentElement.pFunc->GetAssembly()->GetDomain(); |
3444 | |
3445 | AppDomain* pDomain = NULL; |
3446 | DomainFile* pDomainFile = NULL; |
3447 | |
3448 | pDomain = pBaseDomain->AsAppDomain(); |
3449 | |
3450 | _ASSERTE(pDomain != NULL); |
3451 | |
3452 | pDomainFile = pModule->FindDomainFile(pDomain); |
3453 | _ASSERTE(pDomainFile != NULL); |
3454 | |
3455 | currentFrame.vmAppDomain.SetHostPtr(pDomain); |
3456 | currentFrame.vmDomainFile.SetHostPtr(pDomainFile); |
3457 | currentFrame.ip = currentElement.ip; |
3458 | currentFrame.methodDef = currentElement.pFunc->GetMemberDef(); |
3459 | currentFrame.isLastForeignExceptionFrame = currentElement.fIsLastFrameFromForeignStackTrace; |
3460 | } |
3461 | } |
3462 | } |
3463 | |
3464 | #ifdef FEATURE_COMINTEROP |
3465 | |
3466 | PTR_RCW GetRcwFromVmptrObject(VMPTR_Object vmObject) |
3467 | { |
3468 | PTR_RCW pRCW = NULL; |
3469 | |
3470 | Object* objPtr = vmObject.GetDacPtr(); |
3471 | |
3472 | PTR_SyncBlock pSyncBlock = NULL; |
3473 | pSyncBlock = objPtr->PassiveGetSyncBlock(); |
3474 | if (pSyncBlock == NULL) |
3475 | return pRCW; |
3476 | |
3477 | PTR_InteropSyncBlockInfo pInfo = NULL; |
3478 | pInfo = pSyncBlock->GetInteropInfoNoCreate(); |
3479 | if (pInfo == NULL) |
3480 | return pRCW; |
3481 | |
3482 | pRCW = dac_cast<PTR_RCW>(pInfo->DacGetRawRCW()); |
3483 | |
3484 | return pRCW; |
3485 | } |
3486 | |
3487 | #endif |
3488 | |
3489 | BOOL DacDbiInterfaceImpl::IsRcw(VMPTR_Object vmObject) |
3490 | { |
3491 | #ifdef FEATURE_COMINTEROP |
3492 | DD_ENTER_MAY_THROW; |
3493 | return GetRcwFromVmptrObject(vmObject) != NULL; |
3494 | #else |
3495 | return FALSE; |
3496 | #endif // FEATURE_COMINTEROP |
3497 | |
3498 | } |
3499 | |
3500 | void DacDbiInterfaceImpl::GetRcwCachedInterfaceTypes( |
3501 | VMPTR_Object vmObject, |
3502 | VMPTR_AppDomain vmAppDomain, |
3503 | BOOL bIInspectableOnly, |
3504 | DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pDacInterfaces) |
3505 | { |
3506 | #ifdef FEATURE_COMINTEROP |
3507 | |
3508 | DD_ENTER_MAY_THROW; |
3509 | |
3510 | Object* objPtr = vmObject.GetDacPtr(); |
3511 | |
3512 | InlineSArray<PTR_MethodTable, INTERFACE_ENTRY_CACHE_SIZE> rgMT; |
3513 | |
3514 | PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject); |
3515 | if (pRCW != NULL) |
3516 | { |
3517 | pRCW->GetCachedInterfaceTypes(bIInspectableOnly, &rgMT); |
3518 | |
3519 | pDacInterfaces->Alloc(rgMT.GetCount()); |
3520 | |
3521 | for (COUNT_T i = 0; i < rgMT.GetCount(); ++i) |
3522 | { |
3523 | // There is the possiblity that we'll get this far with a dump and not fail, but still |
3524 | // not be able to get full info for a particular param. |
3525 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3526 | { |
3527 | // Fill in the struct using the current TypeHandle |
3528 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
3529 | TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(rgMT[i])); |
3530 | vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); |
3531 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
3532 | vmAppDomain, |
3533 | vmTypeHandle, |
3534 | &((*pDacInterfaces)[i])); |
3535 | } |
3536 | EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3537 | { |
3538 | // On failure for a particular type, default it to NULL. |
3539 | (*pDacInterfaces)[i].elementType = ELEMENT_TYPE_END; |
3540 | } |
3541 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3542 | |
3543 | } |
3544 | |
3545 | } |
3546 | else |
3547 | #endif // FEATURE_COMINTEROP |
3548 | { |
3549 | pDacInterfaces->Alloc(0); |
3550 | } |
3551 | } |
3552 | |
3553 | void DacDbiInterfaceImpl::GetRcwCachedInterfacePointers( |
3554 | VMPTR_Object vmObject, |
3555 | BOOL bIInspectableOnly, |
3556 | DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) |
3557 | { |
3558 | #ifdef FEATURE_COMINTEROP |
3559 | |
3560 | DD_ENTER_MAY_THROW; |
3561 | |
3562 | Object* objPtr = vmObject.GetDacPtr(); |
3563 | |
3564 | InlineSArray<TADDR, INTERFACE_ENTRY_CACHE_SIZE> rgUnks; |
3565 | |
3566 | PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject); |
3567 | if (pRCW != NULL) |
3568 | { |
3569 | pRCW->GetCachedInterfacePointers(bIInspectableOnly, &rgUnks); |
3570 | |
3571 | pDacItfPtrs->Alloc(rgUnks.GetCount()); |
3572 | |
3573 | for (COUNT_T i = 0; i < rgUnks.GetCount(); ++i) |
3574 | { |
3575 | (*pDacItfPtrs)[i] = (CORDB_ADDRESS)(rgUnks[i]); |
3576 | } |
3577 | |
3578 | } |
3579 | else |
3580 | #endif // FEATURE_COMINTEROP |
3581 | { |
3582 | pDacItfPtrs->Alloc(0); |
3583 | } |
3584 | } |
3585 | |
3586 | void DacDbiInterfaceImpl::GetCachedWinRTTypesForIIDs( |
3587 | VMPTR_AppDomain vmAppDomain, |
3588 | DacDbiArrayList<GUID> & iids, |
3589 | OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) |
3590 | { |
3591 | #ifdef FEATURE_COMINTEROP |
3592 | |
3593 | DD_ENTER_MAY_THROW; |
3594 | |
3595 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
3596 | |
3597 | { |
3598 | pTypes->Alloc(iids.Count()); |
3599 | |
3600 | for (int i = 0; i < iids.Count(); ++i) |
3601 | { |
3602 | // There is the possiblity that we'll get this far with a dump and not fail, but still |
3603 | // not be able to get full info for a particular param. |
3604 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3605 | { |
3606 | PTR_MethodTable pMT = pAppDomain->LookupTypeByGuid(iids[i]); |
3607 | |
3608 | // Fill in the struct using the current TypeHandle |
3609 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
3610 | TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(pMT)); |
3611 | vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); |
3612 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
3613 | vmAppDomain, |
3614 | vmTypeHandle, |
3615 | &((*pTypes)[i])); |
3616 | } |
3617 | EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3618 | { |
3619 | // On failure for a particular type, default it to NULL. |
3620 | (*pTypes)[i].elementType = ELEMENT_TYPE_END; |
3621 | } |
3622 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3623 | } |
3624 | } |
3625 | #else // FEATURE_COMINTEROP |
3626 | { |
3627 | pTypes->Alloc(0); |
3628 | } |
3629 | #endif // FEATURE_COMINTEROP |
3630 | } |
3631 | |
3632 | void DacDbiInterfaceImpl::GetCachedWinRTTypes( |
3633 | VMPTR_AppDomain vmAppDomain, |
3634 | OUT DacDbiArrayList<GUID> * pGuids, |
3635 | OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) |
3636 | { |
3637 | #ifdef FEATURE_COMINTEROP |
3638 | |
3639 | DD_ENTER_MAY_THROW; |
3640 | |
3641 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
3642 | |
3643 | InlineSArray<PTR_MethodTable, 32> rgMT; |
3644 | InlineSArray<GUID, 32> rgGuid; |
3645 | |
3646 | { |
3647 | pAppDomain->GetCachedWinRTTypes(&rgMT, &rgGuid, 0, NULL); |
3648 | |
3649 | pTypes->Alloc(rgMT.GetCount()); |
3650 | pGuids->Alloc(rgGuid.GetCount()); |
3651 | |
3652 | for (COUNT_T i = 0; i < rgMT.GetCount(); ++i) |
3653 | { |
3654 | // There is the possiblity that we'll get this far with a dump and not fail, but still |
3655 | // not be able to get full info for a particular param. |
3656 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3657 | { |
3658 | // Fill in the struct using the current TypeHandle |
3659 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
3660 | TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(rgMT[i])); |
3661 | vmTypeHandle.SetDacTargetPtr(th.AsTAddr()); |
3662 | TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, |
3663 | vmAppDomain, |
3664 | vmTypeHandle, |
3665 | &((*pTypes)[i])); |
3666 | } |
3667 | EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3668 | { |
3669 | // On failure for a particular type, default it to NULL. |
3670 | (*pTypes)[i].elementType = ELEMENT_TYPE_END; |
3671 | } |
3672 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER |
3673 | (*pGuids)[i] = rgGuid[i]; |
3674 | |
3675 | } |
3676 | |
3677 | } |
3678 | #else // FEATURE_COMINTEROP |
3679 | { |
3680 | pTypes->Alloc(0); |
3681 | } |
3682 | #endif // FEATURE_COMINTEROP |
3683 | } |
3684 | |
3685 | //----------------------------------------------------------------------------- |
3686 | // DacDbiInterfaceImpl::FindField |
3687 | // Finds information for a particular class field |
3688 | // Arguments: |
3689 | // input: thApprox - type handle for the type to which the field belongs |
3690 | // fldToken - metadata token for the field |
3691 | // Return Value: FieldDesc containing information for the field if found or NULL otherwise |
3692 | //----------------------------------------------------------------------------- |
3693 | PTR_FieldDesc DacDbiInterfaceImpl::FindField(TypeHandle thApprox, mdFieldDef fldToken) |
3694 | { |
3695 | EncApproxFieldDescIterator fdIterator(thApprox.GetMethodTable(), |
3696 | ApproxFieldDescIterator::ALL_FIELDS, |
3697 | FALSE); // don't fixup EnC (we can't, we're stopped) |
3698 | |
3699 | PTR_FieldDesc pCurrentFD; |
3700 | |
3701 | while ((pCurrentFD = fdIterator.Next()) != NULL) |
3702 | { |
3703 | // We're looking for a specific fieldDesc, see if we got it. |
3704 | if (pCurrentFD->GetMemberDef() == fldToken) |
3705 | { |
3706 | return pCurrentFD; |
3707 | } |
3708 | } |
3709 | |
3710 | // we never found it... |
3711 | return NULL; |
3712 | } // DacDbiInterfaceImpl::FindField |
3713 | |
3714 | //----------------------------------------------------------------------------- |
3715 | // DacDbiInterfaceImpl::GetEnCFieldDesc |
3716 | // Get the FieldDesc corresponding to a particular EnC field token |
3717 | // Arguments: |
3718 | // input: pEnCFieldInfo |
3719 | // Return Value: pointer to the FieldDesc that corresponds to the EnC field |
3720 | // Note: this function may throw |
3721 | //----------------------------------------------------------------------------- |
3722 | FieldDesc * DacDbiInterfaceImpl::GetEnCFieldDesc(const EnCHangingFieldInfo * pEnCFieldInfo) |
3723 | { |
3724 | FieldDesc * pFD = NULL; |
3725 | |
3726 | DomainFile * pDomainFile = pEnCFieldInfo->GetObjectTypeData().vmDomainFile.GetDacPtr(); |
3727 | Module * pModule = pDomainFile->GetModule(); |
3728 | |
3729 | // get the type handle for the object |
3730 | TypeHandle typeHandle = ClassLoader::LookupTypeDefOrRefInModule(pModule, |
3731 | pEnCFieldInfo->GetObjectTypeData().metadataToken); |
3732 | if (typeHandle == NULL) |
3733 | { |
3734 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3735 | } |
3736 | // and find the field desc |
3737 | pFD = FindField(typeHandle, pEnCFieldInfo->GetFieldToken()); |
3738 | if (pFD == NULL) |
3739 | { |
3740 | // FieldDesc is not yet available, so can't get EnC field info |
3741 | ThrowHR(CORDBG_E_ENC_HANGING_FIELD); |
3742 | } |
3743 | return pFD; |
3744 | |
3745 | } // DacDbiInterfaceImpl::GetEnCFieldDesc |
3746 | |
3747 | //----------------------------------------------------------------------------- |
3748 | // DacDbiInterfaceImpl::GetPtrToEnCField |
3749 | // Get the address of a field added with EnC. |
3750 | // Arguments: |
3751 | // input: pFD - field desc for the added field |
3752 | // pEnCFieldInfo - information about the new field |
3753 | // Return Value: The field address if the field is available (i.e., it has been accessed) |
3754 | // or NULL otherwise |
3755 | // Note: this function may throw |
3756 | //----------------------------------------------------------------------------- |
3757 | PTR_CBYTE DacDbiInterfaceImpl::GetPtrToEnCField(FieldDesc * pFD, const EnCHangingFieldInfo * pEnCFieldInfo) |
3758 | { |
3759 | #ifndef EnC_SUPPORTED |
3760 | _ASSERTE(!"Trying to get the address of an EnC field where EnC is not supported! " ); |
3761 | return NULL; |
3762 | #else |
3763 | |
3764 | PTR_EditAndContinueModule pEnCModule; |
3765 | DomainFile * pDomainFile = pEnCFieldInfo->GetObjectTypeData().vmDomainFile.GetDacPtr(); |
3766 | Module * pModule = pDomainFile->GetModule(); |
3767 | |
3768 | // make sure we actually have an EditAndContinueModule |
3769 | _ASSERTE(pModule->IsEditAndContinueCapable()); |
3770 | pEnCModule = dac_cast<PTR_EditAndContinueModule>(pModule); |
3771 | |
3772 | // we should also have an EnCFieldDesc |
3773 | _ASSERTE(pFD->IsEnCNew()); |
3774 | EnCFieldDesc * pEnCFieldDesc; |
3775 | pEnCFieldDesc = dac_cast<PTR_EnCFieldDesc>(pFD); |
3776 | |
3777 | // If it hasn't been fixed up yet, then we can't return the pointer. |
3778 | if (pEnCFieldDesc->NeedsFixup()) |
3779 | { |
3780 | ThrowHR(CORDBG_E_ENC_HANGING_FIELD); |
3781 | } |
3782 | // Get a pointer to the field |
3783 | PTR_CBYTE pORField = NULL; |
3784 | |
3785 | PTR_Object pObject = pEnCFieldInfo->GetVmObject().GetDacPtr(); |
3786 | pORField = pEnCModule->ResolveField(ObjectToOBJECTREF(pObject), |
3787 | pEnCFieldDesc); |
3788 | |
3789 | // The field could be absent because the code hasn't accessed it yet. If so, we're not going to add it |
3790 | // since we can't allocate anyway. |
3791 | if (pORField == NULL) |
3792 | { |
3793 | ThrowHR(CORDBG_E_ENC_HANGING_FIELD); |
3794 | } |
3795 | return pORField; |
3796 | #endif // EnC_SUPPORTED |
3797 | } // DacDbiInterfaceImpl::GetPtrToEnCField |
3798 | |
3799 | //----------------------------------------------------------------------------- |
3800 | // DacDbiInterfaceImpl::InitFieldData |
3801 | // Initialize information about a field added with EnC |
3802 | // Arguments : |
3803 | // input: |
3804 | // pFD - provides information about whether the field is static, |
3805 | // the metadata token, etc. |
3806 | // pORField - provides the field address or offset |
3807 | // pEnCFieldData - provides the offset to the fields of the object |
3808 | // output: pFieldData - initialized in accordance with the input information |
3809 | //----------------------------------------------------------------------------- |
3810 | void DacDbiInterfaceImpl::InitFieldData(const FieldDesc * pFD, |
3811 | const PTR_CBYTE pORField, |
3812 | const EnCHangingFieldInfo * pEnCFieldData, |
3813 | FieldData * pFieldData) |
3814 | { |
3815 | |
3816 | pFieldData->ClearFields(); |
3817 | |
3818 | pFieldData->m_fFldIsStatic = (pFD->IsStatic() != 0); |
3819 | pFieldData->m_vmFieldDesc.SetHostPtr(pFD); |
3820 | pFieldData->m_fFldIsTLS = (pFD->IsThreadStatic() == TRUE); |
3821 | pFieldData->m_fldMetadataToken = pFD->GetMemberDef(); |
3822 | pFieldData->m_fFldIsRVA = (pFD->IsRVA() == TRUE); |
3823 | pFieldData->m_fFldIsCollectibleStatic = FALSE; |
3824 | pFieldData->m_fFldStorageAvailable = true; |
3825 | |
3826 | if (pFieldData->m_fFldIsStatic) |
3827 | { |
3828 | //EnC is only supported on regular static fields |
3829 | _ASSERTE(!pFieldData->m_fFldIsTLS); |
3830 | _ASSERTE(!pFieldData->m_fFldIsRVA); |
3831 | |
3832 | // pORField contains the absolute address |
3833 | pFieldData->SetStaticAddress(PTR_TO_TADDR(pORField)); |
3834 | } |
3835 | else |
3836 | { |
3837 | // fldInstanceOffset is computed to work correctly with GetFieldValue |
3838 | // which computes: |
3839 | // addr of pORField = object + pEnCFieldInfo->m_offsetToVars + offsetToFld |
3840 | pFieldData->SetInstanceOffset(PTR_TO_TADDR(pORField) - |
3841 | (PTR_TO_TADDR(pEnCFieldData->GetVmObject().GetDacPtr()) + |
3842 | pEnCFieldData->GetOffsetToVars())); |
3843 | } |
3844 | } // DacDbiInterfaceImpl::InitFieldData |
3845 | |
3846 | |
3847 | // ---------------------------------------------------------------------------- |
3848 | // DacDbi API: GetEnCHangingFieldInfo |
3849 | // After a class has been loaded, if a field has been added via EnC we'll have to jump through |
3850 | // some hoops to get at it (it hangs off the sync block or FieldDesc). |
3851 | // |
3852 | // GENERICS: TODO: this method will need to be modified if we ever support EnC on |
3853 | // generic classes. |
3854 | //----------------------------------------------------------------------------- |
3855 | void DacDbiInterfaceImpl::GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, |
3856 | FieldData * pFieldData, |
3857 | BOOL * pfStatic) |
3858 | { |
3859 | DD_ENTER_MAY_THROW; |
3860 | |
3861 | LOG((LF_CORDB, LL_INFO100000, "DDI::IEnCHFI: Obj:0x%x, objType" |
3862 | ":0x%x, offset:0x%x\n" , pEnCFieldInfo->m_pObject, pEnCFieldInfo->m_objectTypeData.elementType, |
3863 | pEnCFieldInfo->m_offsetToVars)); |
3864 | |
3865 | FieldDesc * pFD = NULL; |
3866 | PTR_CBYTE pORField = NULL; |
3867 | |
3868 | pFD = GetEnCFieldDesc(pEnCFieldInfo); |
3869 | _ASSERTE(pFD->IsEnCNew()); // We shouldn't be here if it wasn't added to an |
3870 | // already loaded class. |
3871 | |
3872 | #ifdef EnC_SUPPORTED |
3873 | pORField = GetPtrToEnCField(pFD, pEnCFieldInfo); |
3874 | #else |
3875 | _ASSERTE(!"We shouldn't be here: EnC not supported" ); |
3876 | #endif // EnC_SUPPORTED |
3877 | |
3878 | InitFieldData(pFD, pORField, pEnCFieldInfo, pFieldData); |
3879 | *pfStatic = (pFD->IsStatic() != 0); |
3880 | |
3881 | } // DacDbiInterfaceImpl::GetEnCHangingFieldInfo |
3882 | |
3883 | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
3884 | |
3885 | |
3886 | void DacDbiInterfaceImpl::GetAssemblyFromDomainAssembly(VMPTR_DomainAssembly vmDomainAssembly, VMPTR_Assembly *vmAssembly) |
3887 | { |
3888 | DD_ENTER_MAY_THROW; |
3889 | |
3890 | _ASSERTE(vmAssembly != NULL); |
3891 | |
3892 | DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); |
3893 | vmAssembly->SetHostPtr(pDomainAssembly->GetAssembly()); |
3894 | } |
3895 | |
3896 | // Determines whether the runtime security system has assigned full-trust to this assembly. |
3897 | BOOL DacDbiInterfaceImpl::IsAssemblyFullyTrusted(VMPTR_DomainAssembly vmDomainAssembly) |
3898 | { |
3899 | DD_ENTER_MAY_THROW; |
3900 | |
3901 | return TRUE; |
3902 | } |
3903 | |
3904 | // Get the full path and file name to the assembly's manifest module. |
3905 | BOOL DacDbiInterfaceImpl::GetAssemblyPath( |
3906 | VMPTR_Assembly vmAssembly, |
3907 | IStringHolder * pStrFilename) |
3908 | { |
3909 | DD_ENTER_MAY_THROW; |
3910 | |
3911 | // Get the manifest module for this assembly |
3912 | Assembly * pAssembly = vmAssembly.GetDacPtr(); |
3913 | Module * pManifestModule = pAssembly->GetManifestModule(); |
3914 | |
3915 | // Get the path for the manifest module. |
3916 | // since we no longer support Win9x, we assume all paths will be in unicode format already |
3917 | const WCHAR * szPath = pManifestModule->GetPath().DacGetRawUnicode(); |
3918 | HRESULT hrStatus = pStrFilename->AssignCopy(szPath); |
3919 | IfFailThrow(hrStatus); |
3920 | |
3921 | if(szPath == NULL || *szPath=='\0') |
3922 | { |
3923 | // The asembly has no (and will never have a) file name, but we didn't really fail |
3924 | return FALSE; |
3925 | } |
3926 | |
3927 | return TRUE; |
3928 | } |
3929 | |
3930 | // DAC/DBI API |
3931 | // Get a resolved type def from a type ref. The type ref may come from a module other than the |
3932 | // referencing module. |
3933 | void DacDbiInterfaceImpl::ResolveTypeReference(const TypeRefData * pTypeRefInfo, |
3934 | TypeRefData * pTargetRefInfo) |
3935 | { |
3936 | DD_ENTER_MAY_THROW; |
3937 | DomainFile * pDomainFile = pTypeRefInfo->vmDomainFile.GetDacPtr(); |
3938 | Module * pReferencingModule = pDomainFile->GetCurrentModule(); |
3939 | BOOL fSuccess = FALSE; |
3940 | |
3941 | // Resolve the type ref |
3942 | // g_pEEInterface->FindLoadedClass is almost what we want, but it isn't guaranteed to work if |
3943 | // the typeRef was originally loaded from a different assembly. Also, we need to ensure that |
3944 | // we can resolve even unloaded types in fully loaded assemblies, so APIs such as |
3945 | // LoadTypeDefOrRefThrowing aren't acceptable. |
3946 | |
3947 | Module * pTargetModule = NULL; |
3948 | mdTypeDef targetTypeDef = mdTokenNil; |
3949 | |
3950 | // The loader won't need to trigger a GC or throw because we've told it not to load anything |
3951 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
3952 | |
3953 | fSuccess = ClassLoader::ResolveTokenToTypeDefThrowing(pReferencingModule, |
3954 | pTypeRefInfo->typeToken, |
3955 | &pTargetModule, |
3956 | &targetTypeDef, |
3957 | Loader::SafeLookup //don't load, no locks/allocations |
3958 | ); |
3959 | if (fSuccess) |
3960 | { |
3961 | _ASSERTE(pTargetModule != NULL); |
3962 | _ASSERTE( TypeFromToken(targetTypeDef) == mdtTypeDef ); |
3963 | |
3964 | AppDomain * pAppDomain = pDomainFile->GetAppDomain(); |
3965 | |
3966 | pTargetRefInfo->vmDomainFile.SetDacTargetPtr(PTR_HOST_TO_TADDR(pTargetModule->GetDomainFile(pAppDomain))); |
3967 | pTargetRefInfo->typeToken = targetTypeDef; |
3968 | } |
3969 | else |
3970 | { |
3971 | // failed - presumably because the target assembly isn't loaded |
3972 | ThrowHR(CORDBG_E_CLASS_NOT_LOADED); |
3973 | } |
3974 | } // DacDbiInterfaceImpl::ResolveTypeReference |
3975 | |
3976 | |
3977 | // Get the full path and file name to the module (if any). |
3978 | BOOL DacDbiInterfaceImpl::GetModulePath(VMPTR_Module vmModule, |
3979 | IStringHolder * pStrFilename) |
3980 | { |
3981 | DD_ENTER_MAY_THROW; |
3982 | |
3983 | Module * pModule = vmModule.GetDacPtr(); |
3984 | PEFile * pFile = pModule->GetFile(); |
3985 | if (pFile != NULL) |
3986 | { |
3987 | if( !pFile->GetPath().IsEmpty() ) |
3988 | { |
3989 | // Module has an on-disk path |
3990 | const WCHAR * szPath = pFile->GetPath().DacGetRawUnicode(); |
3991 | if (szPath == NULL) |
3992 | { |
3993 | szPath = pFile->GetModuleFileNameHint().DacGetRawUnicode(); |
3994 | if (szPath == NULL) |
3995 | { |
3996 | goto NoFileName; |
3997 | } |
3998 | } |
3999 | IfFailThrow(pStrFilename->AssignCopy(szPath)); |
4000 | return TRUE; |
4001 | } |
4002 | } |
4003 | |
4004 | NoFileName: |
4005 | // no filename |
4006 | IfFailThrow(pStrFilename->AssignCopy(W("" ))); |
4007 | return FALSE; |
4008 | } |
4009 | |
4010 | // Get the full path and file name to the ngen image for the module (if any). |
4011 | BOOL DacDbiInterfaceImpl::GetModuleNGenPath(VMPTR_Module vmModule, |
4012 | IStringHolder * pStrFilename) |
4013 | { |
4014 | DD_ENTER_MAY_THROW; |
4015 | #ifdef FEATURE_PREJIT |
4016 | Module * pModule = vmModule.GetDacPtr(); |
4017 | PEFile * pFile = pModule->GetFile(); |
4018 | if (pFile != NULL && pFile->HasNativeImage()) |
4019 | { |
4020 | PEImage * pImage = pFile->GetPersistentNativeImage(); |
4021 | if (pImage != NULL && pImage->IsFile()) |
4022 | { |
4023 | // We have an on-disk ngen image. Return the path. |
4024 | // since we no longer support Win9x, we assume all paths will be in unicode format already |
4025 | const WCHAR * szPath = pImage->GetPath().DacGetRawUnicode(); |
4026 | if (szPath == NULL) |
4027 | { |
4028 | szPath = pFile->GetModuleFileNameHint().DacGetRawUnicode(); |
4029 | if (szPath == NULL) |
4030 | { |
4031 | goto NoFileName; |
4032 | } |
4033 | } |
4034 | IfFailThrow(pStrFilename->AssignCopy(szPath)); |
4035 | return TRUE; |
4036 | } |
4037 | } |
4038 | NoFileName: |
4039 | #endif // FEATURE_PREJIT |
4040 | |
4041 | // no ngen filename |
4042 | IfFailThrow(pStrFilename->AssignCopy(W("" ))); |
4043 | return FALSE; |
4044 | } |
4045 | |
4046 | // Implementation of IDacDbiInterface::GetModuleSimpleName |
4047 | void DacDbiInterfaceImpl::GetModuleSimpleName(VMPTR_Module vmModule, IStringHolder * pStrFilename) |
4048 | { |
4049 | DD_ENTER_MAY_THROW; |
4050 | |
4051 | _ASSERTE(pStrFilename != NULL); |
4052 | |
4053 | Module * pModule = vmModule.GetDacPtr(); |
4054 | LPCUTF8 szNameUtf8 = pModule->GetSimpleName(); |
4055 | |
4056 | SString convert(SString::Utf8, szNameUtf8); |
4057 | IfFailThrow(pStrFilename->AssignCopy(convert.GetUnicode())); |
4058 | } |
4059 | |
4060 | // Helper to intialize a TargetBuffer from a MemoryRange |
4061 | // |
4062 | // Arguments: |
4063 | // memoryRange - memory range. |
4064 | // pTargetBuffer - required out parameter to be initialized to value of memory range. |
4065 | // |
4066 | // Notes: |
4067 | // MemoryRange and TargetBuffer both conceptually describe a single contiguous buffer of memory in the |
4068 | // target. MemoryRange is a VM structure, which can't bleed across the DacDbi boundary. TargetBuffer is |
4069 | // a DacDbi structure, which can cross the DacDbi boundary. |
4070 | void InitTargetBufferFromMemoryRange(const MemoryRange memoryRange, TargetBuffer * pTargetBuffer) |
4071 | { |
4072 | SUPPORTS_DAC; |
4073 | |
4074 | _ASSERTE(pTargetBuffer != NULL); |
4075 | PTR_CVOID p = memoryRange.StartAddress(); |
4076 | CORDB_ADDRESS addr = PTR_TO_CORDB_ADDRESS(PTR_TO_TADDR(p)); |
4077 | |
4078 | _ASSERTE(memoryRange.Size() <= 0xffffffff); |
4079 | pTargetBuffer->Init(addr, (ULONG)memoryRange.Size()); |
4080 | } |
4081 | |
4082 | // Helper to intialize a TargetBuffer (host representation of target) from an SBuffer (target) |
4083 | // |
4084 | // Arguments: |
4085 | // pBuffer - target pointer to a SBuffer structure. If pBuffer is NULL, then target buffer will be empty. |
4086 | // pTargetBuffer - required out pointer to hold buffer description. |
4087 | // |
4088 | // Notes: |
4089 | // PTR_SBuffer and TargetBuffer are both semantically equivalent structures. They both are a pointer and length |
4090 | // describing a buffer in the target address space. (SBufer also has ownership semantics, but for DAC's |
4091 | // read-only nature, that doesn't matter). |
4092 | // Neither of these will actually copy the target buffer into the host without explicit action. |
4093 | // The important difference is that TargetBuffer is a host datastructure and so easier to manipulate. |
4094 | // |
4095 | void InitTargetBufferFromTargetSBuffer(PTR_SBuffer pBuffer, TargetBuffer * pTargetBuffer) |
4096 | { |
4097 | SUPPORTS_DAC; |
4098 | |
4099 | _ASSERTE(pTargetBuffer != NULL); |
4100 | |
4101 | SBuffer * pBufferHost = pBuffer; |
4102 | if (pBufferHost == NULL) |
4103 | { |
4104 | pTargetBuffer->Clear(); |
4105 | return; |
4106 | } |
4107 | |
4108 | MemoryRange m = pBufferHost->DacGetRawBuffer(); |
4109 | InitTargetBufferFromMemoryRange(m, pTargetBuffer); |
4110 | } |
4111 | |
4112 | |
4113 | // Implementation of IDacDbiInterface::GetMetadata |
4114 | void DacDbiInterfaceImpl::GetMetadata(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer) |
4115 | { |
4116 | DD_ENTER_MAY_THROW; |
4117 | |
4118 | pTargetBuffer->Clear(); |
4119 | |
4120 | Module * pModule = vmModule.GetDacPtr(); |
4121 | |
4122 | // Target should only be asking about modules that are visible to debugger. |
4123 | _ASSERTE(pModule->IsVisibleToDebugger()); |
4124 | |
4125 | // For dynamic modules, metadata is stored as an eagerly-serialized buffer hanging off the Reflection Module. |
4126 | if (pModule->IsReflection()) |
4127 | { |
4128 | // Here is the fetch. |
4129 | ReflectionModule * pReflectionModule = pModule->GetReflectionModule(); |
4130 | InitTargetBufferFromTargetSBuffer(pReflectionModule->GetDynamicMetadataBuffer(), pTargetBuffer); |
4131 | } |
4132 | else |
4133 | { |
4134 | PEFile * pFile = pModule->GetFile(); |
4135 | |
4136 | // For non-dynamic modules, metadata is in the pe-image. |
4137 | COUNT_T size; |
4138 | CORDB_ADDRESS address = PTR_TO_CORDB_ADDRESS(dac_cast<TADDR>(pFile->GetLoadedMetadata(&size))); |
4139 | |
4140 | pTargetBuffer->Init(address, (ULONG) size); |
4141 | } |
4142 | |
4143 | if (pTargetBuffer->IsEmpty()) |
4144 | { |
4145 | // We never expect this to happen in a well-behaved scenario. But just in case. |
4146 | ThrowHR(CORDBG_E_MISSING_METADATA); |
4147 | } |
4148 | |
4149 | } |
4150 | |
4151 | // Implementation of IDacDbiInterface::GetSymbolsBuffer |
4152 | void DacDbiInterfaceImpl::GetSymbolsBuffer(VMPTR_Module vmModule, TargetBuffer * pTargetBuffer, SymbolFormat * pSymbolFormat) |
4153 | { |
4154 | DD_ENTER_MAY_THROW; |
4155 | |
4156 | pTargetBuffer->Clear(); |
4157 | *pSymbolFormat = kSymbolFormatNone; |
4158 | |
4159 | Module * pModule = vmModule.GetDacPtr(); |
4160 | |
4161 | // Target should only be asking about modules that are visible to debugger. |
4162 | _ASSERTE(pModule->IsVisibleToDebugger()); |
4163 | |
4164 | PTR_CGrowableStream pStream = pModule->GetInMemorySymbolStream(); |
4165 | if (pStream == NULL) |
4166 | { |
4167 | // Common case is to not have PDBs in-memory. |
4168 | return; |
4169 | } |
4170 | |
4171 | const MemoryRange m = pStream->GetRawBuffer(); |
4172 | if (m.Size() == 0) |
4173 | { |
4174 | // We may be prepared to store symbols (in some particular format) but none are there yet. |
4175 | // We treat this the same as not having any symbols above. |
4176 | return; |
4177 | } |
4178 | InitTargetBufferFromMemoryRange(m, pTargetBuffer); |
4179 | |
4180 | // Set the symbol format appropriately |
4181 | ESymbolFormat symFormat = pModule->GetInMemorySymbolStreamFormat(); |
4182 | switch (symFormat) |
4183 | { |
4184 | case eSymbolFormatPDB: |
4185 | *pSymbolFormat = kSymbolFormatPDB; |
4186 | break; |
4187 | |
4188 | case eSymbolFormatILDB: |
4189 | *pSymbolFormat = kSymbolFormatILDB; |
4190 | break; |
4191 | |
4192 | default: |
4193 | CONSISTENCY_CHECK_MSGF(false, "Unexpected symbol format" ); |
4194 | pTargetBuffer->Clear(); |
4195 | ThrowHR(E_UNEXPECTED); |
4196 | } |
4197 | } |
4198 | |
4199 | |
4200 | |
4201 | void DacDbiInterfaceImpl::GetModuleForDomainFile(VMPTR_DomainFile vmDomainFile, OUT VMPTR_Module * pModule) |
4202 | { |
4203 | DD_ENTER_MAY_THROW; |
4204 | |
4205 | _ASSERTE(pModule != NULL); |
4206 | |
4207 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
4208 | pModule->SetHostPtr(pDomainFile->GetModule()); |
4209 | } |
4210 | |
4211 | |
4212 | // Implement IDacDbiInterface::GetDomainFileData |
4213 | void DacDbiInterfaceImpl::GetDomainFileData(VMPTR_DomainFile vmDomainFile, DomainFileInfo * pData) |
4214 | { |
4215 | DD_ENTER_MAY_THROW; |
4216 | |
4217 | _ASSERTE(pData != NULL); |
4218 | |
4219 | ZeroMemory(pData, sizeof(*pData)); |
4220 | |
4221 | DomainFile * pDomainFile = vmDomainFile.GetDacPtr(); |
4222 | AppDomain * pAppDomain = pDomainFile->GetAppDomain(); |
4223 | |
4224 | // @dbgtodo - is this efficient DAC usage (perhaps a dac-cop rule)? Are we round-tripping the pointer? |
4225 | // Should we have a GetDomainAssembly() that returns a PTR_DomainAssembly? |
4226 | pData->vmDomainAssembly.SetHostPtr(pDomainFile->GetDomainAssembly()); |
4227 | pData->vmAppDomain.SetHostPtr(pAppDomain); |
4228 | } |
4229 | |
4230 | // Implement IDacDbiInterface::GetModuleData |
4231 | void DacDbiInterfaceImpl::GetModuleData(VMPTR_Module vmModule, ModuleInfo * pData) |
4232 | { |
4233 | DD_ENTER_MAY_THROW; |
4234 | |
4235 | _ASSERTE(pData != NULL); |
4236 | |
4237 | ZeroMemory(pData, sizeof(*pData)); |
4238 | |
4239 | Module * pModule = vmModule.GetDacPtr(); |
4240 | PEFile * pFile = pModule->GetFile(); |
4241 | |
4242 | pData->vmPEFile.SetHostPtr(pFile); |
4243 | pData->vmAssembly.SetHostPtr(pModule->GetAssembly()); |
4244 | |
4245 | // Is it dynamic? |
4246 | BOOL fIsDynamic = pModule->IsReflection(); |
4247 | pData->fIsDynamic = fIsDynamic; |
4248 | |
4249 | // Get PE BaseAddress and Size |
4250 | // For dynamic modules, these are 0. Else, |
4251 | pData->pPEBaseAddress = NULL; |
4252 | pData->nPESize = 0; |
4253 | |
4254 | if (!fIsDynamic) |
4255 | { |
4256 | COUNT_T size = 0; |
4257 | pData->pPEBaseAddress = PTR_TO_TADDR(pFile->GetDebuggerContents(&size)); |
4258 | pData->nPESize = (ULONG) size; |
4259 | } |
4260 | |
4261 | // In-memory is determined by whether the module has a filename. |
4262 | pData->fInMemory = FALSE; |
4263 | if (pFile != NULL) |
4264 | { |
4265 | pData->fInMemory = pFile->GetPath().IsEmpty(); |
4266 | } |
4267 | } |
4268 | |
4269 | |
4270 | // Enumerate all AppDomains in the process. |
4271 | void DacDbiInterfaceImpl::EnumerateAppDomains( |
4272 | FP_APPDOMAIN_ENUMERATION_CALLBACK fpCallback, |
4273 | void * pUserData) |
4274 | { |
4275 | DD_ENTER_MAY_THROW; |
4276 | |
4277 | _ASSERTE(fpCallback != NULL); |
4278 | |
4279 | // Only include active appdomains in the enumeration. |
4280 | // This includes appdomains sent before the AD load event, |
4281 | // and does not include appdomains that are in shutdown after the AD exit event. |
4282 | const BOOL bOnlyActive = TRUE; |
4283 | AppDomainIterator iterator(bOnlyActive); |
4284 | |
4285 | while(iterator.Next()) |
4286 | { |
4287 | // It's critical that we don't yield appdomains after the unload event has been sent. |
4288 | // See code:IDacDbiInterface#Enumeration for details. |
4289 | AppDomain * pAppDomain = iterator.GetDomain(); |
4290 | |
4291 | VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr(); |
4292 | vmAppDomain.SetHostPtr(pAppDomain); |
4293 | |
4294 | fpCallback(vmAppDomain, pUserData); |
4295 | } |
4296 | } |
4297 | |
4298 | // Enumerate all Assemblies in an appdomain. |
4299 | void DacDbiInterfaceImpl::EnumerateAssembliesInAppDomain( |
4300 | VMPTR_AppDomain vmAppDomain, |
4301 | FP_ASSEMBLY_ENUMERATION_CALLBACK fpCallback, |
4302 | void * pUserData |
4303 | ) |
4304 | { |
4305 | DD_ENTER_MAY_THROW; |
4306 | |
4307 | _ASSERTE(fpCallback != NULL); |
4308 | |
4309 | // Iterate through all Assemblies (including shared) in the appdomain. |
4310 | AppDomain::AssemblyIterator iterator; |
4311 | |
4312 | // If the containing appdomain is unloading, then don't enumerate any assemblies |
4313 | // in the domain. This is to enforce rules at code:IDacDbiInterface#Enumeration. |
4314 | // See comment in code:DacDbiInterfaceImpl::EnumerateModulesInAssembly code for details. |
4315 | AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); |
4316 | |
4317 | // Pass the magical flags to the loader enumerator to get all Execution-only assemblies. |
4318 | iterator = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoading | kIncludeLoaded | kIncludeExecution)); |
4319 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
4320 | |
4321 | while (iterator.Next(pDomainAssembly.This())) |
4322 | { |
4323 | if (!pDomainAssembly->IsVisibleToDebugger()) |
4324 | { |
4325 | continue; |
4326 | } |
4327 | |
4328 | VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr(); |
4329 | vmDomainAssembly.SetHostPtr(pDomainAssembly); |
4330 | |
4331 | fpCallback(vmDomainAssembly, pUserData); |
4332 | } |
4333 | } |
4334 | |
4335 | // Implementation of IDacDbiInterface::EnumerateModulesInAssembly, |
4336 | // Enumerate all the modules (non-resource) in an assembly. |
4337 | void DacDbiInterfaceImpl::EnumerateModulesInAssembly( |
4338 | VMPTR_DomainAssembly vmAssembly, |
4339 | FP_MODULE_ENUMERATION_CALLBACK fpCallback, |
4340 | void * pUserData) |
4341 | { |
4342 | DD_ENTER_MAY_THROW; |
4343 | |
4344 | _ASSERTE(fpCallback != NULL); |
4345 | |
4346 | DomainAssembly * pDomainAssembly = vmAssembly.GetDacPtr(); |
4347 | |
4348 | // If the domain is not yet fully-loaded, don't advertise it yet. |
4349 | // It's not ready to be inspected. |
4350 | DomainModuleIterator iterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded); |
4351 | |
4352 | while (iterator.Next()) |
4353 | { |
4354 | DomainFile * pDomainFile = iterator.GetDomainFile(); |
4355 | |
4356 | // Debugger isn't notified of Resource / Inspection-only modules. |
4357 | if (!pDomainFile->GetModule()->IsVisibleToDebugger()) |
4358 | { |
4359 | continue; |
4360 | } |
4361 | |
4362 | _ASSERTE(pDomainFile->IsLoaded()); |
4363 | |
4364 | VMPTR_DomainFile vmDomainFile = VMPTR_DomainFile::NullPtr(); |
4365 | vmDomainFile.SetHostPtr(pDomainFile); |
4366 | |
4367 | fpCallback(vmDomainFile, pUserData); |
4368 | } |
4369 | } |
4370 | |
4371 | // Implementation of IDacDbiInterface::ResolveAssembly |
4372 | // Returns NULL if not found. |
4373 | VMPTR_DomainAssembly DacDbiInterfaceImpl::ResolveAssembly( |
4374 | VMPTR_DomainFile vmScope, |
4375 | mdToken tkAssemblyRef) |
4376 | { |
4377 | DD_ENTER_MAY_THROW; |
4378 | |
4379 | |
4380 | DomainFile * pDomainFile = vmScope.GetDacPtr(); |
4381 | AppDomain * pAppDomain = pDomainFile->GetAppDomain(); |
4382 | Module * pModule = pDomainFile->GetCurrentModule(); |
4383 | |
4384 | VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr(); |
4385 | |
4386 | Assembly * pAssembly = pModule->LookupAssemblyRef(tkAssemblyRef); |
4387 | if (pAssembly != NULL) |
4388 | { |
4389 | DomainAssembly * pDomainAssembly = pAssembly->FindDomainAssembly(pAppDomain); |
4390 | vmDomainAssembly.SetHostPtr(pDomainAssembly); |
4391 | } |
4392 | return vmDomainAssembly; |
4393 | } |
4394 | |
4395 | // When stopped at an event, request a synchronization. |
4396 | // See DacDbiInterface.h for full comments |
4397 | void DacDbiInterfaceImpl::RequestSyncAtEvent() |
4398 | { |
4399 | DD_ENTER_MAY_THROW; |
4400 | |
4401 | // To request a sync, we just need to set g_pDebugger->m_RSRequestedSync high. |
4402 | if (g_pDebugger != NULL) |
4403 | { |
4404 | TADDR addr = PTR_HOST_MEMBER_TADDR(Debugger, g_pDebugger, m_RSRequestedSync); |
4405 | |
4406 | BOOL fTrue = TRUE; |
4407 | SafeWriteStructOrThrow<BOOL>(addr, &fTrue); |
4408 | |
4409 | } |
4410 | } |
4411 | |
4412 | HRESULT DacDbiInterfaceImpl::SetSendExceptionsOutsideOfJMC(BOOL sendExceptionsOutsideOfJMC) |
4413 | { |
4414 | DD_ENTER_MAY_THROW |
4415 | |
4416 | HRESULT hr = S_OK; |
4417 | EX_TRY |
4418 | { |
4419 | if (g_pDebugger != NULL) |
4420 | { |
4421 | TADDR addr = PTR_HOST_MEMBER_TADDR(Debugger, g_pDebugger, m_sendExceptionsOutsideOfJMC); |
4422 | SafeWriteStructOrThrow<BOOL>(addr, &sendExceptionsOutsideOfJMC); |
4423 | } |
4424 | } |
4425 | EX_CATCH_HRESULT(hr); |
4426 | return hr; |
4427 | } |
4428 | |
4429 | // Notify the debuggee that a debugger attach is pending. |
4430 | // See DacDbiInterface.h for full comments |
4431 | void DacDbiInterfaceImpl::MarkDebuggerAttachPending() |
4432 | { |
4433 | DD_ENTER_MAY_THROW; |
4434 | |
4435 | if (g_pDebugger != NULL) |
4436 | { |
4437 | DWORD flags = g_CORDebuggerControlFlags; |
4438 | flags |= DBCF_PENDING_ATTACH; |
4439 | |
4440 | // Uses special DAC writing. PTR_TO_TADDR doesn't fetch for globals. |
4441 | // @dbgtodo dac support - the exact mechanism of writing to the target needs to be flushed out, |
4442 | // especially as it relates to DAC cop and enforcing undac-ized writes. |
4443 | g_CORDebuggerControlFlags = flags; |
4444 | } |
4445 | else |
4446 | { |
4447 | // Caller should have guaranteed that the LS is loaded. |
4448 | // If we're detaching, then don't throw because we don't care. |
4449 | ThrowHR(CORDBG_E_NOTREADY); |
4450 | } |
4451 | } |
4452 | |
4453 | |
4454 | // Notify the debuggee that a debugger is attached. |
4455 | // See DacDbiInterface.h for full comments |
4456 | void DacDbiInterfaceImpl::MarkDebuggerAttached(BOOL fAttached) |
4457 | { |
4458 | DD_ENTER_MAY_THROW; |
4459 | |
4460 | if (g_pDebugger != NULL) |
4461 | { |
4462 | // To be attached, we need to set the following |
4463 | // g_CORDebuggerControlFlags |= DBCF_ATTACHED; |
4464 | // To detach (if !fAttached), we need to do the opposite. |
4465 | |
4466 | DWORD flags = g_CORDebuggerControlFlags; |
4467 | if (fAttached) |
4468 | { |
4469 | flags |= DBCF_ATTACHED; |
4470 | } |
4471 | else |
4472 | { |
4473 | flags &= ~ (DBCF_ATTACHED | DBCF_PENDING_ATTACH); |
4474 | } |
4475 | |
4476 | // Uses special DAC writing. PTR_TO_TADDR doesn't fetch for globals. |
4477 | // @dbgtodo dac support - the exact mechanism of writing to the target needs to be flushed out, |
4478 | // especially as it relates to DAC cop and enforcing undac-ized writes. |
4479 | g_CORDebuggerControlFlags = flags; |
4480 | } |
4481 | else if (fAttached) |
4482 | { |
4483 | // Caller should have guaranteed that the LS is loaded. |
4484 | // If we're detaching, then don't throw because we don't care. |
4485 | ThrowHR(CORDBG_E_NOTREADY); |
4486 | } |
4487 | |
4488 | } |
4489 | |
4490 | |
4491 | |
4492 | // Enumerate all threads in the process. |
4493 | void DacDbiInterfaceImpl::EnumerateThreads(FP_THREAD_ENUMERATION_CALLBACK fpCallback, void * pUserData) |
4494 | { |
4495 | DD_ENTER_MAY_THROW; |
4496 | |
4497 | if (ThreadStore::s_pThreadStore == NULL) |
4498 | { |
4499 | return; |
4500 | } |
4501 | |
4502 | Thread *pThread = ThreadStore::GetThreadList(NULL); |
4503 | |
4504 | while (pThread != NULL) |
4505 | { |
4506 | |
4507 | // Don't want to publish threads via enumeration before they're ready to be inspected. |
4508 | // Use the same window that we used in whidbey. |
4509 | Thread::ThreadState threadState = pThread->GetSnapshotState(); |
4510 | if (!((IsThreadMarkedDeadWorker(pThread)) || (threadState & Thread::TS_Unstarted))) |
4511 | { |
4512 | VMPTR_Thread vmThread = VMPTR_Thread::NullPtr(); |
4513 | vmThread.SetHostPtr(pThread); |
4514 | fpCallback(vmThread, pUserData); |
4515 | } |
4516 | |
4517 | pThread = ThreadStore::GetThreadList(pThread); |
4518 | } |
4519 | } |
4520 | |
4521 | // public implementation of IsThreadMarkedDead |
4522 | bool DacDbiInterfaceImpl::IsThreadMarkedDead(VMPTR_Thread vmThread) |
4523 | { |
4524 | DD_ENTER_MAY_THROW; |
4525 | Thread * pThread = vmThread.GetDacPtr(); |
4526 | return IsThreadMarkedDeadWorker(pThread); |
4527 | } |
4528 | |
4529 | // Private worker for IsThreadMarkedDead |
4530 | // |
4531 | // Arguments: |
4532 | // pThread - valid thread to check if dead |
4533 | // |
4534 | // Returns: |
4535 | // true iff thread is marked as dead. |
4536 | // |
4537 | // Notes: |
4538 | // This is an internal method that skips public validation. |
4539 | // See code:IDacDbiInterface::#IsThreadMarkedDead for purpose. |
4540 | bool DacDbiInterfaceImpl::IsThreadMarkedDeadWorker(Thread * pThread) |
4541 | { |
4542 | _ASSERTE(pThread != NULL); |
4543 | |
4544 | Thread::ThreadState threadState = pThread->GetSnapshotState(); |
4545 | |
4546 | bool fIsDead = (threadState & Thread::TS_Dead) != 0; |
4547 | |
4548 | return fIsDead; |
4549 | } |
4550 | |
4551 | |
4552 | // Return the handle of the specified thread. |
4553 | HANDLE DacDbiInterfaceImpl::GetThreadHandle(VMPTR_Thread vmThread) |
4554 | { |
4555 | DD_ENTER_MAY_THROW; |
4556 | |
4557 | Thread * pThread = vmThread.GetDacPtr(); |
4558 | return pThread->GetThreadHandle(); |
4559 | } |
4560 | |
4561 | // Return the object handle for the managed Thread object corresponding to the specified thread. |
4562 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetThreadObject(VMPTR_Thread vmThread) |
4563 | { |
4564 | DD_ENTER_MAY_THROW; |
4565 | |
4566 | Thread * pThread = vmThread.GetDacPtr(); |
4567 | Thread::ThreadState threadState = pThread->GetSnapshotState(); |
4568 | |
4569 | if ( (threadState & Thread::TS_Dead) || |
4570 | (threadState & Thread::TS_Unstarted) || |
4571 | (threadState & Thread::TS_Detached) || |
4572 | g_fProcessDetach ) |
4573 | { |
4574 | ThrowHR(CORDBG_E_BAD_THREAD_STATE); |
4575 | } |
4576 | else |
4577 | { |
4578 | VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
4579 | vmObjHandle.SetDacTargetPtr(pThread->GetExposedObjectHandleForDebugger()); |
4580 | return vmObjHandle; |
4581 | } |
4582 | } |
4583 | |
4584 | // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread |
4585 | // according to the CorDebugThreadState. |
4586 | void DacDbiInterfaceImpl::SetDebugState(VMPTR_Thread vmThread, |
4587 | CorDebugThreadState debugState) |
4588 | { |
4589 | DD_ENTER_MAY_THROW; |
4590 | |
4591 | Thread * pThread = vmThread.GetDacPtr(); |
4592 | |
4593 | // update the field on the host copy |
4594 | if (debugState == THREAD_SUSPEND) |
4595 | { |
4596 | pThread->SetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); |
4597 | } |
4598 | else if (debugState == THREAD_RUN) |
4599 | { |
4600 | pThread->ResetThreadStateNC(Thread::TSNC_DebuggerUserSuspend); |
4601 | } |
4602 | else |
4603 | { |
4604 | ThrowHR(E_INVALIDARG); |
4605 | } |
4606 | |
4607 | // update the field on the target copy |
4608 | TADDR taThreadState = PTR_HOST_MEMBER_TADDR(Thread, pThread, m_StateNC); |
4609 | SafeWriteStructOrThrow<Thread::ThreadStateNoConcurrency>(taThreadState, &(pThread->m_StateNC)); |
4610 | } |
4611 | |
4612 | // Gets the debugger unhandled exception threadstate flag |
4613 | BOOL DacDbiInterfaceImpl::HasUnhandledException(VMPTR_Thread vmThread) |
4614 | { |
4615 | DD_ENTER_MAY_THROW; |
4616 | |
4617 | Thread * pThread = vmThread.GetDacPtr(); |
4618 | |
4619 | // some managed exceptions don't have any underlying |
4620 | // native exception processing going on. They just consist |
4621 | // of a managed throwable that we have stashed away followed |
4622 | // by a debugger notification and some form of failfast. |
4623 | // Everything that comes through EEFatalError is in this category |
4624 | if(pThread->IsLastThrownObjectUnhandled()) |
4625 | { |
4626 | return TRUE; |
4627 | } |
4628 | |
4629 | // most managed exceptions are just a throwable bound to a |
4630 | // native exception. In that case this handle will be non-null |
4631 | OBJECTHANDLE ohException = pThread->GetThrowableAsHandle(); |
4632 | if (ohException != NULL) |
4633 | { |
4634 | // during the UEF we set the unhandled bit, if it is set the exception |
4635 | // was unhandled |
4636 | // however if the exception has intercept info then we consider it handled |
4637 | // again |
4638 | return pThread->GetExceptionState()->GetFlags()->IsUnhandled() && |
4639 | !(pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo()); |
4640 | } |
4641 | |
4642 | return FALSE; |
4643 | } |
4644 | |
4645 | // Return the user state of the specified thread. |
4646 | CorDebugUserState DacDbiInterfaceImpl::GetUserState(VMPTR_Thread vmThread) |
4647 | { |
4648 | DD_ENTER_MAY_THROW; |
4649 | |
4650 | UINT result = 0; |
4651 | result = GetPartialUserState(vmThread); |
4652 | |
4653 | if (!IsThreadAtGCSafePlace(vmThread)) |
4654 | { |
4655 | result |= USER_UNSAFE_POINT; |
4656 | } |
4657 | |
4658 | return (CorDebugUserState)result; |
4659 | } |
4660 | |
4661 | |
4662 | // Return the connection ID of the specified thread. |
4663 | CONNID DacDbiInterfaceImpl::GetConnectionID(VMPTR_Thread vmThread) |
4664 | { |
4665 | DD_ENTER_MAY_THROW; |
4666 | |
4667 | return INVALID_CONNECTION_ID; |
4668 | } |
4669 | |
4670 | // Return the task ID of the specified thread. |
4671 | TASKID DacDbiInterfaceImpl::GetTaskID(VMPTR_Thread vmThread) |
4672 | { |
4673 | DD_ENTER_MAY_THROW; |
4674 | |
4675 | return INVALID_TASK_ID; |
4676 | } |
4677 | |
4678 | // Return the OS thread ID of the specified thread |
4679 | DWORD DacDbiInterfaceImpl::TryGetVolatileOSThreadID(VMPTR_Thread vmThread) |
4680 | { |
4681 | DD_ENTER_MAY_THROW; |
4682 | |
4683 | Thread * pThread = vmThread.GetDacPtr(); |
4684 | _ASSERTE(pThread != NULL); |
4685 | |
4686 | DWORD dwThreadId = pThread->GetOSThreadIdForDebugger(); |
4687 | |
4688 | // If the thread ID is a the magical cookie value, then this is really |
4689 | // a switched out thread and doesn't have an OS tid. In that case, the |
4690 | // DD contract is to return 0 (a much more sane value) |
4691 | const DWORD dwSwitchedOutThreadId = SWITCHED_OUT_FIBER_OSID; |
4692 | if (dwThreadId == dwSwitchedOutThreadId) |
4693 | { |
4694 | return 0; |
4695 | } |
4696 | return dwThreadId; |
4697 | } |
4698 | |
4699 | // Return the unique thread ID of the specified thread. |
4700 | DWORD DacDbiInterfaceImpl::GetUniqueThreadID(VMPTR_Thread vmThread) |
4701 | { |
4702 | DD_ENTER_MAY_THROW; |
4703 | |
4704 | Thread * pThread = vmThread.GetDacPtr(); |
4705 | _ASSERTE(pThread != NULL); |
4706 | |
4707 | return pThread->GetOSThreadId(); |
4708 | } |
4709 | |
4710 | // Return the object handle to the managed Exception object of the current exception |
4711 | // on the specified thread. The return value could be NULL if there is no current exception. |
4712 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetCurrentException(VMPTR_Thread vmThread) |
4713 | { |
4714 | DD_ENTER_MAY_THROW; |
4715 | |
4716 | Thread * pThread = vmThread.GetDacPtr(); |
4717 | |
4718 | // OBJECTHANDLEs are really just TADDRs. |
4719 | OBJECTHANDLE ohException = pThread->GetThrowableAsHandle(); // ohException can be NULL |
4720 | |
4721 | if (ohException == NULL) |
4722 | { |
4723 | if (pThread->IsLastThrownObjectUnhandled()) |
4724 | { |
4725 | ohException = pThread->LastThrownObjectHandle(); |
4726 | } |
4727 | } |
4728 | |
4729 | VMPTR_OBJECTHANDLE vmObjHandle; |
4730 | vmObjHandle.SetDacTargetPtr(ohException); |
4731 | return vmObjHandle; |
4732 | } |
4733 | |
4734 | // Return the object handle to the managed object for a given CCW pointer. |
4735 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetObjectForCCW(CORDB_ADDRESS ccwPtr) |
4736 | { |
4737 | DD_ENTER_MAY_THROW; |
4738 | |
4739 | OBJECTHANDLE ohCCW = NULL; |
4740 | |
4741 | #ifdef FEATURE_COMINTEROP |
4742 | ComCallWrapper *pCCW = DACGetCCWFromAddress(ccwPtr); |
4743 | if (pCCW) |
4744 | { |
4745 | ohCCW = pCCW->GetObjectHandle(); |
4746 | } |
4747 | #endif |
4748 | |
4749 | VMPTR_OBJECTHANDLE vmObjHandle; |
4750 | vmObjHandle.SetDacTargetPtr(ohCCW); |
4751 | return vmObjHandle; |
4752 | } |
4753 | |
4754 | // Return the object handle to the managed CustomNotification object of the current notification |
4755 | // on the specified thread. The return value could be NULL if there is no current notification. |
4756 | // Arguments: |
4757 | // input: vmThread - the thread on which the notification occurred |
4758 | // Return value: object handle for the current notification (if any) on the thread. This will return non-null |
4759 | // if and only if we are currently inside a CustomNotification Callback (or a dump was generated while in this |
4760 | // callback) |
4761 | // |
4762 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetCurrentCustomDebuggerNotification(VMPTR_Thread vmThread) |
4763 | { |
4764 | DD_ENTER_MAY_THROW; |
4765 | |
4766 | Thread * pThread = vmThread.GetDacPtr(); |
4767 | |
4768 | // OBJECTHANDLEs are really just TADDRs. |
4769 | OBJECTHANDLE ohNotification = pThread->GetThreadCurrNotification(); // ohNotification can be NULL |
4770 | |
4771 | VMPTR_OBJECTHANDLE vmObjHandle; |
4772 | vmObjHandle.SetDacTargetPtr(ohNotification); |
4773 | return vmObjHandle; |
4774 | } |
4775 | |
4776 | // Return the current appdomain the specified thread is in. |
4777 | VMPTR_AppDomain DacDbiInterfaceImpl::GetCurrentAppDomain(VMPTR_Thread vmThread) |
4778 | { |
4779 | DD_ENTER_MAY_THROW; |
4780 | |
4781 | Thread * pThread = vmThread.GetDacPtr(); |
4782 | AppDomain * pAppDomain = pThread->GetDomain(); |
4783 | |
4784 | if (pAppDomain == NULL) |
4785 | { |
4786 | ThrowHR(E_FAIL); |
4787 | } |
4788 | |
4789 | VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr(); |
4790 | vmAppDomain.SetDacTargetPtr(PTR_HOST_TO_TADDR(pAppDomain)); |
4791 | return vmAppDomain; |
4792 | } |
4793 | |
4794 | |
4795 | // Returns a bitfield reflecting the managed debugging state at the time of |
4796 | // the jit attach. |
4797 | CLR_DEBUGGING_PROCESS_FLAGS DacDbiInterfaceImpl::GetAttachStateFlags() |
4798 | { |
4799 | DD_ENTER_MAY_THROW; |
4800 | |
4801 | CLR_DEBUGGING_PROCESS_FLAGS res = (CLR_DEBUGGING_PROCESS_FLAGS)0; |
4802 | if (g_pDebugger != NULL) |
4803 | { |
4804 | res = g_pDebugger->GetAttachStateFlags(); |
4805 | } |
4806 | else |
4807 | { |
4808 | // When launching the process under a managed debugger we |
4809 | // request these flags when CLR is loaded (before g_pDebugger |
4810 | // had a chance to be initialized). In these cases simply |
4811 | // return 0 |
4812 | } |
4813 | return res; |
4814 | } |
4815 | |
4816 | //--------------------------------------------------------------------------------------- |
4817 | // Helper to get the address of the 2nd-chance hijack function Or throw |
4818 | // |
4819 | // Returns: |
4820 | // Non-null Target Address of hijack function. |
4821 | TADDR DacDbiInterfaceImpl::GetHijackAddress() |
4822 | { |
4823 | TADDR addr = NULL; |
4824 | if (g_pDebugger != NULL) |
4825 | { |
4826 | // Get the start address of the redirect function for unhandled exceptions. |
4827 | addr = dac_cast<TADDR>(g_pDebugger->m_rgHijackFunction[Debugger::kUnhandledException].StartAddress()); |
4828 | } |
4829 | if (addr == NULL) |
4830 | { |
4831 | ThrowHR(CORDBG_E_NOTREADY); |
4832 | } |
4833 | return addr; |
4834 | } |
4835 | |
4836 | //--------------------------------------------------------------------------------------- |
4837 | // Helper to determine whether a control PC is in any native stub which the runtime knows how to unwind. |
4838 | // |
4839 | // Arguments: |
4840 | // taControlPC - control PC to be checked |
4841 | // |
4842 | // Returns: |
4843 | // Returns true if the control PC is in a runtime unwindable stub. |
4844 | // |
4845 | // Notes: |
4846 | // Currently this function only recognizes the ExceptionHijack() stub, |
4847 | // which is used for unhandled exceptions. |
4848 | // |
4849 | |
4850 | bool DacDbiInterfaceImpl::IsRuntimeUnwindableStub(PCODE targetControlPC) |
4851 | { |
4852 | |
4853 | TADDR controlPC = PCODEToPINSTR(targetControlPC); |
4854 | // we call this function a lot while walking the stack and the values here will never change |
4855 | // Getting the g_pDebugger and each entry in the m_rgHijackFunction is potentially ~7 DAC |
4856 | // accesses per frame. Caching the data into a single local array is much faster. This optimization |
4857 | // recovered a few % of DAC stackwalking time |
4858 | if(!m_isCachedHijackFunctionValid) |
4859 | { |
4860 | Debugger* pDebugger = g_pDebugger; |
4861 | if ((pDebugger == NULL) || (pDebugger->m_rgHijackFunction == NULL)) |
4862 | { |
4863 | // The in-process debugging infrastructure hasn't been fully initialized, which means that we could |
4864 | // NOT have hijacked anything yet. |
4865 | return false; |
4866 | } |
4867 | |
4868 | // PERF NOTE: if needed this array copy could probably be made more efficient |
4869 | // hitting the DAC only once for a single memory block, or even better |
4870 | // put the array inline in the Debugger object so that we only do 1 DAC |
4871 | // access for this entire thing |
4872 | for (int i = 0; i < Debugger::kMaxHijackFunctions; i++) |
4873 | { |
4874 | InitTargetBufferFromMemoryRange(pDebugger->m_rgHijackFunction[i], &m_pCachedHijackFunction[i] ); |
4875 | } |
4876 | m_isCachedHijackFunctionValid = TRUE; |
4877 | } |
4878 | |
4879 | // Check whether the control PC is in any of the thread redirection functions. |
4880 | for (int i = 0; i < Debugger::kMaxHijackFunctions; i++) |
4881 | { |
4882 | CORDB_ADDRESS start = m_pCachedHijackFunction[i].pAddress; |
4883 | CORDB_ADDRESS end = start + m_pCachedHijackFunction[i].cbSize; |
4884 | if ((start <= controlPC) && (controlPC < end)) |
4885 | { |
4886 | return true; |
4887 | } |
4888 | } |
4889 | return false; |
4890 | } |
4891 | |
4892 | //--------------------------------------------------------------------------------------- |
4893 | // Align a stack pointer for the given architecture |
4894 | // |
4895 | // Arguments: |
4896 | // pEsp - in/out: pointer to stack pointer. |
4897 | // |
4898 | void DacDbiInterfaceImpl::AlignStackPointer(CORDB_ADDRESS * pEsp) |
4899 | { |
4900 | SUPPORTS_DAC; |
4901 | |
4902 | // Nop on x86. |
4903 | #if defined(_WIN64) |
4904 | // on 64-bit, stack pointer must be 16-byte aligned. |
4905 | // Stacks grown down, so round down to nearest 0xF bits. |
4906 | *pEsp &= ~((CORDB_ADDRESS) 0xF); |
4907 | #endif |
4908 | } |
4909 | |
4910 | //--------------------------------------------------------------------------------------- |
4911 | // Emulate pushing something on a thread's stack. |
4912 | // |
4913 | // Arguments: |
4914 | // pEsp - in/out: pointer to stack pointer to push object at. On output, |
4915 | // updated stack pointer. |
4916 | // pData - object to push on the stack. |
4917 | // fAlignStack - whether to align the stack pointer before and after the push. |
4918 | // Callers which specify FALSE must be very careful and know exactly |
4919 | // what they are doing. |
4920 | // |
4921 | // Return: |
4922 | // address of pushed object. Throws on error. |
4923 | template <class T> |
4924 | CORDB_ADDRESS DacDbiInterfaceImpl::PushHelper(CORDB_ADDRESS * pEsp, |
4925 | const T * pData, |
4926 | BOOL fAlignStack) |
4927 | { |
4928 | SUPPORTS_DAC; |
4929 | |
4930 | if (fAlignStack == TRUE) |
4931 | { |
4932 | AlignStackPointer(pEsp); |
4933 | } |
4934 | *pEsp -= sizeof(T); |
4935 | if (fAlignStack == TRUE) |
4936 | { |
4937 | AlignStackPointer(pEsp); |
4938 | } |
4939 | SafeWriteStructOrThrow(*pEsp, pData); |
4940 | return *pEsp; |
4941 | } |
4942 | |
4943 | //--------------------------------------------------------------------------------------- |
4944 | // Write an EXCEPTION_RECORD structure to the remote target at the specified address while taking |
4945 | // into account the number of exception parameters. On 64-bit OS and on the WOW64, the OS always |
4946 | // pushes the entire EXCEPTION_RECORD onto the stack. However, on native x86 OS, the OS only pushes |
4947 | // enough of the EXCEPTION_RECORD to cover the specified number of exception parameters. Thus we |
4948 | // need to be extra careful when we overwrite an EXCEPTION_RECORD on the stack. |
4949 | // |
4950 | // Arguments: |
4951 | // pRemotePtr - address of the EXCEPTION_RECORD in the remote target |
4952 | // pExcepRecord - EXCEPTION_RECORD to be written |
4953 | // |
4954 | // Notes: |
4955 | // This function is only used by the code which hijacks a therad when there's an unhandled exception. |
4956 | // It only works when we are actually debugging a live process, not a dump. |
4957 | // |
4958 | |
4959 | void DacDbiInterfaceImpl::WriteExceptionRecordHelper(CORDB_ADDRESS pRemotePtr, |
4960 | const EXCEPTION_RECORD * pExcepRecord) |
4961 | { |
4962 | // Calculate the correct size to push onto the stack. |
4963 | ULONG32 cbSize = offsetof(EXCEPTION_RECORD, ExceptionInformation); |
4964 | cbSize += pExcepRecord->NumberParameters * sizeof(pExcepRecord->ExceptionInformation[0]); |
4965 | |
4966 | // Use the data target to write to the remote target. Here we are assuming that we are debugging a |
4967 | // live process, since this function is only called by the hijacking code for unhandled exceptions. |
4968 | HRESULT hr = m_pMutableTarget->WriteVirtual(pRemotePtr, |
4969 | reinterpret_cast<const BYTE *>(pExcepRecord), |
4970 | cbSize); |
4971 | |
4972 | if (FAILED(hr)) |
4973 | { |
4974 | ThrowHR(hr); |
4975 | } |
4976 | } |
4977 | |
4978 | // Implement IDacDbiInterface::Hijack |
4979 | void DacDbiInterfaceImpl::Hijack( |
4980 | VMPTR_Thread vmThread, |
4981 | ULONG32 dwThreadId, |
4982 | const EXCEPTION_RECORD * pRecord, |
4983 | T_CONTEXT * pOriginalContext, |
4984 | ULONG32 cbSizeContext, |
4985 | EHijackReason::EHijackReason reason, |
4986 | void * pUserData, |
4987 | CORDB_ADDRESS * pRemoteContextAddr) |
4988 | { |
4989 | DD_ENTER_MAY_THROW; |
4990 | |
4991 | // |
4992 | // Validate parameters |
4993 | // |
4994 | |
4995 | // pRecord may be NULL if we're not hijacking at an exception |
4996 | // pOriginalContext may be NULL if caller doesn't want a copy of the context. |
4997 | // (The hijack function already has the context) |
4998 | _ASSERTE((pOriginalContext == NULL) == (cbSizeContext == 0)); |
4999 | _ASSERTE(EHijackReason::IsValid(reason)); |
5000 | #ifdef PLATFORM_UNIX |
5001 | _ASSERTE(!"Not supported on this platform" ); |
5002 | #endif |
5003 | |
5004 | // |
5005 | // If we hijack a thread which might not be managed we can set vmThread = NULL |
5006 | // The only side-effect in this case is that we can't reuse CONTEXT and |
5007 | // EXCEPTION_RECORD space on the stack by an already underway in-process exception |
5008 | // filter. If you depend on those being used and updated you must provide the vmThread |
5009 | // |
5010 | Thread* pThread = NULL; |
5011 | if(!vmThread.IsNull()) |
5012 | { |
5013 | pThread = vmThread.GetDacPtr(); |
5014 | _ASSERTE(pThread->GetOSThreadIdForDebugger() == dwThreadId); |
5015 | } |
5016 | |
5017 | TADDR pfnHijackFunction = GetHijackAddress(); |
5018 | |
5019 | // |
5020 | // Setup context for hijack |
5021 | // |
5022 | T_CONTEXT ctx; |
5023 | HRESULT hr = m_pTarget->GetThreadContext( |
5024 | dwThreadId, |
5025 | CONTEXT_FULL, |
5026 | sizeof(ctx), |
5027 | (BYTE*) &ctx); |
5028 | IfFailThrow(hr); |
5029 | |
5030 | // If caller requested, copy back the original context that we're hijacking from. |
5031 | if (pOriginalContext != NULL) |
5032 | { |
5033 | // Since Dac + DBI are tightly coupled, context sizes should be the same. |
5034 | if (cbSizeContext != sizeof(T_CONTEXT)) |
5035 | { |
5036 | ThrowHR(E_INVALIDARG); |
5037 | } |
5038 | |
5039 | memcpy(pOriginalContext, &ctx, cbSizeContext); |
5040 | } |
5041 | |
5042 | // Make sure the trace flag isn't on. This can happen if we were single stepping the thread when we faulted. This |
5043 | // will ensure that we don't try to single step through the OS's exception logic, which greatly confuses our second |
5044 | // chance hijack logic. This also mimics what the OS does for us automaically when single stepping in process, i.e., |
5045 | // when you turn the trace flag on in-process and go, if there is a fault, the fault is reported and the trace flag |
5046 | // is automatically turned off. |
5047 | // |
5048 | // The debugger could always re-enable the single-step flag if it wants to. |
5049 | #ifndef _TARGET_ARM_ |
5050 | UnsetSSFlag(reinterpret_cast<DT_CONTEXT *>(&ctx)); |
5051 | #endif |
5052 | |
5053 | // Push pointers |
5054 | void* espContext = NULL; |
5055 | void* espRecord = NULL; |
5056 | const void* pData = pUserData; |
5057 | |
5058 | // @dbgtodo cross-plat - this is not cross plat |
5059 | CORDB_ADDRESS esp = GetSP(&ctx); |
5060 | |
5061 | // |
5062 | // Find out where the OS exception dispatcher has pushed the EXCEPTION_RECORD and CONTEXT. The ExInfo and |
5063 | // ExceptionTracker have pointers to these data structures, but when we get the unhandled exception |
5064 | // notification, the OS exception dispatcher is no longer on the stack, so these pointers are no longer |
5065 | // valid. We need to either update these pointers in the ExInfo/ExcepionTracker, or reuse the stack |
5066 | // space used by the OS exception dispatcher. We are using the latter approach here. |
5067 | // |
5068 | |
5069 | CORDB_ADDRESS espOSContext = NULL; |
5070 | CORDB_ADDRESS espOSRecord = NULL; |
5071 | if (pThread != NULL && pThread->IsExceptionInProgress()) |
5072 | { |
5073 | espOSContext = (CORDB_ADDRESS)PTR_TO_TADDR(pThread->GetExceptionState()->GetContextRecord()); |
5074 | espOSRecord = (CORDB_ADDRESS)PTR_TO_TADDR(pThread->GetExceptionState()->GetExceptionRecord()); |
5075 | |
5076 | // The managed exception may not be related to the unhandled exception for which we are trying to |
5077 | // hijack. An example would be when a thread hits a managed exception, VS tries to do func eval on |
5078 | // the thread, but the func eval causes an unhandled exception (e.g. AV in mscorwks.dll). In this |
5079 | // case, the pointers stored on the ExInfo/ExceptionTracker are closer to the root than the current |
5080 | // SP of the thread. The check below makes sure we don't reuse the pointers in this case. |
5081 | if (espOSContext < esp) |
5082 | { |
5083 | SafeWriteStructOrThrow(espOSContext, &ctx); |
5084 | espContext = CORDB_ADDRESS_TO_PTR(espOSContext); |
5085 | |
5086 | // We should have an EXCEPTION_RECORD if we are hijacked at an exception. |
5087 | // We need to be careful when we overwrite the exception record. On x86, the OS doesn't |
5088 | // always push the full record onto the stack, and so we can't blindly use sizeof(EXCEPTION_RECORD). |
5089 | // Instead, we have to look at the number of exception parameters and calculate the size. |
5090 | _ASSERTE(pRecord != NULL); |
5091 | WriteExceptionRecordHelper(espOSRecord, pRecord); |
5092 | espRecord = CORDB_ADDRESS_TO_PTR(espOSRecord); |
5093 | |
5094 | esp = min(espOSContext, espOSRecord); |
5095 | } |
5096 | } |
5097 | |
5098 | // If we haven't reused the pointers, then push everything at the leaf of the stack. |
5099 | if (espContext == NULL) |
5100 | { |
5101 | _ASSERTE(espRecord == NULL); |
5102 | |
5103 | // Push on full Context and ExceptionRecord structures. We'll then push pointers to these, |
5104 | // and those pointers will serve as the actual args to the function. |
5105 | espContext = CORDB_ADDRESS_TO_PTR(PushHelper(&esp, &ctx, TRUE)); |
5106 | |
5107 | // If caller didn't pass an exception-record, then we're not being hijacked at an exception. |
5108 | // We'll just pass NULL for the exception-record to the Hijack function. |
5109 | if (pRecord != NULL) |
5110 | { |
5111 | espRecord = CORDB_ADDRESS_TO_PTR(PushHelper(&esp, pRecord, TRUE)); |
5112 | } |
5113 | } |
5114 | |
5115 | if(pRemoteContextAddr != NULL) |
5116 | { |
5117 | *pRemoteContextAddr = PTR_TO_CORDB_ADDRESS(espContext); |
5118 | } |
5119 | |
5120 | // |
5121 | // Push args onto the stack to be able to call the hijack function |
5122 | // |
5123 | |
5124 | // Prototype of hijack is: |
5125 | // void __stdcall ExceptionHijackWorker(CONTEXT * pContext, EXCEPTION_RECORD * pRecord, EHijackReason, void * pData) |
5126 | // Set up everything so that the hijack stub can just do a "call" instruction. |
5127 | // |
5128 | // Regarding stack overflow: We could do an explicit check against the thread's stack base limit. |
5129 | // However, we don't need an explicit overflow check because if the stack does overflow, |
5130 | // the hijack will just hit a regular stack-overflow exception. |
5131 | #if defined(_TARGET_X86_) // TARGET |
5132 | // X86 calling convention is to push args on the stack in reverse order. |
5133 | // If we fail here, the stack is written, but esp hasn't been committed yet so it shouldn't matter. |
5134 | PushHelper(&esp, &pData, TRUE); |
5135 | PushHelper(&esp, &reason, TRUE); |
5136 | PushHelper(&esp, &espRecord, TRUE); |
5137 | PushHelper(&esp, &espContext, TRUE); |
5138 | #elif defined (_TARGET_AMD64_) // TARGET |
5139 | // AMD64 calling convention is to place first 4 parameters in: rcx, rdx, r8 and r9 |
5140 | ctx.Rcx = (DWORD64) espContext; |
5141 | ctx.Rdx = (DWORD64) espRecord; |
5142 | ctx.R8 = (DWORD64) reason; |
5143 | ctx.R9 = (DWORD64) pData; |
5144 | |
5145 | // Caller must allocate stack space to spill for args. |
5146 | // Push the arguments onto the outgoing argument homes. |
5147 | // Make sure we push pointer-sized values to keep the stack aligned. |
5148 | PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.R9)), FALSE); |
5149 | PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.R8)), FALSE); |
5150 | PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.Rdx)), FALSE); |
5151 | PushHelper(&esp, reinterpret_cast<SIZE_T *>(&(ctx.Rcx)), FALSE); |
5152 | #elif defined(_TARGET_ARM_) |
5153 | ctx.R0 = (DWORD)espContext; |
5154 | ctx.R1 = (DWORD)espRecord; |
5155 | ctx.R2 = (DWORD)reason; |
5156 | ctx.R3 = (DWORD)pData; |
5157 | #elif defined(_TARGET_ARM64_) |
5158 | ctx.X0 = (DWORD64)espContext; |
5159 | ctx.X1 = (DWORD64)espRecord; |
5160 | ctx.X2 = (DWORD64)reason; |
5161 | ctx.X3 = (DWORD64)pData; |
5162 | #else |
5163 | PORTABILITY_ASSERT("CordbThread::HijackForUnhandledException is not implemented on this platform." ); |
5164 | #endif |
5165 | SetSP(&ctx, CORDB_ADDRESS_TO_TADDR(esp)); |
5166 | |
5167 | // @dbgtodo cross-plat - not cross-platform safe |
5168 | SetIP(&ctx, pfnHijackFunction); |
5169 | |
5170 | // |
5171 | // Commit the context. |
5172 | // |
5173 | hr = m_pMutableTarget->SetThreadContext(dwThreadId, sizeof(ctx), reinterpret_cast<BYTE*> (&ctx)); |
5174 | IfFailThrow(hr); |
5175 | } |
5176 | |
5177 | // Return the filter CONTEXT on the LS. |
5178 | VMPTR_CONTEXT DacDbiInterfaceImpl::GetManagedStoppedContext(VMPTR_Thread vmThread) |
5179 | { |
5180 | DD_ENTER_MAY_THROW; |
5181 | |
5182 | VMPTR_CONTEXT vmContext = VMPTR_CONTEXT::NullPtr(); |
5183 | |
5184 | Thread * pThread = vmThread.GetDacPtr(); |
5185 | if (pThread->GetInteropDebuggingHijacked()) |
5186 | { |
5187 | _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); |
5188 | vmContext = VMPTR_CONTEXT::NullPtr(); |
5189 | } |
5190 | else |
5191 | { |
5192 | DT_CONTEXT * pLSContext = reinterpret_cast<DT_CONTEXT *>(pThread->GetFilterContext()); |
5193 | if (pLSContext != NULL) |
5194 | { |
5195 | _ASSERTE(!ISREDIRECTEDTHREAD(pThread)); |
5196 | vmContext.SetHostPtr(pLSContext); |
5197 | } |
5198 | else if (ISREDIRECTEDTHREAD(pThread)) |
5199 | { |
5200 | pLSContext = reinterpret_cast<DT_CONTEXT *>(GETREDIRECTEDCONTEXT(pThread)); |
5201 | _ASSERTE(pLSContext != NULL); |
5202 | |
5203 | if (pLSContext != NULL) |
5204 | { |
5205 | vmContext.SetHostPtr(pLSContext); |
5206 | } |
5207 | } |
5208 | } |
5209 | |
5210 | return vmContext; |
5211 | } |
5212 | |
5213 | // Return a TargetBuffer for the raw vararg signature. |
5214 | TargetBuffer DacDbiInterfaceImpl::GetVarArgSig(CORDB_ADDRESS VASigCookieAddr, |
5215 | CORDB_ADDRESS * pArgBase) |
5216 | { |
5217 | DD_ENTER_MAY_THROW; |
5218 | |
5219 | _ASSERTE(pArgBase != NULL); |
5220 | *pArgBase = NULL; |
5221 | |
5222 | // First, read the VASigCookie pointer. |
5223 | TADDR taVASigCookie = NULL; |
5224 | SafeReadStructOrThrow(VASigCookieAddr, &taVASigCookie); |
5225 | |
5226 | // Now create a DAC copy of VASigCookie. |
5227 | VASigCookie * pVACookie = PTR_VASigCookie(taVASigCookie); |
5228 | |
5229 | // Figure out where the first argument is. |
5230 | #if defined(_TARGET_X86_) // (STACK_GROWS_DOWN_ON_ARGS_WALK) |
5231 | *pArgBase = VASigCookieAddr + pVACookie->sizeOfArgs; |
5232 | #else // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) |
5233 | *pArgBase = VASigCookieAddr + sizeof(VASigCookie *); |
5234 | #endif // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) |
5235 | |
5236 | return TargetBuffer(PTR_TO_CORDB_ADDRESS(pVACookie->signature.GetRawSig()), |
5237 | pVACookie->signature.GetRawSigLen()); |
5238 | } |
5239 | |
5240 | // returns TRUE if the type requires 8-byte alignment |
5241 | BOOL DacDbiInterfaceImpl::RequiresAlign8(VMPTR_TypeHandle thExact) |
5242 | { |
5243 | DD_ENTER_MAY_THROW; |
5244 | |
5245 | #ifdef FEATURE_64BIT_ALIGNMENT |
5246 | TypeHandle th = TypeHandle::FromPtr(thExact.GetDacPtr()); |
5247 | PTR_MethodTable mt = th.AsMethodTable(); |
5248 | |
5249 | return mt->RequiresAlign8(); |
5250 | #else |
5251 | ThrowHR(E_NOTIMPL); |
5252 | #endif |
5253 | } |
5254 | |
5255 | // Resolve the raw generics token to the real generics type token. The resolution is based on the |
5256 | // given index. |
5257 | GENERICS_TYPE_TOKEN DacDbiInterfaceImpl::ResolveExactGenericArgsToken(DWORD dwExactGenericArgsTokenIndex, |
5258 | GENERICS_TYPE_TOKEN rawToken) |
5259 | { |
5260 | DD_ENTER_MAY_THROW; |
5261 | |
5262 | if (dwExactGenericArgsTokenIndex == 0) |
5263 | { |
5264 | // In this case the real generics type token is the MethodTable of the "this" object. |
5265 | // Note that we want the target address here. |
5266 | |
5267 | // Incoming rawToken is actually a PTR_Object for the 'this' pointer. |
5268 | // Need to do some casting to convert GENERICS_TYPE_TOKEN --> PTR_Object |
5269 | TADDR addrObjThis = CORDB_ADDRESS_TO_TADDR(rawToken); |
5270 | PTR_Object pObjThis = dac_cast<PTR_Object>(addrObjThis); |
5271 | |
5272 | |
5273 | PTR_MethodTable pMT = pObjThis->GetMethodTable(); |
5274 | |
5275 | // Now package up the PTR_MethodTable back into a GENERICS_TYPE_TOKEN |
5276 | TADDR addrMT = dac_cast<TADDR>(pMT); |
5277 | GENERICS_TYPE_TOKEN realToken = (GENERICS_TYPE_TOKEN) addrMT; |
5278 | return realToken; |
5279 | } |
5280 | else if (dwExactGenericArgsTokenIndex == (DWORD)ICorDebugInfo::TYPECTXT_ILNUM) |
5281 | { |
5282 | // rawToken is already initialized correctly. Nothing to do here. |
5283 | return rawToken; |
5284 | } |
5285 | |
5286 | // The index of the generics type token should not be anything else. |
5287 | // This is indeed an error condition, and so we throw here. |
5288 | _ASSERTE(!"DDII::REGAT - Unexpected generics type token index." ); |
5289 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
5290 | } |
5291 | |
5292 | // Check if the given method is an IL stub or an LCD method. |
5293 | IDacDbiInterface::DynamicMethodType DacDbiInterfaceImpl::IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) |
5294 | { |
5295 | DD_ENTER_MAY_THROW; |
5296 | |
5297 | MethodDesc * pMD = vmMethodDesc.GetDacPtr(); |
5298 | |
5299 | if (pMD->IsILStub()) |
5300 | { |
5301 | return kILStub; |
5302 | } |
5303 | else if (pMD->IsLCGMethod()) |
5304 | { |
5305 | return kLCGMethod; |
5306 | } |
5307 | else |
5308 | { |
5309 | return kNone; |
5310 | } |
5311 | } |
5312 | |
5313 | //--------------------------------------------------------------------------------------- |
5314 | // |
5315 | // Determine whether the specified thread is at a GC safe place. |
5316 | // |
5317 | // Arguments: |
5318 | // vmThread - the thread to be examined |
5319 | // |
5320 | // Return Value: |
5321 | // Return TRUE if the thread is at a GC safe place. |
5322 | // and under what conditions |
5323 | // |
5324 | // Notes: |
5325 | // This function basically does a one-frame stackwalk. |
5326 | // The logic is adopted from Debugger::IsThreadAtSafePlace(). |
5327 | // |
5328 | |
5329 | BOOL DacDbiInterfaceImpl::IsThreadAtGCSafePlace(VMPTR_Thread vmThread) |
5330 | { |
5331 | DD_ENTER_MAY_THROW; |
5332 | |
5333 | BOOL fIsGCSafe = FALSE; |
5334 | Thread * pThread = vmThread.GetDacPtr(); |
5335 | |
5336 | // Check if the runtime has entered "Shutdown for Finalizer" mode. |
5337 | if ((g_fEEShutDown & ShutDown_Finalize2) != 0) |
5338 | { |
5339 | fIsGCSafe = TRUE; |
5340 | } |
5341 | else |
5342 | { |
5343 | T_CONTEXT ctx; |
5344 | REGDISPLAY rd; |
5345 | SetUpRegdisplayForStackWalk(pThread, &ctx, &rd); |
5346 | |
5347 | ULONG32 flags = (QUICKUNWIND | HANDLESKIPPEDFRAMES | DISABLE_MISSING_FRAME_DETECTION); |
5348 | |
5349 | StackFrameIterator iter; |
5350 | iter.Init(pThread, pThread->GetFrame(), &rd, flags); |
5351 | |
5352 | CrawlFrame * pCF = &(iter.m_crawl); |
5353 | if (pCF->IsFrameless() && pCF->IsActiveFunc()) |
5354 | { |
5355 | if (pCF->IsGcSafe()) |
5356 | { |
5357 | fIsGCSafe = TRUE; |
5358 | } |
5359 | } |
5360 | } |
5361 | |
5362 | return fIsGCSafe; |
5363 | } |
5364 | |
5365 | //--------------------------------------------------------------------------------------- |
5366 | // |
5367 | // Return a partial user state of the specified thread. The returned user state doesn't contain |
5368 | // information about USER_UNSAFE_POINT. The caller needs to call IsThreadAtGCSafePlace() to get |
5369 | // the full user state. |
5370 | // |
5371 | // Arguments: |
5372 | // vmThread - the specified thread |
5373 | // |
5374 | // Return Value: |
5375 | // Return the partial user state except for USER_UNSAFE_POINT |
5376 | // |
5377 | |
5378 | CorDebugUserState DacDbiInterfaceImpl::GetPartialUserState(VMPTR_Thread vmThread) |
5379 | { |
5380 | DD_ENTER_MAY_THROW; |
5381 | |
5382 | Thread * pThread = vmThread.GetDacPtr(); |
5383 | Thread::ThreadState ts = pThread->GetSnapshotState(); |
5384 | |
5385 | UINT result = 0; |
5386 | if (ts & Thread::TS_Background) |
5387 | { |
5388 | result |= USER_BACKGROUND; |
5389 | } |
5390 | |
5391 | if (ts & Thread::TS_Unstarted) |
5392 | { |
5393 | result |= USER_UNSTARTED; |
5394 | } |
5395 | |
5396 | // Don't report a StopRequested if the thread has actually stopped. |
5397 | if (ts & Thread::TS_Dead) |
5398 | { |
5399 | result |= USER_STOPPED; |
5400 | } |
5401 | |
5402 | // The interruptible flag is unreliable (see issue 699245) |
5403 | // The Debugger_SleepWaitJoin is always accurate when it is present, but it is still |
5404 | // just a band-aid fix to cover some of the race conditions interruptible has. |
5405 | |
5406 | if (ts & Thread::TS_Interruptible || pThread->HasThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin)) |
5407 | { |
5408 | result |= USER_WAIT_SLEEP_JOIN; |
5409 | } |
5410 | |
5411 | // CoreCLR does not support user-requested thread suspension |
5412 | _ASSERTE(!(ts & Thread::TS_UserSuspendPending)); |
5413 | |
5414 | if (pThread->IsThreadPoolThread()) |
5415 | { |
5416 | result |= USER_THREADPOOL; |
5417 | } |
5418 | |
5419 | return (CorDebugUserState)result; |
5420 | } |
5421 | |
5422 | //--------------------------------------------------------------------------------------- |
5423 | // |
5424 | // Look up the EnC version number of a particular jitted instance of a managed method. |
5425 | // |
5426 | // Arguments: |
5427 | // pModule - the module containing the managed method |
5428 | // vmMethodDesc - the MethodDesc of the managed method |
5429 | // mdMethod - the MethodDef metadata token of the managed method |
5430 | // pNativeStartAddress - the native start address of the jitted code |
5431 | // pJittedInstanceEnCVersion - out parameter; the version number of the version |
5432 | // corresponding to the specified native start address |
5433 | // pLatestEnCVersion - out parameter; the version number of the latest version |
5434 | // |
5435 | // Assumptions: |
5436 | // vmMethodDesc and mdMethod must match (see below). |
5437 | // |
5438 | // Notes: |
5439 | // mdMethod is not strictly necessary, since we can always get that from vmMethodDesc. |
5440 | // It is just a perf optimization since the caller has the metadata token around already. |
5441 | // |
5442 | // Today, there is no way to retrieve the EnC version number from the RS data structures. |
5443 | // This primitive uses DAC to retrieve it from the LS data structures. This function may |
5444 | // very well be ripped out in the future if we DACize this information, but the current |
5445 | // thinking is that some of the RS data structures will remain, most likely in a reduced form. |
5446 | // |
5447 | |
5448 | void DacDbiInterfaceImpl::LookupEnCVersions(Module* pModule, |
5449 | VMPTR_MethodDesc vmMethodDesc, |
5450 | mdMethodDef mdMethod, |
5451 | CORDB_ADDRESS pNativeStartAddress, |
5452 | SIZE_T * pLatestEnCVersion, |
5453 | SIZE_T * pJittedInstanceEnCVersion /* = NULL */) |
5454 | { |
5455 | MethodDesc * pMD = vmMethodDesc.GetDacPtr(); |
5456 | |
5457 | // make sure the vmMethodDesc and mdMethod match |
5458 | _ASSERTE(pMD->GetMemberDef() == mdMethod); |
5459 | |
5460 | _ASSERTE(pLatestEnCVersion != NULL); |
5461 | |
5462 | // @dbgtodo inspection - once we do EnC, stop using DMIs. |
5463 | // If the method wasn't EnCed, DMIs may not exist. And since this is DAC, we can't create them. |
5464 | |
5465 | // We may not have the memory for the DebuggerMethodInfos in a minidump. |
5466 | // When dump debugging EnC information isn't very useful so just fallback |
5467 | // to default version. |
5468 | DebuggerMethodInfo * pDMI = NULL; |
5469 | DebuggerJitInfo * pDJI = NULL; |
5470 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY |
5471 | { |
5472 | pDMI = g_pDebugger->GetOrCreateMethodInfo(pModule, mdMethod); |
5473 | if (pDMI != NULL) |
5474 | { |
5475 | pDJI = pDMI->FindJitInfo(pMD, CORDB_ADDRESS_TO_TADDR(pNativeStartAddress)); |
5476 | } |
5477 | } |
5478 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; |
5479 | if (pDJI != NULL) |
5480 | { |
5481 | if (pJittedInstanceEnCVersion != NULL) |
5482 | { |
5483 | *pJittedInstanceEnCVersion = pDJI->m_encVersion; |
5484 | } |
5485 | *pLatestEnCVersion = pDMI->GetCurrentEnCVersion(); |
5486 | } |
5487 | else |
5488 | { |
5489 | // If we have no DMI/DJI, then we must never have EnCed. So we can use default EnC info |
5490 | // Several cases where we don't have a DMI/DJI: |
5491 | // - LCG methods |
5492 | // - method was never "touched" by debugger. (DJIs are created lazily). |
5493 | if (pJittedInstanceEnCVersion != NULL) |
5494 | { |
5495 | *pJittedInstanceEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; |
5496 | } |
5497 | *pLatestEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION; |
5498 | } |
5499 | } |
5500 | |
5501 | // Get the address of the Debugger control block on the helper thread |
5502 | // Arguments: none |
5503 | // Return Value: The remote address of the Debugger control block allocated on the helper thread |
5504 | // if it has been successfully allocated or NULL otherwise. |
5505 | CORDB_ADDRESS DacDbiInterfaceImpl::GetDebuggerControlBlockAddress() |
5506 | { |
5507 | DD_ENTER_MAY_THROW; |
5508 | |
5509 | if ((g_pDebugger != NULL) && |
5510 | (g_pDebugger->m_pRCThread != NULL)) |
5511 | { |
5512 | return CORDB_ADDRESS(dac_cast<TADDR>(g_pDebugger->m_pRCThread->GetDCB())); |
5513 | } |
5514 | |
5515 | return NULL; |
5516 | } |
5517 | |
5518 | // DacDbi API: Get the context for a particular thread of the target process |
5519 | void DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) |
5520 | { |
5521 | DD_ENTER_MAY_THROW |
5522 | |
5523 | _ASSERTE(pContextBuffer != NULL); |
5524 | |
5525 | Thread * pThread = vmThread.GetDacPtr(); |
5526 | |
5527 | // @dbgtodo Once the filter context is removed, then we should always |
5528 | // start with the leaf CONTEXT. |
5529 | DT_CONTEXT * pFilterContext = reinterpret_cast<DT_CONTEXT *>(pThread->GetFilterContext()); |
5530 | |
5531 | if (pFilterContext == NULL) |
5532 | { |
5533 | // If the filter context is NULL, then we use the true context of the thread. |
5534 | pContextBuffer->ContextFlags = CONTEXT_ALL; |
5535 | HRESULT hr = m_pTarget->GetThreadContext(pThread->GetOSThreadId(), |
5536 | pContextBuffer->ContextFlags, |
5537 | sizeof(*pContextBuffer), |
5538 | reinterpret_cast<BYTE *>(pContextBuffer)); |
5539 | if (hr == E_NOTIMPL) |
5540 | { |
5541 | // GetThreadContext is not implemented on this data target. |
5542 | // That's why we have to make do with context we can obtain from Frames explicitly stored in Thread object. |
5543 | // It suffices for managed debugging stackwalk. |
5544 | REGDISPLAY tmpRd = {}; |
5545 | T_CONTEXT tmpContext = {}; |
5546 | FillRegDisplay(&tmpRd, &tmpContext); |
5547 | |
5548 | // Going through thread Frames and looking for first (deepest one) one that |
5549 | // that has context available for stackwalking (SP and PC) |
5550 | // For example: RedirectedThreadFrame, InlinedCallFrame, HelperMethodFrame, ComPlusMethodFrame |
5551 | Frame *frame = pThread->GetFrame(); |
5552 | while (frame != NULL && frame != FRAME_TOP) |
5553 | { |
5554 | frame->UpdateRegDisplay(&tmpRd); |
5555 | if (GetRegdisplaySP(&tmpRd) != 0 && GetControlPC(&tmpRd) != 0) |
5556 | { |
5557 | UpdateContextFromRegDisp(&tmpRd, &tmpContext); |
5558 | CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); |
5559 | pContextBuffer->ContextFlags = DT_CONTEXT_CONTROL; |
5560 | return; |
5561 | } |
5562 | frame = frame->Next(); |
5563 | } |
5564 | |
5565 | // It looks like this thread is not running managed code. |
5566 | ZeroMemory(pContextBuffer, sizeof(*pContextBuffer)); |
5567 | } |
5568 | else |
5569 | { |
5570 | IfFailThrow(hr); |
5571 | } |
5572 | } |
5573 | else |
5574 | { |
5575 | *pContextBuffer = *pFilterContext; |
5576 | } |
5577 | |
5578 | } // DacDbiInterfaceImpl::GetContext |
5579 | |
5580 | // Create a VMPTR_Object from a target object address |
5581 | // @dbgtodo validate the VMPTR_Object is in fact a object, possibly by DACizing |
5582 | // Object::Validate |
5583 | VMPTR_Object DacDbiInterfaceImpl::GetObject(CORDB_ADDRESS ptr) |
5584 | { |
5585 | DD_ENTER_MAY_THROW; |
5586 | |
5587 | VMPTR_Object vmObj = VMPTR_Object::NullPtr(); |
5588 | vmObj.SetDacTargetPtr(CORDB_ADDRESS_TO_TADDR(ptr)); |
5589 | return vmObj; |
5590 | } |
5591 | |
5592 | HRESULT DacDbiInterfaceImpl::EnableNGENPolicy(CorDebugNGENPolicy ePolicy) |
5593 | { |
5594 | return E_NOTIMPL; |
5595 | } |
5596 | |
5597 | HRESULT DacDbiInterfaceImpl::SetNGENCompilerFlags(DWORD dwFlags) |
5598 | { |
5599 | DD_ENTER_MAY_THROW; |
5600 | |
5601 | #ifndef FEATURE_PREJIT |
5602 | return CORDBG_E_NGEN_NOT_SUPPORTED; |
5603 | #else |
5604 | // verify that we are still early enough in runtime lifecycle to mutate these |
5605 | // flags. Typically this is done in the CreateProcess event though it is possible |
5606 | // to do it even earlier |
5607 | if(!Debugger::s_fCanChangeNgenFlags) |
5608 | return CORDBG_E_MUST_BE_IN_CREATE_PROCESS; |
5609 | |
5610 | BOOL fAllowOpt = |
5611 | ((dwFlags & CORDEBUG_JIT_DISABLE_OPTIMIZATION) != CORDEBUG_JIT_DISABLE_OPTIMIZATION); |
5612 | PEFile::SetNGENDebugFlags(fAllowOpt); |
5613 | return S_OK; |
5614 | #endif |
5615 | } |
5616 | |
5617 | HRESULT DacDbiInterfaceImpl::GetNGENCompilerFlags(DWORD *pdwFlags) |
5618 | { |
5619 | DD_ENTER_MAY_THROW; |
5620 | |
5621 | #ifndef FEATURE_PREJIT |
5622 | return CORDBG_E_NGEN_NOT_SUPPORTED; |
5623 | #else |
5624 | BOOL fAllowOpt = TRUE; |
5625 | PEFile::GetNGENDebugFlags(&fAllowOpt); |
5626 | if(!fAllowOpt) |
5627 | { |
5628 | *pdwFlags = CORDEBUG_JIT_DISABLE_OPTIMIZATION; |
5629 | } |
5630 | else |
5631 | { |
5632 | *pdwFlags = CORDEBUG_JIT_DEFAULT; |
5633 | } |
5634 | |
5635 | return S_OK; |
5636 | #endif |
5637 | } |
5638 | |
5639 | typedef DPTR(OBJECTREF) PTR_ObjectRef; |
5640 | |
5641 | // Create a VMPTR_Object from an address which points to a reference to an object |
5642 | // @dbgtodo validate the VMPTR_Object is in fact a object, possibly by DACizing |
5643 | // Object::Validate |
5644 | VMPTR_Object DacDbiInterfaceImpl::GetObjectFromRefPtr(CORDB_ADDRESS ptr) |
5645 | { |
5646 | DD_ENTER_MAY_THROW; |
5647 | |
5648 | VMPTR_Object vmObj = VMPTR_Object::NullPtr(); |
5649 | PTR_ObjectRef objRef = PTR_ObjectRef(CORDB_ADDRESS_TO_TADDR(ptr)); |
5650 | vmObj.SetDacTargetPtr(PTR_TO_TADDR(*objRef)); |
5651 | |
5652 | return vmObj; |
5653 | } |
5654 | |
5655 | // Create a VMPTR_OBJECTHANDLE from a handle |
5656 | VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetVmObjectHandle(CORDB_ADDRESS handleAddress) |
5657 | { |
5658 | DD_ENTER_MAY_THROW; |
5659 | |
5660 | VMPTR_OBJECTHANDLE vmObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
5661 | vmObjHandle.SetDacTargetPtr(CORDB_ADDRESS_TO_TADDR(handleAddress)); |
5662 | |
5663 | return vmObjHandle; |
5664 | } |
5665 | |
5666 | |
5667 | // Validate that the VMPTR_OBJECTHANDLE refers to a legitimate managed object |
5668 | BOOL DacDbiInterfaceImpl::IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle) |
5669 | { |
5670 | DD_ENTER_MAY_THROW; |
5671 | |
5672 | BOOL ret = FALSE; |
5673 | // this may cause unallocated debuggee memory to be read |
5674 | // SEH exceptions will be caught |
5675 | EX_TRY |
5676 | { |
5677 | OBJECTREF objRef = ObjectFromHandle((OBJECTHANDLE)vmHandle.GetDacPtr()); |
5678 | |
5679 | // NULL is certainly valid... |
5680 | if (objRef != NULL) |
5681 | { |
5682 | if (objRef->ValidateObjectWithPossibleAV()) |
5683 | { |
5684 | ret = TRUE; |
5685 | } |
5686 | } |
5687 | } |
5688 | EX_CATCH |
5689 | { |
5690 | } |
5691 | EX_END_CATCH(SwallowAllExceptions); |
5692 | |
5693 | return ret; |
5694 | } |
5695 | |
5696 | // determines if the specified module is a WinRT module |
5697 | HRESULT DacDbiInterfaceImpl::IsWinRTModule(VMPTR_Module vmModule, BOOL& isWinRT) |
5698 | { |
5699 | DD_ENTER_MAY_THROW; |
5700 | |
5701 | HRESULT hr = S_OK; |
5702 | isWinRT = FALSE; |
5703 | |
5704 | EX_TRY |
5705 | { |
5706 | Module* pModule = vmModule.GetDacPtr(); |
5707 | isWinRT = pModule->GetFile()->GetAssembly()->IsWindowsRuntime(); |
5708 | } |
5709 | EX_CATCH_HRESULT(hr); |
5710 | |
5711 | return hr; |
5712 | } |
5713 | |
5714 | // Determines the app domain id for the object refered to by a given VMPTR_OBJECTHANDLE |
5715 | ULONG DacDbiInterfaceImpl::GetAppDomainIdFromVmObjectHandle(VMPTR_OBJECTHANDLE vmHandle) |
5716 | { |
5717 | DD_ENTER_MAY_THROW; |
5718 | |
5719 | OBJECTHANDLE handle = (OBJECTHANDLE) vmHandle.GetDacPtr(); |
5720 | return HndGetHandleADIndex(handle).m_dwIndex; |
5721 | } |
5722 | |
5723 | // Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address |
5724 | CORDB_ADDRESS DacDbiInterfaceImpl::GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle) |
5725 | { |
5726 | DD_ENTER_MAY_THROW; |
5727 | |
5728 | CORDB_ADDRESS handle = vmHandle.GetDacPtr(); |
5729 | |
5730 | return handle; |
5731 | } |
5732 | |
5733 | // Create a TargetBuffer which describes the location of the object |
5734 | TargetBuffer DacDbiInterfaceImpl::GetObjectContents(VMPTR_Object vmObj) |
5735 | { |
5736 | DD_ENTER_MAY_THROW; |
5737 | PTR_Object objPtr = vmObj.GetDacPtr(); |
5738 | |
5739 | _ASSERTE(objPtr->GetSize() <= 0xffffffff); |
5740 | return TargetBuffer(PTR_TO_TADDR(objPtr), (ULONG)objPtr->GetSize()); |
5741 | } |
5742 | |
5743 | // ============================================================================ |
5744 | // functions to get information about objects referenced via an instance of CordbReferenceValue or |
5745 | // CordbHandleValue |
5746 | // ============================================================================ |
5747 | |
5748 | // DacDbiInterfaceImpl::FastSanityCheckObject |
5749 | // Helper function for CheckRef. Sanity check an object. |
5750 | // We use a fast and easy check to improve confidence that objPtr points to a valid object. |
5751 | // We can't tell cheaply if this is really a valid object (that would require walking the GC heap), but at |
5752 | // least we can check if we get an EEClass from the supposed method table and then get the method table from |
5753 | // the class. If we can, we have improved the probability that the object is valid. |
5754 | // Arguments: |
5755 | // input: objPtr - address of the object we are checking |
5756 | // Return Value: E_INVALIDARG or S_OK. |
5757 | HRESULT DacDbiInterfaceImpl::FastSanityCheckObject(PTR_Object objPtr) |
5758 | { |
5759 | CONTRACTL |
5760 | { |
5761 | SO_NOT_MAINLINE; |
5762 | NOTHROW; |
5763 | GC_NOTRIGGER; |
5764 | } |
5765 | CONTRACTL_END; |
5766 | |
5767 | HRESULT hr = S_OK; |
5768 | |
5769 | EX_TRY |
5770 | { |
5771 | // NULL is certainly valid... |
5772 | if (objPtr != NULL) |
5773 | { |
5774 | if (!objPtr->ValidateObjectWithPossibleAV()) |
5775 | { |
5776 | LOG((LF_CORDB, LL_INFO10000, "GOI: object methodtable-class invariant doesn't hold.\n" )); |
5777 | hr = E_INVALIDARG; |
5778 | } |
5779 | } |
5780 | } |
5781 | EX_CATCH |
5782 | { |
5783 | LOG((LF_CORDB, LL_INFO10000, "GOI: exception indicated ref is bad.\n" )); |
5784 | hr = E_INVALIDARG; |
5785 | } |
5786 | EX_END_CATCH(SwallowAllExceptions); |
5787 | |
5788 | return hr; |
5789 | } // DacDbiInterfaceImpl::FastSanityCheckObject |
5790 | |
5791 | // Perform a sanity check on an object address to determine if this _could be_ a valid object. |
5792 | // We can't tell this for certain without walking the GC heap, but we do some fast tests to rule |
5793 | // out clearly invalid object addresses. See code:DacDbiInterfaceImpl::FastSanityCheckObject for more |
5794 | // details. |
5795 | // Arguments: |
5796 | // input: objPtr - address of the object we are checking |
5797 | // Return Value: |
5798 | // objRefBad - true iff we have determined the address cannot be pointing to a valid object. |
5799 | // Note that a value of false doesn't necessarily guarantee the object is really |
5800 | // valid |
5801 | bool DacDbiInterfaceImpl::CheckRef(PTR_Object objPtr) |
5802 | { |
5803 | bool objRefBad = false; |
5804 | |
5805 | // Shortcut null references now... |
5806 | if (objPtr == NULL) |
5807 | { |
5808 | LOG((LF_CORDB, LL_INFO10000, "D::GOI: ref is NULL.\n" )); |
5809 | |
5810 | objRefBad = true; |
5811 | } |
5812 | else |
5813 | { |
5814 | // Try to verify the integrity of the object. This is not fool proof. |
5815 | // @todo - this whole idea of expecting AVs is broken, but it does rule |
5816 | // out a fair bit of rubbish. Find another |
5817 | // way to test if the object is valid? |
5818 | if (FAILED(FastSanityCheckObject(objPtr))) |
5819 | { |
5820 | LOG((LF_CORDB, LL_INFO10000, "D::GOI: address is not a valid object.\n" )); |
5821 | |
5822 | objRefBad = true; |
5823 | } |
5824 | } |
5825 | |
5826 | return objRefBad; |
5827 | } // DacDbiInterfaceImpl::CheckRef |
5828 | |
5829 | // DacDbiInterfaceImpl::InitObjectData |
5830 | // Initialize basic object information: type handle, object size, offset to fields and expanded type |
5831 | // information. |
5832 | // Arguments: |
5833 | // input: objPtr - address of object of interest |
5834 | // vmAppDomain - AppDomain for the type f the object |
5835 | // output: pObjectData - object information |
5836 | // Note: It is assumed that pObjectData is non-null. |
5837 | void DacDbiInterfaceImpl::InitObjectData(PTR_Object objPtr, |
5838 | VMPTR_AppDomain vmAppDomain, |
5839 | DebuggerIPCE_ObjectData * pObjectData) |
5840 | { |
5841 | _ASSERTE(pObjectData != NULL); |
5842 | // @todo - this is still dangerous because the object may still be invalid. |
5843 | VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); |
5844 | vmTypeHandle.SetDacTargetPtr(objPtr->GetGCSafeTypeHandle().AsTAddr()); |
5845 | |
5846 | // Save basic object info. |
5847 | pObjectData->objSize = objPtr->GetSize(); |
5848 | pObjectData->objOffsetToVars = dac_cast<TADDR>((objPtr)->GetData()) - dac_cast<TADDR>(objPtr); |
5849 | |
5850 | TypeHandleToExpandedTypeInfo(AllBoxed, vmAppDomain, vmTypeHandle, &(pObjectData->objTypeData)); |
5851 | |
5852 | // If this is a string object, set the type to ELEMENT_TYPE_STRING. |
5853 | if (objPtr->GetGCSafeMethodTable() == g_pStringClass) |
5854 | { |
5855 | pObjectData->objTypeData.elementType = ELEMENT_TYPE_STRING; |
5856 | if(pObjectData->objSize < MIN_OBJECT_SIZE) |
5857 | { |
5858 | pObjectData->objSize = PtrAlign(pObjectData->objSize); |
5859 | } |
5860 | } |
5861 | } // DacDbiInterfaceImpl::InitObjectData |
5862 | |
5863 | // DAC/DBI API |
5864 | |
5865 | // Get object information for a TypedByRef object (System.TypedReference). |
5866 | |
5867 | // These are objects that contain a managed pointer to a location and the type of the value at that location. |
5868 | // They are most commonly used for varargs but also may be used for parameters and locals. They are |
5869 | // stack-allocated. They provide a means for adding dynamic type information to a value type, whereas boxing |
5870 | // provides only static type information. This means they can be passed as reference parameters to |
5871 | // polymorphic methods that don't statically restrict the type of arguments they can receive. |
5872 | |
5873 | // Although they are represented simply as an address, unlike other object references, they don't point |
5874 | // directly to the object. Instead, there is an extra level of indirection. The reference points to a struct |
5875 | // that contains the address of the object, so we need to treat them differently. They have their own |
5876 | // CorElementType (ELEMENT_TYPE_TYPEDBYREF) which makes it possible to identify this special case. |
5877 | |
5878 | // Example: |
5879 | // static int AddABunchOfInts (__arglist) |
5880 | // { |
5881 | // int result = 0; |
5882 | // |
5883 | // System.ArgIterator iter = new System.ArgIterator (__arglist); |
5884 | // int argCount = iter.GetRemainingCount(); |
5885 | // |
5886 | // for (int i = 0; i < argCount; i++) |
5887 | // { |
5888 | // System.TypedReference typedRef = iter.GetNextArg(); |
5889 | // result += (int)TypedReference.ToObject(typedRef); |
5890 | // } |
5891 | // |
5892 | // return result; |
5893 | // } |
5894 | // |
5895 | // static int Main (string[] args) |
5896 | // { |
5897 | // int result = AddABunchOfInts (__arglist (2, 3, 4)); |
5898 | // Console.WriteLine ("Answer: {0}", result); |
5899 | // |
5900 | // if (result != 9) |
5901 | // return 1; |
5902 | // |
5903 | // return 0; |
5904 | // } |
5905 | |
5906 | // Initializes the objRef and typedByRefType fields of pObjectData (type info for the referent). |
5907 | void DacDbiInterfaceImpl::GetTypedByRefInfo(CORDB_ADDRESS pTypedByRef, |
5908 | VMPTR_AppDomain vmAppDomain, |
5909 | DebuggerIPCE_ObjectData * pObjectData) |
5910 | { |
5911 | DD_ENTER_MAY_THROW; |
5912 | |
5913 | // pTypedByRef is really the address of a TypedByRef struct rather than of a normal object. |
5914 | // The data field of the TypedByRef struct is the actual object ref. |
5915 | PTR_TypedByRef refAddr = PTR_TypedByRef(TADDR(pTypedByRef)); |
5916 | |
5917 | _ASSERTE(refAddr != NULL); |
5918 | _ASSERTE(pObjectData != NULL); |
5919 | |
5920 | // The type of the referent is in the type field of the TypedByRef. We need to initialize the object |
5921 | // data type information. |
5922 | TypeHandleToBasicTypeInfo(refAddr->type, |
5923 | &(pObjectData->typedByrefInfo.typedByrefType), |
5924 | vmAppDomain.GetDacPtr()); |
5925 | |
5926 | // The reference to the object is in the data field of the TypedByRef. |
5927 | CORDB_ADDRESS tempRef = dac_cast<TADDR>(refAddr->data); |
5928 | pObjectData->objRef = CORDB_ADDRESS_TO_PTR(tempRef); |
5929 | |
5930 | LOG((LF_CORDB, LL_INFO10000, "D::GASOI: sending REFANY result: " |
5931 | "ref=0x%08x, cls=0x%08x, mod=0x%p\n" , |
5932 | pObjectData->objRef, |
5933 | pObjectData->typedByrefType.metadataToken, |
5934 | pObjectData->typedByrefType.vmDomainFile.GetDacPtr())); |
5935 | } // DacDbiInterfaceImpl::GetTypedByRefInfo |
5936 | |
5937 | // Get the string data associated withn obj and put it into the pointers |
5938 | // DAC/DBI API |
5939 | // Get the string length and offset to string base for a string object |
5940 | void DacDbiInterfaceImpl::GetStringData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) |
5941 | { |
5942 | DD_ENTER_MAY_THROW; |
5943 | |
5944 | PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); |
5945 | LOG((LF_CORDB, LL_INFO10000, "D::GOI: The referent is a string.\n" )); |
5946 | |
5947 | if (objPtr->GetGCSafeMethodTable() != g_pStringClass) |
5948 | { |
5949 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
5950 | } |
5951 | |
5952 | PTR_StringObject pStrObj = dac_cast<PTR_StringObject>(objPtr); |
5953 | |
5954 | _ASSERTE(pStrObj != NULL); |
5955 | pObjectData->stringInfo.length = pStrObj->GetStringLength(); |
5956 | pObjectData->stringInfo.offsetToStringBase = (UINT_PTR) pStrObj->GetBufferOffset(); |
5957 | |
5958 | } // DacDbiInterfaceImpl::GetStringData |
5959 | |
5960 | |
5961 | // DAC/DBI API |
5962 | // Get information for an array type referent of an objRef, including rank, upper and lower |
5963 | // bounds, element size and type, and the number of elements. |
5964 | void DacDbiInterfaceImpl::GetArrayData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) |
5965 | { |
5966 | DD_ENTER_MAY_THROW; |
5967 | |
5968 | PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); |
5969 | PTR_MethodTable pMT = objPtr->GetGCSafeMethodTable(); |
5970 | |
5971 | if (!objPtr->GetGCSafeTypeHandle().IsArray()) |
5972 | { |
5973 | LOG((LF_CORDB, LL_INFO10000, |
5974 | "D::GASOI: object should be an array.\n" )); |
5975 | |
5976 | pObjectData->objRefBad = true; |
5977 | } |
5978 | else |
5979 | { |
5980 | PTR_ArrayBase arrPtr = dac_cast<PTR_ArrayBase>(objPtr); |
5981 | |
5982 | // this is also returned in the type information for the array - we return both for sanity checking... |
5983 | pObjectData->arrayInfo.rank = arrPtr->GetRank(); |
5984 | pObjectData->arrayInfo.componentCount = arrPtr->GetNumComponents(); |
5985 | pObjectData->arrayInfo.offsetToArrayBase = arrPtr->GetDataPtrOffset(pMT); |
5986 | |
5987 | if (arrPtr->IsMultiDimArray()) |
5988 | { |
5989 | pObjectData->arrayInfo.offsetToUpperBounds = SIZE_T(arrPtr->GetBoundsOffset(pMT)); |
5990 | |
5991 | pObjectData->arrayInfo.offsetToLowerBounds = SIZE_T(arrPtr->GetLowerBoundsOffset(pMT)); |
5992 | } |
5993 | else |
5994 | { |
5995 | pObjectData->arrayInfo.offsetToUpperBounds = 0; |
5996 | pObjectData->arrayInfo.offsetToLowerBounds = 0; |
5997 | } |
5998 | |
5999 | pObjectData->arrayInfo.elementSize = arrPtr->GetComponentSize(); |
6000 | |
6001 | LOG((LF_CORDB, LL_INFO10000, "D::GOI: array info: " |
6002 | "baseOff=%d, lowerOff=%d, upperOff=%d, cnt=%d, rank=%d, rank (2) = %d," |
6003 | "eleSize=%d, eleType=0x%02x\n" , |
6004 | pObjectData->arrayInfo.offsetToArrayBase, |
6005 | pObjectData->arrayInfo.offsetToLowerBounds, |
6006 | pObjectData->arrayInfo.offsetToUpperBounds, |
6007 | pObjectData->arrayInfo.componentCount, |
6008 | pObjectData->arrayInfo.rank, |
6009 | pObjectData->objTypeData.ArrayTypeData.arrayRank, |
6010 | pObjectData->arrayInfo.elementSize, |
6011 | pObjectData->objTypeData.ArrayTypeData.arrayTypeArg.elementType)); |
6012 | } |
6013 | } // DacDbiInterfaceImpl::GetArrayData |
6014 | |
6015 | // DAC/DBI API: Get information about an object for which we have a reference, including the object size and |
6016 | // type information. |
6017 | void DacDbiInterfaceImpl::GetBasicObjectInfo(CORDB_ADDRESS objectAddress, |
6018 | CorElementType type, |
6019 | VMPTR_AppDomain vmAppDomain, |
6020 | DebuggerIPCE_ObjectData * pObjectData) |
6021 | { |
6022 | DD_ENTER_MAY_THROW; |
6023 | |
6024 | PTR_Object objPtr = PTR_Object(TADDR(objectAddress)); |
6025 | pObjectData->objRefBad = CheckRef(objPtr); |
6026 | if (pObjectData->objRefBad != true) |
6027 | { |
6028 | // initialize object type, size, offset information. Note: We may have a different element type |
6029 | // after this. For example, we may start with E_T_CLASS but return with something more specific. |
6030 | InitObjectData (objPtr, vmAppDomain, pObjectData); |
6031 | } |
6032 | } // DacDbiInterfaceImpl::GetBasicObjectInfo |
6033 | |
6034 | // This is the data passed to EnumerateBlockingObjectsCallback below |
6035 | struct BlockingObjectUserDataWrapper |
6036 | { |
6037 | CALLBACK_DATA pUserData; |
6038 | IDacDbiInterface::FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback; |
6039 | }; |
6040 | |
6041 | // The callback helper used by EnumerateBlockingObjects below, this |
6042 | // callback in turn invokes the user's callback with the right arguments |
6043 | void EnumerateBlockingObjectsCallback(PTR_DebugBlockingItem obj, VOID* pUserData) |
6044 | { |
6045 | BlockingObjectUserDataWrapper* wrapper = (BlockingObjectUserDataWrapper*)pUserData; |
6046 | DacBlockingObject dacObj; |
6047 | |
6048 | // init to an arbitrary value to avoid mac compiler error about unintialized use |
6049 | // it will be correctly set in the switch and is never used with only this init here |
6050 | dacObj.blockingReason = DacBlockReason_MonitorCriticalSection; |
6051 | |
6052 | dacObj.vmBlockingObject.SetDacTargetPtr(dac_cast<TADDR>(OBJECTREFToObject(obj->pMonitor->GetOwningObject()))); |
6053 | dacObj.dwTimeout = obj->dwTimeout; |
6054 | dacObj.vmAppDomain.SetDacTargetPtr(dac_cast<TADDR>(obj->pAppDomain)); |
6055 | switch(obj->type) |
6056 | { |
6057 | case DebugBlock_MonitorCriticalSection: |
6058 | dacObj.blockingReason = DacBlockReason_MonitorCriticalSection; |
6059 | break; |
6060 | case DebugBlock_MonitorEvent: |
6061 | dacObj.blockingReason = DacBlockReason_MonitorEvent; |
6062 | break; |
6063 | default: |
6064 | _ASSERTE(!"obj->type has an invalid value" ); |
6065 | return; |
6066 | } |
6067 | |
6068 | wrapper->fpCallback(dacObj, wrapper->pUserData); |
6069 | } |
6070 | |
6071 | // DAC/DBI API: |
6072 | // Enumerate all monitors blocking a thread |
6073 | void DacDbiInterfaceImpl::EnumerateBlockingObjects(VMPTR_Thread vmThread, |
6074 | FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback, |
6075 | CALLBACK_DATA pUserData) |
6076 | { |
6077 | DD_ENTER_MAY_THROW; |
6078 | |
6079 | Thread * pThread = vmThread.GetDacPtr(); |
6080 | _ASSERTE(pThread != NULL); |
6081 | |
6082 | BlockingObjectUserDataWrapper wrapper; |
6083 | wrapper.fpCallback = fpCallback; |
6084 | wrapper.pUserData = pUserData; |
6085 | |
6086 | pThread->DebugBlockingInfo.VisitBlockingItems((DebugBlockingItemVisitor)EnumerateBlockingObjectsCallback, |
6087 | (VOID*)&wrapper); |
6088 | } |
6089 | |
6090 | // DAC/DBI API: |
6091 | // Returns the thread which owns the monitor lock on an object and the acquisition count |
6092 | MonitorLockInfo DacDbiInterfaceImpl::GetThreadOwningMonitorLock(VMPTR_Object vmObject) |
6093 | { |
6094 | DD_ENTER_MAY_THROW; |
6095 | MonitorLockInfo info; |
6096 | info.lockOwner = VMPTR_Thread::NullPtr(); |
6097 | info.acquisitionCount = 0; |
6098 | |
6099 | Object* pObj = vmObject.GetDacPtr(); |
6100 | DWORD threadId; |
6101 | DWORD acquisitionCount; |
6102 | if(!pObj->GetHeader()->GetThreadOwningMonitorLock(&threadId, &acquisitionCount)) |
6103 | { |
6104 | return info; |
6105 | } |
6106 | |
6107 | Thread *pThread = ThreadStore::GetThreadList(NULL); |
6108 | while (pThread != NULL) |
6109 | { |
6110 | if(pThread->GetThreadId() == threadId) |
6111 | { |
6112 | info.lockOwner.SetDacTargetPtr(PTR_HOST_TO_TADDR(pThread)); |
6113 | info.acquisitionCount = acquisitionCount; |
6114 | return info; |
6115 | } |
6116 | pThread = ThreadStore::GetThreadList(pThread); |
6117 | } |
6118 | _ASSERTE(!"A thread should have been found" ); |
6119 | return info; |
6120 | } |
6121 | |
6122 | // The data passed to EnumerateThreadsCallback below |
6123 | struct ThreadUserDataWrapper |
6124 | { |
6125 | CALLBACK_DATA pUserData; |
6126 | IDacDbiInterface::FP_THREAD_ENUMERATION_CALLBACK fpCallback; |
6127 | }; |
6128 | |
6129 | // The callback helper used for EnumerateMonitorEventWaitList below. This callback |
6130 | // invokes the user's callback with the correct arguments. |
6131 | void EnumerateThreadsCallback(PTR_Thread pThread, VOID* pUserData) |
6132 | { |
6133 | ThreadUserDataWrapper* wrapper = (ThreadUserDataWrapper*)pUserData; |
6134 | VMPTR_Thread vmThread = VMPTR_Thread::NullPtr(); |
6135 | vmThread.SetDacTargetPtr(dac_cast<TADDR>(pThread)); |
6136 | wrapper->fpCallback(vmThread, wrapper->pUserData); |
6137 | } |
6138 | |
6139 | // DAC/DBI API: |
6140 | // Enumerate all threads waiting on the monitor event for an object |
6141 | void DacDbiInterfaceImpl::EnumerateMonitorEventWaitList(VMPTR_Object vmObject, |
6142 | FP_THREAD_ENUMERATION_CALLBACK fpCallback, |
6143 | CALLBACK_DATA pUserData) |
6144 | { |
6145 | DD_ENTER_MAY_THROW; |
6146 | |
6147 | Object* pObj = vmObject.GetDacPtr(); |
6148 | SyncBlock* psb = pObj->PassiveGetSyncBlock(); |
6149 | |
6150 | // no sync block means no wait list |
6151 | if(psb == NULL) |
6152 | return; |
6153 | |
6154 | ThreadUserDataWrapper wrapper; |
6155 | wrapper.fpCallback = fpCallback; |
6156 | wrapper.pUserData = pUserData; |
6157 | ThreadQueue::EnumerateThreads(psb, (FP_TQ_THREAD_ENUMERATION_CALLBACK)EnumerateThreadsCallback, (VOID*) &wrapper); |
6158 | } |
6159 | |
6160 | |
6161 | bool DacDbiInterfaceImpl::AreGCStructuresValid() |
6162 | { |
6163 | return true; |
6164 | } |
6165 | |
6166 | HeapData::HeapData() |
6167 | : YoungestGenPtr(0), YoungestGenLimit(0), Gen0Start(0), Gen0End(0), SegmentCount(0), Segments(0) |
6168 | { |
6169 | } |
6170 | |
6171 | HeapData::~HeapData() |
6172 | { |
6173 | if (Segments) |
6174 | delete [] Segments; |
6175 | } |
6176 | |
6177 | LinearReadCache::LinearReadCache() |
6178 | : mCurrPageStart(0), mPageSize(0), mCurrPageSize(0), mPage(0) |
6179 | { |
6180 | SYSTEM_INFO si; |
6181 | GetSystemInfo(&si); |
6182 | |
6183 | mPageSize = si.dwPageSize; |
6184 | mPage = new (nothrow) BYTE[mPageSize]; |
6185 | } |
6186 | |
6187 | LinearReadCache::~LinearReadCache() |
6188 | { |
6189 | if (mPage) |
6190 | delete [] mPage; |
6191 | } |
6192 | |
6193 | bool LinearReadCache::MoveToPage(CORDB_ADDRESS addr) |
6194 | { |
6195 | mCurrPageStart = addr - (addr % mPageSize); |
6196 | HRESULT hr = g_dacImpl->m_pTarget->ReadVirtual(mCurrPageStart, mPage, mPageSize, &mCurrPageSize); |
6197 | |
6198 | if (hr != S_OK) |
6199 | { |
6200 | mCurrPageStart = 0; |
6201 | mCurrPageSize = 0; |
6202 | return false; |
6203 | } |
6204 | |
6205 | return true; |
6206 | } |
6207 | |
6208 | |
6209 | CORDB_ADDRESS DacHeapWalker::HeapStart = 0; |
6210 | CORDB_ADDRESS DacHeapWalker::HeapEnd = ~0; |
6211 | |
6212 | DacHeapWalker::DacHeapWalker() |
6213 | : mThreadCount(0), mAllocInfo(0), mHeapCount(0), mHeaps(0), |
6214 | mCurrObj(0), mCurrSize(0), mCurrMT(0), |
6215 | mCurrHeap(0), mCurrSeg(0), mStart((TADDR)HeapStart), mEnd((TADDR)HeapEnd) |
6216 | { |
6217 | } |
6218 | |
6219 | DacHeapWalker::~DacHeapWalker() |
6220 | { |
6221 | if (mAllocInfo) |
6222 | delete [] mAllocInfo; |
6223 | |
6224 | if (mHeaps) |
6225 | delete [] mHeaps; |
6226 | } |
6227 | |
6228 | SegmentData *DacHeapWalker::FindSegment(CORDB_ADDRESS obj) |
6229 | { |
6230 | for (size_t i = 0; i < mHeapCount; ++i) |
6231 | for (size_t j = 0; j < mHeaps[i].SegmentCount; ++j) |
6232 | if (mHeaps[i].Segments[j].Start <= obj && obj <= mHeaps[i].Segments[j].End) |
6233 | return &mHeaps[i].Segments[j]; |
6234 | |
6235 | return NULL; |
6236 | } |
6237 | |
6238 | HRESULT DacHeapWalker::Next(CORDB_ADDRESS *pValue, CORDB_ADDRESS *pMT, ULONG64 *pSize) |
6239 | { |
6240 | if (!HasMoreObjects()) |
6241 | return E_FAIL; |
6242 | |
6243 | if (pValue) |
6244 | *pValue = mCurrObj; |
6245 | |
6246 | if (pMT) |
6247 | *pMT = (CORDB_ADDRESS)mCurrMT; |
6248 | |
6249 | if (pSize) |
6250 | *pSize = (ULONG64)mCurrSize; |
6251 | |
6252 | HRESULT hr = MoveToNextObject(); |
6253 | return FAILED(hr) ? hr : S_OK; |
6254 | } |
6255 | |
6256 | |
6257 | |
6258 | HRESULT DacHeapWalker::MoveToNextObject() |
6259 | { |
6260 | do |
6261 | { |
6262 | // Move to the next object |
6263 | mCurrObj += mCurrSize; |
6264 | |
6265 | // Check to see if we are in the correct bounds. |
6266 | if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) |
6267 | CheckAllocAndSegmentRange(); |
6268 | |
6269 | // Check to see if we've moved off the end of a segment |
6270 | if (mCurrObj >= mHeaps[mCurrHeap].Segments[mCurrSeg].End || mCurrObj > mEnd) |
6271 | { |
6272 | HRESULT hr = NextSegment(); |
6273 | if (FAILED(hr) || hr == S_FALSE) |
6274 | return hr; |
6275 | } |
6276 | |
6277 | // Get the method table pointer |
6278 | if (!mCache.ReadMT(mCurrObj, &mCurrMT)) |
6279 | return E_FAIL; |
6280 | |
6281 | if (!GetSize(mCurrMT, mCurrSize)) |
6282 | return E_FAIL; |
6283 | } while (mCurrObj < mStart); |
6284 | |
6285 | _ASSERTE(mStart <= mCurrObj && mCurrObj <= mEnd); |
6286 | return S_OK; |
6287 | } |
6288 | |
6289 | bool DacHeapWalker::GetSize(TADDR tMT, size_t &size) |
6290 | { |
6291 | // With heap corruption, it's entierly possible that the MethodTable |
6292 | // we get is bad. This could cause exceptions, which we will catch |
6293 | // and return false. This causes the heapwalker to move to the next |
6294 | // segment. |
6295 | bool ret = true; |
6296 | EX_TRY |
6297 | { |
6298 | MethodTable *mt = PTR_MethodTable(tMT); |
6299 | size_t cs = mt->GetComponentSize(); |
6300 | |
6301 | if (cs) |
6302 | { |
6303 | DWORD tmp = 0; |
6304 | if (mCache.Read(mCurrObj+sizeof(TADDR), &tmp)) |
6305 | cs *= tmp; |
6306 | else |
6307 | ret = false; |
6308 | } |
6309 | |
6310 | size = mt->GetBaseSize() + cs; |
6311 | |
6312 | // The size is not guaranteed to be aligned, we have to |
6313 | // do that ourself. |
6314 | if (mHeaps[mCurrHeap].Segments[mCurrSeg].Generation == 3) |
6315 | size = AlignLarge(size); |
6316 | else |
6317 | size = Align(size); |
6318 | } |
6319 | EX_CATCH |
6320 | { |
6321 | ret = false; |
6322 | } |
6323 | EX_END_CATCH(SwallowAllExceptions) |
6324 | |
6325 | return ret; |
6326 | } |
6327 | |
6328 | |
6329 | HRESULT DacHeapWalker::NextSegment() |
6330 | { |
6331 | mCurrObj = 0; |
6332 | mCurrMT = 0; |
6333 | mCurrSize = 0; |
6334 | |
6335 | do |
6336 | { |
6337 | mCurrSeg++; |
6338 | while (mCurrSeg >= mHeaps[mCurrHeap].SegmentCount) |
6339 | { |
6340 | mCurrSeg = 0; |
6341 | mCurrHeap++; |
6342 | |
6343 | if (mCurrHeap >= mHeapCount) |
6344 | { |
6345 | return S_FALSE; |
6346 | } |
6347 | } |
6348 | |
6349 | mCurrObj = mHeaps[mCurrHeap].Segments[mCurrSeg].Start; |
6350 | |
6351 | if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) |
6352 | CheckAllocAndSegmentRange(); |
6353 | |
6354 | if (!mCache.ReadMT(mCurrObj, &mCurrMT)) |
6355 | { |
6356 | return E_FAIL; |
6357 | } |
6358 | |
6359 | if (!GetSize(mCurrMT, mCurrSize)) |
6360 | { |
6361 | return E_FAIL; |
6362 | } |
6363 | } while((mHeaps[mCurrHeap].Segments[mCurrSeg].Start > mEnd) || (mHeaps[mCurrHeap].Segments[mCurrSeg].End < mStart)); |
6364 | |
6365 | return S_OK; |
6366 | } |
6367 | |
6368 | void DacHeapWalker::CheckAllocAndSegmentRange() |
6369 | { |
6370 | const size_t MinObjSize = sizeof(TADDR)*3; |
6371 | |
6372 | for (int i = 0; i < mThreadCount; ++i) |
6373 | if (mCurrObj == mAllocInfo[i].Ptr) |
6374 | { |
6375 | mCurrObj = mAllocInfo[i].Limit + Align(MinObjSize); |
6376 | break; |
6377 | } |
6378 | |
6379 | if (mCurrObj == mHeaps[mCurrHeap].YoungestGenPtr) |
6380 | { |
6381 | mCurrObj = mHeaps[mCurrHeap].YoungestGenLimit + Align(MinObjSize); |
6382 | } |
6383 | } |
6384 | |
6385 | HRESULT DacHeapWalker::Init(CORDB_ADDRESS start, CORDB_ADDRESS end) |
6386 | { |
6387 | // Collect information about the allocation contexts in the process. |
6388 | ThreadStore* threadStore = ThreadStore::s_pThreadStore; |
6389 | if (threadStore != NULL) |
6390 | { |
6391 | int count = (int)threadStore->ThreadCountInEE(); |
6392 | mAllocInfo = new (nothrow) AllocInfo[count]; |
6393 | if (mAllocInfo == NULL) |
6394 | return E_OUTOFMEMORY; |
6395 | |
6396 | Thread *thread = NULL; |
6397 | int j = 0; |
6398 | for (int i = 0; i < count; ++i) |
6399 | { |
6400 | // The thread or allocation context being null is troubling, but not fatal. |
6401 | // We may have stopped the process where the thread list or thread's alloc |
6402 | // context was in an inconsistent state. We will simply skip over affected |
6403 | // segments during the heap walk if we encounter problems due to this. |
6404 | thread = ThreadStore::GetThreadList(thread); |
6405 | if (thread == NULL) |
6406 | continue; |
6407 | |
6408 | gc_alloc_context *ctx = thread->GetAllocContext(); |
6409 | if (ctx == NULL) |
6410 | continue; |
6411 | |
6412 | if ((CORDB_ADDRESS)ctx->alloc_ptr != NULL) |
6413 | { |
6414 | mAllocInfo[j].Ptr = (CORDB_ADDRESS)ctx->alloc_ptr; |
6415 | mAllocInfo[j].Limit = (CORDB_ADDRESS)ctx->alloc_limit; |
6416 | j++; |
6417 | } |
6418 | } |
6419 | |
6420 | mThreadCount = j; |
6421 | } |
6422 | |
6423 | #ifdef FEATURE_SVR_GC |
6424 | HRESULT hr = GCHeapUtilities::IsServerHeap() ? InitHeapDataSvr(mHeaps, mHeapCount) : InitHeapDataWks(mHeaps, mHeapCount); |
6425 | #else |
6426 | HRESULT hr = InitHeapDataWks(mHeaps, mHeapCount); |
6427 | #endif |
6428 | |
6429 | // Set up mCurrObj/mCurrMT. |
6430 | if (SUCCEEDED(hr)) |
6431 | hr = Reset(start, end); |
6432 | |
6433 | // Collect information about GC heaps |
6434 | return hr; |
6435 | } |
6436 | |
6437 | HRESULT DacHeapWalker::Reset(CORDB_ADDRESS start, CORDB_ADDRESS end) |
6438 | { |
6439 | _ASSERTE(mHeaps); |
6440 | _ASSERTE(mHeapCount > 0); |
6441 | _ASSERTE(mHeaps[0].Segments); |
6442 | _ASSERTE(mHeaps[0].SegmentCount > 0); |
6443 | |
6444 | mStart = start; |
6445 | mEnd = end; |
6446 | |
6447 | // Set up first object |
6448 | mCurrObj = mHeaps[0].Segments[0].Start; |
6449 | mCurrMT = 0; |
6450 | mCurrSize = 0; |
6451 | mCurrHeap = 0; |
6452 | mCurrSeg = 0; |
6453 | |
6454 | if (!mCache.ReadMT(mCurrObj, &mCurrMT)) |
6455 | return E_FAIL; |
6456 | |
6457 | if (!GetSize(mCurrMT, mCurrSize)) |
6458 | return E_FAIL; |
6459 | |
6460 | if (mCurrObj < mStart || mCurrObj > mEnd) |
6461 | MoveToNextObject(); |
6462 | |
6463 | return S_OK; |
6464 | } |
6465 | |
6466 | HRESULT DacHeapWalker::ListNearObjects(CORDB_ADDRESS obj, CORDB_ADDRESS *pPrev, CORDB_ADDRESS *pContaining, CORDB_ADDRESS *pNext) |
6467 | { |
6468 | SegmentData *seg = FindSegment(obj); |
6469 | |
6470 | if (seg == NULL) |
6471 | return E_FAIL; |
6472 | |
6473 | HRESULT hr = Reset(seg->Start, seg->End); |
6474 | if (SUCCEEDED(hr)) |
6475 | { |
6476 | CORDB_ADDRESS prev = 0; |
6477 | CORDB_ADDRESS curr = 0; |
6478 | ULONG64 size = 0; |
6479 | bool found = false; |
6480 | |
6481 | while (!found && HasMoreObjects()) |
6482 | { |
6483 | prev = curr; |
6484 | hr = Next(&curr, NULL, &size); |
6485 | if (FAILED(hr)) |
6486 | break; |
6487 | |
6488 | if (obj >= curr && obj < curr + size) |
6489 | found = true; |
6490 | } |
6491 | |
6492 | if (found) |
6493 | { |
6494 | if (pPrev) |
6495 | *pPrev = prev; |
6496 | |
6497 | if (pContaining) |
6498 | *pContaining = curr; |
6499 | |
6500 | if (pNext) |
6501 | { |
6502 | if (HasMoreObjects()) |
6503 | { |
6504 | hr = Next(&curr, NULL, NULL); |
6505 | if (SUCCEEDED(hr)) |
6506 | *pNext = curr; |
6507 | } |
6508 | else |
6509 | { |
6510 | *pNext = 0; |
6511 | } |
6512 | } |
6513 | |
6514 | hr = S_OK; |
6515 | } |
6516 | else if (SUCCEEDED(hr)) |
6517 | { |
6518 | hr = E_FAIL; |
6519 | } |
6520 | } |
6521 | |
6522 | return hr; |
6523 | } |
6524 | |
6525 | HRESULT DacHeapWalker::InitHeapDataWks(HeapData *&pHeaps, size_t &pCount) |
6526 | { |
6527 | // Scrape basic heap details |
6528 | pCount = 1; |
6529 | pHeaps = new (nothrow) HeapData[1]; |
6530 | if (pHeaps == NULL) |
6531 | return E_OUTOFMEMORY; |
6532 | |
6533 | dac_generation gen0 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 0); |
6534 | dac_generation gen1 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 1); |
6535 | dac_generation gen2 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 2); |
6536 | dac_generation loh = *GenerationTableIndex(g_gcDacGlobals->generation_table, 3); |
6537 | pHeaps[0].YoungestGenPtr = (CORDB_ADDRESS)gen0.allocation_context.alloc_ptr; |
6538 | pHeaps[0].YoungestGenLimit = (CORDB_ADDRESS)gen0.allocation_context.alloc_limit; |
6539 | |
6540 | pHeaps[0].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; |
6541 | pHeaps[0].Gen0End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; |
6542 | pHeaps[0].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; |
6543 | |
6544 | // Segments |
6545 | int count = GetSegmentCount(loh.start_segment); |
6546 | count += GetSegmentCount(gen2.start_segment); |
6547 | |
6548 | pHeaps[0].SegmentCount = count; |
6549 | pHeaps[0].Segments = new (nothrow) SegmentData[count]; |
6550 | if (pHeaps[0].Segments == NULL) |
6551 | return E_OUTOFMEMORY; |
6552 | |
6553 | // Small object heap segments |
6554 | DPTR(dac_heap_segment) seg = gen2.start_segment; |
6555 | int i = 0; |
6556 | for (; seg && (i < count); ++i) |
6557 | { |
6558 | pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; |
6559 | if (seg.GetAddr() == (TADDR)*g_gcDacGlobals->ephemeral_heap_segment) |
6560 | { |
6561 | pHeaps[0].Segments[i].End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; |
6562 | pHeaps[0].Segments[i].Generation = 1; |
6563 | pHeaps[0].EphemeralSegment = i; |
6564 | } |
6565 | else |
6566 | { |
6567 | pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; |
6568 | pHeaps[0].Segments[i].Generation = 2; |
6569 | } |
6570 | |
6571 | seg = seg->next; |
6572 | } |
6573 | |
6574 | // Large object heap segments |
6575 | seg = loh.start_segment; |
6576 | for (; seg && (i < count); ++i) |
6577 | { |
6578 | pHeaps[0].Segments[i].Generation = 3; |
6579 | pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; |
6580 | pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; |
6581 | |
6582 | seg = seg->next; |
6583 | } |
6584 | |
6585 | return S_OK; |
6586 | } |
6587 | |
6588 | HRESULT DacDbiInterfaceImpl::CreateHeapWalk(IDacDbiInterface::HeapWalkHandle *pHandle) |
6589 | { |
6590 | DD_ENTER_MAY_THROW; |
6591 | |
6592 | DacHeapWalker *data = new (nothrow) DacHeapWalker; |
6593 | if (data == NULL) |
6594 | return E_OUTOFMEMORY; |
6595 | |
6596 | HRESULT hr = data->Init(); |
6597 | if (SUCCEEDED(hr)) |
6598 | *pHandle = reinterpret_cast<HeapWalkHandle>(data); |
6599 | else |
6600 | delete data; |
6601 | |
6602 | return hr; |
6603 | } |
6604 | |
6605 | void DacDbiInterfaceImpl::DeleteHeapWalk(HeapWalkHandle handle) |
6606 | { |
6607 | DD_ENTER_MAY_THROW; |
6608 | |
6609 | DacHeapWalker *data = reinterpret_cast<DacHeapWalker*>(handle); |
6610 | if (data) |
6611 | delete data; |
6612 | } |
6613 | |
6614 | HRESULT DacDbiInterfaceImpl::WalkHeap(HeapWalkHandle handle, |
6615 | ULONG count, |
6616 | OUT COR_HEAPOBJECT * objects, |
6617 | OUT ULONG *fetched) |
6618 | { |
6619 | DD_ENTER_MAY_THROW; |
6620 | if (fetched == NULL) |
6621 | return E_INVALIDARG; |
6622 | |
6623 | DacHeapWalker *walk = reinterpret_cast<DacHeapWalker*>(handle); |
6624 | *fetched = 0; |
6625 | |
6626 | if (!walk->HasMoreObjects()) |
6627 | return S_FALSE; |
6628 | |
6629 | CORDB_ADDRESS freeMT = (CORDB_ADDRESS)g_pFreeObjectMethodTable.GetAddr(); |
6630 | |
6631 | HRESULT hr = S_OK; |
6632 | CORDB_ADDRESS addr, mt; |
6633 | ULONG64 size; |
6634 | |
6635 | ULONG i = 0; |
6636 | while (i < count && walk->HasMoreObjects()) |
6637 | { |
6638 | hr = walk->Next(&addr, &mt, &size); |
6639 | |
6640 | if (FAILED(hr)) |
6641 | break; |
6642 | |
6643 | if (mt != freeMT) |
6644 | { |
6645 | objects[i].address = addr; |
6646 | objects[i].type.token1 = mt; |
6647 | objects[i].type.token2 = NULL; |
6648 | objects[i].size = size; |
6649 | i++; |
6650 | } |
6651 | } |
6652 | |
6653 | if (SUCCEEDED(hr)) |
6654 | hr = (i < count) ? S_FALSE : S_OK; |
6655 | |
6656 | *fetched = i; |
6657 | return hr; |
6658 | } |
6659 | |
6660 | |
6661 | |
6662 | HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList<COR_SEGMENT> *pSegments) |
6663 | { |
6664 | DD_ENTER_MAY_THROW; |
6665 | |
6666 | |
6667 | size_t heapCount = 0; |
6668 | HeapData *heaps = 0; |
6669 | |
6670 | #ifdef FEATURE_SVR_GC |
6671 | HRESULT hr = GCHeapUtilities::IsServerHeap() ? DacHeapWalker::InitHeapDataSvr(heaps, heapCount) : DacHeapWalker::InitHeapDataWks(heaps, heapCount); |
6672 | #else |
6673 | HRESULT hr = DacHeapWalker::InitHeapDataWks(heaps, heapCount); |
6674 | #endif |
6675 | |
6676 | NewArrayHolder<HeapData> _heapHolder = heaps; |
6677 | |
6678 | // Count the number of segments to know how much to allocate. |
6679 | int total = 0; |
6680 | for (size_t i = 0; i < heapCount; ++i) |
6681 | { |
6682 | // SegmentCount is +1 due to the ephemeral segment containing more than one |
6683 | // generation (Gen1 + Gen0, and sometimes part of Gen2). |
6684 | total += (int)heaps[i].SegmentCount + 1; |
6685 | |
6686 | // It's possible that part of Gen2 lives on the ephemeral segment. If so, |
6687 | // we need to add one more to the output. |
6688 | const size_t eph = heaps[i].EphemeralSegment; |
6689 | _ASSERTE(eph < heaps[i].SegmentCount); |
6690 | if (heaps[i].Segments[eph].Start != heaps[i].Gen1Start) |
6691 | total++; |
6692 | } |
6693 | |
6694 | pSegments->Alloc(total); |
6695 | |
6696 | // Now walk all segments and write them to the array. |
6697 | int curr = 0; |
6698 | for (size_t i = 0; i < heapCount; ++i) |
6699 | { |
6700 | // Generation 0 is not in the segment list. |
6701 | _ASSERTE(curr < total); |
6702 | { |
6703 | COR_SEGMENT &seg = (*pSegments)[curr++]; |
6704 | seg.start = heaps[i].Gen0Start; |
6705 | seg.end = heaps[i].Gen0End; |
6706 | seg.type = CorDebug_Gen0; |
6707 | seg.heap = (ULONG)i; |
6708 | } |
6709 | |
6710 | for (size_t j = 0; j < heaps[i].SegmentCount; ++j) |
6711 | { |
6712 | if (heaps[i].Segments[j].Generation == 1) |
6713 | { |
6714 | // This is the ephemeral segment. We have already written Gen0, |
6715 | // now write Gen1. |
6716 | _ASSERTE(heaps[i].Segments[j].Start <= heaps[i].Gen1Start); |
6717 | _ASSERTE(heaps[i].Segments[j].End > heaps[i].Gen1Start); |
6718 | |
6719 | { |
6720 | _ASSERTE(curr < total); |
6721 | COR_SEGMENT &seg = (*pSegments)[curr++]; |
6722 | seg.start = heaps[i].Gen1Start; |
6723 | seg.end = heaps[i].Gen0Start; |
6724 | seg.type = CorDebug_Gen1; |
6725 | seg.heap = (ULONG)i; |
6726 | } |
6727 | |
6728 | // It's possible for Gen2 to take up a portion of the ephemeral segment. |
6729 | // We test for that here. |
6730 | if (heaps[i].Segments[j].Start != heaps[i].Gen1Start) |
6731 | { |
6732 | _ASSERTE(curr < total); |
6733 | COR_SEGMENT &seg = (*pSegments)[curr++]; |
6734 | seg.start = heaps[i].Segments[j].Start; |
6735 | seg.end = heaps[i].Gen1Start; |
6736 | seg.type = CorDebug_Gen2; |
6737 | seg.heap = (ULONG)i; |
6738 | } |
6739 | } |
6740 | else |
6741 | { |
6742 | // Otherwise, we have a gen2 or gen3 (LOH) segment |
6743 | _ASSERTE(curr < total); |
6744 | COR_SEGMENT &seg = (*pSegments)[curr++]; |
6745 | seg.start = heaps[i].Segments[j].Start; |
6746 | seg.end = heaps[i].Segments[j].End; |
6747 | |
6748 | _ASSERTE(heaps[i].Segments[j].Generation <= CorDebug_LOH); |
6749 | seg.type = (CorDebugGenerationTypes)heaps[i].Segments[j].Generation; |
6750 | seg.heap = (ULONG)i; |
6751 | } |
6752 | } |
6753 | } |
6754 | |
6755 | _ASSERTE(total == curr); |
6756 | return hr; |
6757 | } |
6758 | |
6759 | bool DacDbiInterfaceImpl::IsValidObject(CORDB_ADDRESS addr) |
6760 | { |
6761 | DD_ENTER_MAY_THROW; |
6762 | |
6763 | bool isValid = false; |
6764 | |
6765 | if (addr != 0 && addr != (CORDB_ADDRESS)-1) |
6766 | { |
6767 | EX_TRY |
6768 | { |
6769 | PTR_Object obj(TO_TADDR(addr)); |
6770 | |
6771 | PTR_MethodTable mt = obj->GetMethodTable(); |
6772 | PTR_EEClass cls = mt->GetClass(); |
6773 | |
6774 | if (mt == cls->GetMethodTable()) |
6775 | isValid = true; |
6776 | else if (!mt->IsCanonicalMethodTable()) |
6777 | isValid = cls->GetMethodTable()->GetClass() == cls; |
6778 | } |
6779 | EX_CATCH |
6780 | { |
6781 | isValid = false; |
6782 | } |
6783 | EX_END_CATCH(SwallowAllExceptions) |
6784 | } |
6785 | |
6786 | return isValid; |
6787 | } |
6788 | |
6789 | bool DacDbiInterfaceImpl::GetAppDomainForObject(CORDB_ADDRESS addr, OUT VMPTR_AppDomain * pAppDomain, |
6790 | OUT VMPTR_Module *pModule, OUT VMPTR_DomainFile *pDomainFile) |
6791 | { |
6792 | DD_ENTER_MAY_THROW; |
6793 | |
6794 | if (addr == 0 || addr == (CORDB_ADDRESS)-1) |
6795 | { |
6796 | return false; |
6797 | } |
6798 | |
6799 | PTR_Object obj(TO_TADDR(addr)); |
6800 | MethodTable *mt = obj->GetMethodTable(); |
6801 | |
6802 | PTR_Module module = mt->GetModule(); |
6803 | PTR_Assembly assembly = module->GetAssembly(); |
6804 | BaseDomain *baseDomain = assembly->GetDomain(); |
6805 | |
6806 | if (baseDomain->IsAppDomain()) |
6807 | { |
6808 | pAppDomain->SetDacTargetPtr(PTR_HOST_TO_TADDR(baseDomain->AsAppDomain())); |
6809 | pModule->SetDacTargetPtr(PTR_HOST_TO_TADDR(module)); |
6810 | pDomainFile->SetDacTargetPtr(PTR_HOST_TO_TADDR(module->GetDomainFile(baseDomain->AsAppDomain()))); |
6811 | } |
6812 | else |
6813 | { |
6814 | return false; |
6815 | } |
6816 | |
6817 | return true; |
6818 | } |
6819 | |
6820 | HRESULT DacDbiInterfaceImpl::CreateRefWalk(OUT RefWalkHandle * pHandle, BOOL walkStacks, BOOL walkFQ, UINT32 handleWalkMask) |
6821 | { |
6822 | DD_ENTER_MAY_THROW; |
6823 | |
6824 | DacRefWalker *walker = new (nothrow) DacRefWalker(this, walkStacks, walkFQ, handleWalkMask); |
6825 | |
6826 | if (walker == NULL) |
6827 | return E_OUTOFMEMORY; |
6828 | |
6829 | HRESULT hr = walker->Init(); |
6830 | if (FAILED(hr)) |
6831 | { |
6832 | delete walker; |
6833 | } |
6834 | else |
6835 | { |
6836 | *pHandle = reinterpret_cast<RefWalkHandle>(walker); |
6837 | } |
6838 | |
6839 | return hr; |
6840 | } |
6841 | |
6842 | |
6843 | void DacDbiInterfaceImpl::DeleteRefWalk(IN RefWalkHandle handle) |
6844 | { |
6845 | DD_ENTER_MAY_THROW; |
6846 | |
6847 | DacRefWalker *walker = reinterpret_cast<DacRefWalker*>(handle); |
6848 | |
6849 | if (walker) |
6850 | delete walker; |
6851 | } |
6852 | |
6853 | |
6854 | HRESULT DacDbiInterfaceImpl::WalkRefs(RefWalkHandle handle, ULONG count, OUT DacGcReference * objects, OUT ULONG *pFetched) |
6855 | { |
6856 | if (objects == NULL || pFetched == NULL) |
6857 | return E_POINTER; |
6858 | |
6859 | DD_ENTER_MAY_THROW; |
6860 | |
6861 | DacRefWalker *walker = reinterpret_cast<DacRefWalker*>(handle); |
6862 | if (!walker) |
6863 | return E_INVALIDARG; |
6864 | |
6865 | return walker->Next(count, objects, pFetched); |
6866 | } |
6867 | |
6868 | HRESULT DacDbiInterfaceImpl::GetTypeID(CORDB_ADDRESS dbgObj, COR_TYPEID *pID) |
6869 | { |
6870 | DD_ENTER_MAY_THROW; |
6871 | |
6872 | TADDR obj[3]; |
6873 | ULONG32 read = 0; |
6874 | HRESULT hr = g_dacImpl->m_pTarget->ReadVirtual(dbgObj, (BYTE*)obj, sizeof(obj), &read); |
6875 | if (FAILED(hr)) |
6876 | return hr; |
6877 | |
6878 | pID->token1 = (UINT64)(obj[0] & ~1); |
6879 | pID->token2 = 0; |
6880 | |
6881 | return hr; |
6882 | } |
6883 | |
6884 | HRESULT DacDbiInterfaceImpl::GetTypeIDForType(VMPTR_TypeHandle vmTypeHandle, COR_TYPEID *pID) |
6885 | { |
6886 | DD_ENTER_MAY_THROW; |
6887 | |
6888 | _ASSERTE(pID != NULL); |
6889 | _ASSERTE(!vmTypeHandle.IsNull()); |
6890 | |
6891 | TypeHandle th = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); |
6892 | PTR_MethodTable pMT = th.GetMethodTable(); |
6893 | pID->token1 = pMT.GetAddr(); |
6894 | _ASSERTE(pID->token1 != 0); |
6895 | pID->token2 = 0; |
6896 | return S_OK; |
6897 | } |
6898 | |
6899 | HRESULT DacDbiInterfaceImpl::GetObjectFields(COR_TYPEID id, ULONG32 celt, COR_FIELD *layout, ULONG32 *pceltFetched) |
6900 | { |
6901 | if (layout == NULL || pceltFetched == NULL) |
6902 | return E_POINTER; |
6903 | |
6904 | if (id.token1 == 0) |
6905 | return CORDBG_E_CLASS_NOT_LOADED; |
6906 | |
6907 | DD_ENTER_MAY_THROW; |
6908 | |
6909 | HRESULT hr = S_OK; |
6910 | |
6911 | TypeHandle typeHandle = TypeHandle::FromPtr(TO_TADDR(id.token1)); |
6912 | |
6913 | if (typeHandle.IsTypeDesc()) |
6914 | return E_INVALIDARG; |
6915 | |
6916 | ApproxFieldDescIterator fieldDescIterator(typeHandle.AsMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS); |
6917 | |
6918 | ULONG32 cFields = fieldDescIterator.Count(); |
6919 | |
6920 | // Handle case where user only wanted to know the number of fields. |
6921 | if (layout == NULL) |
6922 | { |
6923 | *pceltFetched = cFields; |
6924 | return S_FALSE; |
6925 | } |
6926 | |
6927 | if (celt < cFields) |
6928 | { |
6929 | cFields = celt; |
6930 | |
6931 | // we are returning less than the total |
6932 | hr = S_FALSE; |
6933 | } |
6934 | |
6935 | // This must be non-null due to check at beginning of function. |
6936 | *pceltFetched = celt; |
6937 | |
6938 | CorElementType componentType = typeHandle.AsMethodTable()->GetInternalCorElementType(); |
6939 | BOOL fReferenceType = CorTypeInfo::IsObjRef_NoThrow(componentType); |
6940 | for (ULONG32 i = 0; i < cFields; ++i) |
6941 | { |
6942 | FieldDesc *pField = fieldDescIterator.Next(); |
6943 | layout[i].token = pField->GetMemberDef(); |
6944 | layout[i].offset = (ULONG32)pField->GetOffset() + (fReferenceType ? Object::GetOffsetOfFirstField() : 0); |
6945 | |
6946 | TypeHandle fieldHandle = pField->LookupFieldTypeHandle(); |
6947 | |
6948 | if (fieldHandle.IsNull()) |
6949 | { |
6950 | layout[i].id.token1 = 0; |
6951 | layout[i].id.token2 = 0; |
6952 | layout[i].fieldType = (CorElementType)0; |
6953 | } |
6954 | else |
6955 | { |
6956 | PTR_MethodTable mt = fieldHandle.GetMethodTable(); |
6957 | layout[i].fieldType = mt->GetInternalCorElementType(); |
6958 | layout[i].id.token1 = (ULONG64)mt.GetAddr(); |
6959 | |
6960 | if (!mt->IsArray()) |
6961 | { |
6962 | layout[i].id.token2 = 0; |
6963 | } |
6964 | else |
6965 | { |
6966 | TypeHandle hnd = mt->GetApproxArrayElementTypeHandle(); |
6967 | PTR_MethodTable cmt = hnd.GetMethodTable(); |
6968 | layout[i].id.token2 = (ULONG64)cmt.GetAddr(); |
6969 | } |
6970 | } |
6971 | } |
6972 | |
6973 | return hr; |
6974 | } |
6975 | |
6976 | |
6977 | HRESULT DacDbiInterfaceImpl::GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout) |
6978 | { |
6979 | if (pLayout == NULL) |
6980 | return E_POINTER; |
6981 | |
6982 | if (id.token1 == 0) |
6983 | return CORDBG_E_CLASS_NOT_LOADED; |
6984 | |
6985 | DD_ENTER_MAY_THROW; |
6986 | |
6987 | PTR_MethodTable mt = PTR_MethodTable(TO_TADDR(id.token1)); |
6988 | PTR_MethodTable parentMT = mt->GetParentMethodTable(); |
6989 | |
6990 | COR_TYPEID parent = {parentMT.GetAddr(), 0}; |
6991 | pLayout->parentID = parent; |
6992 | |
6993 | DWORD size = mt->GetBaseSize(); |
6994 | ApproxFieldDescIterator fieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); |
6995 | |
6996 | pLayout->objectSize = size; |
6997 | pLayout->numFields = fieldDescIterator.Count(); |
6998 | |
6999 | // Get type |
7000 | CorElementType componentType = mt->IsString() ? ELEMENT_TYPE_STRING : mt->GetInternalCorElementType(); |
7001 | pLayout->type = componentType; |
7002 | pLayout->boxOffset = CorTypeInfo::IsObjRef_NoThrow(componentType) ? 0 : sizeof(TADDR); |
7003 | |
7004 | return S_OK; |
7005 | } |
7006 | |
7007 | HRESULT DacDbiInterfaceImpl::GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout) |
7008 | { |
7009 | if (pLayout == NULL) |
7010 | return E_POINTER; |
7011 | |
7012 | if (id.token1 == 0) |
7013 | return CORDBG_E_CLASS_NOT_LOADED; |
7014 | |
7015 | DD_ENTER_MAY_THROW; |
7016 | |
7017 | PTR_MethodTable mt = PTR_MethodTable(TO_TADDR(id.token1)); |
7018 | |
7019 | if (!mt->IsStringOrArray()) |
7020 | return E_INVALIDARG; |
7021 | |
7022 | if (mt->IsString()) |
7023 | { |
7024 | COR_TYPEID token; |
7025 | token.token1 = MscorlibBinder::GetElementType(ELEMENT_TYPE_CHAR).GetAddr(); |
7026 | token.token2 = 0; |
7027 | |
7028 | pLayout->componentID = token; |
7029 | |
7030 | pLayout->rankSize = 4; |
7031 | pLayout->numRanks = 1; |
7032 | pLayout->rankOffset = sizeof(TADDR); |
7033 | pLayout->firstElementOffset = sizeof(TADDR) + 4; |
7034 | pLayout->countOffset = sizeof(TADDR); |
7035 | pLayout->componentType = ELEMENT_TYPE_CHAR; |
7036 | pLayout->elementSize = 2; |
7037 | } |
7038 | else |
7039 | { |
7040 | DWORD ranks = mt->GetRank(); |
7041 | pLayout->rankSize = 4; |
7042 | pLayout->numRanks = ranks; |
7043 | bool multiDim = (ranks > 1); |
7044 | |
7045 | pLayout->rankOffset = multiDim ? sizeof(TADDR)*2 : sizeof(TADDR); |
7046 | pLayout->countOffset = sizeof(TADDR); |
7047 | pLayout->firstElementOffset = ArrayBase::GetDataPtrOffset(mt); |
7048 | |
7049 | |
7050 | TypeHandle hnd = mt->GetApproxArrayElementTypeHandle(); |
7051 | PTR_MethodTable cmt = hnd.GetMethodTable(); |
7052 | |
7053 | CorElementType componentType = cmt->GetInternalCorElementType(); |
7054 | if ((UINT64)cmt.GetAddr() == (UINT64)g_pStringClass.GetAddr()) |
7055 | componentType = ELEMENT_TYPE_STRING; |
7056 | |
7057 | COR_TYPEID token; |
7058 | token.token1 = cmt.GetAddr(); // This could be type handle |
7059 | token.token2 = 0; |
7060 | pLayout->componentID = token; |
7061 | pLayout->componentType = componentType; |
7062 | |
7063 | if (CorTypeInfo::IsObjRef_NoThrow(componentType)) |
7064 | pLayout->elementSize = sizeof(TADDR); |
7065 | else if (CorIsPrimitiveType(componentType)) |
7066 | pLayout->elementSize = gElementTypeInfo[componentType].m_cbSize; |
7067 | else |
7068 | pLayout->elementSize = cmt->GetNumInstanceFieldBytes(); |
7069 | } |
7070 | |
7071 | return S_OK; |
7072 | } |
7073 | |
7074 | |
7075 | void DacDbiInterfaceImpl::GetGCHeapInformation(COR_HEAPINFO * pHeapInfo) |
7076 | { |
7077 | DD_ENTER_MAY_THROW; |
7078 | |
7079 | size_t heapCount = 0; |
7080 | pHeapInfo->areGCStructuresValid = *g_gcDacGlobals->gc_structures_invalid_cnt == 0; |
7081 | |
7082 | #ifdef FEATURE_SVR_GC |
7083 | if (GCHeapUtilities::IsServerHeap()) |
7084 | { |
7085 | pHeapInfo->gcType = CorDebugServerGC; |
7086 | pHeapInfo->numHeaps = DacGetNumHeaps(); |
7087 | } |
7088 | else |
7089 | #endif |
7090 | { |
7091 | pHeapInfo->gcType = CorDebugWorkstationGC; |
7092 | pHeapInfo->numHeaps = 1; |
7093 | } |
7094 | |
7095 | pHeapInfo->pointerSize = sizeof(TADDR); |
7096 | pHeapInfo->concurrent = g_pConfig->GetGCconcurrent() ? TRUE : FALSE; |
7097 | } |
7098 | |
7099 | |
7100 | HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) |
7101 | { |
7102 | DD_ENTER_MAY_THROW; |
7103 | if (pAddrMDInternalRW == NULL) |
7104 | return E_INVALIDARG; |
7105 | PEFile * pPEFile = vmPEFile.GetDacPtr(); |
7106 | *pAddrMDInternalRW = pPEFile->GetMDInternalRWAddress(); |
7107 | return S_OK; |
7108 | } |
7109 | |
7110 | HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pvmReJitInfo) |
7111 | { |
7112 | DD_ENTER_MAY_THROW; |
7113 | _ASSERTE(!"You shouldn't be calling this - use GetActiveRejitILCodeVersionNode instead" ); |
7114 | return S_OK; |
7115 | } |
7116 | |
7117 | HRESULT DacDbiInterfaceImpl::GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) |
7118 | { |
7119 | DD_ENTER_MAY_THROW; |
7120 | if (pVmILCodeVersionNode == NULL) |
7121 | return E_INVALIDARG; |
7122 | #ifdef FEATURE_REJIT |
7123 | PTR_Module pModule = vmModule.GetDacPtr(); |
7124 | CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager(); |
7125 | // Be careful, there are two different definitions of 'active' being used here |
7126 | // For the CodeVersionManager, the active IL version is whatever one should be used in the next invocation of the method |
7127 | // 'rejit active' narrows that to only include rejit IL bodies where the profiler has already provided the definition |
7128 | // for the new IL (ilCodeVersion.GetRejitState()==ILCodeVersion::kStateActive). It is possible that the code version |
7129 | // manager's active IL version hasn't yet asked the profiler for the IL body to use, in which case we want to filter it |
7130 | // out from the return in this method. |
7131 | ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodTk); |
7132 | if (activeILVersion.IsNull() || activeILVersion.IsDefaultVersion() || activeILVersion.GetRejitState() != ILCodeVersion::kStateActive) |
7133 | { |
7134 | pVmILCodeVersionNode->SetDacTargetPtr(0); |
7135 | } |
7136 | else |
7137 | { |
7138 | pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(activeILVersion.AsNode())); |
7139 | } |
7140 | #else |
7141 | _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build" ); |
7142 | pVmILCodeVersionNode->SetDacTargetPtr(0); |
7143 | #endif |
7144 | return S_OK; |
7145 | } |
7146 | |
7147 | HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pvmReJitInfo) |
7148 | { |
7149 | DD_ENTER_MAY_THROW; |
7150 | _ASSERTE(!"You shouldn't be calling this - use GetNativeCodeVersionNode instead" ); |
7151 | return S_OK; |
7152 | } |
7153 | |
7154 | HRESULT DacDbiInterfaceImpl::GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode) |
7155 | { |
7156 | DD_ENTER_MAY_THROW; |
7157 | if (pVmNativeCodeVersionNode == NULL) |
7158 | return E_INVALIDARG; |
7159 | #ifdef FEATURE_REJIT |
7160 | PTR_MethodDesc pMD = vmMethod.GetDacPtr(); |
7161 | CodeVersionManager * pCodeVersionManager = pMD->GetCodeVersionManager(); |
7162 | NativeCodeVersion codeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, (PCODE)codeStartAddress); |
7163 | pVmNativeCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(codeVersion.AsNode())); |
7164 | #else |
7165 | pVmNativeCodeVersionNode->SetDacTargetPtr(0); |
7166 | #endif |
7167 | return S_OK; |
7168 | } |
7169 | |
7170 | HRESULT DacDbiInterfaceImpl::GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, OUT VMPTR_SharedReJitInfo* pvmSharedReJitInfo) |
7171 | { |
7172 | DD_ENTER_MAY_THROW; |
7173 | _ASSERTE(!"You shouldn't be calling this - use GetLCodeVersionNode instead" ); |
7174 | return S_OK; |
7175 | } |
7176 | |
7177 | HRESULT DacDbiInterfaceImpl::GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) |
7178 | { |
7179 | DD_ENTER_MAY_THROW; |
7180 | if (pVmILCodeVersionNode == NULL) |
7181 | return E_INVALIDARG; |
7182 | #ifdef FEATURE_REJIT |
7183 | NativeCodeVersionNode* pNativeCodeVersionNode = vmNativeCodeVersionNode.GetDacPtr(); |
7184 | ILCodeVersion ilCodeVersion = pNativeCodeVersionNode->GetILCodeVersion(); |
7185 | if (ilCodeVersion.IsDefaultVersion()) |
7186 | { |
7187 | pVmILCodeVersionNode->SetDacTargetPtr(0); |
7188 | } |
7189 | else |
7190 | { |
7191 | pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(ilCodeVersion.AsNode())); |
7192 | } |
7193 | |
7194 | #else |
7195 | _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build" ); |
7196 | pVmILCodeVersionNode->SetDacTargetPtr(0); |
7197 | #endif |
7198 | return S_OK; |
7199 | } |
7200 | |
7201 | HRESULT DacDbiInterfaceImpl::GetSharedReJitInfoData(VMPTR_SharedReJitInfo vmSharedReJitInfo, DacSharedReJitInfo* pData) |
7202 | { |
7203 | DD_ENTER_MAY_THROW; |
7204 | _ASSERTE(!"You shouldn't be calling this - use GetILCodeVersionNodeData instead" ); |
7205 | return S_OK; |
7206 | } |
7207 | |
7208 | HRESULT DacDbiInterfaceImpl::GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode vmILCodeVersionNode, DacSharedReJitInfo* pData) |
7209 | { |
7210 | DD_ENTER_MAY_THROW; |
7211 | #ifdef FEATURE_REJIT |
7212 | ILCodeVersion ilCode(vmILCodeVersionNode.GetDacPtr()); |
7213 | pData->m_state = ilCode.GetRejitState(); |
7214 | pData->m_pbIL = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(ilCode.GetIL())); |
7215 | pData->m_dwCodegenFlags = ilCode.GetJitFlags(); |
7216 | const InstrumentedILOffsetMapping* pMapping = ilCode.GetInstrumentedILMap(); |
7217 | if (pMapping) |
7218 | { |
7219 | pData->m_cInstrumentedMapEntries = (ULONG)pMapping->GetCount(); |
7220 | pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(pMapping->GetOffsets())); |
7221 | } |
7222 | else |
7223 | { |
7224 | pData->m_cInstrumentedMapEntries = 0; |
7225 | pData->m_rgInstrumentedMapEntries = 0; |
7226 | } |
7227 | #else |
7228 | _ASSERTE(!"You shouldn't be calling this - rejit isn't supported in this build" ); |
7229 | #endif |
7230 | return S_OK; |
7231 | } |
7232 | |
7233 | HRESULT DacDbiInterfaceImpl::GetDefinesBitField(ULONG32 *pDefines) |
7234 | { |
7235 | DD_ENTER_MAY_THROW; |
7236 | if (pDefines == NULL) |
7237 | return E_INVALIDARG; |
7238 | *pDefines = g_pDebugger->m_defines; |
7239 | return S_OK; |
7240 | } |
7241 | |
7242 | HRESULT DacDbiInterfaceImpl::GetMDStructuresVersion(ULONG32* pMDStructuresVersion) |
7243 | { |
7244 | DD_ENTER_MAY_THROW; |
7245 | if (pMDStructuresVersion == NULL) |
7246 | return E_INVALIDARG; |
7247 | *pMDStructuresVersion = g_pDebugger->m_mdDataStructureVersion; |
7248 | return S_OK; |
7249 | } |
7250 | |
7251 | HRESULT DacDbiInterfaceImpl::EnableGCNotificationEvents(BOOL fEnable) |
7252 | { |
7253 | DD_ENTER_MAY_THROW |
7254 | |
7255 | HRESULT hr = S_OK; |
7256 | EX_TRY |
7257 | { |
7258 | if (g_pDebugger != NULL) |
7259 | { |
7260 | TADDR addr = PTR_HOST_MEMBER_TADDR(Debugger, g_pDebugger, m_isGarbageCollectionEventsEnabled); |
7261 | SafeWriteStructOrThrow<BOOL>(addr, &fEnable); |
7262 | } |
7263 | } |
7264 | EX_CATCH_HRESULT(hr); |
7265 | return hr; |
7266 | } |
7267 | |
7268 | DacRefWalker::DacRefWalker(ClrDataAccess *dac, BOOL walkStacks, BOOL walkFQ, UINT32 handleMask) |
7269 | : mDac(dac), mWalkStacks(walkStacks), mWalkFQ(walkFQ), mHandleMask(handleMask), mStackWalker(NULL), |
7270 | mHandleWalker(NULL), mFQStart(PTR_NULL), mFQEnd(PTR_NULL), mFQCurr(PTR_NULL) |
7271 | { |
7272 | } |
7273 | |
7274 | DacRefWalker::~DacRefWalker() |
7275 | { |
7276 | Clear(); |
7277 | } |
7278 | |
7279 | HRESULT DacRefWalker::Init() |
7280 | { |
7281 | HRESULT hr = S_OK; |
7282 | if (mHandleMask) |
7283 | { |
7284 | // Will throw on OOM, which is fine. |
7285 | mHandleWalker = new DacHandleWalker(); |
7286 | |
7287 | hr = mHandleWalker->Init(GetHandleWalkerMask()); |
7288 | } |
7289 | |
7290 | if (mWalkStacks && SUCCEEDED(hr)) |
7291 | { |
7292 | hr = NextThread(); |
7293 | } |
7294 | |
7295 | return hr; |
7296 | } |
7297 | |
7298 | void DacRefWalker::Clear() |
7299 | { |
7300 | if (mHandleWalker) |
7301 | { |
7302 | delete mHandleWalker; |
7303 | mHandleWalker = NULL; |
7304 | } |
7305 | |
7306 | if (mStackWalker) |
7307 | { |
7308 | delete mStackWalker; |
7309 | mStackWalker = NULL; |
7310 | } |
7311 | } |
7312 | |
7313 | |
7314 | |
7315 | UINT32 DacRefWalker::GetHandleWalkerMask() |
7316 | { |
7317 | UINT32 result = 0; |
7318 | if (mHandleMask & CorHandleStrong) |
7319 | result |= (1 << HNDTYPE_STRONG); |
7320 | |
7321 | if (mHandleMask & CorHandleStrongPinning) |
7322 | result |= (1 << HNDTYPE_PINNED); |
7323 | |
7324 | if (mHandleMask & CorHandleWeakShort) |
7325 | result |= (1 << HNDTYPE_WEAK_SHORT); |
7326 | |
7327 | if (mHandleMask & CorHandleWeakLong) |
7328 | result |= (1 << HNDTYPE_WEAK_LONG); |
7329 | |
7330 | #ifdef FEATURE_COMINTEROP |
7331 | if ((mHandleMask & CorHandleWeakRefCount) || (mHandleMask & CorHandleStrongRefCount)) |
7332 | result |= (1 << HNDTYPE_REFCOUNTED); |
7333 | |
7334 | if (mHandleMask & CorHandleWeakWinRT) |
7335 | result |= (1 << HNDTYPE_WEAK_WINRT); |
7336 | #endif // FEATURE_COMINTEROP |
7337 | |
7338 | if (mHandleMask & CorHandleStrongDependent) |
7339 | result |= (1 << HNDTYPE_DEPENDENT); |
7340 | |
7341 | if (mHandleMask & CorHandleStrongAsyncPinned) |
7342 | result |= (1 << HNDTYPE_ASYNCPINNED); |
7343 | |
7344 | if (mHandleMask & CorHandleStrongSizedByref) |
7345 | result |= (1 << HNDTYPE_SIZEDREF); |
7346 | |
7347 | return result; |
7348 | } |
7349 | |
7350 | |
7351 | |
7352 | HRESULT DacRefWalker::Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched) |
7353 | { |
7354 | if (roots == NULL || pceltFetched == NULL) |
7355 | return E_POINTER; |
7356 | |
7357 | ULONG total = 0; |
7358 | HRESULT hr = S_OK; |
7359 | |
7360 | if (mHandleWalker) |
7361 | { |
7362 | hr = mHandleWalker->Next(celt, roots, &total); |
7363 | |
7364 | if (hr == S_FALSE || FAILED(hr)) |
7365 | { |
7366 | delete mHandleWalker; |
7367 | mHandleWalker = NULL; |
7368 | |
7369 | if (FAILED(hr)) |
7370 | return hr; |
7371 | } |
7372 | } |
7373 | |
7374 | if (total < celt) |
7375 | { |
7376 | while (total < celt && mFQCurr < mFQEnd) |
7377 | { |
7378 | DacGcReference &ref = roots[total++]; |
7379 | |
7380 | ref.vmDomain = VMPTR_AppDomain::NullPtr(); |
7381 | ref.objHnd.SetDacTargetPtr(mFQCurr.GetAddr()); |
7382 | ref.dwType = (DWORD)CorReferenceFinalizer; |
7383 | ref.i64ExtraData = 0; |
7384 | |
7385 | mFQCurr++; |
7386 | } |
7387 | } |
7388 | |
7389 | while (total < celt && mStackWalker) |
7390 | { |
7391 | ULONG fetched = 0; |
7392 | hr = mStackWalker->Next(celt-total, roots+total, &fetched); |
7393 | |
7394 | if (FAILED(hr)) |
7395 | return hr; |
7396 | |
7397 | if (hr == S_FALSE) |
7398 | { |
7399 | hr = NextThread(); |
7400 | |
7401 | if (FAILED(hr)) |
7402 | return hr; |
7403 | } |
7404 | |
7405 | total += fetched; |
7406 | } |
7407 | |
7408 | *pceltFetched = total; |
7409 | |
7410 | return total < celt ? S_FALSE : S_OK; |
7411 | } |
7412 | |
7413 | HRESULT DacRefWalker::NextThread() |
7414 | { |
7415 | Thread *pThread = NULL; |
7416 | if (mStackWalker) |
7417 | { |
7418 | pThread = mStackWalker->GetThread(); |
7419 | delete mStackWalker; |
7420 | mStackWalker = NULL; |
7421 | } |
7422 | |
7423 | pThread = ThreadStore::GetThreadList(pThread); |
7424 | |
7425 | if (!pThread) |
7426 | return S_FALSE; |
7427 | |
7428 | mStackWalker = new DacStackReferenceWalker(mDac, pThread->GetOSThreadId()); |
7429 | return mStackWalker->Init(); |
7430 | } |
7431 | |
7432 | HRESULT DacHandleWalker::Next(ULONG celt, DacGcReference roots[], ULONG *pceltFetched) |
7433 | { |
7434 | SUPPORTS_DAC; |
7435 | |
7436 | if (roots == NULL || pceltFetched == NULL) |
7437 | return E_POINTER; |
7438 | |
7439 | return DoHandleWalk<DacGcReference, ULONG, DacHandleWalker::EnumCallbackDac>(celt, roots, pceltFetched); |
7440 | } |
7441 | |
7442 | |
7443 | void CALLBACK DacHandleWalker::EnumCallbackDac(PTR_UNCHECKED_OBJECTREF handle, uintptr_t *, uintptr_t param1, uintptr_t param2) |
7444 | { |
7445 | SUPPORTS_DAC; |
7446 | |
7447 | DacHandleWalkerParam *param = (DacHandleWalkerParam *)param1; |
7448 | HandleChunkHead *curr = param->Curr; |
7449 | |
7450 | // If we failed on a previous call (OOM) don't keep trying to allocate, it's not going to work. |
7451 | if (FAILED(param->Result)) |
7452 | return; |
7453 | |
7454 | // We've moved past the size of the current chunk. We'll allocate a new chunk |
7455 | // and stuff the handles there. These are cleaned up by the destructor |
7456 | if (curr->Count >= (curr->Size/sizeof(DacGcReference))) |
7457 | { |
7458 | if (curr->Next == NULL) |
7459 | { |
7460 | HandleChunk *next = new (nothrow) HandleChunk; |
7461 | if (next != NULL) |
7462 | { |
7463 | curr->Next = next; |
7464 | } |
7465 | else |
7466 | { |
7467 | param->Result = E_OUTOFMEMORY; |
7468 | return; |
7469 | } |
7470 | } |
7471 | |
7472 | curr = param->Curr = param->Curr->Next; |
7473 | } |
7474 | |
7475 | // Fill the current handle. |
7476 | DacGcReference *dataArray = (DacGcReference*)curr->pData; |
7477 | DacGcReference &data = dataArray[curr->Count++]; |
7478 | |
7479 | data.objHnd.SetDacTargetPtr(handle.GetAddr()); |
7480 | data.vmDomain.SetDacTargetPtr(TO_TADDR(param->AppDomain)); |
7481 | |
7482 | data.i64ExtraData = 0; |
7483 | unsigned int refCnt = 0; |
7484 | |
7485 | switch (param->Type) |
7486 | { |
7487 | case HNDTYPE_STRONG: |
7488 | data.dwType = (DWORD)CorHandleStrong; |
7489 | break; |
7490 | |
7491 | case HNDTYPE_PINNED: |
7492 | data.dwType = (DWORD)CorHandleStrongPinning; |
7493 | break; |
7494 | |
7495 | case HNDTYPE_WEAK_SHORT: |
7496 | data.dwType = (DWORD)CorHandleWeakShort; |
7497 | break; |
7498 | |
7499 | case HNDTYPE_WEAK_LONG: |
7500 | data.dwType = (DWORD)CorHandleWeakLong; |
7501 | break; |
7502 | |
7503 | #ifdef FEATURE_COMINTEROP |
7504 | case HNDTYPE_REFCOUNTED: |
7505 | data.dwType = (DWORD)(data.i64ExtraData ? CorHandleStrongRefCount : CorHandleWeakRefCount); |
7506 | GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &refCnt, NULL, NULL, NULL); |
7507 | data.i64ExtraData = refCnt; |
7508 | break; |
7509 | |
7510 | case HNDTYPE_WEAK_WINRT: |
7511 | data.dwType = (DWORD)CorHandleWeakWinRT; |
7512 | break; |
7513 | #endif |
7514 | |
7515 | case HNDTYPE_DEPENDENT: |
7516 | data.dwType = (DWORD)CorHandleStrongDependent; |
7517 | data.i64ExtraData = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); |
7518 | break; |
7519 | |
7520 | case HNDTYPE_ASYNCPINNED: |
7521 | data.dwType = (DWORD)CorHandleStrongAsyncPinned; |
7522 | break; |
7523 | |
7524 | case HNDTYPE_SIZEDREF: |
7525 | data.dwType = (DWORD)CorHandleStrongSizedByref; |
7526 | break; |
7527 | } |
7528 | } |
7529 | |
7530 | |
7531 | void DacStackReferenceWalker::GCEnumCallbackDac(LPVOID hCallback, OBJECTREF *pObject, uint32_t flags, DacSlotLocation loc) |
7532 | { |
7533 | GCCONTEXT *gcctx = (GCCONTEXT *)hCallback; |
7534 | DacScanContext *dsc = (DacScanContext*)gcctx->sc; |
7535 | |
7536 | CORDB_ADDRESS obj = 0; |
7537 | |
7538 | if (flags & GC_CALL_INTERIOR) |
7539 | { |
7540 | if (loc.targetPtr) |
7541 | obj = (CORDB_ADDRESS)(*PTR_PTR_Object((TADDR)pObject)).GetAddr(); |
7542 | else |
7543 | obj = (CORDB_ADDRESS)TO_TADDR(pObject); |
7544 | |
7545 | HRESULT hr = dsc->pWalker->mHeap.ListNearObjects(obj, NULL, &obj, NULL); |
7546 | |
7547 | // If we failed don't add this instance to the list. ICorDebug doesn't handle invalid pointers |
7548 | // very well, and the only way the heap walker's ListNearObjects will fail is if we have heap |
7549 | // corruption...which ICorDebug doesn't deal with anyway. |
7550 | if (FAILED(hr)) |
7551 | return; |
7552 | } |
7553 | |
7554 | DacGcReference *data = dsc->pWalker->GetNextObject<DacGcReference>(dsc); |
7555 | if (data != NULL) |
7556 | { |
7557 | data->vmDomain.SetDacTargetPtr(dac_cast<PTR_AppDomain>(dsc->pCurrentDomain).GetAddr()); |
7558 | if (obj) |
7559 | data->pObject = obj | 1; |
7560 | else if (loc.targetPtr) |
7561 | data->objHnd.SetDacTargetPtr(TO_TADDR(pObject)); |
7562 | else |
7563 | data->pObject = pObject->GetAddr() | 1; |
7564 | |
7565 | data->dwType = CorReferenceStack; |
7566 | data->i64ExtraData = 0; |
7567 | } |
7568 | } |
7569 | |
7570 | |
7571 | void DacStackReferenceWalker::GCReportCallbackDac(PTR_PTR_Object ppObj, ScanContext *sc, uint32_t flags) |
7572 | { |
7573 | DacScanContext *dsc = (DacScanContext*)sc; |
7574 | |
7575 | TADDR obj = ppObj.GetAddr(); |
7576 | if (flags & GC_CALL_INTERIOR) |
7577 | { |
7578 | CORDB_ADDRESS fixed_addr = 0; |
7579 | HRESULT hr = dsc->pWalker->mHeap.ListNearObjects((CORDB_ADDRESS)obj, NULL, &fixed_addr, NULL); |
7580 | |
7581 | // If we failed don't add this instance to the list. ICorDebug doesn't handle invalid pointers |
7582 | // very well, and the only way the heap walker's ListNearObjects will fail is if we have heap |
7583 | // corruption...which ICorDebug doesn't deal with anyway. |
7584 | if (FAILED(hr)) |
7585 | return; |
7586 | |
7587 | obj = TO_TADDR(fixed_addr); |
7588 | } |
7589 | |
7590 | DacGcReference *data = dsc->pWalker->GetNextObject<DacGcReference>(dsc); |
7591 | if (data != NULL) |
7592 | { |
7593 | data->vmDomain.SetDacTargetPtr(dac_cast<PTR_AppDomain>(dsc->pCurrentDomain).GetAddr()); |
7594 | data->objHnd.SetDacTargetPtr(obj); |
7595 | data->dwType = CorReferenceStack; |
7596 | data->i64ExtraData = 0; |
7597 | } |
7598 | } |
7599 | |
7600 | |
7601 | |
7602 | HRESULT DacStackReferenceWalker::Next(ULONG count, DacGcReference stackRefs[], ULONG *pFetched) |
7603 | { |
7604 | if (stackRefs == NULL || pFetched == NULL) |
7605 | return E_POINTER; |
7606 | |
7607 | HRESULT hr = DoStackWalk<ULONG, DacGcReference, |
7608 | DacStackReferenceWalker::GCReportCallbackDac, |
7609 | DacStackReferenceWalker::GCEnumCallbackDac> |
7610 | (count, stackRefs, pFetched); |
7611 | |
7612 | return hr; |
7613 | } |
7614 | |