Examining a Program During Debugging
To examine a program during debugging, the debugger must be able to access managed stack frames and evaluate expressions. The following sections describe how a debugger uses the common language runtime (CLR) debugging API to perform these tasks.
Accessing Call Stacks
The following list provides a step-by-step description of how a debugger accesses managed stack frames. The debuggee process must be stopped for access to stack frames.
The debugger obtains an enumerator for the stack chains. The debugger calls the ICorDebugThread::EnumerateChains method for the thread for which stack chains are to be accessed to obtain an ICorDebugChainEnum object to enumerate the stack chains.
The debugger iterates through the stack chains by calling the ICorDebugChainEnum::Next method.
The debugger obtains an enumerator for the stack frames in the chain by calling the ICorDebugChain::EnumerateFrames method.
The debugger continues to iterate through the stack frames by calling the ICorDebugFrameEnum::Next method.
The debugger optionally obtains the IP address. The debugger calls the ICorDebugILFrame::GetIP method to obtain the IP address relative to the start of the function for the stack frame.
The debugger optionally obtains other information about the stack frame. For example, the debugger may call the ICorDebugFrame::GetFunctionToken method to obtain the metadata token for the function for the code that the stack frame is running. The debugger may also call the ICorDebugCode::GetCode method to obtain an object that represents the code that the stack frame is running.
Evaluating Expressions
Expressions in unmanaged native code can be evaluated by using the same mechanisms that conventional debuggers use.
In managed code, the debugger can evaluate an expression as follows:
Parse the expression.
Call the debugging API to access the values of variables in the expression and to invoke the functions in the expression.
Alternatively, the debugger can do the following:
Wrap the expression in a global function and compile the function.
Call the debugging API (Edit and Continue) to add the global function.
Call the debugging API to evaluate the function.
The following list provides a step-by-step description of how a debugger evaluates an expression. In this scenario, the expression is A + Foo() where A is assumed to be in a register and the code being debugged is managed native code.
The debugger obtains the value of A. The debugger calls the ICorDebugNativeFrame::GetLocalRegisterValue method for the stack frame in which the expression is to be evaluated.
The debugger creates an evaluation object by calling the ICorDebugThread::CreateEval method for the thread in which the expression is to be evaluated.
The debugger computes the value of Foo() by calling the ICorDebugEval::CallFunction method.
The debugger evaluates the expression. The debugger applies constant folding to the expression by using the values obtained in the previous two steps.