#include "precomp.h" #pragma hdrstop SetFile() BREAKPOINT masterBP = {0L,0L}; PBREAKPOINT bpList = &masterBP; extern HTHDX thdList; extern CRITICAL_SECTION csThreadProcList; PBREAKPOINT GetNewBp( HPRCX hprc, HTHDX hthd, BPTP BpType, BPNS BpNotify, ADDR *AddrBp, HPID id, PBREAKPOINT BpUse ) /*++ Routine Description: Allocates a BREAKPOINT structure and initializes it. Note that this does NOT add the structure to the breakpoint list (bplist). If it is not an address bp (i.e. it is a watchpoint), the hwalk field must be initialized later. Arguments: hprc - Supplies process to put BP in hthd - Supplies optional thread AddrBp - Supplies address structure for the breakpoint id - Supplies EM id for BP BpUse - Supplies other BP on same address (so we can steal the original code from it instead of reading). This last one is optional. Return Value: PBREAKPOINT - Pointer to allocated and initialized structure. --*/ { PBREAKPOINT Bp; ADDR Addr; DWORD i; assert( !BpUse || ( BpUse->hthd != hthd ) ); Bp = (PBREAKPOINT)malloc(sizeof(BREAKPOINT)); assert( Bp ); if ( Bp ) { assert( bpList ); Bp->next = NULL; Bp->hprc = hprc; Bp->hthd = hthd; Bp->id = id; Bp->instances = 1; Bp->isStep = FALSE; Bp->hBreakPoint = 0; Bp->bpType = BpType; Bp->bpNotify = BpNotify; Bp->hWalk = NULL; memset(&Bp->addr, 0, sizeof(Bp->addr)); // // Get the opcode from the indicated address // if ( BpUse ) { Bp->instr1 = BpUse->instr1; } else if (AddrBp) { Bp->instr1 = 0; Addr = *AddrBp; Bp->addr = *AddrBp; // // Check to make sure that what we have is not a linker index // number in the address structure. If it is then we can not // currently set this breakpoint due to the fact that we // don't have a real address // if ( ADDR_IS_LI(Addr) ) { assert(!"Addr is LI"); free( Bp ); Bp = NULL; } else if (!AddrReadMemory(hprc, hthd, &Addr, &(Bp->instr1), BP_SIZE, &i) || (i != BP_SIZE)) { #ifdef KERNEL Bp->instr1 = 0; #else assert(!"AddrReadMemory failed"); free(Bp); Bp = NULL; #endif } } } return Bp; } PBREAKPOINT SetBP( HPRCX hprc, HTHDX hthd, BPTP bptype, BPNS bpnotify, LPADDR paddr, HPID id ) /*++ Routine Description: Set a breakpoint, or increment instance count on an existing bp. if hthd is non-NULL, BP is only for that thread. Arguments: hprc - Supplies process to put BP in hthd - Supplies optional thread bptype - Supplies OSDEBUG BP type paddr - Supplies address structure for the breakpoint id - Supplies EM id for BP Return Value: pointer to bp structure, or NULL for failure --*/ { PBREAKPOINT pbp; PBREAKPOINT pbpT; ADDR addr; ADDR addr2; if (!hprc) { return (PBREAKPOINT)NULL; } EnterCriticalSection(&csThreadProcList); /* * First let's try to find a breakpoint that * matches this description */ pbpT = FindBP(hprc, hthd, bptype, bpnotify, paddr, FALSE); /* * If this thread has a breakpoint here, * increment reference count. */ if (pbpT && pbpT->hthd == hthd) { pbp = pbpT; pbp->instances++; } else if ( pbp = GetNewBp( hprc, hthd, bptype, bpnotify, paddr, id, pbpT )) { if ( !pbpT ) { // // Now write the cpu-specific breakpoint code. // assert(!ADDR_IS_LI(pbp->addr)); if ( WriteBreakPoint(pbp) ) { AddBpToList(pbp); } else { free( pbp ); pbp = NULL; } } else { // // Breakpoint exists, but for wrong thread. // *pbp = *pbpT; pbp->hthd = hthd; pbp->instances = 1; } /* * Make it a linear address to start with */ addr2 = *paddr; TranslateAddress(hprc, hthd, &addr2, TRUE); /* * Check with the threads to see if we are at this address. If so then * we need to set the BP field so we don't hit the bp imeadiately */ if (hthd) { AddrFromHthdx(&addr, hthd); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &addr, &addr2)) { SetBPFlag(hthd, pbp); } } else { for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) { AddrFromHthdx(&addr, hthd); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &addr, &addr2)) { SetBPFlag(hthd, pbp); } } } } LeaveCriticalSection(&csThreadProcList); return pbp; } /* SetBP() */ #ifdef KERNEL BOOL SetBPEx( HPRCX hprc, HTHDX hthd, HPID id, DWORD Count, ADDR *Addrs, PBREAKPOINT *Bps, DWORD ContinueStatus ) /*++ Routine Description: Allocates a bunch of breakpoints from a given list of linear offsets. Arguments: hprc - Supplies process to put BP in hthd - Supplies optional thread Count - Supplies count of breakpoints to set Addrs - Supplies list with Count addresses Bps - Supplies buffer to be filled with Count pointers to BREAKPOINT structures. Original contents is overwritten. ContinueStatus - Return Value: BOOL - If TRUE, then ALL breakpoints were set. If FALSE, then NONE of the breakpoints were set. NOTENOTE - Not sure of what will happen if the list contains duplicated addresses! --*/ { PDBGKD_WRITE_BREAKPOINT DbgKdBp; PDBGKD_RESTORE_BREAKPOINT DbgKdBpRes; DWORD SetCount = 0; DWORD NewCount = 0; DWORD i; DWORD j; PBREAKPOINT BpT; BOOL Ok; ADDR Addr; ADDR Addr2; if (!hprc) { assert(!"hprc == NULL is SetBPEx"); return FALSE; } assert( Count > 0 ); assert( Addrs ); assert( Bps ); if ( Count == 1 ) { // // Only one breakpoint, faster to simply call SetBP // Bps[0] = SetBP( hprc, hthd, bptpExec, bpnsStop, &Addrs[0], id ); return ( Bps[0] != NULL ); } EnterCriticalSection(&csThreadProcList); AddrInit( &Addr, 0, 0, 0, TRUE, TRUE, FALSE, FALSE ); // // Allocate space for Count breakpoints // DbgKdBp = (PDBGKD_WRITE_BREAKPOINT) malloc( sizeof(DBGKD_WRITE_BREAKPOINT) * Count ); assert( DbgKdBp ); if ( !DbgKdBp ) { LeaveCriticalSection(&csThreadProcList); return FALSE; } for ( i=0; ihthd != hthd ) { BpT = Bps[i]; Bps[i] = GetNewBp( hprc, hthd, bptpExec, bpnsStop, &Addrs[i], id, NULL ); // BUGBUG kentf should instances be changed? *Bps[i] = *BpT; Bps[i]->hthd = hthd; } } Ok = TRUE; if ( NewCount > 0 ) { // // Set all new breakpoints // assert( NewCount <= Count ); Ok = WriteBreakPointEx( hthd, NewCount, DbgKdBp, ContinueStatus ); } if ( Ok ) { // // Fill in the breakpoint list // j = 0; for ( i=0; ihBreakPoint && Bps[i]->hthd == hthd ) { // // Will reuse BP, just increment reference count. // Bps[i]->instances++; } else { assert( (PVOID)GetAddrOff(Addrs[i]) == DbgKdBp[j].BreakPointAddress ); // // Allocate new BP structure and get handle from // the breakpoint packet. Note that we rely on the // order of the breakpoints in the breakpoint packet. // Bps[i]->hBreakPoint = DbgKdBp[j].BreakPointHandle; AddBpToList(Bps[i]); j++; } SetCount++; // // Check with the threads to see if we are at this address. // If so then we need to set the BP field so we don't hit // the bp imeadiately // Addr2 = Bps[i]->addr; if ( hthd ) { AddrFromHthdx( &Addr, hthd ); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &Addr, &Addr2 )) { SetBPFlag(hthd, Bps[i]); } } else { for (hthd=hprc->hthdChild; hthd; hthd = hthd->nextSibling) { AddrFromHthdx( &Addr, hthd ); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &Addr, &Addr2)) { SetBPFlag(hthd, Bps[i]); } } } } assert( j == NewCount ); } else { // // Clean up any breakpoints that were set. // DbgKdBpRes = (PDBGKD_RESTORE_BREAKPOINT) malloc( sizeof(DBGKD_RESTORE_BREAKPOINT) * NewCount ); assert( DbgKdBpRes ); if ( DbgKdBpRes ) { // // Put all breakpoints with a valid handle on the list of // breakpoints to be removed. // j = 0; for ( i=0; i 0 ) { assert( j <= NewCount ); RestoreBreakPointEx( j, DbgKdBpRes ); } free( DbgKdBpRes ); // // Remove allocated BP structures // for ( i=0; ihBreakPoint ) { assert( !Bps[i]->next ); free( Bps[i] ); Bps[i] = NULL; } } } } free( DbgKdBp ); LeaveCriticalSection(&csThreadProcList); return (SetCount == Count); } #else // KERNEL BOOL SetBPEx( HPRCX hprc, HTHDX hthd, HPID id, DWORD Count, ADDR *Addrs, PBREAKPOINT *Bps, DWORD ContinueStatus ) /*++ Routine Description: Allocates a bunch of breakpoints from a given list of linear offsets. Arguments: hprc - Supplies process to put BP in hthd - Supplies optional thread Count - Supplies count of breakpoints to set Addrs - Supplies list with Count addresses Bps - Supplies buffer to be filled with Count pointers to BREAKPOINT structures. Original contents is overwritten. ContinueStatus - Return Value: BOOL - If TRUE, then ALL breakpoints were set. If FALSE, then NONE of the breakpoints were set. NOTENOTE - Not sure of what will happen if the list contains duplicated addresses! --*/ { DWORD SetCount = 0; DWORD NewCount = 0; DWORD i; DWORD j; DWORD k; ADDR Addr; ADDR Addr2; BP_UNIT opcode = BP_OPCODE; if (!hprc) { assert(!"hprc == NULL in SetBPEx"); return FALSE; } assert( Count > 0 ); assert( Addrs ); assert( Bps ); if ( Count == 1 ) { // // Only one breakpoint, faster to simply call SetBP // Bps[0] = SetBP( hprc, hthd, bptpExec, bpnsStop, &Addrs[0], id ); return ( Bps[0] != NULL ); } EnterCriticalSection(&csThreadProcList); for ( i=0; ihthd == hthd ) { // // Reuse this breakpoint // Bps[i]->instances++; assert( Bps[i]->instances > 1 ); } else { // // Get new breakpoint // Bps[i] = GetNewBp(hprc, hthd, bptpExec, bpnsStop, &Addrs[i], id, NULL); if ( !Bps[i] ) { assert(!"GetNewBp failed in SetBPEx"); break; } if ( !ADDR_IS_LI(Bps[i]->addr) ) { if ( !AddrWriteMemory(hprc, hthd, &Bps[i]->addr, (LPBYTE) &opcode, BP_SIZE, &j) ) { free( Bps[i] ); Bps[i] = NULL; assert(!"DbgWriteMemory failed in SetBPEx"); break; } } } } if ( i < Count ) { // // Something went wrong, will backtrack // assert(!"i < Count in SetBPEx"); for ( j=0; jinstances--; if ( Bps[j]->instances == 0 ) { if ( !ADDR_IS_LI(Bps[j]->addr) ) { AddrWriteMemory(hprc, hthd, &Bps[j]->addr, (LPBYTE) &Bps[j]->instr1, BP_SIZE, &k); } free( Bps[j] ); Bps[j] = NULL; } } } else { // // Add all the new breakpoints to the list // for ( i=0; iinstances == 1 ) { AddBpToList(Bps[i]); } // // Check with the threads to see if we are at this address. If so then // we need to set the BP field so we don't hit the bp imeadiately // Addr2 = Bps[i]->addr; if ( hthd ) { AddrFromHthdx( &Addr, hthd ); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &Addr, &Addr2 )) { SetBPFlag(hthd, Bps[i]); } } else { for (hthd=hprc->hthdChild; hthd; hthd = hthd->nextSibling) { AddrFromHthdx( &Addr, hthd ); if ((hthd->tstate & ts_stopped) && (AtBP(hthd) == NULL) && AreAddrsEqual(hprc, hthd, &Addr, &Addr2)) { SetBPFlag(hthd, Bps[i]); } } } } SetCount = Count; } LeaveCriticalSection(&csThreadProcList); return (SetCount == Count); } #endif // KERNEL BOOL BPInRange( HPRCX hprc, HTHDX hthd, PBREAKPOINT bp, LPADDR paddrStart, DWORD cb, LPDWORD offset, BP_UNIT * instr ) { ADDR addr1; ADDR addr2; /* * If the breakpoint has a Loader index address then we can not * possibly match it */ assert (!ADDR_IS_LI(*paddrStart) ); if (ADDR_IS_LI(bp->addr)) { return FALSE; } *offset = 0; /* * Now check for "equality" of the addresses. * * Need to include size of BP in the address range check. Since * the address may start half way through a breakpoint. */ if ((ADDR_IS_FLAT(*paddrStart) == TRUE) && (ADDR_IS_FLAT(bp->addr) == TRUE)) { if ((GetAddrOff(*paddrStart) - sizeof(BP_UNIT) + 1 <= GetAddrOff(bp->addr)) && (GetAddrOff(bp->addr) < GetAddrOff(*paddrStart) + cb)) { *offset = (DWORD) GetAddrOff(bp->addr) - (DWORD) GetAddrOff(*paddrStart); *instr = bp->instr1; return TRUE; } return FALSE; } /* * The two addresses did not start out as flat addresses. So change * them to linear addresses so that we can see if the addresses are * are really the same */ addr1 = *paddrStart; if (!TranslateAddress(hprc, hthd, &addr1, TRUE)) { return FALSE; } addr2 = bp->addr; if (!TranslateAddress(hprc, hthd, &addr2, TRUE)) { return FALSE; } if ((GetAddrOff(addr1) - sizeof(BP_UNIT) + 1 <= GetAddrOff(addr2)) && (GetAddrOff(addr2) < GetAddrOff(addr1) + cb)) { *offset = (DWORD) GetAddrOff(addr2) - (DWORD) GetAddrOff(addr1); *instr = bp->instr1; return TRUE; } return FALSE; } PBREAKPOINT FindBP( HPRCX hprc, HTHDX hthd, BPTP bptype, BPNS bpnotify, LPADDR paddr, BOOL fExact ) /*++ Routine Description: Find and return a pointer to a BP struct. Always returns a BP that matches hthd thread if one exists; if fExact is FALSE and there is no exact match, a BP matching only hprc and address will succeed. Arguments: hprc - Supplies process hthd - Supplies thread bptype - Supplies OSDEBUG BP type paddr - Supplies address fExact - Supplies TRUE if must be for a certain thread Return Value: pointer to BREAKPOINT struct, or NULL if not found. --*/ { PBREAKPOINT pbp; PBREAKPOINT pbpFound = NULL; ADDR addr; EnterCriticalSection(&csThreadProcList); /* * Pre-translate the address to a linear address */ addr = *paddr; TranslateAddress(hprc, hthd, &addr, TRUE); /* * Check for an equivalent breakpoint. Breakpoints will be equal if * * 1. The process is the same * 2. The addresses of the breakpoints are the same */ for (pbp=bpList->next; pbp; pbp=pbp->next) { if ((pbp->hprc == hprc) && AreAddrsEqual(hprc, hthd, &pbp->addr, &addr)) { pbpFound = pbp; if (pbp->hthd == hthd) { break; } } } LeaveCriticalSection(&csThreadProcList); if (!fExact || (pbpFound && pbpFound->hthd == hthd)) { return pbpFound; } else { return NULL; } } /* FindBP() */ PBREAKPOINT BPNextHprcPbp( HPRCX hprc, PBREAKPOINT pbp ) /*++ Routine Description: Find the next breakpoint for the given process after pbp. If pbp is NULL start at the front of the list, for a find first, find next behaviour. Arguments: hprc - Supplies the process handle to match breakpoints for pbp - Supplies pointer to breakpoint item to start searching after Return Value: NULL if no matching breakpoint is found else a pointer to the matching breakpoint --*/ { EnterCriticalSection(&csThreadProcList); if (pbp == NULL) { pbp = bpList->next; } else { pbp = pbp->next; } for ( ; pbp; pbp = pbp->next ) { if (pbp->hprc == hprc) { break; } } LeaveCriticalSection(&csThreadProcList); return pbp; } /* BPNextHprcPbp() */ PBREAKPOINT BPNextHthdPbp( HTHDX hthd, PBREAKPOINT pbp ) /*++ Routine Description: Find the next breakpoint for the given thread after pbp. If pbp is NULL start at the front of the list for find first, find next behaviour. Arguments: hthd - Supplies the thread handle to match breakpoints for pbp - Supplies pointer to breakpoint item to start searching after Return Value: NULL if no matching breakpoint is found else a pointer to the matching breakpoint --*/ { EnterCriticalSection(&csThreadProcList); if (pbp == NULL) { pbp = bpList->next; } else { pbp = pbp->next; } for ( ; pbp; pbp = pbp->next ) { if (pbp->hthd == hthd) { break; } } LeaveCriticalSection(&csThreadProcList); return pbp; } /* BPNextHthdPbp() */ BOOL RemoveBPHelper( PBREAKPOINT pbp, BOOL fRestore ) { PBREAKPOINT pbpPrev; PBREAKPOINT pbpCur; PBREAKPOINT pbpT; HTHDX hthd; BOOL rVal = FALSE; // // first, is it real? // if (!pbp || pbp == EMBEDDED_BP) { return FALSE; } EnterCriticalSection(&csThreadProcList); /* Decrement the instances counter */ if (--pbp->instances) { /* * BUGBUG: jimsch -- Feb 29 1993 * This piece of code is most likely incorrect. We need to * know if we are the DM freeing a breakpoint or the user * freeing a breakpoint before we clear the step bit. Otherwise * we may be in the following situation * * Set a thread specific breakpoint on an address * Step the thread so that the address is the destination is * where the step ends up (but it takes some time such * as over a function call) * Clear the thread specific breakpoint * * This will cause the step breakpoint to be cleared so we will * stop at the address instead of just continuing stepping. */ pbp->isStep = FALSE; LeaveCriticalSection(&csThreadProcList); return FALSE; } /* Search the list for the specified breakpoint */ for ( pbpPrev = bpList, pbpCur = bpList->next; pbpCur; pbpPrev = pbpCur, pbpCur = pbpCur->next) { if (pbpCur == pbp) { /* * Remove this bp from the list: */ pbpPrev->next = pbpCur->next; /* * see if there is another bp on the same address: */ pbpT = FindBP(pbpCur->hprc, pbpCur->hthd, pbpCur->bpType, pbpCur->bpNotify, &pbpCur->addr, FALSE); if (!pbpT && pbpCur->bpType == bptpExec) { /* * if this was the only one, put the * opcode back where it belongs. */ if ( fRestore ) { RestoreBreakPoint( pbpCur ); } } /* * Now we have to go through all the threads to see * if any of them are on this breakpoint and clear * the breakpoint indicator on these threads */ /* * Could be on any thread: */ /* * (We are already in the ThreadProcList critical section) */ for (hthd = thdList->next; hthd; hthd = hthd->next) { if (hthd->atBP == pbpCur) { hthd->atBP = pbpT; } } free(pbpCur); rVal = TRUE; break; } } LeaveCriticalSection(&csThreadProcList); return rVal; } BOOL RemoveBP( PBREAKPOINT pbp ) { return RemoveBPHelper( pbp, TRUE ); } #ifdef KERNEL BOOL RemoveBPEx( DWORD Count, PBREAKPOINT *Bps ) { PDBGKD_RESTORE_BREAKPOINT DbgKdBp; DWORD RestoreCount = 0; DWORD GoneCount = 0; DWORD i; PBREAKPOINT BpCur; PBREAKPOINT BpOther; assert( Count > 0 ); if ( Count == 1 ) { // // Only one breakpoint, its faster to simply call RemoveBP // return RemoveBP( Bps[0] ); } EnterCriticalSection(&csThreadProcList); DbgKdBp = (PDBGKD_RESTORE_BREAKPOINT)malloc( sizeof(DBGKD_RESTORE_BREAKPOINT) * Count ); assert( DbgKdBp ); if ( DbgKdBp ) { // // Find out what breakpoints we have to restore and put them in // the list. // for ( i=0; inext; BpCur; BpCur = BpCur->next) { if ( BpCur == Bps[i] ) { // // See if there is another bp on the same address. // for ( BpOther = bpList->next; BpOther; BpOther = BpOther->next ) { if ( (BpOther != BpCur) && AreAddrsEqual( BpCur->hprc, BpCur->hthd, &BpCur->addr, &BpOther->addr ) ) { break; } } if ( !BpOther ) { // // If this was the only one, put it in the list. // DbgKdBp[GoneCount++].BreakPointHandle = Bps[i]->hBreakPoint; } break; } } } // // Restore the breakpoints in the list. // if ( GoneCount > 0 ) { assert( GoneCount <= Count ); RestoreBreakPointEx( GoneCount, DbgKdBp ); } // // All breakpoints that were to be restored have been // restored, now go ahead and do the cleaning up stuff. // for ( i=0; i 0 ); for ( i=0; iatBP = bp; } PBREAKPOINT AtBP( HTHDX hthd ) { return hthd->atBP; } void ClearBPFlag( HTHDX hthd ) { hthd->atBP = NULL; } void RestoreInstrBP( HTHDX hthd, PBREAKPOINT bp ) /*++ Routine Description: Replace the instruction for a breakpoint. If it was not the debugger's BP, skip the IP past it. Arguments: hthd - Thread bp - breakpoint data Return Value: --*/ { // // Check if this is an embedded breakpoint // if (bp == EMBEDDED_BP) { // // It was, so there is no instruction to restore, // just increment the EIP // IncrementIP(hthd); return; } if (bp->hWalk) { // // This is really a hardware breakpoint. Let the // walk manager fix this. // ExprBPClearBPForStep(hthd); } else { // // Replace the breakpoint current in memory with the correct // instruction // RestoreBreakPoint( bp ); bp->hBreakPoint = 0; } return; } VOID DeleteAllBps( VOID ) /*++ Routine Description: Arguments: Return Value: --*/ { PBREAKPOINT pbp, bpn; EnterCriticalSection(&csThreadProcList); pbp = bpList->next; while (pbp) { bpn = pbp->next; if (bpn) { free( pbp ); } pbp = bpn; } bpList->next = NULL; bpList->hprc = NULL; LeaveCriticalSection(&csThreadProcList); } void AddBpToList( PBREAKPOINT pbp ) { assert(bpList); EnterCriticalSection(&csThreadProcList); pbp->next = bpList->next; bpList->next = pbp; LeaveCriticalSection(&csThreadProcList); } PBREAKPOINT FindBpForWalk( PVOID pWalk ) { PBREAKPOINT pbp; EnterCriticalSection(&csThreadProcList); pbp = bpList; while (pbp) { if (IsWalkInGroup(pbp->hWalk, pWalk)) { break; } pbp = pbp->next; } LeaveCriticalSection(&csThreadProcList); return pbp; } PBREAKPOINT SetWP( HPRCX hprc, HTHDX hthd, BPTP bptype, BPNS bpnotify, ADDR addr ) { return (PBREAKPOINT)0; }