diff options
Diffstat (limited to 'src/debug/inc/dacdbiinterface.h')
-rw-r--r-- | src/debug/inc/dacdbiinterface.h | 2726 |
1 files changed, 2726 insertions, 0 deletions
diff --git a/src/debug/inc/dacdbiinterface.h b/src/debug/inc/dacdbiinterface.h new file mode 100644 index 0000000000..fe58724fc5 --- /dev/null +++ b/src/debug/inc/dacdbiinterface.h @@ -0,0 +1,2726 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// DacDbiInterface.h +// + +// +// Define the interface between the DAC and DBI. +//***************************************************************************** + +#ifndef _DACDBI_INTERFACE_H_ +#define _DACDBI_INTERFACE_H_ + +#include <metahost.h> + +// The DAC/DBI interface can use structures and LSPTR declarations from the +// existing V2 interfaces +#include "dbgipcevents.h" + +//----------------------------------------------------------------------------- +// Deallocation function for memory allocated with the global IAllocator object. +// +// Arguments: +// p - pointer to delete. Allocated with IAllocator::Alloc +// +// Notes: +// This should invoke the dtor and then call IAllocator::Free. +// In the DAC implementation, this will call via IAllocator. +// In the DBI implementation, this can directly call delete (assuming the IAllocator::Free +// directly called new). +template<class T> void DeleteDbiMemory(T *p); +// Need a class to serve as a tag that we can use to overload New/Delete. +class forDbiWorker {}; +#define forDbi (*(forDbiWorker *)NULL) +extern void * operator new(size_t lenBytes, const forDbiWorker &); +extern void * operator new[](size_t lenBytes, const forDbiWorker &); +extern void operator delete(void *p, const forDbiWorker &); +extern void operator delete[](void *p, const forDbiWorker &); + +// The dac exposes a way to walk all GC references in the process. This +// includes both strong references and weak references. This is done +// through a referece walk. +typedef void* * RefWalkHandle; + +#include "dacdbistructures.h" + +// This is the current format of code:DbiVersion. It needs to be rev'ed when we decide to store something +// else other than the product version of the DBI in DbiVersion (e.g. a timestamp). See +// code:CordbProcess::CordbProcess#DBIVersionChecking for more information. +const DWORD kCurrentDbiVersionFormat = 1; + +//----------------------------------------------------------------------------- +// This is a low-level interface between DAC and DBI. +// The DAC is the raw DAC-ized code from the EE. +// DBI is the implementation of ICorDebug on top of that. +// +// This interface should be: +// - Stateless: The DAC component should not have any persistent state. It should not have any resources +// that it needs to clean up. DBI can store all the state (eg, list of of modules). +// Using IAllocator/IStringHolder interfaces to allocate data to pass back out is ok because DBI owns +// the resources, not the DAC layer. +// - blittable: The types on the interface should be blittable. For example, use TIDs instead of OS Thread handles. +// Passing pointers to be used as out-parameters is ok. +// - lightweight: it will inevitably have many methods on it and should be very fluid to use. +// - very descriptive: heavily call out liabilities on the runtime. For example, don't just have a method like +// "GetName" where Name is ambiguous. Heavily comment exactly what Name is, when it may fail, if it's 0-length, +// if it's unique, etc. This serves two purposes: +// a) it helps ensure the right invariants flow up to the public API level. +// b) it helps ensure that the debugger is making the right assumptions about the runtime's behavior. +// +// #Marshaling: +// This interface should be marshalable such that the caller (the Right Side) can exist in one +// process, while the implementation of Dac could be on another machine. +// - All types need to be marshable. +// - Use OUT and OPTIONAL as defined in windef.h to guide the marshaler. Here are how types are marshaled: +// T : value-type, copied on input. +// T* : will be marshaled as non-null by-ref (copy on input, copy on return), +// const T*: non-null, copy on input only. +// OUT T*: non-null copy-on-return only. +// OPTIONAL T*: by-ref, could be null. +// - The marshaler has special knowledge of IStringHolder and DacDbiArrayList<T>. +// - You can write custom marshalers for non-blittable structures defined in DacDbiStructures.h. +// - There is custom handling for marshalling callbacks. +// +// +// Threading: The interface (and the underlying DataTarget) are free-threaded to leverage +// concurrency. +// +// Allocation: +// This interface can use IAllocator to allocate objects and hand them back. The allocated objects should be: +// - closed, serializable object graphs. +// - should have private fields and public accessors +// - have dtors that free any allocated the memory via calling DeleteDbiMemory. +// Objects can be declared in a header and shared between both dbi and dac. +// Consider using DacDbiArrayList<T> instead of custom allocations. + +// Error handling: +// Any call on the interface may fail. For example, the data-target may not have access to the necessary memory. +// Methods should throw on error. +// +// #Enumeration +// General rules about Enumerations: +// - Do not assume that enumerations exposed here are in any particular order. +// - many enumerations also correspond to Load/Unload events. Since load/unload aren't atomic with publishing +// in an enumeration, this is a Total Ordering of things: +// a) object shows up in enumeration +// b) load event. +// c) ... steady state ... +// d) object removed from DacDbi enumeration; +// Any existing handles we get beyond this are explicitly associated with a Cordb* object; which can be +// neutered on the unload event by Dbi. +// e) unload event. +// - Send after it's reachability from other objects is broken. (Eg, For AppDomain unload +// means no threads left in that appdomain) +// - Send before it's deleted (so VMPTR is still valid; not yet recycled). +// - Send early enough that property access can at least gracefully fail. (eg, +// Module::GetName should either return the name, or fail) +// +// Cordb must neuter any Cordb objects that have any pre-existing handles to the object. +// After this point, gauranteed that nobody can discover the VMPTR any more: +// - doesn't show up in enumerations (so can't be discoverered implicitly) +// - object should not be discoverable by other objects in VM. +// - any Cordb object that already had it would be neutered by Dbi. +// - Therefore nothing should even be asking Dac for it. +// f) object deleted. +// Think of it like this: The event occurs to let you know that the enumeration has been updated. +// +// A robust debugger should not rely on events for correctness. For example, +// a corrupt debuggee may send: +// 1) multiple load events. (if target repeats due to an issue) +// 2) no load event and only an unload event. (if target fails inbetween +// publish (a) and load (b), and then backout code sends the unload). +// 3) no unload event. (eg, if target is rudely killed) +// 4) multiple unload events (if target repeats due to bug) +// +// This satisfies the following rules: +// - once you get the load event, you can find the object via enumeration +// - once an item is discoverable, it must immediately show up in the enumeration. +// - once you get the unload event, the object is dead and can't be rediscovered via enumeration. +// +// This is an issue even for well-behaved targets. Imagine if a debugger attaches right after +// an unload event is sent. We don't want the debugger to enumerate and re-discover the +// unloaded object because now that the unload event is already sent, the debugger won't get +// any further notification of when the object is deleted in the target. +// Thus it's valuable for the debugger to have debug-only checks after unload events to assert +// that the object is no longer discoverable. +// +//............................................................................. +// The purpose of this object is to provide EE funcationality back to +// the debugger. This represents the entire set of EE functions used +// by the debugger. +// +// We will make this interface larger over time to grow the functionality +// between the EE and the Debugger. +// +// +//----------------------------------------------------------------------------- +class IDacDbiInterface +{ +public: + class IStringHolder; + + // The following tag tells the DD-marshalling tool to start scanning. + // BEGIN_MARSHAL + + //----------------------------------------------------------------------------- + // Functions to control the behavior of the DacDbi implementation itself. + //----------------------------------------------------------------------------- + + // + // Check whether the version of the DBI matches the version of the runtime. + // This is only called when we are remote debugging. On Windows, we should have checked all the + // versions before we call any API on the IDacDbiInterface. See + // code:CordbProcess::CordbProcess#DBIVersionChecking for more information on version checks. + // + // Return Value: + // S_OK on success. + // + // Notes: + // THIS MUST BE THE FIRST API ON THE INTERFACE! + // + virtual + HRESULT CheckDbiVersion(const DbiVersion * pVersion) = 0; + + // + // Flush the DAC cache. This should be called when target memory changes. + // + // + // Return Value: + // S_OK on success. + // + // Notes: + // If this fails, the interface is in an undefined state. + // This must be called anytime target memory changes, else all other functions + // (besides Destroy) may yield out-of-date or semantically incorrect results. + // + virtual + HRESULT FlushCache() = 0; + + // + // Control DAC's checking of the target's consistency. Specifically, if this is disabled then + // ASSERTs in VM code are ignored. The default is disabled, since DAC should do it's best to + // return results even with a corrupt or unsyncrhonized target. See + // code:ClrDataAccess::TargetConsistencyAssertsEnabled for more details. + // + // When testing with a non-corrupt and properly syncrhonized target, this should be enabled to + // help catch bugs. + // + // Arguments: + // fEnableAsserts - whether ASSERTs should be raised when consistency checks fail (_DEBUG + // builds only) + // + // Notes: + // In the future we may want to extend DAC target consistency checks to be retail checks + // (exceptions) as well. We'll also need a mechanism for disabling them (eg. when an advanced + // user wants to try to get a result anyway even though the target is inconsistent). In that + // case we'll want an additional argument here for enabling/disabling the throwing of + // consistency failures exceptions (this is independent from asserts - there are legitimate + // scenarios for all 4 combinations). + // + virtual + void DacSetTargetConsistencyChecks(bool fEnableAsserts) = 0; + + // + // Destroy the interface object. The client should call this when it's done + // with the IDacDbiInterface to free up any resources. + // + // Return Value: + // None. + // + // Notes: + // The client should not call anything else on this interface after Destroy. + // + virtual + void Destroy() = 0; + + //----------------------------------------------------------------------------- + // General purpose target inspection functions + //----------------------------------------------------------------------------- + + // + // Query if Left-side is started up? + // + // + // Return Value: + // BOOL whether Left-side is intialized. + // + // Notes: + // If the Left-side is not yet started up, then data in the LS is not yet initialized enough + // for us to make meaningful queries, but the runtime will fire "Startup Exception" when it is. + // + // If the left-side is started up, then data is ready. (Although data may be temporarily inconsistent, + // see DataSafe). We may still get a Startup Exception in these cases, but it can be ignored. + // + virtual + BOOL IsLeftSideInitialized() = 0; + + + // + // Get an LS Appdomain via an AppDomain unique ID. + // Fails if the AD is not found or if the ID is invalid. + // + // Arguments: + // appdomainId - "unique appdomain ID". Must be a valid Id. + // + // Return Value: + // VMPTR_AppDomain for the corresponding AppDomain ID. Else throws. + // + // Notes: + // This query is based off the lifespan of the AppDomain from the VM's perspective. + // The AppDomainId is most likely obtained from an AppDomain-Created debug events. + // An AppDomainId is unique for the lifetime of the VM. + // This is the inverse function of GetAppDomainId(). + // + virtual + VMPTR_AppDomain GetAppDomainFromId(ULONG appdomainId) = 0; + + + // + // Get the AppDomain ID for an AppDomain. + // + // Arguments: + // vmAppDomain - VM pointer to the AppDomain object of interest + // + // Return Value: + // AppDomain ID for appdomain. Else throws. + // + // Notes: + // An AppDomainId is unique for the lifetime of the VM. It is non-zero. + // + virtual + ULONG GetAppDomainId(VMPTR_AppDomain vmAppDomain) = 0; + + // + // Get the managed AppDomain object for an AppDomain. + // + // Arguments: + // vmAppDomain - VM pointer to the AppDomain object of interest + // + // Return Value: + // objecthandle for the managed app domain object or the Null VMPTR if there is no + // object created yet + // + // Notes: + // The AppDomain managed object is lazily constructed on the AppDomain the first time + // it is requested. It may be NULL. + // + virtual + VMPTR_OBJECTHANDLE GetAppDomainObject(VMPTR_AppDomain vmAppDomain) = 0; + + // + // Determine if the specified AppDomain is the default domain + // + // Arguments: + // vmAppDomain - VM pointer to the AppDomain ojbect of interest + // + // Return Value: + // TRUE if this is the default appdomain, else FALSE. + // + // Notes: + // The default domain is the only one which cannot be unloaded and exists for the life + // of the process. + // A well behaved target only has 1 default domain. + // + virtual + BOOL IsDefaultDomain(VMPTR_AppDomain vmAppDomain) = 0; + + + virtual + void GetAssemblyFromDomainAssembly(VMPTR_DomainAssembly vmDomainAssembly, OUT VMPTR_Assembly * vmAssembly) = 0; + + // + // Determines whether the runtime security system has assigned full-trust to this assembly. + // + // Arguments: + // vmDomainAssembly - VM pointer to the assembly in question. + // + // Return Value: + // Returns trust status for the assembly. + // Throws on error. + // + // Notes: + // Of course trusted malicious code in the process could always cause this API to lie. However, + // an assembly loaded without full-trust should have no way of causing this API to return true. + // + virtual + BOOL IsAssemblyFullyTrusted(VMPTR_DomainAssembly vmDomainAssembly) = 0; + + + // + // Get the full AD friendly name for the given EE AppDomain. + // + // Arguments: + // vmAppDomain - VM pointer to the AppDomain. + // pStrName - required out parameter where the name will be stored. + // + // Return Value: + // None. On success, sets the string via the holder. Throws on error. + // This either sets pStrName or Throws. It won't do both. + // + // Notes: + // AD names have an unbounded length. AppDomain friendly names can also change, and + // so callers should be prepared to listen for name-change events and requery. + // AD names are specified by the user. + // + virtual + void GetAppDomainFullName( + VMPTR_AppDomain vmAppDomain, + IStringHolder * pStrName) = 0; + + + // + // #ModuleNames + // + // Modules / Assemblies have many different naming schemes: + // + // 1) Metadata Scope name: All modules have metadata, and each metadata scope has a name assigned + // by the creator of that scope (eg, the compiler). This usually is similar to the filename, but could + // be arbitrary. + // eg: "Foo" + // + // 2) FileRecord: the File record entry in the manifest module's metadata (table 0x26) for this module. + // eg: "Foo" + // + // 3) Managed module path: This is path that the image was loaded from. Eg, "c:\foo.dll". For non-file + // based modules (like in-memory, dynamic), there is no file path. The specific path is determined by + // fusion / loader policy. + // eg: "c:\foo.dll" + // + // 4) GAC path: If the module is loaded from the GAC, this is the path on disk into the gac cache that + // the image was pulled from. + // eg: " + // + // 5) Ngen path: If the module was ngenned, this is the path on disk into the ngen cache that the image + // was pulled from. + // eg: + // + // 6) Fully Qualified Assembly Name: this is an abstract name, which the CLR (fusion / loader) will + // resolve (to a filename for file-based modules). Managed apps may need to deal in terms of FQN, + // but the debugging services generally avoid them. + // eg: "Foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL". + // + + + // + // Get the "simple name" of a module. This is a heuristic within the CLR to return a simple, + // not-well-specified, but meaningful, name for a module. + // + // Arguments: + // vmModule - module to query + // pStrFileName - string holder to get simple name. + // + // Return Value: + // None, but pStrFilename will be initialized upon return. + // Throws if there was a problem reading the data with DAC or if there is an OOM exception, + // in which case no string was stored into pStrFilename. + // + // Notes: + // See code:#ModuleNames for an overview on module names. + // + // This is really just using code:Module::GetSimpleName. + // This gives back a meaningful name, which is generally some combination of the metadata + // name of the FileRecord name. This is important because it's valid even when a module + // doesn't have a filename. + // + // The simple name does not have any meaning. It is not a filename, does not necessarily have any + // relationship to the filename, and it's not necesarily the metadata name. + // Do not use the simple name for anything other than as a pretty string to give the an end user. + // + virtual + void GetModuleSimpleName(VMPTR_Module vmModule, IStringHolder * pStrFilename) = 0; + + + // + // Get the full path and file name to the assembly's manifest module. + // + // Arguments: + // vmAssembly - VM pointer to the Assembly. + // pStrFilename - required out parameter where the filename will be stored. + // + // Return Value: + // TRUE on success, in which case the filename was stored into pStrFilename + // FALSE if the assembly has no filename (eg. for in-memory assemblies), in which + // case an empty string was stored into pStrFilename. + // Throws if there was a problem reading the data with DAC, in which case + // no string was stored into pStrFilename. + // + // Notes: + // See code:#ModuleNames for an overview on module names. + // + // Normally this is just the filename from which the dll containing the assembly was + // loaded. In the case of multi-module assemblies, this is the filename for the + // manifest module (the one containing the assembly manifest). For in-memory + // assemblies (eg. those loaded from a Byte[], and those created by Reflection.Emit + // which will not be saved to disk) there is no filename. In that case this API + // returns an empty string. + // + virtual + BOOL GetAssemblyPath(VMPTR_Assembly vmAssembly, + IStringHolder * pStrFilename) = 0; + + + // get a type def resolved across modules + // Arguments: + // input: pTypeRefInfo - domain file and type ref from the referencing module + // output: pTargetRefInfo - domain file and type def from the referenced type (this may + // come from a module other than the referencing module) + // Note: throws + virtual + void ResolveTypeReference(const TypeRefData * pTypeRefInfo, + TypeRefData * pTargetRefInfo) = 0; + // + // Get the full path and file name to the module (if any). + // + // Arguments: + // vmModule - VM pointer to the module. + // pStrFilename - required out parameter where the filename will be stored. + // + // Return Value: + // TRUE on success, in which case the filename was stored into pStrFilename + // FALSE the module has no filename (eg. for in-memory assemblies), in which + // case an empty string was stored into pStrFilename. + // Throws an exception if there was a problem reading the data with DAC, in which case + // no string was stored into pStrFilename. + // + // Notes: + // See code:#ModuleNames for an overview on module names. + // + // Normally this is just the filename from which the module was loaded. + // For in-memory module (eg. those loaded from a Byte[], and those created by Reflection.Emit + // which will not be saved to disk) there is no filename. In that case this API + // returns an empty string. Consider GetModuleSimpleName in those cases. + // + // We intentionally don't use the function name "GetModuleFileName" here because + // winbase #defines that token (along with many others) to have an A or W suffix. + // + virtual + BOOL GetModulePath(VMPTR_Module vmModule, + IStringHolder * pStrFilename) = 0; + + + // + // Get the full path and file name to the ngen image for the module (if any). + // + // Arguments: + // vmModule - VM pointer to the module. + // pStrFilename - required out parameter where the filename will be stored. + // + // Return Value: + // TRUE on success, in which case the filename was stored into pStrFilename + // FALSE the module has no filename (eg. for in-memory assemblies), in which + // case an empty string was stored into pStrFilename. + // Throws an exception if there was a problem reading the data with DAC, in which case + // no string was stored into pStrFilename. + // + // Notes: + // See code:#ModuleNames for an overview on module names. + // + virtual + BOOL GetModuleNGenPath(VMPTR_Module vmModule, + IStringHolder * pStrFilename) = 0; + + + + // Get the metadata for the target module + // + // Arguments: + // vmModule - target module to get metadata for. + // pTargetBuffer - Out parameter to get target-buffer for metadata. Gauranteed to be non-empty on + // return. This will throw CORDBG_E_MISSING_METADATA hr if the buffer is empty. + // This does not gaurantee that the buffer is readable. For example, in a minidump, buffer's + // memory may not be present. + // + // Notes: + // Each module's metadata exists as a raw buffer in the target. This finds that target buffer and + // returns it. The host can then use OpenScopeOnMemory to create an instance of the metadata in + // the host process space. + // + // For dynamic modules, the CLR will eagerly serialize the metadata at "debuggable" points. This + // could be after each type is loaded; or after a bulk update. + // For non-dynamic modules (both in-memory and file-based), the metadata exists in the PEFile's image. + // + // Failure cases: + // This should succeed in normal, live-debugging scenarios. However, common failure paths here would be: + // + // 1. Data structures are intact, but Unable to even find the TargetBuffer in the target. In this + // case Metadata is truly missing. Likely means: + // - target is in the middle of generating metadata for a large bulk operation. (For example, attach + // to a TypeLibConverter using Ref.Emit to emit a module for a very large .tlb file). + // - corrupted target, + // - or the target had some error(out-of-memory?) generating the metadata. + // This throws CORDBG_E_MISSING_METADATA. + // + // 2. Target buffer is found, but memory it describes is not present. Likely means a minidump + // scenario with missing memory. Client should use alternative metadata location techniques (such as + // an ImagePath to locate the original image and then pulling metadata from that file). + // + virtual + void GetMetadata(VMPTR_Module vmModule, OUT TargetBuffer * pTargetBuffer) = 0; + + + // Definitions for possible symbol formats + // This is equivalent to code:ESymbolFormat in the runtime + typedef enum + { + kSymbolFormatNone, // No symbols available + kSymbolFormatPDB, // PDB symbol format - use diasymreader.dll + kSymbolFormatILDB, // ILDB symbol format - use ildbsymlib + } SymbolFormat; + + // + // Get the in-memory symbol (PDB/ILDB) buffer in the target if present. + // + // Arguments: + // vmModule- module to query for. + // pTargetBuffer - out parameter to get buffer in target of symbols. If no symbols, pTargetBuffer is empty on return. + // pSymbolFormat - out parameter to get the format of the symbols. + // + // Returns: + // 1) If there are in-memory symbols for the given module, pTargetBuffer is set to the buffer describing + // the symbols and pSymbolFormat is set to indicate PDB or ILDB format. This buffer can then be read, + // converted into an IStream, and passed to ISymUnmanagedBinder::CreateReaderForStream. + // 2) If the target is valid, but there is no symbols for the module, then pTargetBuffer->IsEmpty() == true + // and *pSymbolFormat == kSymbolFormatNone. + // 3) Else, throws exception. + // + // + // Notes: + // For file-based modules, PDBs are normally on disk and the debugger retreieves them via a symbol + // path without any help from ICorDebug. + // However, in some cases, the PDB is stored in-memory and so the debugger needs ICorDebug. Common + // cases include: + // - dynamic modules generated with reflection-emit. + // - in-memory modules loaded by Load(Byte[],Byte[]), which provide the PDB as a byte[]. + // - hosted modules where the host (such as SQL) store the PDB. + // + // In all cases, this can commonly fail. Executable code does not need to have a PDB. + virtual + void GetSymbolsBuffer(VMPTR_Module vmModule, OUT TargetBuffer * pTargetBuffer, OUT SymbolFormat * pSymbolFormat) = 0; + + // + // Get properties for a module + // + // Arguments: + // vmModule - vm handle to a module + // pData - required out parameter which will be filled out with module properties + // + // Notes: + // See definition of DomainFileInfo for more details about what properties + // this gives back. + virtual + void GetModuleData(VMPTR_Module vmModule, OUT ModuleInfo * pData) = 0; + + + // + // Get properties for a DomainFile + // + // Arguments: + // vmDomainFile - vm handle to a DomainFile + // pData - required out parameter which will be filled out with module properties + // + // Notes: + // See definition of DomainFileInfo for more details about what properties + // this gives back. + virtual + void GetDomainFileData(VMPTR_DomainFile vmDomainFile, OUT DomainFileInfo * pData) = 0; + + virtual + void GetModuleForDomainFile(VMPTR_DomainFile vmDomainFile, OUT VMPTR_Module * pModule) = 0; + + //......................................................................... + // These methods were the methods that DBI was calling from IXClrData in V2. + // We imported them over to this V3 interface so that we can sever all ties between DBI and the + // old IXClrData. + // + // The exact semantics of these are whatever their V2 IXClrData counterpart did. + // We may eventually migrate these to their real V3 replacements. + //......................................................................... + + // "types" of addresses. This is taken exactly from the definition, but renamed to match + // CLR coding conventions. + typedef enum + { + kAddressUnrecognized, + kAddressManagedMethod, + kAddressRuntimeManagedCode, + kAddressRuntimeUnmanagedCode, + kAddressGcData, + kAddressRuntimeManagedStub, + kAddressRuntimeUnmanagedStub, + } AddressType; + + // + // Get the "type" of address. + // + // Arguments: + // address - address to query type. + // + // Return Value: + // Type of address. Throws on error. + // + // Notes: + // This is taken exactly from the IXClrData definition. + // This is provided for V3 compatibility to support Interop-debugging. + // This should eventually be deprecated. + // + virtual + AddressType GetAddressType(CORDB_ADDRESS address) = 0; + + + // + // Query if address is a CLR stub. + // + // Arguments: + // address - Target address to query for. + // + // + // Return Value: + // true if the address is a CLR stub. + // + // Notes: + // This is used to implement ICorDebugProcess::IsTransitionStub + // This yields true if the address is claimed by a CLR stub manager, or if the IP is in mscorwks. + // Conceptually, This should eventually be merged with GetAddressType(). + // + virtual + BOOL IsTransitionStub(CORDB_ADDRESS address) = 0; + + //......................................................................... + // Get the values of the JIT Optimization and EnC flags. + // + // Arguments: + // vmDomainFile - (input) VM DomainFile (module) for which we are retrieving flags + // pfAllowJITOpts - (mandatory output) true iff this is not compiled for debug, + // i.e., without optimization + // pfEnableEnc - (mandatory output) true iff this module has EnC enabled + // + // Return Value: + // Returns on success. Throws on failure. + // + // Notes: + // This is used to implement both ICorDebugModule2::GetJitCompilerFlags and + // ICorDebugCode2::GetCompilerFlags. + //......................................................................... + + virtual + void GetCompilerFlags( + VMPTR_DomainFile vmDomainFile, + OUT BOOL * pfAllowJITOpts, + OUT BOOL * pfEnableEnC) = 0; + + //......................................................................... + // Set the values of the JIT optimization and EnC flags. + // + // Arguments: + // vmDomainFile - (input) VM DomainFile (module) for which we are retrieving flags + // pfAllowJITOpts - (input) true iff this should not be compiled for debug, + // i.e., without optimization + // pfEnableEnc - (input) true iff this module should have EnC enabled. If this is + // false, no change is made to the EnC flags. In other words, once EnC is enabled, + // there is no way to disable it. + // + // Return Value: + // S_OK on success and all bits were set. + // CORDBG_S_NOT_ALL_BITS_SET - if not all bits are set. Must use GetCompileFlags to + // determine which bits were set. + // CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE - if module is ngenned. + // Throw on other errors. + // + // Notes: + // Caller can only use this at module-load before any methods are jitted. + // This may be called multiple times. + // This is used to implement both ICorDebugModule2::SetJitCompilerFlags and + // ICorDebugModule::EnableJITDebugging. + //......................................................................... + + virtual + HRESULT SetCompilerFlags(VMPTR_DomainFile vmDomainFile, + BOOL fAllowJitOpts, + BOOL fEnableEnC) = 0; + + // + // Enumerate all AppDomains in the process. + // + // Arguments: + // fpCallback - callback to invoke on each appdomain + // pUserData - user data to supply for each callback. + // + // Return Value: + // Returns on success. Throws on error. + // + // Notes: + // Enumerates all appdomains in the process, including the Default-domain. + // Appdomains must show up in this list before the AD Load event is sent, and before + // that appdomain is discoverable from the debugger. + // See enumeration rules for details. + // + typedef void (*FP_APPDOMAIN_ENUMERATION_CALLBACK)(VMPTR_AppDomain vmAppDomain, CALLBACK_DATA pUserData); + virtual + void EnumerateAppDomains(FP_APPDOMAIN_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + + // + // Eunmerate all Assemblies in an appdomain. Enumerations is in load-order + // + // Arguments: + // vmAppDomain - domain in which to enumerate + // fpCallback - address to query type. + // pUserData - required out parameter for type of address. + // + // Return Value: + // Returns on success. Throws on error. + // + // Notes: + // Enumerates all executable assemblies (both shared and unshared) within an appdomain. + // This does not include inspection-only assemblies because those are just data and + // not executable (eg, they'll never show up on the stack and you can't set a breakpoint in them). + // This enumeration needs to be consistent with load/unload events. + // See enumeration rules for details. + // + // The order of the enumeration is the order the assemblies where loaded. + // Ultimately, the debugger needs to be able to tell the user the load + // order of assemblies (it can do this with native dlls). Since + // managed assembliees don't 1:1 correspond to native dlls, debuggers + // need this information from the runtime. + // + + typedef void (*FP_ASSEMBLY_ENUMERATION_CALLBACK)(VMPTR_DomainAssembly vmDomainAssembly, CALLBACK_DATA pUserData); + virtual + void EnumerateAssembliesInAppDomain(VMPTR_AppDomain vmAppDomain, + FP_ASSEMBLY_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + + + // + // Callback function for EnumerateModulesInAssembly + // + // This can throw on error. + // + // Arguments: + // vmModule - new module from the enumeration + // pUserData - user data passed to EnumerateModulesInAssembly + typedef void (*FP_MODULE_ENUMERATION_CALLBACK)(VMPTR_DomainFile vmModule, CALLBACK_DATA pUserData); + + // + // Enumerates all the code Modules in an assembly. + // + // Arguments: + // vmAssembly - assembly to enumerate within + // fpCallback - callback function to invoke on each module + // pUserData - arbitrary data passed to the callback + // + // Notes: + // This only enumerates "code" modules (ie, modules that have executable code in them). That + // includes normal file-based, ngenned, in-memory, and even dynamic modules. + // That excludes: + // - Resource modules (which have no code or metadata) + // - Inspection-only modules. These are viewed as pure data from the debugger's perspective. + // + virtual + void EnumerateModulesInAssembly( + VMPTR_DomainAssembly vmAssembly, + FP_MODULE_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + + + // + // When stopped at an event, request a synchronization. + // + // + // Return Value: + // Returns on success. Throws on error. + // + // Notes: + // Call this when an event is dispatched (eg, LoadModule) to request the runtime + // synchronize. This does a cooperative sync with the LS. This is not an async break + // and can not be called at arbitrary points. + // This primitive lets the LS always take the V3 codepath and defer decision making to the RS. + // The V2 behavior is to call this after every event (Since that's what V2 did). + // The V3 behavior is to never call this. + // + // If this is called, the LS will sync and we will get a SyncComplete. + // + // This is also like a precursor to "AsyncBreakAllOtherThreads" + // + virtual + void RequestSyncAtEvent() = 0; + + // Sets a flag inside LS.Debugger that indicates that + // 1. all "first chance exception" events should not be sent to the debugger + // 2. "exception handler found" events for exceptions never crossing JMC frames should not be sent to the debugger + // + // Arguments: + // sendExceptionsOutsideOfJMC - new value for the flag Debugger::m_sendExceptionsOutsideOfJMC. + // + // Return Value: + // Returns error code, never throws. + // + // Note: This call is used by ICorDebugProcess8.EnableExceptionCallbacksOutsideOfMyCode. + virtual + HRESULT SetSendExceptionsOutsideOfJMC(BOOL sendExceptionsOutsideOfJMC) = 0; + + // + // Notify the debuggee that a debugger atach is pending. + // + // Arguments: + // None + // + // Return Value: + // Returns on success. Throws on error. + // + // Notes: + // Attaching means that CORDebuggerPendingAttach() will now return true. + // This doesn't do anything else (eg, no fake events). + // + // @dbgtodo- still an open Feature-Crew decision how this is exposed publicly. + virtual + void MarkDebuggerAttachPending() = 0; + + // + // Notify the debuggee that a debugger is attached / detached. + // + // Arguments: + // fAttached - true if we're attaching, false if we're detaching. + // + // Return Value: + // Returns on success. Throws on error. + // + // Notes: + // Attaching means that CorDebuggerAttached() will now return true. + // This doesn't do anything else (eg, no fake events). + // This lets the V3 codepaths invade the LS to subscribe to events. + // + // @dbgtodo- still an open Feature-Crew decision how this is exposed publicly. + virtual + void MarkDebuggerAttached(BOOL fAttached) = 0; + + + + // + // Hijack a thread. This will effectively do a native func-eval of the thread to set the IP + // to a hijack stub and push the parameters. + // + // Arguments: + // dwThreadId - OS thread to hijack. This must be consistent with pRecord and pOriginalContext + // pRecord - optional pointer to Exception record. Required if this is hijacked at an exception. + // NULL if this is hijacked at a managed IP. + // pOriginalContext - optional pointer to buffer to receive the context that the thread is hijacked from. + // The caller can use this to either restore the hijack or walk the hijack. + // cbSizeContext - size in bytes of buffer pointed to by pContext + // reason - reason code for the hijack. The hijack stub can then delegate to the proper hijack. + // pUserData - arbitrary data passed through to hijack. This is reason-depedendent. + // pRemoteContextAddr - If non-NULL this receives the remote address where the CONTEXT was written in the + // in the debuggee. + // + // Assumptions: + // Caller must guarantee this is safe. + // This is intended to be used at a thread that either just had an exception or is at a managed IP. + // If this is hijacked at an exception, client must cancel the exception (gh / DBG_CONTINUE) + // so that the OS exception processing doesn't interfere with the hijack. + // + // Notes: + // Hijack is hard, so we want 1 hijack stub that handles all our hijacking needs. + // This lets us share: + // - assembly stubs (which are very platform specific) + // - hijacking / restoration mechanics, + // - making the hijack walkable via the stackwalker. + // + // Hijacking can be used to implement: func-eval, FE abort, Synchronizing, + // dispatching Unhandled Exception notifications. + // + // Nesting: Since Hijacking passes the key state off to the hijacked thread, (such as original + // context to be used with restoring the hijack), the raw hijacking nests just like function + // calls. However, the client may need to keep additional state to handle nesting. For example, + // nested hijacks will require the client to track multiple CONTEXT*. + // + // If the thread is in jitted code, then the hijack needs to cooperate with the in-process + // stackwalker that the GC uses. It must be in cooperative mode, and push a Frame on the + // frame chain to protect the managed frames it hijacked from before it goes to preemptive mode. + + virtual + void Hijack( + VMPTR_Thread vmThread, + ULONG32 dwThreadId, + const EXCEPTION_RECORD * pRecord, + T_CONTEXT * pOriginalContext, + ULONG32 cbSizeContext, + EHijackReason::EHijackReason reason, + void * pUserData, + CORDB_ADDRESS * pRemoteContextAddr) = 0; + + + // + // Callback function for connection enumeration. + // + // Arguments: + // id - the connection ID. + // pName - the name of the connection. + // pUserData - user data supplied to EnumerateConnections + typedef void (*FP_CONNECTION_CALLBACK)(DWORD id, LPCWSTR pName, CALLBACK_DATA pUserData); + + // + // Enumerate all the Connections in the process. + // + // Arguments: + // fpCallback - callback to invoke for each connection + // pUserData - random user data to pass to callback. + // + // Notes: + // This enumerates all the connections. The host notifies the debugger of Connections + // via the ICLRDebugManager interface. + // ICorDebug has no interest in connections. It's merely the transport between the host and the debugger. + // Ideally, that transport would be more general. + // + // V2 Attach would provide faked up CreateConnection, ChangeConnection events on attach. + // This enumeration ability allows V3 to emulate that behavior. + // +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + virtual void EnumerateConnections(FP_CONNECTION_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0; +#endif //FEATURE_INCLUDE_ALL_INTERFACES + + // + // Enumerate all threads in the target. + // + // Arguments: + // fpCallback - callback function to invoke on each thread. + // pUserData - arbitrary user data supplied to each callback. + // + // Notes: + // This enumerates the ThreadStore in the target, which is all the Thread* objects. + // This includes threads that have entered the runtime. This may include threads + // even before that thread has executed IL and after that thread no longer has managed + // code on its stack. + + // Callback invoked for each thread. + typedef void (*FP_THREAD_ENUMERATION_CALLBACK)(VMPTR_Thread vmThread, CALLBACK_DATA pUserData); + + virtual + void EnumerateThreads(FP_THREAD_ENUMERATION_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0; + + + // Check if the thread is dead + // + // Arguments: + // vmThread - valid thread to check if it's dead. + // + // Returns: true if the thread is "dead", which means it can never call managed code again. + // + // Notes: + // #IsThreadMarkedDead + // Threads shutdown states are: + // 1) Thread is running managed code normally. Thread eventually exits all managed code and + // gets to a point where it will never call managed code again. + // 2) Thread is marked as dead. + // - For threads created outside of the runtime (such as a native thread that wanders into + // managed code), this mark can happen in DllMain(ThreadDetach) + // - For threads created by the runtime (eg, System.Threading.Thread.Start), this may be done + // at the top of the threads stack after it calls the user's Thread-Proc. + // 3) MAYBE Native thread exits at this point (or it may not). This would be the common case + // for threads created outside the runtime. + // 4) Thread exit event is sent. + // - For threads created by the runtime, this may be sent at the top of the thread's + // stack (or even when we know that the thread will never execute managed code again) + // - For threads created outside the runtime, this is more difficult. A thread can + // call into managed code and then return, and then call back into managed code at a + // later time (The finalizer does this!). So it's not clear when the native thread + // actually exits and will never call managed code again. The only hook we have for + // this is DllMain(Thread-Detach). We can mark bits in DllMain, but we can't send + // debugger notifications (too dangerous from such a restricted context). + // So we may mark the thread as dead, but then sweep later (perhaps on the finalizer + // thread), and thus send the Exit events later. + // 5) Native thread may exit at this point. This is the common case for threads created by + // the runtime. + // + // The underlying native thread may have exited at eitehr #3 or #5. Because of this + // flexibility, we don't want to rely on native thread exit events. + // This function checks if a Thread is passed state #2 (marked as dead). The key invariant + // is that once a thread is marked as dead: + // - it can never call managed code again. + // - it should not be discoverable by DacDbi enumerations. + // + // DBI should prefer relying on IsThreadMarkedDead rather than event notifications (either + // managed or native) because tracking events requires that DBI maintain state, which means + // that attach + dump cases may break. For example, we want a full dump at the ExitThread + // event to have the same view as a live process at the ExitThread event. + // + // We avoid relying on the native thread exit notifications because: + // - that's a specific feature of the Win32 debugging API that may not be available on other platforms. + // - the only native events the pipeline gets are Exceptions. + // + // Whether a thread is dead can be inferred from the ICorDebug API. However, we have this + // on DacDbi to ensure that this definition is consistent with the other DacDbi methods, + // especially the enumeration and discovery rules. + virtual + bool IsThreadMarkedDead(VMPTR_Thread vmThread) = 0; + + + // + // Return the handle of the specified thread. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the handle of the specified thread + // + // @dbgtodo- this should go away in V3. This is useless on a dump. + + virtual + HANDLE GetThreadHandle(VMPTR_Thread vmThread) = 0; + + // + // Return the object handle for the managed Thread object corresponding to the specified thread. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // This function returns the object handle for the managed Thread object corresponding to the + // specified thread. The return value may be NULL if a managed Thread object has not been created + // for the specified thread yet. + // + + virtual + VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread) = 0; + + // + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread + // according to the CorDebugThreadState. + // + // Arguments: + // vmThread - the specified thread + // debugState - the desired CorDebugThreadState + // + + virtual + void SetDebugState(VMPTR_Thread vmThread, + CorDebugThreadState debugState) = 0; + + // + // Returns TRUE if this thread has an unhandled exception + // + // Arguments: + // vmThread - the thread to query + // + // Return Value + // TRUE iff this thread has an unhandled exception + // + virtual + BOOL HasUnhandledException(VMPTR_Thread vmThread) = 0; + + // + // Return the user state of the specified thread. Most of the state are derived from + // the ThreadState of the specified thread, e.g. TS_Background, TS_Unstarted, etc. + // The exception is USER_UNSAFE_POINT, which we need to do a one-frame stackwalk to figure out. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the user state of the specified thread + // + + virtual + CorDebugUserState GetUserState(VMPTR_Thread vmThread) = 0; + + + // + // Returns most of the user state of the specified thread, + // i.e. flags which can be derived from the ThreadState: + // USER_STOP_REQUESTED, USER_SUSPEND_REQUESTED, USER_BACKGROUND, USER_UNSTARTED + // USER_STOPPED, USER_WAIT_SLEEP_JOIN, USER_SUSPENDED, USER_THREADPOOL + // + // Only USER_UNSAFE_POINT is always set to 0, since it takes additional stackwalk. + // If you need USER_UNSAFE_POINT, use GetUserState(VMPTR_Thread); + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the user state of the specified thread + // + virtual + CorDebugUserState GetPartialUserState(VMPTR_Thread vmThread) = 0; + + + // + // Return the connection ID of the specified thread. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the connection ID of the specified thread + // + + virtual + CONNID GetConnectionID(VMPTR_Thread vmThread) = 0; + + // + // Return the task ID of the specified thread. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the task ID of the specified thread + // + + virtual + TASKID GetTaskID(VMPTR_Thread vmThread) = 0; + + // + // Return the OS thread ID of the specified thread + // + // Arguments: + // vmThread - the specified thread; cannot be NULL + // + // Return Value: + // the OS thread ID of the specified thread. Returns 0 if not scheduled. + // + + virtual + DWORD TryGetVolatileOSThreadID(VMPTR_Thread vmThread) = 0; + + // + // Return the unique thread ID of the specified thread. The value used for the thread ID changes + // depending on whether the runtime is being hosted. In non-hosted scenarios, a managed thread will + // always be associated with the same native thread, and so we can use the OS thread ID as the thread ID + // for the managed thread. In hosted scenarios, however, a managed thread may run on multiple native + // threads. It may not even have a backing native thread if it's switched out. Therefore, we can't use + // the OS thread ID as the thread ID. Instead, we use the internal managed thread ID. + // + // Arguments: + // vmThread - the specified thread; cannot be NULL + // + // Return Value: + // Returns a stable and unique thread ID for the lifetime of the specified managed thread. + // + + virtual + DWORD GetUniqueThreadID(VMPTR_Thread vmThread) = 0; + + // + // Return the object handle to the managed Exception object of the current exception + // on the specified thread. The return value could be NULL if there is no current exception. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // This function returns the object handle to the managed Exception object of the current exception. + // The return value may be NULL if there is no exception being processed, or if the specified thread + // is an unmanaged thread which has entered and exited the runtime. + // + + virtual + VMPTR_OBJECTHANDLE GetCurrentException(VMPTR_Thread vmThread) = 0; + + // + // Return the object handle to the managed object for a given CCW pointer. + // + // Arguments: + // ccwPtr - the specified ccw pointer + // + // Return Value: + // This function returns the object handle to the managed object for a given CCW pointer. + // + + virtual + VMPTR_OBJECTHANDLE GetObjectForCCW(CORDB_ADDRESS ccwPtr) = 0; + + // + // Return the object handle to the managed CustomNotification object of the current notification + // on the specified thread. The return value could be NULL if there is no current notification. + // + // Arguments: + // vmThread - the specified thread on which the notification occurred + // + // Return Value: + // This function returns the object handle to the managed CustomNotification object of the current notification. + // The return value may be NULL if there is no current notification. + // + + virtual + VMPTR_OBJECTHANDLE GetCurrentCustomDebuggerNotification(VMPTR_Thread vmThread) = 0; + + + // + // Return the current appdomain the specified thread is in. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the current appdomain of the specified thread + // + // Notes: + // This function throws if the current appdomain is NULL for whatever reason. + // + + virtual + VMPTR_AppDomain GetCurrentAppDomain(VMPTR_Thread vmThread) = 0; + + + // + // Resolve an assembly + // + // Arguments: + // vmScope - module containing metadata that the token is scoped to. + // tkAssemblyRef - assembly ref token to lookup. + // + // Returns: + // Assembly that the loader/fusion has bound to the given assembly ref. + // Returns NULL if the assembly has not yet been loaded (a common case). + // Throws on error. + // + // Notes: + // A single module has metadata that specifies references via tokens. The + // loader/fusion goes through tremendous and random policy hoops to determine + // which specific file actually gets bound to the reference. This policy includes + // things like config files, registry settings, and many other knobs. + // + // The debugger can't duplicate this policy with 100% accuracy, and + // so we need DAC to lookup the assembly that was actually loaded. + virtual + VMPTR_DomainAssembly ResolveAssembly(VMPTR_DomainFile vmScope, mdToken tkAssemblyRef) = 0; + + //----------------------------------------------------------------------------- + // Interface for initializing the native/IL sequence points and native var info + // for a function. + // Arguments: + // input: + // vmMethodDesc MethodDesc of the function + // startAddr starting address of the function--this serves to + // differentiate various EnC versions of the function + // fCodePitched indicates whether code for the function has been pitched + // fJitComplete indicates whether the function has been jitted + // output: + // pNativeVarData space for the native code offset information for locals + // pSequencePoints space for the IL/native sequence points + // Return value: + // none, but may throw an exception + // Assumptions: + // vmMethodDesc, pNativeVarInfo and pSequencePoints are non-NULL + + // Notes: + //----------------------------------------------------------------------------- + + virtual + void GetNativeCodeSequencePointsAndVarInfo(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS startAddress, + BOOL fCodeAvailabe, + OUT NativeVarData * pNativeVarData, + OUT SequencePoints * pSequencePoints) = 0; + + // + // Return the filter CONTEXT on the LS. Once we move entirely over to the new managed pipeline + // built on top of the Win32 debugging API, this won't be necessary. + // + // Arguments: + // vmThread - the specified thread + // + // Return Value: + // the filter CONTEXT of the specified thread + // + // Notes: + // This function should go away when everything is moved OOP and + // we don't have a filter CONTEXT on the LS anymore. + // + + virtual + VMPTR_CONTEXT GetManagedStoppedContext(VMPTR_Thread vmThread) = 0; + + typedef enum + { + kInvalid, + kManagedStackFrame, + kExplicitFrame, + kNativeStackFrame, + kNativeRuntimeUnwindableStackFrame, + kAtEndOfStack, + } FrameType; + + // The stackwalker functions allocate persistent state within DDImpl. Clients can hold onto + // this via an opaque StackWalkHandle. + typedef void* * StackWalkHandle; + + // + // Create a stackwalker on the specified thread and return a handle to it. + // Initially, the stackwalker is at the filter CONTEXT if there is one. + // Otherwise it is at the leaf CONTEXT. It DOES NOT fast forward to the first frame of interest. + // + // Arguments: + // vmThread - the specified thread + // pInternalContextBuffer - a CONTEXT buffer for the stackwalker to work with + // ppSFIHandle - out parameter; return a handle to the stackwalker + // + // Notes: + // Call DeleteStackWalk() to delete the stackwalk buffer. + // This is a special case that violates the 'no state' tenant. + // + + virtual + void CreateStackWalk(VMPTR_Thread vmThread, + DT_CONTEXT * pInternalContextBuffer, + OUT StackWalkHandle * ppSFIHandle) = 0; + + // Delete the stackwalk object created from CreateStackWalk. + virtual + void DeleteStackWalk(StackWalkHandle ppSFIHandle) = 0; + + // + // Get the CONTEXT of the current frame where the stackwalker is stopped at. + // + // Arguments: + // pSFIHandle - the handle to the stackwalker + // pContext - OUT: the CONTEXT to be filled out. The context control flags are ignored. + // + + virtual + void GetStackWalkCurrentContext(StackWalkHandle pSFIHandle, + DT_CONTEXT * pContext) = 0; + + // + // Set the stackwalker to the given CONTEXT. The CorDebugSetContextFlag indicates whether + // the CONTEXT is "active", meaning that the IP is point at the current instruction, + // not the return address of some function call. + // + // Arguments: + // vmThread - the current thread + // pSFIHandle - the handle to the stackwalker + // flag - flag to indicate whether the specified CONTEXT is "active" + // pContext - the specified CONTEXT. This may make correctional adjustments to the context's IP. + // + + virtual + void SetStackWalkCurrentContext(VMPTR_Thread vmThread, + StackWalkHandle pSFIHandle, + CorDebugSetContextFlag flag, + DT_CONTEXT * pContext) = 0; + + // + // Unwind the stackwalker to the next frame. The next frame could be any actual stack frame, + // explicit frame, native marker frame, etc. Call GetStackWalkCurrentFrameInfo() to find out + // more about the frame. + // + // Arguments: + // pSFIHandle - the handle to the stackwalker + // + // Return Value: + // Return TRUE if we successfully unwind to the next frame. + // Return FALSE if there is no more frames to walk. + // Throw on error. + // + + virtual + BOOL UnwindStackWalkFrame(StackWalkHandle pSFIHandle) = 0; + + // + // Check whether the specified CONTEXT is valid. The only check we perform right now is whether the + // SP in the specified CONTEXT is in the stack range of the thread. + // + // Arguments: + // vmThread - the specified thread + // pContext - the CONTEXT to be checked + // + // Return Value: + // Return S_OK if the CONTEXT passes our checks. + // Returns CORDBG_E_NON_MATCHING_CONTEXT if the SP in the specified CONTEXT doesn't fall in the stack + // range of the thread. + // Throws on error. + // + + virtual + HRESULT CheckContext(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext) = 0; + + // + // Fill in the DebuggerIPCE_STRData structure with information about the current frame + // where the stackwalker is stopped at. + // + // Arguments: + // pSFIHandle - the handle to the stackwalker + // pFrameData - the DebuggerIPCE_STRData to be filled out; + // it can be NULL if you just want to know the frame type + // + // Return Value: + // Return the type of the current frame + // + + virtual + FrameType GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle, + OPTIONAL DebuggerIPCE_STRData * pFrameData) = 0; + + // + // Return the number of internal frames on the specified thread. + // + // Arguments: + // vmThread - the thread whose internal frames are being retrieved + // + // Return Value: + // Return the number of internal frames. + // + // Notes: + // Explicit frames are "marker objects" the runtime pushes on the stack to mark special places, e.g. + // appdomain transition, managed-to- unmanaged transition, etc. Internal frames are only a subset of + // explicit frames. Explicit frames which are not interesting to the debugger are not exposed (e.g. + // GCFrame). Internal frames are interesting to the debugger if they have a CorDebugInternalFrameType + // other than STUBFRAME_NONE. + // + // The user should call this function before code:IDacDbiInterface::EnumerateInternalFrames to figure + // out how many interesting internal frames there are. + // + + virtual + ULONG32 GetCountOfInternalFrames(VMPTR_Thread vmThread) = 0; + + // + // Enumerate the internal frames on the specified thread and invoke the provided callback on each of + // them. Information about the internal frame is stored in the DebuggerIPCE_STRData. + // + // Arguments: + // vmThread - the thread to be walked fpCallback - callback function invoked on each internal frame + // pUserData - user-specified custom data + // + // Notes: + // The user can call code:IDacDbiInterface::GetCountOfInternalFrames to figure out how many internal + // frames are on the thread before calling this function. Also, refer to the comment of that function + // to find out more about internal frames. + // + + typedef void (*FP_INTERNAL_FRAME_ENUMERATION_CALLBACK)(const DebuggerIPCE_STRData * pFrameData, CALLBACK_DATA pUserData); + + virtual + void EnumerateInternalFrames(VMPTR_Thread vmThread, + FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + // + // Given the FramePointer of the parent frame and the FramePointer of the current frame, + // check if the current frame is the parent frame. fpParent should have been returned + // previously by the DacDbiInterface via GetStackWalkCurrentFrameInfo(). + // + // Arguments: + // fpToCheck - the FramePointer of the current frame + // fpParent - the FramePointer of the parent frame; should have been returned earlier by the DDI + // + // Return Value: + // Return TRUE if the current frame is indeed the parent frame + // + // Note: + // Because of the complexity involved in checking for the parent frame, we should always + // ask the ExceptionTracker to do it. + // + + virtual + BOOL IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent) = 0; + + // + // Return the stack parameter size of a given method. This is necessary on x86 for unwinding. + // + // Arguments: + // controlPC - any address in the specified method; you can use the current PC of the stack frame + // + // Return Value: + // Return the size of the stack parameters of the given method. + // Return 0 for vararg methods. + // + // Assumptions: + // The callee stack parameter size is constant throughout a method. + // + + virtual + ULONG32 GetStackParameterSize(CORDB_ADDRESS controlPC) = 0; + + // + // Return the FramePointer of the current frame where the stackwalker is stopped at. + // + // Arguments: + // pSFIHandle - the handle to the stackwalker + // + // Return Value: + // the FramePointer of the current frame + // + // Notes: + // The FramePointer of a stack frame is: + // the stack address of the return address on x86, + // the current SP on AMD64, + // + // On x86, to get the stack address of the return address, we need to unwind one more frame + // and use the SP of the caller frame as the FramePointer of the callee frame. This + // function does NOT do that. It just returns the SP. The caller needs to handle the + // unwinding. + // + // The FramePointer of an explicit frame is just the stack address of the explicit frame. + // + + virtual + FramePointer GetFramePointer(StackWalkHandle pSFIHandle) = 0; + + // + // Check whether the specified CONTEXT is the CONTEXT of the leaf frame. This function doesn't care + // whether the leaf frame is native or managed. + // + // Arguments: + // vmThread - the specified thread + // pContext - the CONTEXT to check + // + // Return Value: + // Return TRUE if the specified CONTEXT is the leaf CONTEXT. + // + // Notes: + // Currently we check the specified CONTEXT against the filter CONTEXT first. + // This will be deprecated in V3. + // + + virtual + BOOL IsLeafFrame(VMPTR_Thread vmThread, + const DT_CONTEXT * pContext) = 0; + + // Get the context for a particular thread of the target process. + // Arguments: + // input: vmThread - the thread for which the context is required + // output: pContextBuffer - the address of the CONTEXT to be initialized. + // The memory for this belongs to the caller. It must not be NULL. + // Note: throws + virtual + void GetContext(VMPTR_Thread vmThread, DT_CONTEXT * pContextBuffer) = 0; + + // + // This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this + // inside DDI because the RS has no notion of REGDISPLAY. + // + // Arguments: + // pInContext - the CONTEXT to be converted + // pOutDRD - the converted DebuggerREGDISPLAY + // fActive - Indicate whether the CONTEXT is active or not. An active CONTEXT means that the + // IP is the next instruction to be executed, not the return address of a function call. + // The opposite of an active CONTEXT is an unwind CONTEXT, which is obtained from + // unwinding. + // + + virtual + void ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext, + DebuggerREGDISPLAY * pOutDRD, + BOOL fActive) = 0; + + typedef enum + { + kNone, + kILStub, + kLCGMethod, + } DynamicMethodType; + + // + // Check whether the specified method is an IL stub or an LCG method. This answer determines if we + // need to expose the method in a V2-style stackwalk. + // + // Arguments: + // vmMethodDesc - the method to be checked + // + // Return Value: + // Return kNone if the method is neither an IL stub or an LCG method. + // Return kILStub if the method is an IL stub. + // Return kLCGMethod if the method is an LCG method. + // + + virtual + DynamicMethodType IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) = 0; + + // + // Return a TargetBuffer for the raw vararg signature. + // Also return the address of the first argument in the vararg signature. + // + // Arguments: + // VASigCookieAddr - the target address of the VASigCookie pointer (double indirection) + // pArgBase - out parameter; return the target address of the first word of the arguments + // + // Return Value: + // Return a TargetBuffer for the raw vararg signature. + // + // Notes: + // We can't take a VMPTR here because VASigCookieAddr does not come from the DDI. Instead, + // we use the native variable information to figure out which stack slot contains the + // VASigCookie pointer. So a remote address is all we have got. + // + // Ideally we should be able to return just a SigParser, but doing so has a not-so-trivial problem. + // The memory used for the signature pointed to by the SigParser cannot be allocated in the DAC cache, + // since it'll be used by mscordbi. We don't have a clean way to allocate memory in mscordbi without + // breaking the Signature abstraction. + // + // The other option would be to create a new sub-type like "SignatureCopy" which allocates and frees + // its own backing memory. Currently we don't want to share heaps between mscordacwks.dll and + // mscordbi.dll, and so we would have to jump through some hoops to allocate with an allocator + // in mscordbi.dll. + // + + virtual + TargetBuffer GetVarArgSig(CORDB_ADDRESS VASigCookieAddr, + OUT CORDB_ADDRESS * pArgBase) = 0; + + // + // Indicates if the specified type requires 8-byte alignment. + // + // Arguments: + // thExact - the exact TypeHandle of the type to query + // + // Return Value: + // TRUE if the type requires 8-byte alignment. + // + + virtual + BOOL RequiresAlign8(VMPTR_TypeHandle thExact) = 0; + + // + // Resolve the raw generics token to the real generics type token. The resolution is based on the + // given index. See Notes below. + // + // Arguments: + // dwExactGenericArgsTokenIndex - the variable index of the generics type token + // rawToken - the raw token to be resolved + // + // Return Value: + // Return the actual generics type token. + // + // Notes: + // DDI tells the RS which variable stores the generics type token, but DDI doesn't retrieve the value + // of the variable itself. Instead, the RS retrieves the value of the variable. However, + // in some cases, the variable value is not the generics type token. In this case, we need to + // "resolve" the variable value to the generics type token. The RS should call this API to do that. + // + // If the index is 0, then the generics type token is the MethodTable of the "this" object. + // rawToken will be the address of the "this" object. + // + // If the index is TYPECTXT_ILNUM, the generics type token is a secret argument. + // It could be a MethodDesc or a MethodTable, and in this case no resolution is actually necessary. + // rawToken will be the actual secret argument, and this API really is just a nop. + // + // However, we don't want the RS to know all this logic. + // + + virtual + GENERICS_TYPE_TOKEN ResolveExactGenericArgsToken(DWORD dwExactGenericArgsTokenIndex, + GENERICS_TYPE_TOKEN rawToken) = 0; + + //----------------------------------------------------------------------------- + // Functions to get information about code objects + //----------------------------------------------------------------------------- + + // GetILCodeAndSig returns the function's ILCode and SigToken given + // a module and a token. The info will come from a MethodDesc, if + // one exists or from metadata. + // + // Arguments: + // Input: + // vmDomainFile - module containing metadata for the method + // functionToken - metadata token for the function + // Output (required): + // codeInfo - start address and size of the IL + // pLocalSigToken - signature token for the method + virtual + void GetILCodeAndSig(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + OUT TargetBuffer * pCodeInfo, + OUT mdToken * pLocalSigToken) = 0; + + // Gets information about a native code blob: + // it's method desc, whether it's an instantiated generic, its EnC version number + // and hot and cold region information. + // Arguments: + // Input: + // vmDomainFile - module containing metadata for the method + // functionToken - token for the function for which we need code info + // Output (required): + // pCodeInfo - data structure describing the native code regions. + // Notes: If the function is unjitted, the method desc will be NULL and the + // output parameter will be invalid. In general, if the native start address + // is unavailable for any reason, the output parameter will also be + // invalid (i.e., pCodeInfo->IsValid is false). + + virtual + void GetNativeCodeInfo(VMPTR_DomainFile vmDomainFile, + mdToken functionToken, + OUT NativeCodeFunctionData * pCodeInfo) = 0; + + // Gets information about a native code blob: + // it's method desc, whether it's an instantiated generic, its EnC version number + // and hot and cold region information. + // This is similar to function above, just works from a different starting point + // Also this version can get info for any particular EnC version instance + // because they all have different start addresses whereas the above version gets + // the most recent one + // Arguments: + // Input: + // hotCodeStartAddr - the beginning of the code hot code region + // Output (required): + // pCodeInfo - data structure describing the native code regions. + + virtual + void GetNativeCodeInfoForAddr(VMPTR_MethodDesc vmMethodDesc, + CORDB_ADDRESS hotCodeStartAddr, + NativeCodeFunctionData * pCodeInfo) = 0; + + //----------------------------------------------------------------------------- + // Functions to get information about types + //----------------------------------------------------------------------------- + + // Determine if a type is a ValueType + // + // Arguments: + // input: vmTypeHandle - the type being checked (works even on unrestored types) + // + // Return: + // TRUE iff the type is a ValueType + + virtual + BOOL IsValueType (VMPTR_TypeHandle th) = 0; + + // Determine if a type has generic parameters + // + // Arguments: + // input: vmTypeHandle - the type being checked (works even on unrestored types) + // + // Return: + // TRUE iff the type has generic parameters + + virtual + BOOL HasTypeParams (VMPTR_TypeHandle th) = 0; + + // Get type information for a class + // + // Arguments: + // input: vmAppDomain - appdomain where we will fetch field data for the type + // thExact - exact type handle for type + // output: + // pData - structure containing information about the class and its + // fields + + virtual + void GetClassInfo (VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle thExact, + ClassInfo * pData) = 0; + + // get field information and object size for an instantiated generic + // + // Arguments: + // input: vmDomainFile - module containing metadata for the type + // thExact - exact type handle for type (may be NULL) + // thApprox - approximate type handle for the type + // output: + // pFieldList - array of structures containing information about the fields. Clears any previous + // contents. Allocated and initialized by this function. + // pObjectSize - size of the instantiated object + // + virtual + void GetInstantiationFieldInfo (VMPTR_DomainFile vmDomainFile, + VMPTR_TypeHandle vmThExact, + VMPTR_TypeHandle vmThApprox, + OUT DacDbiArrayList<FieldData> * pFieldList, + OUT SIZE_T * pObjectSize) = 0; + + // use a type handle to get the information needed to create the corresponding RS CordbType instance + // + // Arguments: + // input: boxed - indicates what, if anything, is boxed. See code:AreValueTypesBoxed for more + // specific information + // vmAppDomain - module containing metadata for the type + // vmTypeHandle - type handle for the type + // output: pTypeInfo - holds information needed to build the corresponding CordbType + // + virtual + void TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + + virtual + void GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + CORDB_ADDRESS addr, + OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + + + virtual + void GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, + VMPTR_AppDomain vmAppDomain, + COR_TYPEID id, + OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + + + // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. + // Note there is no guarantee the returned handle will be fully restored (in pre-jit scenarios), + // only that it exists. Later functions that use this type handle should fail if they require + // information not yet available at the current restoration level + // + // Arguments: + // input: vmModule - the module scope in which to look up the type def + // metadataToken - the type definition to retrieve + // + // Return value: the type handle if it exists or throws CORDBG_E_CLASS_NOT_LOADED if it isn't loaded + // + virtual + VMPTR_TypeHandle GetTypeHandle(VMPTR_Module vmModule, + mdTypeDef metadataToken) = 0; + + // Get the approximate type handle for an instantiated type. This may be identical to the exact type handle, + // but if we have code sharing for generics, it may differ in that it may have canonical type parameters. + // This will occur if we have not yet loaded an exact type but we have loaded the canonical form of the + // type. + // + // Arguments: + // input: pTypeData - information needed to get the type handle, this includes a list of type parameters + // and the number of entries in the list. Allocated and initialized by the caller. + // Return value: the approximate type handle + // + virtual + VMPTR_TypeHandle GetApproxTypeHandle(TypeInfoList * pTypeData) = 0; + + // Get the exact type handle from type data. + // Arguments: + // input: pTypeData - type information for the type. includes information about + // the top-level type as well as information + // about the element type for array types, the referent for + // pointer types, or actual parameters for generic class or + // valuetypes, as appropriate for the top-level type. + // pArgInfo - This is preallocated and initialized by the caller and contains two fields: + // genericArgsCount - number of type parameters (these may be actual type parameters + // for generics or they may represent the element type or referent + // type. + // pGenericArgData - list of type parameters + // vmTypeHandle - the exact type handle derived from the type information + // Return Value: an HRESULT indicating the result of the operation + virtual + HRESULT GetExactTypeHandle(DebuggerIPCE_ExpandedTypeData * pTypeData, + ArgInfoList * pArgInfo, + VMPTR_TypeHandle& vmTypeHandle) = 0; + + // + // Retrieve the generic type params for a given MethodDesc. This function is specifically + // for stackwalking because it requires the generic type token on the stack. + // + // Arguments: + // vmAppDomain - the appdomain of the MethodDesc + // vmMethodDesc - the method in question + // genericsToken - the generic type token in the stack frame owned by the method + // + // pcGenericClassTypeParams - out parameter; returns the number of type parameters for the class + // containing the method in question; must not be NULL + // pGenericTypeParams - out parameter; returns an array of type parameters and + // the count of the total number of type parameters; must not be NULL + // + // Notes: + // The memory for the array is allocated by this function on the Dbi heap. + // The caller is responsible for releasing it. + // + + virtual + void GetMethodDescParams(VMPTR_AppDomain vmAppDomain, + VMPTR_MethodDesc vmMethodDesc, + GENERICS_TYPE_TOKEN genericsToken, + OUT UINT32 * pcGenericClassTypeParams, + OUT TypeParamsList * pGenericTypeParams) = 0; + + // Get the target field address of a context or thread local static. + // Arguments: + // input: vmField - pointer to the field descriptor for the static field + // vmRuntimeThread - thread to which the static field belongs. This must + // NOT be NULL + // Return Value: The target address of the field if the field is allocated. + // NULL if the field storage is not yet allocated. + // + // Note: + // Static field storage is lazily allocated, so this may commonly return NULL. + // This is an inspection only method and can not allocate the static storage. + // Field storage is constant once allocated, so this value can be cached. + + virtual + CORDB_ADDRESS GetThreadOrContextStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_Thread vmRuntimeThread) = 0; + + // Get the target field address of a collectible types static. + // Arguments: + // input: vmField - pointer to the field descriptor for the static field + // vmAppDomain - AppDomain to which the static field belongs. This must + // NOT be NULL + // Return Value: The target address of the field if the field is allocated. + // NULL if the field storage is not yet allocated. + // + // Note: + // Static field storage may not exist yet, so this may commonly return NULL. + // This is an inspection only method and can not allocate the static storage. + // Field storage is not constant once allocated so this value can not be cached + // across a Continue + + virtual + CORDB_ADDRESS GetCollectibleTypeStaticAddress(VMPTR_FieldDesc vmField, + VMPTR_AppDomain vmAppDomain) = 0; + + // Get information about a field added with Edit And Continue. + // Arguments: + // intput: pEnCFieldInfo - information about the EnC added field including: + // object to which it belongs (if this is null the field is static) + // the field token + // the class token for the class to which the field was added + // the offset to the fields + // the domain file + // an indication of the type: whether it's a class or value type + // output: pFieldData - information about the EnC added field + // pfStatic - flag to indicate whether the field is static + virtual + void GetEnCHangingFieldInfo(const EnCHangingFieldInfo * pEnCFieldInfo, + OUT FieldData * pFieldData, + OUT BOOL * pfStatic) = 0; + + + // GetTypeHandleParams gets the necessary data for a type handle, i.e. its + // type parameters, e.g. "String" and "List<int>" from the type handle + // for "Dict<String,List<int>>", and sends it back to the right side. + // Arguments: + // input: vmAppDomain - app domain to which the type belongs + // vmTypeHandle - type handle for the type + // output: pParams - list of instances of DebuggerIPCE_ExpandedTypeData, + // one for each type parameter. These will be used on the + // RS to build up an instantiation which will allow + // building an instance of CordbType for the top-level + // type. The memory for this list is allocated on the dbi + // heap in this function. + // This will not fail except for OOM + + virtual + void GetTypeHandleParams(VMPTR_AppDomain vmAppDomain, + VMPTR_TypeHandle vmTypeHandle, + OUT TypeParamsList * pParams) = 0; + + // GetSimpleType + // gets the metadata token and domain file corresponding to a simple type + // Arguments: + // input: vmAppDomain - Appdomain in which simpleType resides + // simpleType - CorElementType value corresponding to a simple type + // output: pMetadataToken - the metadata token corresponding to simpleType, + // in the scope of vmDomainFile. + // vmDomainFile - the domainFile for simpleType + // Notes: + // This is inspection-only. If the type is not yet loaded, it will throw CORDBG_E_CLASS_NOT_LOADED. + // It will not try to load a type. + // If the type has been loaded, vmDomainFile will be non-null unless the target is somehow corrupted. + // In that case, we will throw CORDBG_E_TARGET_INCONSISTENT. + + virtual + void GetSimpleType(VMPTR_AppDomain vmAppDomain, + CorElementType simpleType, + OUT mdTypeDef * pMetadataToken, + OUT VMPTR_Module * pVmModule, + OUT VMPTR_DomainFile * pVmDomainFile) = 0; + + // for the specified object returns TRUE if the object derives from System.Exception + virtual + BOOL IsExceptionObject(VMPTR_Object vmObject) = 0; + + // gets the list of raw stack frames for the specified exception object + virtual + void GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames) = 0; + + // Returns true if the argument is a runtime callable wrapper + virtual + BOOL IsRcw(VMPTR_Object vmObject) = 0; + + // retrieves the list of COM interfaces implemented by vmObject, as it is known at + // the time of the call (the list may change as new interface types become available + // in the runtime) + virtual + void GetRcwCachedInterfaceTypes( + VMPTR_Object vmObject, + VMPTR_AppDomain vmAppDomain, + BOOL bIInspectableOnly, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pDacInterfaces) = 0; + + // retrieves the list of interfaces pointers implemented by vmObject, as it is known at + // the time of the call (the list may change as new interface types become available + // in the runtime) + virtual + void GetRcwCachedInterfacePointers( + VMPTR_Object vmObject, + BOOL bIInspectableOnly, + OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) = 0; + + // retrieves a list of interface types corresponding to the passed in + // list of IIDs. the interface types are retrieved from an app domain + // IID / Type cache, that is updated as new types are loaded. will + // have NULL entries corresponding to unknown IIDs in "iids" + virtual + void GetCachedWinRTTypesForIIDs( + VMPTR_AppDomain vmAppDomain, + DacDbiArrayList<GUID> & iids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) = 0; + + // retrieves the whole app domain cache of IID / Type mappings. + virtual + void GetCachedWinRTTypes( + VMPTR_AppDomain vmAppDomain, + OUT DacDbiArrayList<GUID> * piids, + OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes) = 0; + + + // ---------------------------------------------------------------------------- + // functions to get information about reference/handle referents for ICDValue + // ---------------------------------------------------------------------------- + + // Get object information for a TypedByRef object. Initializes the objRef and typedByRefType fields of + // pObjectData (type info for the referent). + // Arguments: + // input: pTypedByRef - pointer to a TypedByRef struct + // vmAppDomain - AppDomain for the type of the object referenced + // output: pObjectData - information about the object referenced by pTypedByRef + // Note: Throws + virtual + void GetTypedByRefInfo(CORDB_ADDRESS pTypedByRef, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData) = 0; + + // Get the string length and offset to string base for a string object + // Arguments: + // input: objPtr - address of a string object + // output: pObjectData - fills in the string fields stringInfo.offsetToStringBase and + // stringInfo.length + // Note: throws + virtual + void GetStringData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) = 0; + + // Get information for an array type referent of an objRef, including rank, upper and lower bounds, + // element size and type, and the number of elements. + // Arguments: + // input: objectAddress - the address of an array object + // output: pObjectData - fills in the array-related fields: + // arrayInfo.offsetToArrayBase, + // arrayInfo.offsetToLowerBounds, + // arrayInfo.offsetToUpperBounds, + // arrayInfo.componentCount, + // arrayInfo.rank, + // arrayInfo.elementSize, + // Note: throws + virtual + void GetArrayData(CORDB_ADDRESS objectAddress, DebuggerIPCE_ObjectData * pObjectData) = 0; + + // Get information about an object for which we have a reference, including the object size and + // type information. + // Arguments: + // input: objectAddress - address of the object for which we want information + // type - the basic type of the object (we may find more specific type + // information for the object) + // vmAppDomain - the appdomain to which the object belong + // output: pObjectData - fills in the size and type information fields + // Note: throws + virtual + void GetBasicObjectInfo(CORDB_ADDRESS objectAddress, + CorElementType type, + VMPTR_AppDomain vmAppDomain, + DebuggerIPCE_ObjectData * pObjectData) = 0; + + // -------------------------------------------------------------------------------------------- +#ifdef TEST_DATA_CONSISTENCY + // Determine whether a crst is held by the left side. When the DAC is executing VM code that takes a + // lock, we want to know whether the LS already holds that lock. If it does, we will assume the locked + // data is in an inconsistent state and will throw an exception, rather than relying on this data. This + // function is part of a self-test that will ensure we are correctly detecting when the LS holds a lock + // on data the RS is trying to inspect. + // Argument: + // input: vmCrst - the lock to test + // output: none + // Notes: + // Throws + // For this code to run, the environment variable TestDataConsistency must be set to 1. + virtual + void TestCrst(VMPTR_Crst vmCrst) = 0; + + // Determine whether a crst is held by the left side. When the DAC is executing VM code that takes a + // lock, we want to know whether the LS already holds that lock. If it does, we will assume the locked + // data is in an inconsistent state and will throw an exception, rather than relying on this data. This + // function is part of a self-test that will ensure we are correctly detecting when the LS holds a lock + // on data the RS is trying to inspect. + // Argument: + // input: vmRWLock - the lock to test + // output: none + // Notes: + // Throws + // For this code to run, the environment variable TestDataConsistency must be set to 1. + + virtual + void TestRWLock(VMPTR_SimpleRWLock vmRWLock) = 0; +#endif + // -------------------------------------------------------------------------------------------- + // Get the address of the Debugger control block on the helper thread. The debugger control block + // contains information about the status of the debugger, handles to various events and space to hold + // information sent back and forth between the debugger and the debuggee's helper thread. + // Arguments: none + // Return Value: The remote address of the Debugger control block allocated on the helper thread + // if it has been successfully allocated or NULL otherwise. + virtual + CORDB_ADDRESS GetDebuggerControlBlockAddress() = 0; + + // Creates a VMPTR of an Object. The Object is found by dereferencing ptr + // as though it is a target address to an OBJECTREF. This is similar to + // GetObject with another level of indirection. + // + // Arguments: + // ptr - A target address pointing to an OBJECTREF + // + // Return Value: + // A VMPTR to the Object which ptr points to + // + // Notes: + // The VMPTR this produces can be deconstructed by GetObjectContents. + // This function will throw if given a NULL or otherwise invalid pointer, + // but if given a valid address to an invalid pointer, it will produce + // a VMPTR_Object which points to invalid memory. + virtual + VMPTR_Object GetObjectFromRefPtr(CORDB_ADDRESS ptr) = 0; + + // Creates a VMPTR of an Object. The Object is assumed to be at the target + // address supplied by ptr + // + // Arguments: + // ptr - A target address to an Object + // + // Return Value: + // A VMPTR to the Object which was at ptr + // + // Notes: + // The VMPTR this produces can be deconstructed by GetObjectContents. + // This will produce a VMPTR_Object regardless of whether the pointer is + // valid or not. + virtual + VMPTR_Object GetObject(CORDB_ADDRESS ptr) = 0; + + // Sets state in the native binder. + // + // Arguments: + // ePolicy - the NGEN policy to change + // + // Return Value: + // HRESULT indicating if the state was successfully updated + // + virtual + HRESULT EnableNGENPolicy(CorDebugNGENPolicy ePolicy) = 0; + + // Sets the NGEN compiler flags. This restricts NGEN to only use images with certain + // types of pregenerated code. With respect to debugging this is used to specify that + // the NGEN image must be debuggable aka non-optimized code. Note that these flags + // are merged with other sources of configuration so it is possible that the final + // result retrieved from GetDesiredNGENCompilerFlags does not match what was specfied + // in this call. + // + // If an NGEN image of the appropriate type isn't available then one of two things happens: + // a) the NGEN image isn't loaded and CLR loads the MSIL image instead + // b) the NGEN image is loaded, but we don't use the pregenerated code it contains + // and instead use only the MSIL and metadata + // + // This function is only legal to call at app startup before any decisions have been + // made about NGEN image loading. Once we begin loading this configuration is immutable. + // + // + // Arguments: + // dwFlags - the new NGEN compiler flags that should go into effect + // + // Return Value: + // HRESULT indicating if the state was successfully updated. On error the + // current flags in effect will not have changed. + // + virtual + HRESULT SetNGENCompilerFlags(DWORD dwFlags) = 0; + + // Gets the NGEN compiler flags currently in effect. This accounts for settings that + // were caused by SetDesiredNGENCompilerFlags as well as other configuration sources. + // See SetDesiredNGENCompilerFlags for more info + // + // Arguments: + // pdwFlags - the NGEN compiler flags currently in effect + // + // Return Value: + // HRESULT indicating if the state was successfully retrieved. + // + virtual + HRESULT GetNGENCompilerFlags(DWORD *pdwFlags) = 0; + + // Create a VMPTR_OBJECTHANDLE from a CORDB_ADDRESS pointing to an object handle + // + // Arguments: + // handle: target address of a GC handle + // + // ReturnValue: + // returns a VMPTR_OBJECTHANDLE with the handle as the m_addr field + // + // Notes: + // This will produce a VMPTR_OBJECTHANDLE regardless of whether handle is + // valid. + // Ideally we'd be using only strongly-typed variables on the RS, and then this would be unnecessary + virtual + VMPTR_OBJECTHANDLE GetVmObjectHandle(CORDB_ADDRESS handleAddress) = 0; + + // Validate that the VMPTR_OBJECTHANDLE refers to a legitimate managed object + // + // Arguments: + // handle: the GC handle to be validated + // + // Return value: + // TRUE if the object appears to be valid (its a heuristic), FALSE if it definately is not valid + // + virtual + BOOL IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle) = 0; + + // indicates if the specified module is a WinRT module + // + // Arguments: + // vmModule: the module to check + // isWinRT: out parameter indicating state of module + // + // Return value: + // S_OK indicating that the operation succeeded + // + virtual + HRESULT IsWinRTModule(VMPTR_Module vmModule, BOOL& isWinRT) = 0; + + // Determines the app domain id for the object refered to by a given VMPTR_OBJECTHANDLE + // + // Arguments: + // handle: the GC handle which refers to the object of interest + // + // Return value: + // The app domain id of the object of interest + // + // This may throw if the object handle is corrupt (it doesn't refer to a managed object) + virtual + ULONG GetAppDomainIdFromVmObjectHandle(VMPTR_OBJECTHANDLE vmHandle) = 0; + + + // Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address + // Arguments: + // vmHandle - (input) the VMPTR_OBJECTHANDLE from which we need the target address + // Return value: the target address from the VMPTR_OBJECTHANDLE + // + virtual + CORDB_ADDRESS GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle) = 0; + + // Given a VMPTR to an Object return the target address + // + // Arguments: + // obj - the Object VMPTR to get the address from + // + // Return Value: + // Return the target address which obj is using + // + // Notes: + // The VMPTR this consumes can be reconstructed using GetObject and + // providing the address stored in the returned TargetBuffer. This has + // undefined behavior for invalid VMPTR_Objects. + + virtual + TargetBuffer GetObjectContents(VMPTR_Object obj) = 0; + + // The callback used to enumerate blocking objects + typedef void (*FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK)(DacBlockingObject blockingObject, + CALLBACK_DATA pUserData); + + // + // Enumerate all monitors blocking a thread + // + // Arguments: + // vmThread - the thread to get monitor data for + // fpCallback - callback to invoke on the blocking data for each monitor + // pUserData - user data to supply for each callback. + // + // Return Value: + // Returns on success. Throws on error. + // + // + virtual + void EnumerateBlockingObjects(VMPTR_Thread vmThread, + FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + + + // + // Returns the thread which owns the monitor lock on an object and the acquisition + // count + // + // Arguments: + // vmObject - The object to check for ownership + + // + // Return Value: + // Throws on error. Inside the structure we have: + // pVmThread - the owning or thread or VMPTR_Thread::NullPtr() if unowned + // pAcquisitionCount - the number of times the lock would need to be released in + // order for it to be unowned + // + virtual + MonitorLockInfo GetThreadOwningMonitorLock(VMPTR_Object vmObject) = 0; + + // + // Enumerate all threads waiting on the monitor event for an object + // + // Arguments: + // vmObject - the object whose monitor event we are interested in + // fpCallback - callback to invoke on each thread in the queue + // pUserData - user data to supply for each callback. + // + // Return Value: + // Returns on success. Throws on error. + // + // + virtual + void EnumerateMonitorEventWaitList(VMPTR_Object vmObject, + FP_THREAD_ENUMERATION_CALLBACK fpCallback, + CALLBACK_DATA pUserData) = 0; + + // + // Returns the managed debugging flags for the process (a combination + // of the CLR_DEBUGGING_PROCESS_FLAGS flags). This function specifies, + // beyond whether or not a managed debug event is pending, also if the + // event (if one exists) is caused by a Debugger.Launch(). This is + // important b/c Debugger.Launch calls should *NOT* cause the debugger + // to terminate the process when the attach is canceled. + virtual + CLR_DEBUGGING_PROCESS_FLAGS GetAttachStateFlags() = 0; + + virtual + bool GetMetaDataFileInfoFromPEFile(VMPTR_PEFile vmPEFile, + DWORD & dwTimeStamp, + DWORD & dwImageSize, + bool & isNGEN, + IStringHolder* pStrFilename) = 0; + + virtual + bool GetILImageInfoFromNgenPEFile(VMPTR_PEFile vmPEFile, + DWORD & dwTimeStamp, + DWORD & dwSize, + IStringHolder* pStrFilename) = 0; + + + virtual + bool IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread) = 0; + + + typedef void* * HeapWalkHandle; + + // Returns true if it is safe to walk the heap. If this function returns false, + // you could still create a heap walk and attempt to walk it, but there's no + // telling how much of the heap will be available. + virtual + bool AreGCStructuresValid() = 0; + + // Creates a HeapWalkHandle which can be used to walk the managed heap with the + // WalkHeap function. Note if this function completes successfully you will need + // to delete the handle by passing it into DeleteHeapWalk. + // + // Arguments: + // pHandle - the location to store the heap walk handle in + // + // Returns: + // S_OK on success, an error code on failure. + virtual + HRESULT CreateHeapWalk(OUT HeapWalkHandle * pHandle) = 0; + + + // Deletes the give HeapWalkHandle. Note you must call this function if + // CreateHeapWalk returns success. + virtual + void DeleteHeapWalk(HeapWalkHandle handle) = 0; + + // Walks the heap using the given heap walk handle, enumerating objects + // on the managed heap. Note that walking the heap requires that the GC + // data structures be in a valid state, which you can find by calling + // AreGCStructuresValid. + // + // Arguments: + // handle - a HeapWalkHandle obtained from CreateHeapWalk + // count - the number of object addresses to obtain; pValues must + // be at least as large as count + // objects - the location to stuff the object addresses found during + // the heap walk; this array should be at least "count" in + // length; this field must not be null + // pFetched - a location to store the actual number of values filled + // into pValues; this field must not be null + // + // Returns: + // S_OK on success, a failure HRESULT otherwise. + // + // Note: + // You should iteratively call WalkHeap requesting more values until + // *pFetched != count.. This signifies that we have reached the end + // of the heap walk. + virtual + HRESULT WalkHeap(HeapWalkHandle handle, + ULONG count, + OUT COR_HEAPOBJECT * objects, + OUT ULONG * pFetched) = 0; + + virtual + HRESULT GetHeapSegments(OUT DacDbiArrayList<COR_SEGMENT> * pSegments) = 0; + + virtual + bool IsValidObject(CORDB_ADDRESS obj) = 0; + + virtual + bool GetAppDomainForObject(CORDB_ADDRESS obj, OUT VMPTR_AppDomain * pApp, + OUT VMPTR_Module * pModule, + OUT VMPTR_DomainFile * pDomainFile) = 0; + + + // Reference Walking. + + // Creates a reference walk. + // Parameters: + // pHandle - out - the reference walk handle to create + // walkStacks - in - whether or not to report stack references + // walkFQ - in - whether or not to report references from the finalizer queue + // handleWalkMask - in - the types of handles report (see CorGCReferenceType, cordebug.idl) + // Returns: + // An HRESULT indicating whether it succeded or failed. + // Exceptions: + // Does not throw, but does not catch exceptions either. + virtual + HRESULT CreateRefWalk(OUT RefWalkHandle * pHandle, BOOL walkStacks, BOOL walkFQ, UINT32 handleWalkMask) = 0; + + // Deletes a reference walk. + // Parameters: + // handle - in - the handle of the reference walk to delete + // Excecptions: + // Does not throw, but does not catch exceptions either. + virtual + void DeleteRefWalk(RefWalkHandle handle) = 0; + + // Enumerates GC references in the process based on the parameters passed to CreateRefWalk. + // Parameters: + // handle - in - the RefWalkHandle to enumerate + // count - in - the capacity of "refs" + // refs - in/out - an array to write the references to + // pFetched - out - the number of references written + virtual + HRESULT WalkRefs(RefWalkHandle handle, ULONG count, OUT DacGcReference * refs, OUT ULONG * pFetched) = 0; + + virtual + HRESULT GetTypeID(CORDB_ADDRESS obj, COR_TYPEID * pType) = 0; + + virtual + HRESULT GetObjectFields(COR_TYPEID id, ULONG32 celt, OUT COR_FIELD * layout, OUT ULONG32 * pceltFetched) = 0; + + virtual + HRESULT GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT * pLayout) = 0; + + virtual + HRESULT GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT * pLayout) = 0; + + virtual + void GetGCHeapInformation(OUT COR_HEAPINFO * pHeapInfo) = 0; + + // If a PEFile has an RW capable IMDInternalImport, this returns the address of the MDInternalRW + // object which implements it. + // + // + // Arguments: + // vmPEFile - target PEFile to get metadata MDInternalRW for. + // pAddrMDInternalRW - If a PEFile has an RW capable IMDInternalImport, this will be set to the address + // of the MDInternalRW object which implements it. Otherwise it will be NULL. + // + virtual + HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) = 0; + + // Retrieves the active ReJitInfo for a given module/methodDef, if it exists. + // Active is defined as after GetReJitParameters returns from the profiler dll and + // no call to Revert has completed yet. + // + // + // Arguments: + // vmModule - The module to search in + // methodTk - The methodDef token indicates the method within the module to check + // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request + // is active this will be pReJitInfo->IsNull() == TRUE. + // + // Returns: + // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo) = 0; + + // Retrieves the active ReJitInfo for a given MethodDesc/code address, if it exists. + // Active is defined as after GetReJitParameters returns from the profiler dll and + // no call to Revert has completed yet. + // + // + // Arguments: + // vmMethod - The method to look for + // codeStartAddress - The code start address disambiguates between multiple rejitted instances + // of the method. + // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request + // is active this will be pReJitInfo->IsNull() == TRUE. + // + // Returns: + // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo) = 0; + + + // Retrieves the SharedReJitInfo for a given ReJitInfo. + // + // + // Arguments: + // vmReJitInfo - The ReJitInfo to inspect + // pSharedReJitInfo - [out] The SharedReJitInfo that is pointed to by vmReJitInfo. + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo) = 0; + + // Retrieves useful data from a SharedReJitInfo such as IL code and IL mapping. + // + // + // Arguments: + // sharedReJitInfo - The SharedReJitInfo to inspect + // pData - [out] Various properties of the SharedReJitInfo such as IL code and IL mapping. + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetSharedReJitInfoData(VMPTR_SharedReJitInfo sharedReJitInfo, DacSharedReJitInfo* pData) = 0; + + // Retrieves a bit field indicating which defines were in use when clr was built. This only includes + // defines that are specified in the Debugger::_Target_Defines enumeration, which is a small subset of + // all defines. + // + // + // Arguments: + // pDefines - [out] The set of defines clr.dll was built with. Bit offsets are encoded using the + // enumeration Debugger::_Target_Defines + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetDefinesBitField(ULONG32 *pDefines) = 0; + + // Retrieves a version number indicating the shape of the data structures used in the Metadata implementation + // inside clr.dll. This number changes anytime a datatype layout changes so that they can be correctly + // deserialized from out of process + // + // + // Arguments: + // pMDStructuresVersion - [out] The layout version number for metadata data structures. See + // Debugger::Debugger() in Debug\ee\Debugger.cpp for a description of the options. + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion) = 0; + + // The following tag tells the DD-marshalling tool to stop scanning. + // END_MARSHAL + + //----------------------------------------------------------------------------- + // Utility interface used for passing strings out of these APIs. The caller + // provides an implementation of this that uses whatever memory allocation + // strategy it desires, and IDacDbiInterface APIs will call AssignCopy in order + // to pass back the contents of strings. + // + // This permits the client and implementation of IDacDbiInterface to be in + // different DLLs with their own heap allocation mechanism, while avoiding + // the ugly and verbose 2-call C-style string passing API pattern. + //----------------------------------------------------------------------------- + class IStringHolder + { + public: + // + // Store a copy of of the provided string. + // + // Arguments: + // psz - The null-terminated unicode string to copy. + // + // Return Value: + // S_OK on success, typical HRESULT return values on failure. + // + // Notes: + // The underlying object is responsible for allocating and freeing the + // memory for this copy. The object must not store the value of psz, + // it is no longer valid after this call returns. + // + virtual + HRESULT AssignCopy(const WCHAR * psz) = 0; + }; + + + //----------------------------------------------------------------------------- + // Interface for allocations + // This lets DD allocate buffers to pass back to DBI; and thus avoids + // the common 2-step (query size/allocate/query data) pattern. + // + // Note that mscordacwks.dll and clients cannot share the same heap allocator, + // DAC statically links the CRT to avoid run-time dependencies on non-OS libraries. + //----------------------------------------------------------------------------- + class IAllocator + { + public: + // Allocate + // Expected to throw on error. + virtual + void * Alloc(SIZE_T lenBytes) = 0; + + // Free. This shouldn't throw. + virtual + void Free(void * p) = 0; + }; + + + //----------------------------------------------------------------------------- + // Callback interface to provide Metadata lookup. + //----------------------------------------------------------------------------- + class IMetaDataLookup + { + public: + // + // Lookup a metadata importer via PEFile. + // + // Returns: + // A IMDInternalImport used by dac-ized VM code. The object is NOT addref-ed. See lifespan notes below. + // Returns NULL if no importer is available. + // Throws on exceptional circumstances (eg, detects the debuggee is corrupted). + // + // Notes: + // IMDInternalImport is a property of PEFile. The DAC-ized code uses it as a weak reference, + // and so we avoid doing an AddRef() here because that would mean we need to add Release() calls + // in DAC-only paths. + // The metadata importers are not DAC-ized, and thus we have a local copy in the host. + // If it was dac-ized, then DAC would get the importer just like any other field. + // + // lifespan of returned object: + // - DBI owns the metadata importers. + // - DBI must not free the importer without calling Flush() on DAC first. + // - DAC will only invoke this when in a DD primitive, which was in turn invoked by DBI. + // - For performance reasons, we want to allow DAC to cache this between Flush() calls. + // - If DAC caches the importer, it will only use it when DBI invokes a DD primitive. + // - the reference count of the returned object is not adjusted. + // + virtual + IMDInternalImport * LookupMetaData(VMPTR_PEFile addressPEFile, bool &isILMetaDataForNGENImage) = 0; + }; + +}; // end IDacDbiInterface + + +#endif // _DACDBI_INTERFACE_H_ |