http://www.blogcn.com/user8/flier_lu/index.html?id=2042872
In the previous section, the frame structure of the CLR debugger is briefly introduced, where the CLR debugging environment is mentioned while supporting both Native and Managed models of debugging events. This section will make a general introduction from the overall debug event.
First look at the CLR through the Managed debug event provided by the IcordebugManagedCallback callback interface. This part of the debugging event can be roughly divided into passive debug events and active debug events: The former automatically triggered a passive debug event by the CLR, such as creating a new thread; the latter is controlled by the debugger through the CLR. The CLR debug environment completes a certain debug task and triggers active debug events, such as breakpoints and expressions.
For passive debugging events, several steps that are basically corresponding to the CLR load running program
The first is the establishment of the dynamic environment, divided into process, Appdomain, and thread three levels, and has corresponding establishment and exiting debug events:
The following is quoted:
Interface IcordebugManagedCallback: IUNKNOWN
{
// ...
HRESULT CREATEPROCESS ([In] IcordebugProcess * PPRocess);
HRESULT EXITPROCESS ([In] IcordebugProcess * PPRocess);
HRESULT CreateAppDomain ([in] ICorDebugProcess * pProcess, [in] ICorDebugAppDomain * pAppDomain); HRESULT ExitAppDomain ([in] ICorDebugProcess * pProcess, [in] ICorDebugAppDomain * pAppDomain);
HRESULT CREATTHREAD ([in] icordebugappdomain * pappdomain, [in] icordebugthread * thread); HRESULT EXITTHREAD ([in] icordebugappdomain * PAPPDOMAIN, [IN] icordebugthread * thread);
HRESULT NAMECHANGE ([in] icordebugappdomain * PAPPDOMAIN, [IN] icordebugthread * pthread; // ...};
In the implementation of the CLR, there is actually there is two concepts with physical Native Thread and logical managed thread. Process and Native Thread correspond to the concept provided by the operating system, and Appdomain and Managed Thread correspond to the relevant abstraction within the CLR. The above thread-related debug events, in fact, Native Thread once first triggered with Managed Thread as a Managed Code. More complete controls require the debug events of Native Threads to be mentioned later. In addition, AppDomain and Managed Thread will change nam according to the situation after creating and start running, and call NAMECHANGE debug events, so that the debugger has the opportunity to update the information on the interface display.
Secondly, it is the load and parsing of static metadata, which is also divided into assembly, module, and class three levels and has corresponding establishment and exiting debug events:
The following is quoted:
Interface IcordebugManagedCallback: iunknown {
// ...
HRESULT loadassembly ([in] icordebugappdomain * pappdomain,
[in] icordebugassembly * passembly);
HRESULT UNLOADASSEMBLY ([in] icordebugappdomain * PAPPDOMAIN,
[in] icordebugassembly * passembly);
HRESULT loadModule ([in] icordebugappdomain * PAPPDOMAIN, [IN] icordebugmodule * pmodule); HRESULT UNLOADMODULE ([in] icordebugappdomain * pappdomain, [in] icordebugmodule * pmodule);
HRESULT loadClass ([in] icordebugappdomain * pappdomain, [in] icordebugclass * c); HRESULT UNLOADEBUGCLASS * PAPPDOMAIN, [IN] icordebugclass * c); // ...};
In CLR, Assembly is largely a logical aggregate that is truly implemented in achieving more Module. When an assembly is loaded, it can just protect the relevant manifest and metadata, the real code and data can be stored in multiple modules in different locations. Therefore, in the Managed debug event, the life cycle of Assembly and Module is explicitly separated.
Then the support of the special instructions and functions in the IL code:
The following is quoted:
Interface IcordebugManagedCallback: IUNKNOWN
{
// ...
HRESULT BREAK ([in] icordebugappdomain * PAPPDOMAIN,
[in] icordebugthread * thread);
HRESULT Exception ([in] icordebugappdomain * pappdomain, [in] icordebugthread * pthread, [in] bool unhandled;
HRESULT Debuggererror ([in] icordebugprocess * pprocess, [in] HRESULT ERRORHR, [IN] DWORD ERRORCODE);
HRESULT logMessage ([in] icordebugappdomain * pappdomain, [in] icordebugthread * pthread, [in] long llevel, [in] wchar * plogswitchname, [in] wchar * pimentage);
HRESULT LogSwitch ([in] ICorDebugAppDomain * pAppDomain, [in] ICorDebugThread * pThread, [in] LONG lLevel, [in] ULONG ulReason, [in] WCHAR * pLogSwitchName, [in] WCHAR * pParentName); HRESULT ControlCTrap ([in] IcorDebugProcess * pprocess);
HRESULT UPDATEMODULESYMBOLS ([in] icordebugappdomain * pappdomain, [in] icordebugmodule * PModule, [in] istream * psymbolstream); // ...};
Break Events are triggered when performing IL instructions Break, can be used to implement special breakpoints, etc.; Exception events are thrown when the code is thrown, and the exception is not processed, similar to an exception event in the Win32 Debug API . Behind the debugger is described in detail when the abnormal processing method is introduced; the debuggererror event is triggered when the debugging system handles the Win32 debug event error; logMessage and logswitch events are used to process internal SYSTEM.DIAGNOSTICS.LOG related features Similar to the function of the OutputDebugString function, etc., etc., etc. When the system updates a module debug symbol library, the debugger has the opportunity to synchronize.
Finally, several active debug events will be saved, and the related functionality of the debugger calls the CLR debug interface is completed or exception:
The following is quoted:
Interface IcordebugManagedCallback: IUNKNOWN
{
// ...
HRESULT BREAKPOINT ([in] icordebugappdomain * PAPPDOMAIN,
[in] icordebugthread * pthread,
[in] icordebugbreakpoint * pbreakpoint);
HRESULT BREAKPOINTSETERROR ([in] icordebugappdomain * PAPPDOMAIN,
[in] icordebugthread * pthread,
[in] icordebugbreakpoint * PBREAKPOINT,
DWORD DWERROR);
HRESULT StepComplete ([in] icordebugappdomain * PAPPDOMAIN, [IN] icordebugthread * pthread, [in] icordebugstepper * pstepper, [in] Cordebugstepreason REASON);
HRESULT EvalComplete ([in] ICorDebugAppDomain * pAppDomain, [in] ICorDebugThread * pThread, [in] ICorDebugEval * pEval); HRESULT EvalException ([in] ICorDebugAppDomain * pAppDomain, [in] ICorDebugThread * pThread, [in] ICorDebugEval * pEval); HRESULT EditAndContinueremap ([in] icordebugappdomain * PAPPDOMAIN, [IN] icordebugthread * pthread, [in] icordebugfunction * pfunction, [in] bool faccurate); // ...};
BreakPoint and BreakPointSeterse are called when the breakpoint is triggered or sets breakpoints. The next section describes the implementation of breakpoints. Detailed discussion; StepComplete is completed in the debug environment because of a code stepper (Step) Call, after introducing the functional implementation of single-step tracking, then discussed in detail; EVALCOMPLETE and EVALEXCEPTION are called when the expression evaluation is completed or fails, and later introduces the current information of the debug environment to obtain again; EditAndContinuereMap is used to implement debugging code Edit function, not involved.
Below is a more intuitive example, showing a simple CLR debug environment running a normal CLR program unless the order of the related debug event
The following is quoted:
ManageDeventhandler.createProcess (3636)
ManageDeventhandler.createAppdomain (DefaultDomain @ 3636)
ManagedEventHandler.LoadAssembly (e: windowsmicrosoft.net rameworkv1.1.4322mscorlib.dll @ DefaultDomain) ManagedEventHandler.LoadModule (e: windowsmicrosoft.net rameworkv1.1.4322mscorlib.dll @ DefaultDomain)
ManageDeventHandler.NameChange (Appdomain = Cordbg)
ManageDeventhandler.createthread (3944 @ Cordbg)
ManageDeventHandler.Loadassembly (f: studydotnetdebuggercordbgindebugcordbg.exe @ Cordbg) ManageDeventHandler.LoadModule (f: studydotnetdebuggercordbgindebugcordbg.exe @ Cordbg)
ManageDeventHandler.NameChange (Appdomain = Cordbg.exe)
ManagedEventHandler.LoadAssembly (e: windowsassemblygacsystem.0.5000.0__b77a5c561934e089system.dll @ cordbg.exe) ManagedEventHandler.LoadModule (e: windowsassemblygacsystem.0.5000.0__b77a5c561934e089system.dll @ cordbg.exe) ManagedEventHandler.CreateThread (2964 @ cordbg.exe)
ManagedEventHandler.UnloadModule (F: StudyDotNetDebuggercordbginDebugcordbg.exe @ cordbg.exe) ManagedEventHandler.UnloadAssembly (F: StudyDotNetDebuggercordbginDebugcordbg.exe @ cordbg.exe)
ManagedEventHandler.UnloadModule (e: windowsassemblygacsystem.0.5000.0__b77a5c561934e089system.dll @ cordbg.exe) ManagedEventHandler.UnloadAssembly (e: windowsassemblygacsystem.0.5000.0__b77a5c561934e089system.dll @ cordbg.exe)
ManagedEventHandler.UnloadModule (e: windowsmicrosoft.net rameworkv1.1.4322mscorlib.dll @ cordbg.exe) ManagedEventHandler.UnloadAssembly (e: windowsmicrosoft.net rameworkv1.1.4322mscorlib.dll @ cordbg.exe)
ManageDeventhandler.exitAppdomain (Cordbg.exe @ 3636) ManageDeventHandler.exitthread (3944 @ Cordbg.exe) ManageDeventHandler.exitProcess (3636)
You can see the CLR first construct the process and appdomain; then execute the required MscorLib.dll load; then load the assembly and default module to be executed; and analyze the external application (System.dll), load it; Create a new Managed Thread execution; finally uninstall the related module and askEMBLY, and exit the environment.
It is worth noting that many debug interfaces provide a similar function to get strings or integers from unmanaged environments, such as
The following is quoted:
Interface Icordebugappdomain: icordebugController
{
HRESULT GETNAME ([in] ulong32 cchname,
[OUT] ulong32 * pcchname,
[OUT, SIZE_IS (Cchname),
Length_is (* pcchname)] Wchar Szname []);
}
interface ICorDebugAssembly: IUnknown {HRESULT GetName ([in] ULONG32 cchName, [out] ULONG32 * pcchName, [out, size_is (cchName), length_is (* pcchName)] WCHAR szName []);}; so the implementation may be the Abstract is a delegate to share data acquisition algorithms based on trying policies, such as
The following is quoted:
Public Class Corobject
{
Protected delegate void getStrfunc (uint cchname, out uint pchname, intptr szname);
Protected string getString (getStrfunc func, uint bufsize) {uint size = bufsize;
INTPTR SZNAME = Marshal.allochglobal (INT) size);
Func (Size, Out Size, Szname);
IF (size> bufsize) {szname = Marshal.Reallochglobal (Szname, New INTPTR (SIZE));
Func (Size, Out size, Szname);
String name = marshal.ptrtostringuni (szname, (int) size-1);
Marshal.Freehglobal (Szname);
Return name;}
Protected string getString (getstrfunc func) {Return GetString (Func, 256);}}
Here, Marshal uses the direct operation of Native memory to avoid writing UNSAFE codes. You can use it very simply when you use it.
The following is quoted:
Public Class Corassembly: Corobject
{
Private icordebugassembly_asm;
Public Corassembly (Icordebugassembly ASM) {_ASM = ASM;
Public string name {get {return getString (new getstrfunc (_ASM.Getname));}}}
Wait until CLR 2.0 supports generic programming, it will be more convenient. : P
This section, from the whole, from the whole, analyze the classification and related functions of the Managed debug event. Specific use will be targeted in the later articles. As for the Win32 API debugging incident, the information introduced is much more, it is not in Luo Wei, and friends who are interested in further study can refer to my previous series of articles.
Analysis of Win32 Debug Interface Design and Implementation [2] Debugging Events
The next section will introduce how the CLR debug interface interrupt point is implemented and used.
To Be Continue ...