#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; iCount; 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; iBpCount; 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; iBpCount; 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; icount; 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 */