Windows2000/private/windbg64/debugger/dm/step.c
2020-09-30 17:12:32 +02:00

3610 lines
90 KiB
C

#include "precomp.h"
#pragma hdrstop
#include "resource.h"
extern DMTLFUNCTYPE DmTlFunc;
extern DEBUG_EVENT64 falseSSEvent;
extern METHOD EMNotifyMethod;
extern BYTE abEMReplyBuf[];
extern HINSTANCE hInstance; // The DM DLLs hInstance
void
SSActionRBAndContinue(
DEBUG_EVENT64 *de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
);
VOID
SendDBCErrorStep(
HPRCX hprc
)
/*++
Routine Description:
This function notifies the user when an invalid step command is
attempted
Arguments:
hprc - Supplies the thread handle to be stepped.
Return Value:
None.
--*/
{
char buf[1000];
if (!LoadString(hInstance, IDS_CANT_TRACE, buf, sizeof(buf))) {
assert(FALSE);
}
SendDBCError(hprc, xosdCannotStep, buf);
} // SendDBCErrorStep
VOID
SingleStep(
HTHDX hthd,
METHOD* notify,
BOOL stopOnBP,
BOOL fInFuncEval
)
/*++
Routine Description:
This function is used to do a single step operation on the specified
thread.
Arguments:
hthd - Supplies the thread handle to be stepped.
notify -
stopOnBp - Supplies TRUE if a bp at current PC should cause a stop
fInFuncEval - Supplies TRUE if called by the fucntion evaluation code
Return Value:
None.
--*/
{
ReturnStepEx( hthd, notify, stopOnBP, fInFuncEval, NULL, NULL, TRUE );
}
VOID
SingleStepEx(
HTHDX hthd,
METHOD* notify,
BOOL stopOnBP,
BOOL fInFuncEval,
BOOL fDoContinue
)
/*++
Routine Description:
This function is used to do a single step operation on the specified
thread.
Arguments:
hthd - Supplies the thread handle to be stepped.
notify -
stopOnBp - Supplies TRUE if a bp at current PC should cause a stop
fInFuncEval - Supplies TRUE if called by the fucntion evaluation code
fDoContinue - Supplies TRUE if thread should be continued
Return Value:
None.
--*/
{
ReturnStepEx( hthd, notify, stopOnBP, fInFuncEval, NULL, NULL, fDoContinue );
}
VOID
ReturnStep(
HTHDX hthd,
METHOD* notify,
BOOL stopOnBP,
BOOL fInFuncEval,
LPADDR addrRA,
LPADDR addrStack
)
/*++
Routine Description:
This function is used to do a single step operation on the specified
thread.
Arguments:
hthd - Supplies the thread handle to be stepped.
notify -
stopOnBp - Supplies TRUE if a bp at current PC should cause a stop
fInFuncEval - Supplies TRUE if called by the fucntion evaluation code
addrRA -
addrStack -
Return Value:
None.
--*/
{
ReturnStepEx( hthd, notify, stopOnBP, fInFuncEval, addrRA, addrStack, TRUE) ;
}
UOFFSET GetReturnDestination(HTHDX hthd)
/*++
Routine Description:
Gets the destination of the return address from the current PC.
--*/
{
ADDR CurrentAddress;
UOFFSET ReturnAddress = 0;
// BUGBUG 64 bit
AddrFromHthdx (&CurrentAddress, hthd);
assert (IsRet (hthd, &CurrentAddress));
if (IsRet (hthd, &CurrentAddress)) {
#ifdef TARGET_i386
ULONG cb;
DbgReadMemory (hthd->hprc, STACK_POINTER (hthd), &ReturnAddress, sizeof (ReturnAddress), &cb);
#else
ReturnAddress = GetNextOffset (hthd, FALSE);
#endif
}
return ReturnAddress;
}
VOID
ReturnStepEx(
HTHDX hthd,
METHOD* notify,
BOOL stopOnBP,
BOOL fInFuncEval,
LPADDR addrRA,
LPADDR addrStack,
BOOL fDoContinue
)
/*++
Routine Description:
This function is used to do a single step operation on the specified
thread.
Arguments:
hthd - Supplies the thread handle to be stepped.
notify -
stopOnBp - Supplies TRUE if a bp at current PC should cause a stop
fInFuncEval - Supplies TRUE if called by the fucntion evaluation code
addrRA - Supplies address to step to
addrStack - Supplies
fDoContinue - Supplies TRUE if thread should be continued now
Return Value:
None.
--*/
{
ADDR currAddr;
ADDR currAddrActual;
ACVECTOR action = NO_ACTION;
int lpf = 0;
PBREAKPOINT bp;
LPCONTEXT lpContext = &(hthd->context);
ULONG ContinueCmd;
#ifndef KERNEL
// If we are stepping into an ORPC section, CheckAndSetup ... will take
// care of everything for us. Just return.
if (CheckAndSetupForOrpcSection (hthd)) {
return;
}
#endif
// Get the current IP of the thread
AddrFromHthdx(&currAddr, hthd);
currAddrActual = currAddr;
bp = AtBP(hthd);
if (!stopOnBP && !bp) {
bp = FindBP(hthd->hprc, hthd, bptpExec, (BPNS)-1, &currAddr, FALSE);
SetBPFlag(hthd, bp);
}
// Check if we are on a BP
if (bp == EMBEDDED_BP) {
DPRINT(3, ("-- At embedded BP, skipping it.\n"));
// If it isnt a BP we set then just increment past it
// & pretend that a single step actually took place
ClearBPFlag(hthd);
hthd->fIsCallDone = FALSE;
if (addrRA == NULL) {
IncrementIP(hthd);
NotifyEM(&falseSSEvent, hthd, 0, 0);
} else {
AddrFromHthdx(&hthd->addrFrom, hthd);
IncrementIP(hthd);
hthd->fReturning = TRUE;
SetupReturnStep(hthd, fDoContinue, addrRA, addrStack);
}
} else if (bp) {
RestoreInstrBP(hthd, bp);
ClearBPFlag(hthd);
// Issue the single step command
if (!addrRA) {
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)EXCEPTION_SINGLE_STEP,
notify,
SSActionReplaceByte,
FALSE,
(UINT_PTR)bp);
SetupSingleStep(hthd, TRUE);
} else {
// Step over the bp and then continue.
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)EXCEPTION_SINGLE_STEP,
DONT_NOTIFY,
SSActionRBAndContinue,
FALSE,
(UINT_PTR)bp);
SetupSingleStep(hthd, FALSE);
AddrFromHthdx(&hthd->addrFrom, hthd);
hthd->fReturning = TRUE;
SetupReturnStep(hthd, fDoContinue, addrRA, addrStack);
}
} else { // bp == NULL
// Determine if the current instruction is a breakpoint
// instruction. If it is then based on the stopOnBP flag we either
// execute to hit the breakpoint or skip over it and create a
// single step event
IsCall(hthd, &currAddr, &lpf, FALSE);
#if 0
// If there is an exception and we're passing it on to the program,
// we do not want to trace.
if (IsPassingException (hthd) && lpf == INSTR_TRACE_BIT) {
lpf = INSTR_CANNOT_TRACE;
}
#endif
if (lpf == INSTR_CANNOT_STEP) {
SendDBCErrorStep(hthd->hprc);
return;
}
if (lpf == INSTR_BREAKPOINT) {
if (stopOnBP) {
/*
* We were instructed to stop on breakpoints
* Just issue an execute command and execute
* the breakpoint.
*/
ContinueProcess (hthd->hprc);
} else {
/*
* else just increment past it
* & pretend that a single step actually took place
*/
DPRINT(3, (" At an embedded bp -- ignoring\n\r"));
IncrementIP(hthd);
hthd->fIsCallDone = FALSE;
ClearBPFlag(hthd);
if (notify) {
(notify->notifyFunction)(&falseSSEvent,
hthd,
0,
notify->lparam);
} else {
NotifyEM(&falseSSEvent, hthd, 0, 0);
}
}
} else {
if (lpf == INSTR_CANNOT_TRACE) {
bp = SetBP( hthd->hprc,
hthd,
bptpExec,
bpnsStop,
&currAddr,
(HPID) INVALID);
bp->isStep = TRUE;
}
// Place this on our list of expected events
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)EXCEPTION_SINGLE_STEP,
notify,
NO_ACTION,
FALSE,
0);
// Issue the single step command
if (!addrRA) {
if (hthd->fDisplayReturnValues && IsRet (hthd, &currAddrActual)) {
AddrFromHthdx(&hthd->addrFrom, hthd);
NotifyEM(&FuncExitEvent, hthd, 0, (UINT_PTR)&currAddrActual);
}
SetupSingleStep(hthd, TRUE);
} else {
AddrFromHthdx(&hthd->addrFrom, hthd);
hthd->fReturning = TRUE;
SetupReturnStep(hthd, fDoContinue, addrRA, addrStack);
}
}
}
return;
} /* ReturnStepEx() */
void
IncrementIP(
HTHDX hthd
)
{
#if defined(TARGET_IA64)
// EM's IIP contains bundle address. Single step must be done on the
// instruction slot.
DPRINT(1,("Incrementing IP: Was IIP: %I64x Slot:%i",
hthd->context.StIIP,
(hthd->context.StIPSR & IPSR_RI_MASK) >> PSR_RI));
switch( PC(hthd) & 0xf ) {
case 0:
hthd->context.StIPSR = (hthd->context.StIPSR & ~(IPSR_RI_MASK)) |
((ULONGLONG)0x1 << PSR_RI);
break;
case 4:
hthd->context.StIPSR = (hthd->context.StIPSR & ~(IPSR_RI_MASK)) |
((ULONGLONG)0x2 << PSR_RI);
// now let's see if we may have an MLI template here (we'll have
// to move to slot 1 from slot 2 then
{
ADDR addr;
DWORD InstLen;
ULONGLONG Template;
AddrFromHthdx(&addr,hthd);
GetAddrOff(addr) &= ~0xF;
if (!AddrReadMemory(hthd->hprc,
hthd,
&addr,
&Template,
sizeof(ULONGLONG),
&InstLen)) {
DPRINT(0,("AddrReadMemory @ %p failed in IncrementIP!",
GetAddrOff(addr)));
}
Template = (Template & INST_TEMPL_MASK) >> 1;
if ( Template == 0x02 ) {
DPRINT(0,("Moving slot from 1 to 0 of the next bundle for MLI template\n"));
hthd->context.StIPSR = hthd->context.StIPSR & ~(IPSR_RI_MASK);
hthd->context.StIIP += BUNDLE_SIZE;
}
}
break;
case 8:
hthd->context.StIPSR = hthd->context.StIPSR & ~(IPSR_RI_MASK);
hthd->context.StIIP += BUNDLE_SIZE;
break;
default:
assert(!"Bad Slot Number");
}
DPRINT(1,("NOW IIP: %I64x Slot:%i\n",
hthd->context.StIIP,
(hthd->context.StIPSR & IPSR_RI_MASK) >> PSR_RI));
#else
Set_PC(hthd, PC(hthd) + BP_SIZE);
#endif
assert(hthd->tstate & ts_stopped);
hthd->fContextDirty = TRUE;
return;
} /* IncrementIP() */
void
DecrementIP(
HTHDX hthd
)
{
#if defined(TARGET_IA64)
// EM's IIP contains bundle address. Single step must be done on the
// instruction slot.
switch( PC(hthd) & 0xf ) {
case 0:
hthd->context.StIPSR = (hthd->context.StIPSR & ~(IPSR_RI_MASK)) |
((ULONGLONG)0x2 << PSR_RI);
hthd->context.StIIP -= BUNDLE_SIZE;
// now let's see if we may have an MLI template here (we'll have
// to move to slot 1 from slot 2 then
{
ADDR addr;
DWORD InstLen;
ULONGLONG Template;
AddrFromHthdx(&addr,hthd);
GetAddrOff(addr) &= ~0xF;
if (!AddrReadMemory(hthd->hprc,
hthd,
&addr,
&Template,
sizeof(ULONGLONG),
&InstLen)) {
DPRINT(0,("AddrReadMemory @ %p failed in DecrementIP!",
GetAddrOff(addr)));
}
Template = (Template & INST_TEMPL_MASK) >> 1;
if ( Template == 0x02 ) {
DPRINT(0,("Moving slot from 2 to 1 for MLI template\n"));
hthd->context.StIPSR = (hthd->context.StIPSR & ~(IPSR_RI_MASK)) |
((ULONGLONG)0x1 << PSR_RI);
}
}
break;
case 4:
hthd->context.StIPSR = hthd->context.StIPSR & ~(IPSR_RI_MASK);
break;
case 8:
hthd->context.StIPSR = (hthd->context.StIPSR & ~(IPSR_RI_MASK)) |
((ULONGLONG)0x1 << PSR_RI);
break;
default:
assert(!"Bad Slot Number");
}
#else
// M00BUG -- two byte version of int 3
Set_PC(hthd, PC(hthd) - BP_SIZE);
#endif
assert(hthd->tstate & ts_stopped);
hthd->fContextDirty = TRUE;
return;
} /* DecrementIP() */
VOID
MoveIPToException(
HTHDX hthd,
LPDEBUG_EVENT64 pde
)
/*++
Routine Description:
This function moves the EIP for a thread to where an exception occurred.
This is primarily used for breakpoints. There are two advantages of
this over simply decrementing the IP: (1) its CPU-independent, and
(2) it helps work around an NT bug. The NT bug is, if an app which is
NOT being debugged has a hard-coded INT 3, and the user starts post-
mortem debugging, then when NT gives us the INT 3 exception, it will
give us the wrong EIP: it gives us the address of the INT 3, rather
than one byte past that. But it gives us the correct ExceptionAddress.
Arguments:
Return Value:
None
--*/
{
#if defined(TARGET_IA64)
hthd->context.StIIP = (UOFFSET) EXADDR(pde) & ~0xF; //bundle address
hthd->context.StIPSR &= ~IPSR_RI_MASK; //clear the bundle flag
hthd->context.StIPSR |= (((UOFFSET) EXADDR(pde) & 0xF) >> 2) << PSR_RI; //set it to bundle <0,1,2> <-> <0,4,8> of the address
#else
Set_PC(hthd, (IP_TYPE) EXADDR(pde) );
#endif
assert(hthd->tstate & ts_stopped);
hthd->fContextDirty = TRUE;
}
VOID
StepOver(
HTHDX hthd,
METHOD* notify,
BOOL stopOnBP,
BOOL fInFuncEval
)
/*++
Routine Desription:
Arguments:
Return Value:
--*/
{
ADDR currAddr;
int lpf = 0;
PBREAKPOINT bp;
PBREAKPOINT atbp;
LPCONTEXT lpContext = &hthd->context;
HPRCX hprc=hthd->hprc;
METHOD *method;
DPRINT(3, ("** SINGLE STEP OVER "));
#ifndef KERNEL
// if we are stepping into an ORPC section, the following function will
// take care of everythging for us and return TRUE. Just return.
if (CheckAndSetupForOrpcSection (hthd)) {
return;
}
#endif // !KERNEL
// Get the current IP of the thread
AddrFromHthdx(&currAddr, hthd);
// Determine what type of instruction we are presently on
IsCall(hthd, &currAddr, &lpf, TRUE);
// If the instruction is not a call or an intrpt then do a SS
if (lpf == INSTR_TRACE_BIT) {
DPRINT(0, ("lpf == INSTR_TRACE_BIT: Doing SingleStep from Addr %I64x\n",GetAddrOff(currAddr)));
SingleStep(hthd, notify, stopOnBP, fInFuncEval);
return;
} else if (lpf == INSTR_CANNOT_STEP) {
DPRINT(0, ("lpf == INSTR_CANNOT_STEP: @ Addr %I64x\n",GetAddrOff(currAddr)));
SendDBCErrorStep(hthd->hprc);
return;
}
ExprBPContinue(hthd->hprc, hthd);
// If the instruction is a BP then "uncover" it
if (lpf == INSTR_BREAKPOINT) {
DPRINT(0, ("lpf == INSTR_BREAKPOINT: We have hit a breakpoint instruction @ %I64x\n",GetAddrOff(currAddr)));
if (stopOnBP) {
/*
** We were instructed to stop on breakpoints
** Just issue an execute command and execute
** the breakpoint.
*/
ContinueProcess (hthd->hprc);
} else {
IncrementIP(hthd);
hthd->fIsCallDone = FALSE;
ClearBPFlag(hthd);
if (notify) {
(notify->notifyFunction)(&falseSSEvent, hthd, 0, notify->lparam);
} else {
NotifyEM(&falseSSEvent, hthd, 0, 0);
}
}
} else {
/*
** If control gets to this point, then the instruction
** that we are at is either a call or an interrupt.
*/
BOOL fDisplayReturnValues = hthd->fDisplayReturnValues && notify->lparam;
DPRINT(0, ("lpf == INSTR_CALL or INTERRUPT @ %I64x\n",GetAddrOff(currAddr)));
atbp = bp = AtBP(hthd);
if (!stopOnBP && !atbp) {
atbp = bp = FindBP(hthd->hprc, hthd, bptpExec, (BPNS)-1, &currAddr, FALSE);
SetBPFlag(hthd, bp);
}
if (atbp) {
/*
** Put the single step on our list of expected
** events and set the action to "Replace Byte and Continue"
** without notifying the EM
*/
DPRINT(0, ("Restoring bp @ %I64x\n",GetAddrOff(bp->addr)));
RestoreInstrBP(hthd, bp);
ClearBPFlag(hthd);
// We don't want to Continue automatically when return values are
// being displayed because we need to note the addr the Call is
// going to after all the thunk walking is done.
if (!fDisplayReturnValues) {
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)EXCEPTION_SINGLE_STEP,
DONT_NOTIFY,
SSActionRBAndContinue,
FALSE,
(UINT_PTR)bp);
/*
** Issue the single step
*/
if (lpf != INSTR_CANNOT_TRACE) {
DPRINT(0, ("Setting SingleStep\n"));
SetupSingleStep(hthd, FALSE);
}
}
}
if (lpf == INSTR_IS_CALL || lpf == INSTR_CANNOT_TRACE) {
DPRINT(0, ("lpf == INSTR_IS_CALL || lpf == INSTR_CANNOT_TRACE: Placing a Breakpoint @ %I64x ",
GetAddrOff(currAddr)));
/*
** Set a BP after this call instruction
*/
bp = SetBP(hprc, hthd, bptpExec, bpnsStop, &currAddr, (HPID)INVALID);
assert(bp);
/*
** Make a copy of the notification method
*/
method = (METHOD*)MHAlloc(sizeof(METHOD));
*method = *notify;
// Store the breakpoint with this notification method
method->lparam2 = (LPVOID)bp;
// lparam is NULL if we're doing StepOver from disassembly,
// which doesn't do a range step. We'll have to make
// ProcessSingleStep do a RangeStep to change this.
if (fDisplayReturnValues && lpf == INSTR_IS_CALL) {
ACVECTOR action = NO_ACTION;
DWORDLONG lParam = 0;
RANGESTEP *rs = (RANGESTEP *)method -> lparam; // ??? OK?
rs -> fGetReturnValue = TRUE;
rs -> safetyBP = bp;
if ( atbp ) {
action = SSActionReplaceByte;
lParam = (DWORDLONG)atbp;
}
assert (!hthd -> fReturning);
RegisterExpectedEvent(
hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)EXCEPTION_SINGLE_STEP,
method,
action,
FALSE,
lParam);
SetupSingleStep (hthd, TRUE);
return;
}
/*
** Place this on our list of expected events
** (Let the action function do the notification, other-
** wise the EM will receive a breakpoint notification,
** rather than a single step notification).NOTE:
** This is the reason why we make a copy of the notif-
** ication method -- because the action function must
** know which notification method to use, in addition
** to the breakpoint that was created.
*/
RegisterExpectedEvent(hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)bp,
DONT_NOTIFY,
SSActionRemoveBP,
FALSE,
(UINT_PTR)method);
DPRINT(7, ("PID= %lx TID= %lx\n", hprc->pid, hthd->tid));
}
/*
** Issue the execute command
*/
ContinueProcess (hthd->hprc);
// If we hit a DIFFERENT BP while we are in the called routine we
// must clear this (and ALL other consumable events) from the
// expected event queue.
}
return;
} /* StepOver() */
void
SSActionRemoveBP(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This action function is called upon the receipt of a breakpoint
event in order to remove the breakpoint and fake a single step event.
Note that the lparam to this action function must be a METHOD* and
the BREAKPOINT* to remove must be stored in the lparam2 field of the
method.
Arguments:
Return Value:
--*/
{
METHOD* method = (METHOD *)lparam;
BREAKPOINT* bp = (BREAKPOINT*)method->lparam2;
Unreferenced( de );
DEBUG_PRINT("** SS Action Remove BP called\n");
// Remove the temporary breakpoint
RemoveBP(bp);
// Notify whoever is concerned, that a SS event has occured
(method->notifyFunction)(&falseSSEvent, hthd, 0, method->lparam);
// Free the temporary notification method.
MHFree(method);
}
void
SSActionReplaceByte(
DEBUG_EVENT64 *de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This action function is called upon the receipt of a single step
event in order to replace the breakpoint instruction (INT 3, 0xCC)
that was written over.
Arguments:
Return Value:
--*/
{
PBREAKPOINT bp = (PBREAKPOINT)lparam;
Unreferenced( de );
Unreferenced( unused );
if (bp->hWalk) {
DPRINT(5, ("** SS Action Replace Byte is really a data BP\n"));
ExprBPResetBP(hthd, bp);
} else {
DPRINT(5, ("** SS Action Replace Byte at %d:%04x:%016I64x with %I64x\n",
ADDR_IS_FLAT(bp->addr),
bp->addr.addr.seg,
bp->addr.addr.off,
(ULONG64) BP_OPCODE
));
WriteBreakPoint( bp );
}
return;
}
void
SSActionRBAndContinue(
DEBUG_EVENT64 *de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This action function is called upon the receipt of a single step
event in order to replace the breakpoint instruction (INT 3, 0xCC)
that was written over, and then continuing execution.
Arguments:
Return Value:
--*/
{
PBREAKPOINT bp = (PBREAKPOINT)lparam;
Unreferenced( de );
Unreferenced( unused );
if (bp->hWalk) {
// Really a hardware BP, let walk manager fix it.
DPRINT(5, ("** SS Action RB and continue is really a data BP\n"));
ExprBPResetBP(hthd, bp);
} else {
DPRINT(5, ("** SS Action RB and Continue: Replace byte @ %d:%04x:%016I64x with %I64x\n",
ADDR_IS_FLAT(bp->addr),
bp->addr.addr.seg,
bp->addr.addr.off,
(ULONG64)BP_OPCODE
));
WriteBreakPoint( bp );
}
ContinueThread (hthd);
}
BOOL InsideRange( HTHDX, ADDR*, ADDR*, ADDR* );
BRANCH_LIST * GetBranchList ( HTHDX, ADDR*, ADDR* );
RANGESTRUCT * SetupRange ( HTHDX, ADDR*, ADDR*, BRANCH_LIST *, BOOL, BOOL, METHOD* );
VOID AddRangeBp( RANGESTRUCT*, ADDR*, BOOL );
VOID SetRangeBp( RANGESTRUCT* );
VOID RemoveRangeBp( RANGESTRUCT* );
BOOL GetThunkTarget( HTHDX, RANGESTRUCT*, ADDR*, ADDR* );
VOID RecoverFromSingleStep( ADDR*, RANGESTRUCT*);
BOOL ContinueFromInsideRange( ADDR*, RANGESTRUCT*);
BOOL ContinueFromOutsideRange( ADDR*, RANGESTRUCT*);
BOOL
SmartRangeStep(
HTHDX hthd,
UOFFSET offStart,
UOFFSET offEnd,
BOOL fStopOnBP,
BOOL fStepOver
)
/*++
Routine Description:
This function is used to implement range stepping the the DM. Range
stepping is used to cause all instructions between a pair of addresses
to be executed.
The segment is implied to be the current segment. This is validated
in the EM.
Arguments:
hthd - Supplies the thread to be stepped.
offStart - Supplies the initial offset in the range
offEnd - Supplies the final offset in the range
fStopOnBP - Supplies TRUE if stop on an initial breakpoint
fStepOver - Supplies TRUE if to step over call type instructions
Return Value:
TRUE if successful. If the disassembler fails, a breakpoint cannot
be set or other problems, FALSE will be returned, and the caller will
fall back to the slow range step method.
--*/
{
BRANCH_LIST *BranchList;
METHOD *Method;
RANGESTRUCT *RangeStruct;
ADDR AddrStart;
ADDR AddrEnd;
// Initialize start and end addresses
AddrInit(&AddrStart, 0, PcSegOfHthdx(hthd), offStart,
hthd->fAddrIsFlat, hthd->fAddrOff32, FALSE, hthd->fAddrIsReal);
AddrInit(&AddrEnd, 0, PcSegOfHthdx(hthd), offEnd,
hthd->fAddrIsFlat, hthd->fAddrOff32, FALSE, hthd->fAddrIsReal);
// Locate all the branch instructions inside the range (and their
// targets if available) and obtain a branch list.
BranchList = GetBranchList( hthd, &AddrStart, &AddrEnd );
if (!BranchList) {
return FALSE;
}
// Setup range step method
Method = (METHOD*)MHAlloc(sizeof(METHOD));
assert( Method );
Method->notifyFunction = MethodSmartRangeStep;
// Set up the range structure (this will set all safety breakpoints).
RangeStruct = SetupRange( hthd,
&AddrStart,
&AddrEnd,
BranchList,
fStopOnBP,
fStepOver,
Method );
if (!RangeStruct) {
MHFree(BranchList);
return FALSE;
}
// Now let the thread run.
ContinueProcess (hthd->hprc);
#ifdef KERNEL
hthd->tstate |= ts_stepping;
#endif
return TRUE;
}
BOOL
InsideRange(
HTHDX hthd,
ADDR *AddrStart,
ADDR *AddrEnd,
ADDR *Addr
)
{
ADDR AddrS;
ADDR AddrE;
ADDR AddrC;
assert( AddrStart );
assert( AddrEnd );
assert( Addr );
if ( ADDR_IS_LI(*Addr) ) {
return FALSE;
}
AddrS = *AddrStart;
AddrE = *AddrEnd;
AddrC = *Addr;
if (!ADDR_IS_FLAT(AddrS)) {
if ( !TranslateAddress(hthd->hprc, hthd, &AddrS, TRUE) ) {
return FALSE;
}
}
if (!ADDR_IS_FLAT(AddrE)) {
if ( !TranslateAddress(hthd->hprc, hthd, &AddrE, TRUE) ) {
return FALSE;
}
}
if (!ADDR_IS_FLAT(AddrC)) {
if ( !TranslateAddress(hthd->hprc, hthd, &AddrC, TRUE) ) {
return FALSE;
}
}
if ( GetAddrOff( AddrC ) >= GetAddrOff( AddrS ) &&
GetAddrOff( AddrC ) <= GetAddrOff( AddrE ) ) {
return TRUE;
}
return FALSE;
}
BRANCH_LIST *
GetBranchList (
HTHDX hthd,
ADDR *AddrStart,
ADDR *AddrEnd
)
/*++
Routine Description:
Locates all the branch instructions within a range and builds a
branch list.
Arguments:
hthd - Supplies thread
AddrStart - Supplies start of range
AddrEnd - Supplies end of range
Return Value:
BRANCH_LIST * - Pointer to branch list.
--*/
{
void *Memory;
BRANCH_LIST *BranchList = NULL;
BRANCH_LIST *BranchListTmp;
DWORD RangeSize;
LONG Length;
BYTE *Instr;
DWORD ListSize;
DWORD i;
ADDR Addr;
assert( AddrStart );
assert( AddrEnd );
RangeSize = (DWORD)(GetAddrOff(*AddrEnd) - GetAddrOff(*AddrStart) + 1);
// Read the code.
Memory = MHAlloc( RangeSize );
assert( Memory );
if (!Memory) {
return NULL;
}
// Allocate and initialize the branch list structure
ListSize = sizeof( BRANCH_LIST );
BranchList = (BRANCH_LIST *)MHAlloc( ListSize );
assert( BranchList );
if (!BranchList) {
MHFree(Memory);
return NULL;
}
BranchList->AddrStart = *AddrStart;
BranchList->AddrEnd = *AddrEnd;
BranchList->Count = 0;
Addr = *AddrStart;
AddrReadMemory(hthd->hprc, hthd, &Addr, Memory, RangeSize, &Length );
#ifndef KERNEL
assert(Length==(LONG)RangeSize);
#endif
// If the code crosses a page boundary and the second
// page is not present, the read will be short.
// Fail, and we will fall back to the slow step code.
if (Length != (LONG)RangeSize) {
MHFree(BranchList);
MHFree(Memory);
return NULL;
}
// Unassemble the code and determine where all branches are.
Instr = (BYTE *)Memory;
#if defined(TARGET_IA64)
// IA64 Address must begin at the bundle bundary or else can not be unassembled
Instr += ((GetAddrOff(Addr) + 0xf) & ~(0xf) - GetAddrOff(Addr));
GetAddrOff(Addr) = (GetAddrOff(Addr) + 0xf) & ~(0xf);
#endif
while ( Length > 0 ) {
BOOL IsBranch;
BOOL TargetKnown;
BOOL IsCall;
BOOL IsTable;
ADDR Target;
DWORD Consumed;
// Unassemble one instruction
Consumed = BranchUnassemble(hthd,
(void *)Instr,
Length,
&Addr,
&IsBranch,
&TargetKnown,
&IsCall,
&IsTable,
&Target );
assert( Consumed > 0 );
if ( Consumed == 0 ) {
// Could not unassemble the instruction, give up.
MHFree(BranchList);
BranchList = NULL;
Length = 0;
} else {
if (IsBranch && IsTable &&
InsideRange(hthd, AddrStart, AddrEnd, &Target)) {
// this is a vectored jump with the table included
// in the source range. Rather than try to figure
// out what we can and cannot disassemble, punt
// here and let the slow step code catch it.
MHFree(BranchList);
BranchList = NULL;
Length = 0;
} else if ( IsBranch ) {
// If instruction is a branch, and the branch falls outside
// of the range, add a branch node to the list.
BOOLEAN fAdded = FALSE;
if ( TargetKnown ) {
if ( ADDR_IS_FLAT(Target) ) {
if ( GetAddrOff(Target) != 0 ) {
GetAddrSeg(Target) = PcSegOfHthdx(hthd);
}
} else {
ADDR_IS_REAL(Target) = (BYTE)hthd->fAddrIsReal;
}
}
if ( !InsideRange( hthd, AddrStart, AddrEnd, &Target ) ||
!TargetKnown ) {
// this loop is to ensure that we dont get duplicate
// breakpoints set
for (i=0; i<BranchList->Count; i++) {
if ( TargetKnown &&
FAddrsEq( BranchList->BranchNode[i].Target, Target ) ) {
break;
}
}
if (i == BranchList->Count) {
ListSize += sizeof( BRANCH_NODE );
BranchListTmp = (BRANCH_LIST *)MHRealloc( BranchList, ListSize );
assert( BranchListTmp );
BranchList = BranchListTmp;
BranchList->BranchNode[ BranchList->Count ].TargetKnown= TargetKnown;
BranchList->BranchNode[ BranchList->Count ].IsCall = IsCall;
BranchList->BranchNode[ BranchList->Count ].Addr = Addr;
BranchList->BranchNode[ BranchList->Count ].Target = Target;
BranchList->Count++;
fAdded = TRUE;
}
}
}
#if defined(TARGET_IA64)
// each bundle must be unassembled three times before advancing to next bundle
switch(GetAddrOff(Addr) & 0xf) {
case 0:
case 4:
GetAddrOff(Addr) += 4;
break;
case 8:
GetAddrOff(Addr) += 8;
Instr += Consumed;
break;
default:
Instr += ((GetAddrOff(Addr) + 0xf) & ~(0xf) - GetAddrOff(Addr));
GetAddrOff(Addr) = (GetAddrOff(Addr) + 0xf) & ~(0xf);
break;
}
#else
Instr += Consumed;
GetAddrOff(Addr) += Consumed;
#endif
Length -= Consumed;
}
}
MHFree( Memory );
return BranchList;
}
RANGESTRUCT *
SetupRange (
HTHDX hthd,
ADDR *AddrStart,
ADDR *AddrEnd,
BRANCH_LIST *BranchList,
BOOL fStopOnBP,
BOOL fStepOver,
METHOD *Method
)
/*++
Routine Description:
Helper function for RangeStep.
Arguments:
hthd - Supplies thread
AddrStart - Supplies start of range
AddrEnd - Supplies end of range
BranchList - Supplies branch list
fStopOnBP - Supplies fStopOnBP flag
fStepOver - Supplies fStepOver flag
Return Value:
RANGESTRUCT * - Pointer to range structure
--*/
{
RANGESTRUCT *RangeStruct;
BREAKPOINT *Bp;
DWORD i;
BOOLEAN fAddedAtEndOfRange = FALSE;
ADDR Addr;
CANSTEP CanStep;
UOFFSET dwOffset;
DWORD dwSize;
UOFFSET dwPC;
assert( AddrStart );
assert( AddrEnd );
assert( BranchList );
assert( Method );
// Allocate and initialize the range structure
RangeStruct = (RANGESTRUCT *)MHAlloc( sizeof(RANGESTRUCT) );
assert( RangeStruct );
RangeStruct->hthd = hthd;
RangeStruct->BranchList = BranchList;
RangeStruct->fStepOver = fStepOver;
RangeStruct->fStopOnBP = fStopOnBP;
RangeStruct->BpCount = 0;
RangeStruct->BpAddrs = NULL;
RangeStruct->BpList = NULL;
RangeStruct->fSingleStep = FALSE;
RangeStruct->fInCall = FALSE;
RangeStruct->Method = Method;
Method->lparam = (UINT_PTR)RangeStruct;
DPRINT(0,("Setting step range to %I64x-%I64x\n",GetAddrOff(*AddrStart),GetAddrOff(*AddrEnd)));
// If the given range has branches, set branch breakpoints according to
// the fStepOver flag.
if ( BranchList->Count > 0 ) {
if ( fStepOver ) {
// Ignore calls (Were stepping over them), set BPs in all
// known target (if outside of range) and all branch instructions
// with unknown targets.
for ( i=0; i < BranchList->Count; i++ ) {
if ( !BranchList->BranchNode[i].IsCall ) {
if ( !BranchList->BranchNode[i].TargetKnown ) {
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Addr, FALSE );
} else if ( !InsideRange( hthd, AddrStart, AddrEnd, &BranchList->BranchNode[i].Target ) ) {
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Target, FALSE );
}
}
}
} else {
// Set BPs in all branches/calls with unknown targets, all
// branch targets (if outside of range) and all call targets
// for which we have source.
for ( i=0; i < BranchList->Count; i++ ) {
if ( !BranchList->BranchNode[i].TargetKnown ) {
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Addr, FALSE );
} else if ( !InsideRange( hthd, AddrStart, AddrEnd, &BranchList->BranchNode[i].Target ) ) {
if ( !BranchList->BranchNode[i].IsCall ) {
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Target, FALSE );
} else {
// BUGBUG - If debugging WOW, we dont set a
// breakpoint in a function prolog, instead we set the
// breakpoint in the call instruction and single step
// to the function.
if (!ADDR_IS_FLAT(BranchList->BranchNode[i].Addr) ) {
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Addr, FALSE );
} else {
// Dont look for source lines until we hit the call target.
// If the call target is a thunk, we might not be able to interpret
// it until the thread is sitting on it. Therefore, we always have
// to stop at the call target, so there is no point in looking now
// to see whether there is source data for it until we get there.
// Walk thunks, as far as we can.
dwPC = GetAddrOff(BranchList->BranchNode[i].Target);
while (IsThunk(hthd, dwPC, NULL, &dwPC, NULL )) {
GetAddrOff(BranchList->BranchNode[i].Target) = dwPC;
}
AddRangeBp( RangeStruct, &BranchList->BranchNode[i].Target, FALSE );
}
}
}
}
}
}
if ( !fAddedAtEndOfRange ) {
// We always set a safety breakpoint at the instruction past the end
// of the range.
ADDR Addr = *AddrEnd;
GetAddrOff(Addr) += 1;
AddRangeBp( RangeStruct, &Addr, FALSE );
}
// If we currently are at a BP and the address is not already in the
// list, then we must setup a single step for the instruction.
Bp = AtBP(hthd);
if ( Bp == EMBEDDED_BP ) {
// we must step off the harcoded bp
ClearBPFlag( hthd );
IncrementIP( hthd );
hthd->fIsCallDone = FALSE;
} else if ( Bp ) {
// Make sure that the BP is not in the list
for ( i=0; i<RangeStruct->BpCount; i++ ) {
if ( FAddrsEq( RangeStruct->BpAddrs[i], Bp->addr )) {
break;
}
}
if ( i >= RangeStruct->BpCount ) {
// We have to single step the breakpoint.
ClearBPFlag( hthd );
RestoreInstrBP( RangeStruct->hthd, Bp );
RangeStruct->PrevAddr = Bp->addr;
RangeStruct->fSingleStep = TRUE;
// Set the fInCall flag so that the stepping method knows whether
// or not it should stop stepping in case we get out of the range.
for ( i=0; i < RangeStruct->BranchList->Count; i++ ) {
if ( FAddrsEq( Bp->addr,
RangeStruct->BranchList->BranchNode[i].Addr ) ) {
RangeStruct->fInCall =
RangeStruct->BranchList->BranchNode[i].IsCall;
break;
}
}
#ifdef TARGET_i386
#ifndef KERNEL
RangeStruct->hthd->context.EFlags |= TF_BIT_MASK;
RangeStruct->hthd->fContextDirty = TRUE;
#endif
#elif defined (TARGET_IA64)
#ifndef KERNEL
RangeStruct->hthd->context.StIPSR |= TF_BIT_MASK;
RangeStruct->hthd->fContextDirty = TRUE;
#endif
#else // i386
{
ADDR Addr;
UOFFSET NextOffset;
NextOffset = GetNextOffset( hthd, RangeStruct->fStepOver );
#ifndef KERNEL
if ( NextOffset != 0x00000000 ) {
AddrInit( &Addr, 0, 0, NextOffset, TRUE, TRUE, FALSE, FALSE );
RangeStruct->TmpAddr = Addr;
RangeStruct->TmpBp = SetBP( RangeStruct->hthd->hprc,
RangeStruct->hthd,
bptpExec,
bpnsStop,
&Addr,
(HPID) INVALID);
}
assert( RangeStruct->TmpBp );
#else // KERNEL
AddrInit( &Addr, 0, 0, NextOffset, TRUE, TRUE, FALSE, FALSE );
GetCanStep(RangeStruct->hthd->hprc->hpid,
RangeStruct->hthd->htid,
&Addr,
&CanStep);
if (CanStep.Flags == CANSTEP_YES) {
GetAddrOff(Addr) += CanStep.PrologOffset;
}
RangeStruct->TmpAddr = Addr;
RangeStruct->TmpBp = SetBP( RangeStruct->hthd->hprc,
RangeStruct->hthd,
bptpExec,
bpnsStop,
&Addr,
(HPID) INVALID);
assert( RangeStruct->TmpBp );
#endif // KERNEL
}
#endif // i386
}
}
SetRangeBp( RangeStruct );
return RangeStruct;
}
VOID
AddRangeBp(
RANGESTRUCT *RangeStruct,
ADDR *Addr,
BOOL fSet
)
/*++
Routine Description:
Sets a breakpoint at a particular address and adds it to the breakpoint
list in a RANGESTRUCT
Arguments:
RangeStruct - Supplies pointer to range structure
Offset - Supplies flat address of breakpoint
fSet - Supplies flag which if true causes the BP to be set
Return Value:
None
--*/
{
BREAKPOINT **BpList;
ADDR *BpAddrs;
DWORD i;
assert( RangeStruct );
assert( Addr );
// Add the breakpoint to the list in the range structure
if ( RangeStruct->BpList ) {
assert( RangeStruct->BpCount > 0 );
assert( RangeStruct->BpAddrs );
// Do not add duplicates
for ( i=0; i<RangeStruct->BpCount; i++ ) {
if ( FAddrsEq( RangeStruct->BpAddrs[i], *Addr ) ) {
return;
}
}
BpList = ( BREAKPOINT** )MHRealloc( RangeStruct->BpList, sizeof( BREAKPOINT *) * (RangeStruct->BpCount + 1) );
BpAddrs = ( ADDR* )MHRealloc( RangeStruct->BpAddrs, sizeof( ADDR ) * (RangeStruct->BpCount + 1) );
} else {
assert( RangeStruct->BpCount == 0 );
assert( RangeStruct->BpAddrs == NULL );
BpList = ( BREAKPOINT** )MHAlloc( sizeof( BREAKPOINT * ) );
BpAddrs = ( ADDR* )MHAlloc( sizeof( ADDR ) );
}
assert( BpList );
assert( BpAddrs );
BpList[RangeStruct->BpCount] = NULL;
BpAddrs[ RangeStruct->BpCount] = *Addr;
if ( fSet ) {
BpList[ RangeStruct->BpCount ] =
SetBP( RangeStruct->hthd->hprc,
RangeStruct->hthd,
bptpExec,
bpnsStop,
Addr,
(HPID) INVALID
);
assert( BpList[ RangeStruct->BpCount ] );
}
RangeStruct->BpCount++;
RangeStruct->BpList = BpList;
RangeStruct->BpAddrs = BpAddrs;
}
VOID
SetRangeBp(
RANGESTRUCT *RangeStruct
)
/*++
Routine Description:
Sets the breakpoints in the range
Arguments:
RangeStruct - Supplies pointer to range structure
Return Value:
None
--*/
{
BOOL BpSet;
DWORD Class;
DWORD_PTR SubClass;
assert( RangeStruct );
if ( RangeStruct->fSingleStep ) {
#ifdef TARGET_i386
Class = EXCEPTION_DEBUG_EVENT;
SubClass = (DWORD_PTR)STATUS_SINGLE_STEP;
#else
Class = BREAKPOINT_DEBUG_EVENT;
SubClass = NO_SUBCLASS;
#endif
} else {
Class = BREAKPOINT_DEBUG_EVENT;
SubClass = NO_SUBCLASS;
}
// Register the expected breakpoint event.
RegisterExpectedEvent(RangeStruct->hthd->hprc,
RangeStruct->hthd,
Class,
SubClass,
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
if ( RangeStruct->BpCount ) {
assert( RangeStruct->BpList );
assert( RangeStruct->BpAddrs );
// Set all the breakpoints at once
BpSet = SetBPEx( RangeStruct->hthd->hprc,
RangeStruct->hthd,
(HPID) INVALID,
RangeStruct->BpCount,
RangeStruct->BpAddrs,
RangeStruct->BpList,
0
//DBG_CONTINUE
);
assert( BpSet );
}
}
VOID
RemoveRangeBp(
RANGESTRUCT *RangeStruct
)
/*++
Routine Description:
Sets the breakpoints in the range
Arguments:
RangeStruct - Supplies pointer to range structure
Return Value:
None
--*/
{
BOOL BpRemoved;
assert( RangeStruct );
if ( RangeStruct->BpCount ) {
assert( RangeStruct->BpList );
assert( RangeStruct->BpAddrs );
// Reset all the breakpoints at once
BpRemoved = RemoveBPEx( RangeStruct->BpCount,
RangeStruct->BpList
);
}
}
void
MethodSmartRangeStep(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
{
RANGESTRUCT* RangeStruct = (RANGESTRUCT*)lparam;
ADDR AddrCurrent;
BOOL fSingleStep = FALSE;
assert( de );
assert( RangeStruct );
// Get the current address
AddrFromHthdx( &AddrCurrent, hthd );
if ( RangeStruct->fSingleStep ) {
// Recover from single step
RecoverFromSingleStep( &AddrCurrent, RangeStruct );
fSingleStep = TRUE;
}
// See what we must do now.
if ( InsideRange( hthd,
&RangeStruct->BranchList->AddrStart,
&RangeStruct->BranchList->AddrEnd,
&AddrCurrent ) ) {
// Still inside the range.
if ( ContinueFromInsideRange( &AddrCurrent, RangeStruct ) ) {
return;
}
} else {
// Outside the range
if ( fSingleStep && RangeStruct->fStepOver && RangeStruct->fInCall ) {
// Recovering from a single step, continue.
RangeStruct->fInCall = FALSE;
RegisterExpectedEvent( RangeStruct->hthd->hprc,
RangeStruct->hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
ContinueThreadEx (hthd,
DBG_CONTINUE,
RangeStruct->fSingleStep ?
QT_TRACE_DEBUG_EVENT :
QT_CONTINUE_DEBUG_EVENT,
ts_running
);
return;
}
if ( ContinueFromOutsideRange( &AddrCurrent, RangeStruct ) ) {
return;
}
}
// If we get here then we must clean up all the allocated resources
// and notify the EM.
if ( RangeStruct->BpCount > 0 ) {
assert( RangeStruct->BpList );
assert( RangeStruct->BpAddrs );
RemoveRangeBp( RangeStruct );
MHFree( RangeStruct->BpList );
MHFree( RangeStruct->BpAddrs );
}
assert( RangeStruct->BranchList );
MHFree( RangeStruct->BranchList );
assert( RangeStruct->Method );
MHFree( RangeStruct->Method );
MHFree( RangeStruct );
// Notify the EM that this thread has stopped.
#ifdef KERNEL
hthd->tstate &= ~ts_stepping;
#endif
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
NotifyEM(&falseSSEvent, hthd, 0, 0);
}
VOID
RecoverFromSingleStep(
ADDR *AddrCurrent,
RANGESTRUCT* RangeStruct
)
{
BREAKPOINT *Bp;
DWORD i;
BP_UNIT opcode = BP_OPCODE;
assert( AddrCurrent );
assert( RangeStruct );
assert( RangeStruct->fSingleStep );
// Recovering from a single step.
// Reset previous BP
Bp = FindBP( RangeStruct->hthd->hprc,
RangeStruct->hthd,
bptpExec,
(BPNS)-1,
&RangeStruct->PrevAddr,
FALSE );
assert( Bp );
if ( Bp ) {
WriteBreakPoint( Bp );
}
#ifdef TARGET_i386
#ifndef KERNEL
// Clear trace flag
RangeStruct->hthd->context.EFlags &= ~(TF_BIT_MASK);
RangeStruct->hthd->fContextDirty = TRUE;
#endif // KERNEL
#elif defined (TARGET_IA64)
#ifndef KERNEL
// Clear trace flag
RangeStruct->hthd->context.StIPSR &= ~TF_BIT_MASK;
RangeStruct->hthd->fContextDirty = TRUE;
#endif // KERNEL
#else // TARGET_i386
// Remove temporary breakpoint
assert( FAddrsEq( RangeStruct->TmpBp->addr, *AddrCurrent ) );
assert( RangeStruct->TmpBp );
RemoveBP( RangeStruct->TmpBp );
RangeStruct->TmpBp = NULL;
#endif // TARGET_i386
RangeStruct->fSingleStep = FALSE;
}
BOOL
ContinueFromInsideRange(
ADDR *AddrCurrent,
RANGESTRUCT *RangeStruct
)
{
DWORD i;
BREAKPOINT *Bp;
UOFFSET NextOffset;
ADDR NextAddr;
BOOL fContinue = FALSE;
assert( AddrCurrent );
assert( RangeStruct );
assert( !RangeStruct -> hthd -> fDisplayReturnValues); // AutoDisplay of return values code should be added to this routine. See RAID 2914
Bp = AtBP(RangeStruct->hthd);
if ( RangeStruct->BranchList->Count > 0 && Bp ) {
if ( Bp != EMBEDDED_BP ) {
// Look for the branch node corresponding to this address.
// When found, determine the target address and set a
// safety breakpoint there if necessary. Then let the
// thread continue.
for ( i=0; i < RangeStruct->BranchList->Count; i++ ) {
if ( FAddrsEq( RangeStruct->BranchList->BranchNode[i].Addr,
*AddrCurrent ) ) {
// This is our guy.
// Determine the next address
RangeStruct->fInCall = RangeStruct->BranchList->BranchNode[i].IsCall;
#ifdef TARGET_i386
UNREFERENCED_PARAMETER( NextOffset );
UNREFERENCED_PARAMETER( NextAddr );
#else // TARGET_i386
NextOffset = GetNextOffset( RangeStruct->hthd, RangeStruct->fStepOver );
AddrInit(&NextAddr, 0, 0, NextOffset, TRUE, TRUE, FALSE, FALSE );
#endif // TARGET_i386
// We have to single step the current instruction.
// We set a temporary breakpoint at the next offset,
// recover the current breakpoint and set the flags to
// reset the breakpoint when we hit the temporary
// breakpoint.
ClearBPFlag( RangeStruct->hthd );
RestoreInstrBP( RangeStruct->hthd, Bp );
RangeStruct->PrevAddr = *AddrCurrent;
RangeStruct->fSingleStep = TRUE;
#ifdef TARGET_i386
#ifndef KERNEL
RangeStruct->hthd->context.EFlags |= TF_BIT_MASK;
RangeStruct->hthd->fContextDirty = TRUE;
#endif // KERNEL
#elif defined (TARGET_IA64)
#ifndef KERNEL
RangeStruct->hthd->context.StIPSR |= TF_BIT_MASK;
RangeStruct->hthd->fContextDirty = TRUE;
#endif // KERNEL
#else // TARGET_i386
RangeStruct->TmpAddr = NextAddr;
RangeStruct->TmpBp = SetBP( RangeStruct->hthd->hprc,
RangeStruct->hthd,
bptpExec,
bpnsStop,
&NextAddr,
(HPID) INVALID);
assert( RangeStruct->TmpBp );
#endif // TARGET_i386
// Register the expected event.
RegisterExpectedEvent(
RangeStruct->hthd->hprc,
RangeStruct->hthd,
#if (defined(TARGET_i386) || defined(TARGET_IA64)) && !defined(NO_TRACE_FLAG)
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)STATUS_SINGLE_STEP,
#else // TARGET_i386 ...
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
#endif // TARGET_i386 ...
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
ContinueThreadEx (RangeStruct->hthd,
DBG_CONTINUE,
RangeStruct->fSingleStep ?
QT_TRACE_DEBUG_EVENT :
QT_CONTINUE_DEBUG_EVENT,
ts_running
);
fContinue = TRUE;
break;
}
}
}
} else {
// We might end up here if continuing from a single step.
RegisterExpectedEvent( RangeStruct->hthd->hprc,
RangeStruct->hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
ContinueThreadEx (RangeStruct->hthd,
DBG_CONTINUE,
RangeStruct->fSingleStep ?
QT_TRACE_DEBUG_EVENT :
QT_CONTINUE_DEBUG_EVENT,
ts_running
);
fContinue = TRUE;
}
return fContinue;
}
BOOL
ContinueFromOutsideRange(
ADDR *AddrCurrent,
RANGESTRUCT *RangeStruct
)
{
BOOL fContinue = FALSE;
ADDR Addr;
CANSTEP CanStep;
BREAKPOINT *Bp;
assert( AddrCurrent );
assert( RangeStruct );
assert( !RangeStruct -> hthd -> fDisplayReturnValues); // AutoDisplay of return values code should be added to this routine. See RAID 2914
Bp = AtBP(RangeStruct->hthd);
if (Bp == EMBEDDED_BP) {
// always stop.
} else if (!RangeStruct->fInCall) {
// if we weren't in a call, this should just be some other line
// of code in the same function (or the parent function?), so stop.
} else {
// stopping after a call instruction.
// this might actually not be a new function; we just know
// that there was a call instruction in the source line.
GetCanStep(RangeStruct->hthd->hprc->hpid,
RangeStruct->hthd->htid,
AddrCurrent,
&CanStep
);
switch ( CanStep.Flags ) {
case CANSTEP_YES:
// if there is a known prolog, run ahead to the end
if ( CanStep.PrologOffset > 0 ) {
Addr = *AddrCurrent;
GetAddrOff(Addr) += CanStep.PrologOffset;
AddRangeBp( RangeStruct, &Addr, TRUE );
RegisterExpectedEvent(
RangeStruct->hthd->hprc,
RangeStruct->hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
ClearBPFlag( RangeStruct->hthd );
if ( Bp ) {
RestoreInstrBP( RangeStruct->hthd, Bp );
}
ContinueThread (RangeStruct->hthd);
fContinue = TRUE;
}
break;
case CANSTEP_NO:
// no source here.
// step some more...
// what is going to happen here? do we just keep stepping
// until we get somewhere that has source?
// Register the expected event.
RegisterExpectedEvent(
RangeStruct->hthd->hprc,
RangeStruct->hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
RangeStruct->Method,
NO_ACTION,
FALSE,
0);
ClearBPFlag( RangeStruct->hthd );
if ( Bp ) {
RestoreInstrBP( RangeStruct->hthd, Bp );
}
ContinueThreadEx (RangeStruct->hthd,
DBG_CONTINUE,
RangeStruct->fSingleStep ?
QT_TRACE_DEBUG_EVENT :
QT_CONTINUE_DEBUG_EVENT,
ts_running
);
fContinue = TRUE;
break;
}
}
return fContinue;
}
VOID
RangeStepContinue(
HTHDX hthd,
LPVOID Args
);
typedef struct _RANGE_STEP_CONTINUE_ARGS
{
UOFFSET offStart;
UOFFSET offEnd;
BOOL fStopOnBP;
BOOL fstepOver;
} RANGE_STEP_CONTINUE_ARGS;
VOID
RangeStep(
HTHDX hthd,
UOFFSET offStart,
UOFFSET offEnd,
BOOL fStopOnBP,
BOOL fstepOver
)
/*++
Routine Description:
This function is used to implement range stepping the the DM. Range
stepping is used to cause all instructions between a pair of addresses
to be executed.
The segment is implied to be the current segment. This is validated
in the EM.
Range stepping is done by registering an expected debug event at the
end of a step and seeing if the current program counter is still in
the correct range. If it is not then the range step is over, if it
is then a new event is register and we loop.
Arguments:
hthd - Supplies the thread to be stepped.
offStart - Supplies the initial offset in the range
offEnd - Supplies the final offset in the range
fStopOnBP - Supplies TRUE if stop on an initial breakpoint
fStepOver - Supplies TRUE if to step over call type instructions
Return Value:
None.
Comments:
This function has been broken into two. RangeStep and RangeStepContinue.
See RangeStepContinue for most of the functionality.
--*/
{
RANGE_STEP_CONTINUE_ARGS* Args;
Args = (RANGE_STEP_CONTINUE_ARGS*) MHAlloc (sizeof (*Args));
Args->offStart = offStart;
Args->offEnd = offEnd;
Args->fStopOnBP = fStopOnBP;
Args->fstepOver = fstepOver;
#ifndef KERNEL
EnsureOleRpcStatus (hthd, RangeStepContinue, Args);
#else
RangeStepContinue(hthd, Args);
#endif
}
VOID
RangeStepContinue(
HTHDX hthd,
LPVOID Args
)
/*++
Routine Description:
See the function RangeStep for a description of the Args fields. In the
normal case -- where we are not changing our OLE debugging state -- this
function is just called from EnsureOleRpcStatus ().
--*/
{
RANGE_STEP_CONTINUE_ARGS* rscArgs = (RANGE_STEP_CONTINUE_ARGS*) Args;
UOFFSET offStart = rscArgs->offStart;
UOFFSET offEnd = rscArgs->offEnd;
BOOL fStopOnBP= rscArgs->fStopOnBP;
BOOL fstepOver= rscArgs->fstepOver;
RANGESTEP * rs;
METHOD * method;
HPRCX hprc = hthd->hprc;
int lpf = 0;
ADDR addr;
MHFree (rscArgs);
rscArgs = NULL;
// Create and fill a range step structure
rs = (RANGESTEP*) MHAlloc(sizeof(RANGESTEP));
rs->hthd = hthd;
rs->addrStart = offStart;
rs->addrEnd = offEnd;
rs->segCur = PcSegOfHthdx(hthd);
rs->fInThunk = FALSE;
rs->safetyBP = NULL;
rs->SavedSeg = 0;
rs->SavedAddrStart = 0;
rs->SavedAddrEnd = 0;
#if defined (TARGET_IA64)
rs->fSkipProlog = TRUE;
#else
rs->fSkipProlog = FALSE;
#endif
rs->fGetReturnValue = FALSE;
rs->fIsCall = FALSE;
// Create a notification method for this range step
method = (METHOD*) MHAlloc(sizeof(METHOD));
method->notifyFunction = MethodRangeStep;
method->lparam = (UINT_PTR)rs;
rs->method = method;
if ( fstepOver ) {
rs->stepFunction = StepOver;
} else {
rs->stepFunction = SingleStep;
/*
* Check to see if we are currently at a call instruction. If we
* are then we need to set a breakpoint at the end of the call
* instruction as the "safety" breakpoint.
* This will allow us to recover back to the current level
* if the call we are just about to step into does not have
* any source information (in which case the range step
* is defined to continue).
*/
AddrInit(&addr, 0, rs->segCur, offStart,
hthd->fAddrIsFlat, hthd->fAddrOff32, FALSE, hthd->fAddrIsReal);
IsCall( hthd, &addr, &lpf, FALSE);
if ( lpf == INSTR_IS_CALL ) {
rs->safetyBP = SetBP(hprc, hthd, bptpExec, bpnsStop, &addr, (HPID)INVALID);
rs->fIsCall = TRUE;
} else if (lpf == INSTR_CANNOT_STEP) {
SendDBCErrorStep(hthd->hprc);
return;
}
}
// Call the step over function to send notifications
// to the RangeStepper (NOT THE EM!)
if (rs->fIsCall) {
SingleStep(hthd, method, fStopOnBP, FALSE);
} else {
(rs->stepFunction)(hthd, method, fStopOnBP, FALSE);
}
} /* RangeStep() */
VOID
MethodRangeStep(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This is the range step method for the "stupid" or single stepping range
step method. This is called for every instruction executed during a range
step, except when it has decided to run free to the safety breakpoint. It
does that when the destination code has no source line information.
Arguments:
de - Supplies the single step or breakpoint debug event
hthd - Supplies the thread
unused -
rs - Supplies a structure containing state information for the step
Return Value:
none
--*/
{
RANGESTEP* rs = (RANGESTEP*)lparam;
LPCONTEXT lpContext = &hthd->context;
DWORDLONG currAddr = cPC(lpContext);
int lpf = 0;
HPRCX hprc = hthd->hprc;
METHOD * method;
ADDR AddrPC;
ADDR AddrTmp;
HPID hpid = hprc->hpid;
HTID htid = hthd->htid;
CANSTEP CanStep;
PBREAKPOINT bp;
DWORD dwSize;
Unreferenced( de );
DEBUG_PRINT_3("** MethodRangeStep called: %08x-%08x PC=%08x",
rs->addrStart, rs->addrEnd, currAddr);
AddrFromHthdx(&AddrPC, hthd);
// auto return value
if (hthd -> fReturning) {
// Out of a call.
hthd -> fReturning = FALSE;
assert (rs -> fGetReturnValue);
// Got it!
rs->fGetReturnValue = FALSE;
NotifyEM(&FuncExitEvent, hthd, 0, (DWORDLONG)&hthd->addrFrom );
}
// see if we ran past a call or hit a BP.
bp = AtBP(hthd);
if (bp && bp == rs->safetyBP) {
// we stepped over the function. continue the range step...
// Note: we only get here if we didn't stop in the called function.
// In the usual case, the safety BP has been changed into an expected
// event and is gone by the time we get here.
RemoveBP(bp);
rs->safetyBP = NULL;
rs->fIsCall = FALSE;
} else if (bp) {
// always stop on a real bp.
goto EndStep;
} else if (rs->fIsCall) {
rs->fIsCall = FALSE;
if (rs->stepFunction == StepOver) {
// This is the destination of a CALL which we are supposed to
// step over.
// if it is a nested function, we change it into a "Step Into",
// otherwise we want to just run to the Safety BP.
if (GetAddrOff(rs->CallSiteInfo.AddrStart) > 0 &&
GetAddrOff(AddrPC) >= GetAddrOff(rs->CallSiteInfo.AddrStart) &&
GetAddrOff(AddrPC) <= GetAddrOff(rs->CallSiteInfo.AddrEnd)) {
// it's nested - (try to) stay here.
;
} else {
// We don't want to go here, so continue.
// make the safety BP an expected event and run free.
method = (METHOD*)MHAlloc(sizeof(METHOD));
*method = *rs->method;
method->lparam2 = (LPVOID)rs->safetyBP;
RegisterExpectedEvent(
hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)rs->safetyBP,
DONT_NOTIFY,
SSActionRemoveBP,
FALSE,
(UINT_PTR)method);
// The safety is the expected event now, so the rs struct
// can forget about it.
rs->safetyBP = NULL;
ContinueThread (hthd);
return;
}
}
}
// Check if we are still within the range
if ((rs->addrStart > currAddr) ||
(currAddr > rs->addrEnd) ||
(PcSegOfHthdx(hthd) != rs->segCur)) {
// For thunking.
if (rs->SavedAddrStart) {
rs->addrStart = rs->SavedAddrStart;
rs->addrEnd = rs->SavedAddrEnd;
rs->segCur = rs->SavedSeg;
rs->SavedAddrStart = 0;
rs->SavedAddrEnd = 0;
rs->SavedSeg = 0;
}
}
if ((rs->addrStart <= currAddr) &&
(currAddr <= rs->addrEnd) &&
(PcSegOfHthdx(hthd) == rs->segCur)) {
// We still are in the range, continue stepping
// On Win95 if we try to step into a system call, the system
// forces execution to the instruction after the call and sends
// a single step. This is because it doesnt want user-mode
// debuggers to step into system code. In that case we are
// already at the safety bp and we should consume it.
if (IsChicago() && rs->safetyBP &&
(rs->stepFunction != (STEPPER)StepOver || rs->fGetReturnValue))
{
bp = FindBP(hprc, hthd, bptpExec, (BPNS)-1, &AddrPC, FALSE);
if (bp == rs->safetyBP) {
RemoveBP(rs->safetyBP);
rs->safetyBP = NULL;
}
if (rs->fGetReturnValue) {
rs->fGetReturnValue = FALSE;
}
}
AddrTmp = AddrPC;
IsCall(hthd, &AddrPC, &lpf, FALSE);
if (lpf != INSTR_IS_CALL) {
(rs->stepFunction)(hthd, rs->method, TRUE, FALSE);
} else {
// Get the function information for the call site,
// so we can tell when we get to the target whether
// it is a nested function.
ZeroMemory(&rs->CallSiteInfo, sizeof(FUNCTION_INFORMATION));
DMSendRequestReply(dbceGetFunctionInformation,
hpid,
htid,
sizeof(ADDR),
&AddrTmp,
sizeof(FUNCTION_INFORMATION),
&rs->CallSiteInfo
);
// Before we step into this function, lets
// put a "safety-net" breakpoint on the instruction
// after this call. This way if we don't have
// source for this function, we can always continue
// and break at this safety-net breakpoint.
assert(!rs->safetyBP);
rs->safetyBP = SetBP(hprc, hthd, bptpExec, bpnsStop, &AddrPC, (HPID)INVALID);
rs->fIsCall = TRUE;
#ifndef KERNEL
if (rs->stepFunction != (STEPPER)StepOver) {
// Are we stepping into ORPC code?
if (hthd->hprc->OrpcDebugging == ORPC_DEBUGGING) {
RegisterExpectedEvent (hthd->hprc,
hthd,
OLE_DEBUG_EVENT,
orpcClientGetBufferSize,
DONT_NOTIFY,
ActionOrpcClientGetBufferSize,
FALSE,
0
);
}
}
#endif // !KERNEL
SingleStep(hthd, rs->method, TRUE, FALSE);
}
} else {
// we have left the range.
// If we are at the safety bp && chicago && return values,
// we have attempted to step over a system call, failed,
// and ended up here. Remove the safety bp and continue on.
// If we are at an NLG_RETURN label, we need to continue on.
// If there is source here,
// if we are in a prolog
// run to the end of the prolog
// else
// stop.
// If we were in a thunk or a call was pending,
// we have either hit a thunk or a new function.
// if we hit a thunk
// set the range to cover the thunk,
// set the thunk flag
// and continue stepping.
// else
// run to the safety bp
// if there is no source and no safety bp, stop.
// Ask the debugger if we can step on this instruction
GetCanStep(hthd->hprc->hpid, hthd->htid, &AddrPC, &CanStep);
if (CanStep.Flags == CANSTEP_YES && !rs -> fGetReturnValue ) {
if (rs->safetyBP) {
RemoveBP(rs->safetyBP);
rs->safetyBP = NULL;
}
}
#ifndef KERNEL
if (CheckNLG(hthd->hprc, hthd, NLG_RETURN, &AddrPC)) {
// We should have just stepped over a ret instruction,
// there should be no safety BP's.
assert(rs->safetyBP == NULL);
SetupNLG(hthd, NULL);
ContinueThread (hthd);
} else
#endif // !KERNEL
if ((CanStep.Flags == CANSTEP_YES) && (rs->fSkipProlog) &&
(CanStep.PrologOffset > 0) && !rs -> fGetReturnValue ) {
// if there is a known prolog, run ahead to the end
ADDR Addr = AddrPC;
GetAddrOff(Addr) += CanStep.PrologOffset;
bp = SetBP(hprc, hthd, bptpExec, bpnsStop, &Addr, (HPID)INVALID);
assert(bp);
method = (METHOD*)MHAlloc(sizeof(METHOD));
*method = *rs->method;
method->lparam2 = (LPVOID)bp;
RegisterExpectedEvent(
hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
DONT_NOTIFY,
SSActionRemoveBP,
FALSE,
(UINT_PTR)method);
ContinueThread (hthd);
} else if ((CanStep.Flags == CANSTEP_THUNK) ||
(CanStep.Flags == CANSTEP_NO) &&
(rs->safetyBP || rs->fInThunk) || rs -> fGetReturnValue) {
// came from a call or thunk, and found no source.
rs->fInThunk = IsThunk(hthd, PC(hthd), NULL, NULL, &dwSize );
if (rs->fInThunk) {
// TODO what about THUNK_SYSTEM here??
// set new range and continue.
rs->SavedAddrStart = rs->addrStart;
rs->SavedAddrEnd = rs->addrEnd;
rs->SavedSeg = rs->segCur;
rs->addrStart = currAddr;
rs->addrEnd = currAddr + dwSize;
rs->segCur = PcSegOfHthdx(hthd);
(rs->stepFunction)(hthd, rs->method, TRUE, FALSE);
} else if (!rs->safetyBP) {
// came from a thunk, but there was no BP
assert(0);
} else {
if (rs -> fGetReturnValue) {
hthd -> fReturning = TRUE;
// Must the seg be set as well?
SE_SetAddrOff (&hthd -> addrFrom, currAddr);
}
// make the safety BP an expected event and run free.
method = (METHOD*)MHAlloc(sizeof(METHOD));
*method = *rs->method;
method->lparam2 = (LPVOID)rs->safetyBP;
RegisterExpectedEvent(
hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)rs->safetyBP,
DONT_NOTIFY,
(ACVECTOR) SSActionRemoveBP,
FALSE,
(DWORDLONG)method);
// The safety is the expected event now, so the rs struct
// can forget about it.
rs->safetyBP = NULL;
ContinueThread (hthd);
}
} else {
EndStep:
DEBUG_PRINT(" Ending range step\n");
// We are no longer in the range, free all consumable
// events on the queue for this thread
ConsumeAllThreadEvents(hthd, FALSE);
// Free the structures created for range-stepping
if (rs->safetyBP) {
RemoveBP(rs->safetyBP);
}
MHFree(rs->method);
MHFree(rs);
// Notify the EM that this thread has stopped on a SS
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
NotifyEM(&falseSSEvent, hthd, 0, 0);
}
}
return;
} /* MethodRangeStep */
void
WtPrintCallNode(
LPWTNODE wt
)
{
DWORD i;
static CHAR margin[] =
" ";
i = wt->lex*3;
if (i > 60) {
i = 60;
}
DMPrintShellMsg( "%4d %4d %*.*s%s\r\n",
wt->icnt,
wt->scnt,
i, i, margin,
wt->fname );
}
void WtGetSymbolName(HTHDX hthd, LPADDR lpaddr, LPSTR *lpFname, LPUOFFSET lpdwSymAddress, LPUOFFSET lpdwReturnAddress)
{
DMSYM DmSym;
__try {
DMSendRequestReply(dbceGetSymbolFromOffset,
hthd->hprc->hpid,
hthd->htid,
sizeof(ADDR),
lpaddr,
sizeof(DMSYM),
&DmSym
);
*lpFname = MHStrdup( DmSym.fname );
*lpdwSymAddress = GetAddrOff(DmSym.AddrSym);
*lpdwReturnAddress = DmSym.Ra;
} __except(EXCEPTION_EXECUTE_HANDLER) {
*lpFname = NULL;
*lpdwReturnAddress = 0;
*lpdwSymAddress = 0;
}
}
/*** WtMethodRangeStep
** Synopsis:
** Entry:
** Returns:
** Description:
** This method is called upon the receipt of a single step event
** while inside of a range step. It checks if the IP is still in the
** specified range, if it isnt then the EM is notified that the
** process has stopped outside the range, and all the RS structs and
** notification method are freed.
*/
void
WtMethodRangeStep(
DEBUG_EVENT64 *de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
{
RANGESTEP *rs = (RANGESTEP*)lparam;
UOFFSET currAddr = PC(hthd);
HPRCX hprc = hthd->hprc;
ADDR addr;
LPWTNODE wt;
LPWTNODE wt1;
UOFFSET ra;
UOFFSET symaddr;
AddrInit( &addr, 0, PcSegOfHthdx(hthd), currAddr,
hthd->fAddrIsFlat, hthd->fAddrOff32, FALSE, hthd->fAddrIsReal );
hthd->wtcurr->icnt++;
if (rs->fIsRet) {
rs->fIsRet = FALSE;
WtPrintCallNode( hthd->wtcurr );
if (hthd->wtcurr->caller) {
wt1 = hthd->wtcurr;
wt = wt1->caller;
wt->scnt += wt1->icnt + wt1->scnt;
wt->callee = NULL;
hthd->wtcurr = wt;
MHFree(wt1);
}
}
if (rs->addrEnd == 0 || currAddr == rs->addrEnd || hthd->wtmode == 2) {
// unwind the stack, print totals
wt = hthd->wtcurr;
while (wt) {
WtPrintCallNode( wt );
if (wt1 = wt->caller ) {
wt1->scnt += wt->icnt + wt->scnt;
MHFree(wt);
}
wt = wt1;
}
finished:
hthd->wtmode = 0;
ConsumeAllThreadEvents(hthd, FALSE);
MHFree(rs->method);
MHFree(rs);
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
NotifyEM(&falseSSEvent, hthd, 0, 0);
return;
}
if (rs->fIsCall) {
LPSTR p;
// we just completed a call instruction
// the pc is the first instruction of a new function
wt = MHAlloc( sizeof(WTNODE) );
ZeroMemory( wt, sizeof(WTNODE) );
hthd->wtcurr->callee = wt;
wt->caller = hthd->wtcurr;
wt->lex = hthd->wtcurr->lex + 1;
wt->offset = currAddr;
wt->sp = STACK_POINTER(hthd);
WtGetSymbolName( hthd, &addr, &p, &symaddr, &ra );
if (!p) {
p = MHAlloc( 16 );
sprintf( p, "0x%016I64x", currAddr );
} else if (symaddr != currAddr) {
DWORD l = _tcslen(p);
p = MHRealloc(p, l + 12);
sprintf(p + l, "+0x%I64x", currAddr - symaddr);
}
wt->fname = p;
// put new node at head of chain.
hthd->wtcurr = wt;
}
if (STACK_POINTER(hthd) > hthd->wtcurr->sp) {
// attempt to compensate for unwinds and longjumps.
// also catches cases that miss the target address.
// unwind the stack, print totals
wt = hthd->wtcurr;
while (wt && STACK_POINTER(hthd) > wt->sp) {
WtPrintCallNode( wt );
if (wt1 = wt->caller ) {
wt1->scnt += wt->icnt + wt->scnt;
MHFree(wt);
}
wt = wt1;
}
if (wt) {
hthd->wtcurr = wt;
} else {
hthd->wtcurr = &hthd->wthead;
goto finished;
}
}
rs->fIsCall = FALSE;
rs->fIsRet = IsRet(hthd, &addr);
if (!rs->fIsRet) {
int CallFlag;
IsCall( hthd, &addr, &CallFlag, FALSE );
if (CallFlag == INSTR_IS_CALL) {
// we are about to trace a call instruction
rs->fIsCall = TRUE;
WtPrintCallNode( hthd->wtcurr );
}
}
SingleStep( hthd, rs->method, TRUE, FALSE );
return;
} /* WtMethodRangeStep() */
void
WtRangeStep(
HTHDX hthd
)
/*++
Routine Description:
This function is used to implement the watch trace feature in the DM. Range
stepping is used to cause all instructions between a pair of addresses
to be executed.
The segment is implied to be the current segment. This is validated
in the EM.
Range stepping is done by registering an expected debug event at the
end of a step and seeing if the current program counter is still in
the correct range. If it is not then the range step is over, if it
is then a new event is register and we loop.
Arguments:
hthd - Supplies the thread to be stepped.
Return Value:
None.
--*/
{
RANGESTEP *rs;
METHOD *method;
HPRCX hprc = hthd->hprc;
int CallFlag = 0;
ADDR addr;
LPSTR fname;
UOFFSET ra;
UOFFSET symaddr;
UOFFSET instrOff;
if (hthd->wtmode != 0) {
DMPrintShellMsg( "wt command already running for this thread\r\n" );
return;
}
AddrInit( &addr,
0,
PcSegOfHthdx(hthd),
PC(hthd),
hthd->fAddrIsFlat,
hthd->fAddrOff32,
FALSE,
hthd->fAddrIsReal
);
WtGetSymbolName( hthd, &addr, &fname, &symaddr, &ra );
// Create and fill a range step structure
rs = (RANGESTEP*) MHAlloc(sizeof(RANGESTEP));
ZeroMemory( rs, sizeof(RANGESTEP) );
// Create a notification method for this range step
method = (METHOD*) MHAlloc(sizeof(METHOD));
method->notifyFunction = (ACVECTOR)WtMethodRangeStep;
method->lparam = (UINT_PTR)rs;
rs->hthd = hthd;
rs->segCur = PcSegOfHthdx(hthd);
rs->method = method;
rs->safetyBP = NULL;
rs->stepFunction = NULL;
rs->addrStart = PC(hthd);
// always tell the watch stepper that the first instruction
// was a call. that way, it makes a frame for the place that
// we are returning to.
rs->fIsCall = TRUE;
hthd->wtcurr = &hthd->wthead;
ZeroMemory( hthd->wtcurr, sizeof(WTNODE) );
hthd->wtcurr->offset = PC(hthd);
hthd->wtcurr->sp = STACK_POINTER(hthd);
hthd->wtcurr->fname = fname;
hthd->wtmode = 1;
IsCall( hthd, &addr, &CallFlag, FALSE);
if (CallFlag == INSTR_IS_CALL) {
ra = GetAddrOff(addr);
}
rs->addrEnd = ra;
DMPrintShellMsg( "Tracing %s to return address %08x\r\n", fname, ra );
if (CallFlag == INSTR_IS_CALL) {
// This is a call instruction. Assume that we
// want to trace the function that is about to
// be called. The call instruction will be the
// only instruction counted in the current frame.
// Call the step over function to send notifications
// to the RangeStepper (NOT THE EM!)
SingleStep(hthd, method, TRUE, FALSE);
} else {
// tracing to return address.
// tell it that we just did a call so that a new
// frame will be pushed, leaving the current frame
// to contain this functions caller.
hthd->wtcurr->icnt = -1;
WtMethodRangeStep(&falseSSEvent, hthd, 0, (UINT_PTR)rs);
}
return;
} /* WtRangeStep() */
#ifndef KERNEL
BOOL
SetupNLG(
HTHDX hthd,
LPADDR lpaddr
)
{
HNLG hnlg = hnlgNull;
BOOL fRetVal = FALSE;
hthd->fStopOnNLG = TRUE;
while (NULL != (hnlg = LLNext ( hthd->hprc->llnlg, hnlg ))) {
LPNLG lpnlg = LLLock ( hnlg );
PBREAKPOINT bp = SetBP( hthd->hprc,
hthd,
bptpExec,
bpnsStop,
&lpnlg->addrNLGDispatch,
(HPID)INVALID);
RegisterExpectedEvent( hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)bp,
DONT_NOTIFY,
ActionNLGDispatch,
FALSE,
(UINT_PTR)bp);
fRetVal = TRUE;
LLUnlock ( hnlg );
}
if (lpaddr == NULL) {
SetAddrOff ( &hthd->addrStack, STACK_POINTER(hthd));
} else {
SetAddrOff ( &hthd->addrStack, GetAddrOff(*lpaddr) );
}
return( fRetVal );
}
#endif
void
ActionExceptionDuringStep(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
If an exception is hit while stepping then ask the EM for the addresses
of all possible catches and set SS breakpoints thereat.
Dolphin V3 may go to a new method that does not involve disassembly EH
registration nodes. See Dolphin 9036 and 8510 for details.
Arguments:
de - Current debug event
hthd - Thread where debug event occurred
Return Value:
none
--*/
{
DWORD subclass = de->u.Exception.ExceptionRecord.ExceptionCode;
DWORD firstChance = de->u.Exception.dwFirstChance;
XOSD xosd;
#if !defined(TARGET_i386)
DWORD size;
EXHDLR ExHdlr;
#endif
EXHDLR *pExHdlr = NULL;
if ((subclass == STATUS_SINGLE_STEP) || !firstChance) {
ProcessExceptionEvent(de, hthd);
return;
}
switch (ExceptionAction(hthd->hprc, subclass)) {
case efdNotify:
NotifyEM(de, hthd, 0, efdNotify);
break;
// Dont bother asking for handler addresses
case efdStop:
case efdCommand:
ProcessExceptionEvent(de, hthd);
return;
}
#if defined(TARGET_i386)
assert(lparam != 0);
// Walk the exception registration stack and get the catch locations
if ((pExHdlr = GetExceptionCatchLocations(hthd, (LPVOID)lparam)) != NULL) {
xosd = xosdNone;
} else {
xosd = xosdGeneral; /* so we dont instantiate bps below*/
}
#else // TARGET_i386
/* Ask the EM for catch locations */
xosd = DMSendRequestReply(dbceExceptionDuringStep,
hthd->hprc->hpid,
hthd->htid,
0,
NULL,
sizeof(EXHDLR),
&ExHdlr
);
size = sizeof(EXHDLR) + ExHdlr.count * sizeof(ADDR);
pExHdlr = (EXHDLR *)DMCopyLargeReply(size);
#endif // TARGET_i386
if (xosd == xosdNone) {
DWORD i;
for (i=0; i<pExHdlr->count; i++) {
PBREAKPOINT bp = SetBP(hthd->hprc,
hthd,
bptpExec,
bpnsStop,
pExHdlr->addr+i,
0);
if (bp != NULL) {
METHOD * method = (METHOD *)MHAlloc(sizeof(METHOD));
method->lparam2 = (LPVOID) bp;
method->notifyFunction = ConsumeThreadEventsAndNotifyEM;
RegisterExpectedEvent(hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)bp,
DONT_NOTIFY,
(ACVECTOR) SSActionRemoveBP,
FALSE,
(UINT_PTR)method);
}
}
}
#if defined(TARGET_i386)
if (pExHdlr) {
MHFree(pExHdlr);
}
#endif // TARGET_i386
/* Re-enable myself */
RegisterExpectedEvent(hthd->hprc,
(HTHDX)hthd,
EXCEPTION_DEBUG_EVENT,
NO_SUBCLASS,
DONT_NOTIFY,
ActionExceptionDuringStep,
FALSE,
lparam);
ContinueThreadEx(hthd,
(ULONG)DBG_EXCEPTION_NOT_HANDLED,
QT_CONTINUE_DEBUG_EVENT,
ts_running);
}
/*** ISINSYSTEMDLL
* PURPOSE:
* Determine if the given uoffDest is in a system DLL
* INPUT:
* OUTPUT:
* Returns TRUE if uoffDest is in a system dll otherwise FALSE
* EXCEPTIONS:
* IMPLEMENTATION:
* This function takes a uoffset and determines if it is in the range of
* one of the system DLLs by examining the LPIAL list ( Pointer to
* invalid address List) that was built up in LoadDll.
*/
extern SYSTEM_INFO SystemInfo;
BOOL
IsInSystemDll (
UOFFSET uoffDest
)
{
#ifdef KERNEL
return FALSE;
#else // KERNEL
if (IsChicago()) {
// On Chicago, any address above the maximum application address
// is part of a system DLL.
return (uoffDest > (UOFFSET)SystemInfo.lpMaximumApplicationAddress);
} else {
// On NT, we return FALSE for *any* address, because system
// DLLs don't need special treatment.
return FALSE;
}
#endif // KERNEL
} /* ISINSYSTEMDLL */