sl@0: // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #include sl@0: #include sl@0: #include "d_rmd_breakpoints.h" sl@0: #include "d_process_tracker.h" sl@0: #include "d_rmd_stepping.h" sl@0: #include "rm_debug_kerneldriver.h" // needed to access DRM_DebugChannel sl@0: #include "rm_debug_driver.h" sl@0: #include "debug_utils.h" sl@0: #include "debug_logging.h" sl@0: sl@0: using namespace Debug; sl@0: sl@0: /* @internalTechnology sl@0: * sl@0: * Checks whether aAddress is correctly aligned for placing a breakpoint of sl@0: * cpu architecture aMode. sl@0: * sl@0: * @param aAddress - Virtual memory address to check sl@0: * @param aMode - The CPU architecture mode of the breakpoint to be placed at aAddress sl@0: * @return ETrue if aAddress is suitably aligned, EFalse otherwise. sl@0: */ sl@0: TBool D_RMD_Breakpoints::Aligned(TUint32 aAddress, Debug::TArchitectureMode aMode) sl@0: { sl@0: switch(aMode) sl@0: { sl@0: case Debug::EArmMode: sl@0: // ARM breakpoints must be 32-bit aligned (lower two bits must be zero) sl@0: if (aAddress & 0x3) sl@0: { sl@0: // Not 32-bit aligned. sl@0: return EFalse; sl@0: } sl@0: break; sl@0: case Debug::EThumbMode: sl@0: // Thumb breakpoints must be 16-bit aligned (low bit must be zero) sl@0: if (aAddress & 0x1) sl@0: { sl@0: // Not 16-bit aligned sl@0: return EFalse; sl@0: } sl@0: break; sl@0: case Debug::EThumb2EEMode: sl@0: // Thumb-EE instructions are half-word aligned. See ARM ARM DDI0406A, section A3.2 Alignment Support sl@0: // Note that some instructions need to be word-aligned, but this function does not know which ones. sl@0: // It may also depend on the System Control register U bit. sl@0: if (aAddress & 0x1) sl@0: { sl@0: // Not 16-bit aligned sl@0: return EFalse; sl@0: } sl@0: break; sl@0: default: sl@0: { sl@0: // No idea sl@0: return EFalse; sl@0: } sl@0: } sl@0: sl@0: // Must be OK sl@0: return ETrue; sl@0: }; sl@0: sl@0: /* @internalTechnology sl@0: * sl@0: * Returns the size of a breakpoint of architecture aMode in bytes sl@0: * sl@0: * @param aMode - The architure of the breakpoint sl@0: * @return The size of the breakpoints in bytes. 0 if un-recognised architecture. sl@0: */ sl@0: TInt D_RMD_Breakpoints::BreakSize(Debug::TArchitectureMode aMode) sl@0: { sl@0: switch(aMode) sl@0: { sl@0: case Debug::EArmMode: sl@0: { sl@0: return 4; sl@0: } sl@0: case Debug::EThumbMode: sl@0: { sl@0: return 2; sl@0: } sl@0: case Debug::EThumb2EEMode: sl@0: { sl@0: // Only needs to be two bytes in size. sl@0: return 2; sl@0: } sl@0: default: sl@0: { sl@0: // No idea sl@0: return 0; sl@0: } sl@0: } sl@0: }; sl@0: sl@0: /* @internalTechnology sl@0: * sl@0: * Checks whether two TBreakEntrys overlap sl@0: * sl@0: * @param aFirst - A TBreakEntry with valid iAddress and iMode fields. sl@0: * @param aSecond - A TBreakEntry with valid iAddress and iMode fields. sl@0: * @return ETrue if the aFirst and aSecond overlap or the overlap cannot be determined sl@0: * , EFalse otherwise sl@0: */ sl@0: TBool D_RMD_Breakpoints::BreakpointsOverlap(TBreakEntry& aFirst, TBreakEntry& aSecond) sl@0: { sl@0: TInt firstSize = BreakSize(aFirst.iMode); sl@0: TInt secondSize = BreakSize(aSecond.iMode); sl@0: sl@0: // Do we know the size of each breakpoint? sl@0: if ((firstSize <= 0) || (secondSize <= 0)) sl@0: { sl@0: // We don't know the size of the breakpoint, so assume they overlap sl@0: return ETrue; sl@0: } sl@0: sl@0: TInt firstStartAddress = aFirst.iAddress; sl@0: TInt secondStartAddress = aSecond.iAddress; sl@0: TInt firstEndAddress = firstStartAddress + firstSize - 1; sl@0: TInt secondEndAddress = secondStartAddress + secondSize - 1; sl@0: sl@0: // If second breakpoint is past the end of the first then we're ok sl@0: if(firstEndAddress < secondStartAddress) sl@0: { sl@0: return EFalse; sl@0: } sl@0: sl@0: // If first breakpoint is past the end of the second then we're ok sl@0: if(secondEndAddress < firstStartAddress) sl@0: { sl@0: return EFalse; sl@0: } sl@0: sl@0: // The breakpoints overlap sl@0: return ETrue; sl@0: } sl@0: sl@0: /* @internalTechnology sl@0: * sl@0: * Returns the breakpoint bitpattern to use for each architecture type sl@0: * sl@0: * @param aMode - the cpu architecture type sl@0: * @return The bit-pattern to use for the specified architecture, or 0 if unsupported. sl@0: */ sl@0: TUint32 D_RMD_Breakpoints::BreakInst(Debug::TArchitectureMode aMode) sl@0: { sl@0: switch(aMode) sl@0: { sl@0: case Debug::EArmMode: sl@0: { sl@0: return KArmBreakPoint; sl@0: } sl@0: case Debug::EThumbMode: sl@0: { sl@0: return KThumbBreakPoint; sl@0: } sl@0: case Debug::EThumb2EEMode: sl@0: { sl@0: return KT2EEBreakPoint; sl@0: } sl@0: default: sl@0: { sl@0: // No idea what the breakpoint should be sl@0: return 0; sl@0: } sl@0: } sl@0: }; sl@0: sl@0: /** sl@0: Constructor. Initialises its internal list of empty breakpoints. sl@0: */ sl@0: D_RMD_Breakpoints::D_RMD_Breakpoints(DRM_DebugChannel* aChannel) sl@0: : iBreakPointList(NUMBER_OF_TEMP_BREAKPOINTS, 0), sl@0: iNextBreakId(NUMBER_OF_TEMP_BREAKPOINTS), sl@0: iChannel(aChannel), sl@0: iInitialised(EFalse) sl@0: { sl@0: iBreakPointList.Reset(); sl@0: TBreakEntry emptyTempBreak; sl@0: sl@0: for (TInt i = 0; i < NUMBER_OF_TEMP_BREAKPOINTS; i++) sl@0: { sl@0: emptyTempBreak.iBreakId = i; sl@0: sl@0: if (KErrNone != iBreakPointList.Append(emptyTempBreak)) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::D_RMD_Breakpoints() - Error appending blank temp break entry"); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Destructor. Clears all the breakpoints in the system, deletes its internal list of breakpoints, sl@0: and closes the exclusivity semaphore. sl@0: */ sl@0: D_RMD_Breakpoints::~D_RMD_Breakpoints() sl@0: { sl@0: ClearAllBreakPoints(); sl@0: sl@0: // close the breakpoint list and free the memory associated with it sl@0: iBreakPointList.Close(); sl@0: sl@0: if (iLock) sl@0: iLock->Close(NULL); sl@0: } sl@0: sl@0: /** sl@0: Initialises the breakpoint list exclusion semaphore. This should be called once immediately after sl@0: the constructor. sl@0: sl@0: @return KErrNone if successful, one of the other system wide error codes otherwise. sl@0: */ sl@0: TInt D_RMD_Breakpoints::Init() sl@0: { sl@0: TInt err = KErrNone; sl@0: sl@0: // Only create a semaphore if we are not initialised sl@0: if(!iInitialised) sl@0: { sl@0: // Initialise the semaphore ensuring exclusive access to the breakpoint list sl@0: err = Kern::SemaphoreCreate(iLock, _L("RM_DebugBreakpointLock"), 1 /* Initial count */); sl@0: if (err == KErrNone) sl@0: { sl@0: iInitialised = ETrue; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: err = KErrNone; sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Public member function which sets a thread-specific breakpoint in the specified thread sl@0: and returns an opaque handle to the caller. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoSetBreak sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aBreakId - Reference to a TUint32 into which the function will return a unique breakpoint Id. sl@0: @param aThreadId - The thread Id in which to place the breakpoint sl@0: @param aAddress - Address to place the breakpoint sl@0: @param aMode - The cpu instruction set architecture type breakpoint (e.g. EArmMode or EThumbMode) sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoSetBreak(TInt32 &aBreakId, const TUint64 aId, const TBool aThreadSpecific, const TUint32 aAddress, const TArchitectureMode aMode) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoSetBreak(aBreakId, aId, aThreadSpecific, aAddress,aMode); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: /** sl@0: Private member function which sets a thread-specific breakpoint in the specified thread sl@0: and returns an opaque handle to the caller. sl@0: sl@0: @see DoSetBreak sl@0: sl@0: @param aBreakId - Reference to a TUint32 into which the function will return a unique breakpoint Id. sl@0: @param aThreadId - The thread Id in which to place the breakpoint sl@0: @param aAddress - Address to place the breakpoint sl@0: @param aMode - The cpu instruction set architecture type breakpoint (e.g. EArmMode or EThumbMode) sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoSetBreak(TInt32 &aBreakId, const TUint64 aId, const TBool aThreadSpecific, const TUint32 aAddress, const TArchitectureMode aMode) sl@0: { sl@0: LOG_MSG4("D_RMD_Breakpoints::priv_DoSetBreak(aThreadId = 0x%016lx, aAddress = 0x%08x, aMode = %d)",aId,aAddress,aMode); sl@0: sl@0: // EThumb2EEMode breakpoints are not supported sl@0: if (EThumb2EEMode == aMode) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - EThumb2EEMode breakpoints are not supported"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Check how many breakpoints we have in existence sl@0: if ((iBreakPointList.Count()+1) >= NUMBER_OF_MAX_BREAKPOINTS) sl@0: { sl@0: // Too many breakpoints are set! sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - Too many breakpoints set"); sl@0: return KErrOverflow; sl@0: } sl@0: sl@0: // check the alignment of the breakpoint sl@0: if (!Aligned(aAddress,aMode)) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - Unaligned address"); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: // make sure there is not already a breakpoint at this address sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: /* We need to check if the breakpoint overlaps the address at all, sl@0: * and this depends upon the size of the two breakpoints as well as sl@0: * their address. sl@0: */ sl@0: sl@0: // newInstSize = size in bytes of new breakpoint sl@0: TInt newInstSize = BreakSize(aMode); sl@0: if (newInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - Unknown architecture type for new breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // oldInstSize = size in bytes of the existing breakpoint sl@0: TInt oldInstSize = BreakSize(iBreakPointList[i].iMode); sl@0: if (oldInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - : Unknown architecture type of existing breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Overlap checking - temp is used as the new breakpoint description for checking purposes only sl@0: TBreakEntry temp; sl@0: sl@0: temp.iAddress = aAddress; sl@0: temp.iMode = aMode; sl@0: sl@0: // do they overlap? sl@0: if ( BreakpointsOverlap(temp,iBreakPointList[i]) ) sl@0: { sl@0: // Yes sl@0: if(iBreakPointList[i].iThreadSpecific && aThreadSpecific) sl@0: { sl@0: if(aId == iBreakPointList[i].iId) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - New thread specific breakpoint overlaps an existing thread specific breakpoint"); sl@0: return KErrAlreadyExists; sl@0: } sl@0: } sl@0: else if(!iBreakPointList[i].iThreadSpecific && aThreadSpecific) sl@0: { sl@0: DThread* thread = DebugUtils::OpenThreadHandle(aId); sl@0: if(!thread) sl@0: { sl@0: return KErrNotFound; sl@0: } sl@0: if(thread->iOwningProcess->iId == iBreakPointList[i].iId) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - New thread specific breakpoint overlaps an existing breakpoint"); sl@0: thread->Close(NULL); sl@0: return KErrAlreadyExists; sl@0: } sl@0: thread->Close(NULL); sl@0: } sl@0: else if(iBreakPointList[i].iThreadSpecific && !aThreadSpecific) sl@0: { sl@0: DThread* thread = DebugUtils::OpenThreadHandle(iBreakPointList[i].iId); sl@0: if(!thread) sl@0: { sl@0: return KErrNotFound; sl@0: } sl@0: if(thread->iOwningProcess->iId == aId) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - New breakpoint overlaps an existing thread specific breakpoint"); sl@0: thread->Close(NULL); sl@0: return KErrAlreadyExists; sl@0: } sl@0: thread->Close(NULL); sl@0: } sl@0: else // !iBreakPointList[i].iThreadSpecific && !aThreadSpecific sl@0: { sl@0: if(iBreakPointList[i].iId == aId) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - New breakpoint overlaps an existing breakpoint"); sl@0: return KErrAlreadyExists; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: // increment the break id sl@0: aBreakId = iNextBreakId++; sl@0: sl@0: // create the new breakpoint entry sl@0: TBreakEntry breakEntry(aBreakId, aId, aThreadSpecific, aAddress, aMode); sl@0: sl@0: TInt err = priv_DoEnableBreak(breakEntry, ETrue); sl@0: if (KErrNone != err) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - Could not enable the breakpoint"); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: err = iBreakPointList.Append(breakEntry); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoSetBreak() - Failed to append breakpoint"); sl@0: } sl@0: sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoSetBreak(breakId = 0x%08x) done",aBreakId); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Public member function which enables a previously set breakpoint. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoEnableBreak sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: Note 3 sl@0: Historically, this function accepted a reference to a TBreakEntry in the class' own sl@0: iBreakPointList. It now checks whether the reference is to an element of its own list, sl@0: or one invented by the caller. sl@0: sl@0: @param aEntry reference to a TBreakEntry datastructure describing the breakpoint to be re-enabled. sl@0: @param aSaveOldInstruction ETrue preserves the instruction at the breakpoint address, EFalse otherwise. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoEnableBreak(TBreakEntry &aEntry, TBool aSaveOldInstruction) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoEnableBreak(aEntry,aSaveOldInstruction); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function which enables a previously set breakpoint, as per DoEnableBreak, but sl@0: does not serialise access. sl@0: sl@0: @see DoEnableBreak sl@0: sl@0: @param aEntry reference to a TBreakEntry datastructure describing the breakpoint to be re-enabled. sl@0: @param aSaveOldInstruction ETrue preserves the instruction at the breakpoint address, EFalse otherwise. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoEnableBreak(TBreakEntry &aEntry, TBool aSaveOldInstruction) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::DoEnableBreak()"); sl@0: sl@0: TUint32 inst = BreakInst(aEntry.iMode); sl@0: TInt instSize = BreakSize(aEntry.iMode); sl@0: if (instSize == 0 || inst == 0) sl@0: { sl@0: // not supported sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoEnableBreak - unsupported breakpoint architecture"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: TInt err = KErrNone; sl@0: sl@0: // Get thread id sl@0: TUint64 threadId = aEntry.iId + (aEntry.iThreadSpecific ? 0 : 1); sl@0: sl@0: DThread* threadObj = DebugUtils::OpenThreadHandle(threadId); sl@0: if (!threadObj) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoEnableBreak - bad handle. Could not identify a threadObj"); sl@0: return KErrBadHandle; sl@0: } sl@0: sl@0: if (aSaveOldInstruction) sl@0: { sl@0: TUint32 instruction; sl@0: sl@0: // read the instruction at the address so we can store it in the break entry for when we clear this breakpoint sl@0: // trap exceptions in case the address is invalid sl@0: XTRAPD(r, XT_DEFAULT, err = iChannel->TryToReadMemory(threadObj, (TAny *)aEntry.iAddress, (TAny *)&instruction, instSize)); sl@0: sl@0: //consider the leave as more important than the error code so store the leave if it's not KErrNone sl@0: if(KErrNone != r) sl@0: { sl@0: err = r; sl@0: } sl@0: sl@0: if(KErrNone != err) sl@0: { sl@0: threadObj->Close(NULL); sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoEnableBreak() - failed to read memory"); sl@0: return err; sl@0: } sl@0: sl@0: aEntry.iInstruction.Copy((TUint8 *)&instruction, instSize); sl@0: } sl@0: sl@0: TBool breakpointAlredySet = EFalse; sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: if(iBreakPointList[i].iAddress == aEntry.iAddress && !iBreakPointList[i].iDisabledForStep ) sl@0: { sl@0: breakpointAlredySet = ETrue; sl@0: break; sl@0: } sl@0: } sl@0: if(!breakpointAlredySet) sl@0: { sl@0: XTRAPD(r, XT_DEFAULT, err = DebugSupport::ModifyCode(threadObj, aEntry.iAddress, instSize, inst, DebugSupport::EBreakpointGlobal)); sl@0: if(r != DebugSupport::EBreakpointGlobal) sl@0: { sl@0: err = r; sl@0: } sl@0: } sl@0: sl@0: // Close the thread handle which has been opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Public member function which clears a previously set breakpoint. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoClearBreak sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aBreakId A breakpoint Id as previously returned by DoSetBreak. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoClearBreak(const TInt32 aBreakId, TBool aIgnoreTerminatedThreads) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoClearBreak(aBreakId, aIgnoreTerminatedThreads); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function which clears a previously set breakpoint, as per DoClearBreak, but sl@0: does not serialise access. sl@0: sl@0: @see DoClearBreak sl@0: sl@0: @param aBreakId A breakpoint Id as previously returned by DoSetBreak. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoClearBreak(const TInt32 aBreakId, TBool aIgnoreTerminatedThreads) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoClearBreak(0x%08x)",aBreakId); sl@0: sl@0: // find the break entry matching this id. note that the breakpoints are already sorted in ascending order by id sl@0: TBreakEntry entry; sl@0: entry.iBreakId = aBreakId; sl@0: TInt index = iBreakPointList.FindInSignedKeyOrder(entry); sl@0: sl@0: TInt err = KErrNone; sl@0: if (index >= 0) sl@0: { sl@0: //only let the agent clear the break if they have previously suspended the thread sl@0: //iThreadSpecific value decides whether the the iBreakPointList.Id has a thread id(TID) or the process id(PID) sl@0: //the assumption here that TID = PID + 1 sl@0: if(!TheDProcessTracker.CheckSuspended((iBreakPointList[index].iId + (iBreakPointList[index].iThreadSpecific ? 0 : 1)))) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoClearBreak() - Thread with id 0x%08x not suspended", iBreakPointList[index].iId); sl@0: // should be "return KErrInUse;" but not always possible, e.g. cleaning up threads which die after debugger disconnects sl@0: } sl@0: // if this breakpoint was set in a library and that library has already been unloaded, don't try to clear it sl@0: if (!iBreakPointList[index].iObsoleteLibraryBreakpoint) sl@0: { sl@0: DThread* threadObj = DebugUtils::OpenThreadHandle(iBreakPointList[index].iId + (iBreakPointList[index].iThreadSpecific ? 0 : 1)); sl@0: if (threadObj) sl@0: { sl@0: TBool needToCallCodeModifier = ETrue; sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: if (i != index) sl@0: { sl@0: if ( BreakpointsOverlap(iBreakPointList[index],iBreakPointList[i]) ) sl@0: { sl@0: needToCallCodeModifier = EFalse; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: if(needToCallCodeModifier) sl@0: { sl@0: XTRAPD(r, XT_DEFAULT, err = DebugSupport::RestoreCode(threadObj, iBreakPointList[index].iAddress)); sl@0: if (r != KErrNone) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoClearBreak() - restore code trap harness returned error %d",r); sl@0: } sl@0: sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoClearBreak() - restore code returned error %d",err); sl@0: } sl@0: err = (KErrNone == r) ? err : r; sl@0: } sl@0: sl@0: // Close the thread handle opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: } sl@0: else sl@0: { sl@0: err = KErrBadHandle; sl@0: } sl@0: } sl@0: sl@0: LOG_MSG4("D_RMD_Breakpoints::priv_DoClearBreak() - Clearing breakpoint at address: %x, err: %d, ignore terminated: %d", iBreakPointList[index].iAddress, err, aIgnoreTerminatedThreads?1:0); sl@0: if ((aIgnoreTerminatedThreads && KErrBadHandle == err) || KErrNone == err) sl@0: { sl@0: // if this is a temp breakpoint, just clear out the values, otherwise remove it from the list sl@0: err = KErrNone; sl@0: if (index < NUMBER_OF_TEMP_BREAKPOINTS) sl@0: { sl@0: iBreakPointList[index].Reset(); sl@0: } sl@0: else sl@0: { sl@0: LOG_MSG3("D_RMD_Breakpoints::priv_DoClearBreak() - Removing breakpoint 0x%08x as breakid 0x%08x\n",index, entry.iBreakId); sl@0: iBreakPointList.Remove(index); sl@0: } sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoClearBreak() - Break Id %d not found", aBreakId); sl@0: sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: Public member function which modifies a previously set breakpoint. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoModifyBreak sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aBreakInfo A TModifyBreakInfo describing the breakpoint properties that are wanted. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoModifyBreak(TModifyBreakInfo* aBreakInfo) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoModifyBreak(aBreakInfo); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function which modifies a previously set breakpoint, as per DoModifyBreak, but sl@0: does not serialise access. sl@0: sl@0: @see DoModifyBreak sl@0: sl@0: @param aBreakInfo A TModifyBreakInfo describing the breakpoint properties that are wanted. sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoModifyBreak(TModifyBreakInfo* aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak()"); sl@0: sl@0: // Check arguments sl@0: if (!aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak() was passed a NULL argument"); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: //User side memory is not accessible directly sl@0: TSetBreakInfo info; sl@0: TInt err = Kern::ThreadRawRead(iChannel->iClientThread, aBreakInfo, (TUint8*)&info, sizeof(TSetBreakInfo)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak() was passed a bad argument"); sl@0: return err; sl@0: } sl@0: sl@0: // EThumb2EEMode breakpoints are not supported sl@0: if (EThumb2EEMode == info.iMode) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak() - EThumb2EEMode breakpoints are not supported"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // find the break entry matching this id. note that the breakpoints are already sorted in ascending order by id sl@0: TBreakEntry entry; sl@0: entry.iBreakId = (TUint32)info.iBreakId; sl@0: TInt index = iBreakPointList.FindInSignedKeyOrder(entry); sl@0: if (index < 0) sl@0: { sl@0: // Could not find the breakpoint sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyBreak() - Could not find the breakpoint id 0x%08x",(TUint32)info.iBreakId); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: //assert that the thread we're moving the break from is suspended sl@0: if(!TheDProcessTracker.CheckSuspended(iBreakPointList[index].iId)) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyBreak() - Thread with id 0x%08x not suspended", iBreakPointList[index].iId); sl@0: return KErrInUse; sl@0: } sl@0: sl@0: //assert that the thread we're moving the break to is suspended sl@0: if(!TheDProcessTracker.CheckSuspended(info.iId)) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyBreak() - Thread with id 0x%08x not suspended", info.iId); sl@0: return KErrInUse; sl@0: } sl@0: sl@0: // first check its not obsolete sl@0: if (!iBreakPointList[index].iObsoleteLibraryBreakpoint) sl@0: { sl@0: // its still a valid breakpoint sl@0: sl@0: // remove the old breakpoint sl@0: DThread* threadObj = DebugUtils::OpenThreadHandle(iBreakPointList[index].iId); sl@0: if (threadObj) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyBreak - Unsetting breakpoint at address 0x%08x",iBreakPointList[index].iAddress); sl@0: sl@0: XTRAPD(r, XT_DEFAULT, err = DebugSupport::RestoreCode(threadObj, iBreakPointList[index].iAddress)); sl@0: if (r != 0) sl@0: { sl@0: LOG_MSG("Failed to construct trap handler for DebugSupport::RestoreCode"); sl@0: } sl@0: sl@0: // Close the thread handle which has been opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: } sl@0: else sl@0: { sl@0: // Bad handle sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak - Could not identify the breakpoint thread id"); sl@0: return KErrBadHandle; sl@0: } sl@0: } sl@0: sl@0: // make sure there is not already a breakpoint at the new address sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: // Ignore data for the breakpoint entry being modified. sl@0: if (i != index) sl@0: { sl@0: /* We need to check if the breakpoint overlaps the address at all, sl@0: * and this depends upon the size of the two breakpoints as well as sl@0: * their address. sl@0: */ sl@0: sl@0: // newInstSize = size in bytes of new breakpoint sl@0: TInt newInstSize = BreakSize(info.iMode); sl@0: if (newInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak - Unknown architecture type for new breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // oldInstSize = size in bytes of the existing breakpoint sl@0: TInt oldInstSize = BreakSize(iBreakPointList[i].iMode); sl@0: if (oldInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak - Unknown architecture type of existing breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Overlap checking - temp is used as the new breakpoint description for checking purposes only sl@0: TBreakEntry temp; sl@0: sl@0: temp.iAddress = info.iAddress; sl@0: temp.iMode = info.iMode; sl@0: sl@0: // do they overlap? sl@0: if ( BreakpointsOverlap(temp,iBreakPointList[i]) ) sl@0: { sl@0: // Yes sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak() - New breakpoint overlaps an existing breakpoint"); sl@0: return KErrAlreadyExists; sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Prepare iBreakPointList[index] with the new information, then set the breakpoint sl@0: iBreakPointList[index].iId = info.iId; sl@0: iBreakPointList[index].iAddress = info.iAddress; sl@0: iBreakPointList[index].iMode = info.iMode; sl@0: sl@0: TBreakEntry& newBreakEntry = iBreakPointList[index]; sl@0: sl@0: // Decide the size of the breakpoint instruction sl@0: TUint32 inst = BreakInst(newBreakEntry.iMode); sl@0: TInt instSize = BreakSize(newBreakEntry.iMode); sl@0: sl@0: if (inst == 0 || instSize == 0) sl@0: { sl@0: // Unsupported architecture sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak - unsupported breakpoint architecture"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: sl@0: //if thread id is 0xFFFFFFFF, then the breakpoint is not thread specific sl@0: if (newBreakEntry.iId != 0xFFFFFFFF) sl@0: { sl@0: newBreakEntry.iThreadSpecific = ETrue; sl@0: } sl@0: sl@0: // Get thread id from the process that we are debugging sl@0: TProcessInfo * proc = NULL; sl@0: TUint64 threadId = NULL; sl@0: sl@0: threadId = newBreakEntry.iId; sl@0: sl@0: DThread* threadObj = DebugUtils::OpenThreadHandle(threadId); sl@0: //if we don't have the right thread id for the address, sl@0: //then try with the thread id of the process that we are debugging sl@0: if (!threadObj && iChannel->iDebugProcessList.Count()) sl@0: { sl@0: proc = &iChannel->iDebugProcessList[0]; sl@0: if (proc) sl@0: { sl@0: threadId = proc->iId+1; sl@0: } sl@0: threadObj = DebugUtils::OpenThreadHandle(threadId); sl@0: } sl@0: sl@0: if(!threadObj) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyBreak() - bad handle. Could not identify a threadObj"); sl@0: return KErrBadHandle; sl@0: } sl@0: sl@0: // save the old instruction sl@0: TUint32 instruction; sl@0: sl@0: // read the instruction at the address so we can store it in the break entry for when we clear this breakpoint sl@0: // trap exceptions in case the address is invalid sl@0: XTRAPD(r, XT_DEFAULT, err = iChannel->TryToReadMemory(threadObj, (TAny *)newBreakEntry.iAddress, (TAny *)&instruction, instSize)); sl@0: sl@0: //consider the leave as more important than the error code so store the leave if it's not KErrNone sl@0: if(KErrNone != r) sl@0: { sl@0: err = r; sl@0: } sl@0: if(KErrNone != err) sl@0: { sl@0: threadObj->Close(NULL); sl@0: return err; sl@0: } sl@0: sl@0: newBreakEntry.iInstruction.Copy((TUint8 *)&instruction, instSize); sl@0: sl@0: newBreakEntry.iId = threadId; //set the thread ID here sl@0: LOG_MSG3("ModifyCode2 instSize:%d, inst: 0x%08x", instSize, inst); sl@0: XTRAPD(s, XT_DEFAULT, err = DebugSupport::ModifyCode(threadObj, newBreakEntry.iAddress, instSize, inst, DebugSupport::EBreakpointGlobal)); sl@0: if(s != DebugSupport::EBreakpointGlobal) sl@0: { sl@0: err = s; sl@0: } sl@0: sl@0: // Close the thread handle which has been opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: // sl@0: // D_RMD_Breakpoints::DoModifyProcessBreak sl@0: // sl@0: TInt D_RMD_Breakpoints::DoModifyProcessBreak(TModifyProcessBreakInfo* aBreakInfo) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoModifyProcessBreak(aBreakInfo); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: TInt D_RMD_Breakpoints::priv_DoModifyProcessBreak(TModifyProcessBreakInfo* aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak()"); sl@0: sl@0: // Check arguments sl@0: if (!aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() was passed a NULL argument"); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: //User side memory is not accessible directly sl@0: TSetBreakInfo info; sl@0: TInt err = Kern::ThreadRawRead(iChannel->iClientThread, aBreakInfo, (TUint8*)&info, sizeof(TModifyProcessBreakInfo)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() was passed a bad argument"); sl@0: return err; sl@0: } sl@0: sl@0: // EThumb2EEMode breakpoints are not supported sl@0: if (EThumb2EEMode == info.iMode) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - EThumb2EEMode breakpoints are not supported"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // find the break entry matching this id. note that the breakpoints are already sorted in ascending order by id sl@0: TBreakEntry entry; sl@0: entry.iBreakId = (TUint32)info.iBreakId; sl@0: TInt index = iBreakPointList.FindInSignedKeyOrder(entry); sl@0: if (index < 0) sl@0: { sl@0: // Could not find the breakpoint sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - Could not find the breakpoint id 0x%08x",(TUint32)info.iBreakId); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: // first check its not obsolete sl@0: if (!iBreakPointList[index].iObsoleteLibraryBreakpoint) sl@0: { sl@0: // its still a valid breakpoint sl@0: sl@0: // remove the old breakpoint sl@0: DProcess *process = DebugUtils::OpenProcessHandle(iBreakPointList[index].iId); sl@0: DThread* threadObj = NULL; sl@0: if(process) sl@0: { sl@0: threadObj = process->FirstThread(); sl@0: if(threadObj) sl@0: { sl@0: threadObj = DebugUtils::OpenThreadHandle(threadObj->iId); sl@0: } sl@0: process->Close(NULL); sl@0: } sl@0: sl@0: if (threadObj) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - Unsetting breakpoint at address 0x%08x",iBreakPointList[index].iAddress); sl@0: sl@0: XTRAPD(r, XT_DEFAULT, err = DebugSupport::RestoreCode(threadObj, iBreakPointList[index].iAddress)); sl@0: if (r != 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - Failed to construct trap handler for DebugSupport::RestoreCode"); sl@0: } sl@0: sl@0: // Close the thread handle which has been opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: } sl@0: else sl@0: { sl@0: // Bad handle sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - Could not identify the breakpoint process id"); sl@0: return KErrBadHandle; sl@0: } sl@0: } sl@0: sl@0: // make sure there is not already a breakpoint at the new address sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: // Ignore data for the breakpoint entry being modified. sl@0: if (i != index) sl@0: { sl@0: /* We need to check if the breakpoint overlaps the address at all, sl@0: * and this depends upon the size of the two breakpoints as well as sl@0: * their address. sl@0: */ sl@0: sl@0: // newInstSize = size in bytes of new breakpoint sl@0: TInt newInstSize = BreakSize(info.iMode); sl@0: if (newInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - Unknown architecture type for new breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // oldInstSize = size in bytes of the existing breakpoint sl@0: TInt oldInstSize = BreakSize(iBreakPointList[i].iMode); sl@0: if (oldInstSize == 0) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - : Unknown architecture type of existing breakpoint"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Overlap checking - temp is used as the new breakpoint description for checking purposes only sl@0: TBreakEntry temp; sl@0: sl@0: temp.iAddress = info.iAddress; sl@0: temp.iMode = info.iMode; sl@0: sl@0: // do they overlap? sl@0: if ( BreakpointsOverlap(temp,iBreakPointList[i]) ) sl@0: { sl@0: // Yes sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - New breakpoint overlaps an existing breakpoint"); sl@0: return KErrAlreadyExists; sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Prepare iBreakPointList[index] with the new information, then set the breakpoint sl@0: iBreakPointList[index].iId = info.iId; sl@0: iBreakPointList[index].iAddress = info.iAddress; sl@0: iBreakPointList[index].iMode = info.iMode; sl@0: sl@0: TBreakEntry& newBreakEntry = iBreakPointList[index]; sl@0: sl@0: // Decide the size of the breakpoint instruction sl@0: TUint32 inst = BreakInst(newBreakEntry.iMode); sl@0: TInt instSize = BreakSize(newBreakEntry.iMode); sl@0: sl@0: if (inst == 0 || instSize == 0) sl@0: { sl@0: // Unsupported architecture sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - unsupported breakpoint architecture"); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: newBreakEntry.iThreadSpecific = EFalse; sl@0: sl@0: DProcess* process = DebugUtils::OpenProcessHandle(newBreakEntry.iId); sl@0: if(!process) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoModifyProcessBreak() - bad handle. Could not identify a process"); sl@0: return KErrBadHandle; sl@0: } sl@0: sl@0: DThread* threadObj = process->FirstThread(); sl@0: if(threadObj) sl@0: { sl@0: threadObj = DebugUtils::OpenThreadHandle(threadObj->iId); sl@0: } sl@0: process->Close(NULL); sl@0: if(!threadObj) sl@0: { sl@0: return KErrNotFound; sl@0: } sl@0: // save the old instruction sl@0: TUint32 instruction; sl@0: sl@0: // read the instruction at the address so we can store it in the break entry for when we clear this breakpoint sl@0: // trap exceptions in case the address is invalid sl@0: XTRAPD(r, XT_DEFAULT, err = iChannel->TryToReadMemory(threadObj, (TAny *)newBreakEntry.iAddress, (TAny *)&instruction, instSize)); sl@0: sl@0: //consider the leave as more important than the error code so store the leave if it's not KErrNone sl@0: if(KErrNone != r) sl@0: { sl@0: err = r; sl@0: } sl@0: if(KErrNone != err) sl@0: { sl@0: threadObj->Close(NULL); sl@0: return err; sl@0: } sl@0: sl@0: newBreakEntry.iInstruction.Copy((TUint8 *)&instruction, instSize); sl@0: sl@0: XTRAPD(s, XT_DEFAULT, err = DebugSupport::ModifyCode(threadObj, newBreakEntry.iAddress, instSize, inst, DebugSupport::EBreakpointGlobal)); sl@0: if(s != DebugSupport::EBreakpointGlobal) sl@0: { sl@0: err = s; sl@0: } sl@0: sl@0: // Close the thread handle which has been opened by OpenThreadHandle sl@0: threadObj->Close(NULL); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Public member function which returns information about a previously set breakpoint. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoBreakInfo sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aBreakInfo Address of aBreakInfo structure in user-side memory within the DSS client thread. CAN ONLY BE ACCESSED VIA Kern::ThreadRawRead() sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoBreakInfo(TGetBreakInfo* aBreakInfo) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoBreakInfo(aBreakInfo); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function function which returns information about a previously set breakpoint.. sl@0: sl@0: @see DoBreakInfo sl@0: sl@0: @param aBreakInfo Address of aBreakInfo structure in user-side memory within the DSS client thread. CAN ONLY BE ACCESSED VIA Kern::ThreadRawRead() sl@0: @return KErrNone if successful, otherwise one of the other system wide error codes. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoBreakInfo(TGetBreakInfo* aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo()"); sl@0: sl@0: if (!aBreakInfo) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() was passed a NULL argument"); sl@0: sl@0: return KErrArgument; sl@0: } sl@0: sl@0: //User side memory is not accessible directly sl@0: TGetBreakInfo info; sl@0: TInt err = Kern::ThreadRawRead(iChannel->iClientThread, aBreakInfo, (TUint8*)&info, sizeof(TGetBreakInfo)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() was passed a bad argument"); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: // find the break entry matching this id. note that the breakpoints are already sorted in ascending order by id sl@0: TBreakEntry entry; sl@0: entry.iBreakId = (TUint32)info.iBreakId; sl@0: TInt index = iBreakPointList.FindInSignedKeyOrder(entry); sl@0: sl@0: if (index >=0) sl@0: { sl@0: // get the thread id for this breakpoint sl@0: TUint64 threadId = iBreakPointList[index].iId; sl@0: sl@0: err = Kern::ThreadRawWrite(iChannel->iClientThread,(TUint8*)info.iId,&threadId,sizeof(TUint64)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() - failed to return breakpoint iThreadId information"); sl@0: return err; sl@0: } sl@0: sl@0: // get the threadSpecific-ness sl@0: TBool threadSpecific = iBreakPointList[index].iThreadSpecific; sl@0: sl@0: err = Kern::ThreadRawWrite(iChannel->iClientThread,(TUint8*)info.iThreadSpecific,&threadSpecific,sizeof(TBool)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() - failed to return thread specific information"); sl@0: return err; sl@0: } sl@0: sl@0: sl@0: // get the address sl@0: TUint32 address = iBreakPointList[index].iAddress; sl@0: sl@0: err = Kern::ThreadRawWrite(iChannel->iClientThread,(TUint8*)info.iAddress,&address,sizeof(TUint32)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() - failed to return breakpoint iAddress information"); sl@0: return err; sl@0: } sl@0: sl@0: sl@0: // get the architecture sl@0: TArchitectureMode mode = iBreakPointList[index].iMode; sl@0: sl@0: err = Kern::ThreadRawWrite(iChannel->iClientThread,(TUint8*)info.iMode,&mode,sizeof(TUint32)); sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoBreakInfo() - failed to return breakpoint iMode information"); sl@0: return err; sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoBreakInfo - Could not find the breakpoint id specified 0x%08x", entry.iBreakId); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: Public member function which clears all the breakpoints in the system. Generally used for shutting down sl@0: the debug device driver. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_ClearAllBreakPoints sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: */ sl@0: void D_RMD_Breakpoints::ClearAllBreakPoints() sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: priv_ClearAllBreakPoints(); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: sl@0: /** sl@0: Private member function which clears all the breakpoints in the system. Generally used for shutting down sl@0: the debug device driver. sl@0: sl@0: @see DoClearAllBreakPoints sl@0: */ sl@0: void D_RMD_Breakpoints::priv_ClearAllBreakPoints() sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_ClearAllBreakPoints()"); sl@0: sl@0: TInt err = KErrNone; sl@0: sl@0: for (TInt i=0; iClose(NULL); sl@0: } sl@0: else sl@0: { sl@0: err = KErrBadHandle; sl@0: } sl@0: sl@0: if (KErrNone != err) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_ClearAllBreakPoints() - Error 0x%08x while clearing breakpoint", err); sl@0: } sl@0: } sl@0: } sl@0: sl@0: iBreakPointList.Reset(); sl@0: } sl@0: sl@0: /** sl@0: Public member function which disables the breakpoint at the specified address. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DisableBreakAtAddress sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aAddress Address at which to disable breakpoints (all threads) sl@0: @return KErrNone if successful, one of the other system wide error codes otherwise. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DisableBreakAtAddress(TUint32 aAddress) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DisableBreakAtAddress(aAddress); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function which clears all the breakpoints in the system. Generally used for shutting down sl@0: the debug device driver. sl@0: sl@0: @see DisableBreakAtAddress sl@0: sl@0: @param aAddress clears the breakpoint at the specified address sl@0: @return KErrNone if successful, one of the other system wide error codes otherwise. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DisableBreakAtAddress(TUint32 aAddress) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DisableBreakAtAddress()"); sl@0: sl@0: TInt err = KErrNone; sl@0: sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: if (iBreakPointList[i].iAddress == aAddress) sl@0: { sl@0: iBreakPointList[i].iDisabledForStep = ETrue; sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DisableBreakAtAddress - Disabling breakpoint at address 0x%x", iBreakPointList[i].iAddress); sl@0: sl@0: //clear the breakpoint with code modifier sl@0: //code modifier will restore the org instruction and also frees the shadow page if necessary sl@0: TUint64 id = iBreakPointList[i].iId + (iBreakPointList[i].iThreadSpecific ? 0 : 1); sl@0: DThread* threadObj = NULL; sl@0: if(iBreakPointList[i].iThreadSpecific) sl@0: { sl@0: threadObj = DebugUtils::OpenThreadHandle(id); sl@0: } sl@0: else sl@0: { sl@0: DProcess *process = DebugUtils::OpenProcessHandle(iBreakPointList[i].iId); sl@0: if(process) sl@0: { sl@0: threadObj = process->FirstThread(); sl@0: if(threadObj) sl@0: { sl@0: if(KErrNone != threadObj->Open()) sl@0: { sl@0: LOG_MSG("Couldn't open threadObj"); sl@0: threadObj = NULL; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: LOG_MSG("threadObj is NULL"); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: LOG_MSG("Process is NULL"); sl@0: } sl@0: } sl@0: if (threadObj) sl@0: { sl@0: XTRAPD(r, XT_DEFAULT, err = DebugSupport::RestoreCode(threadObj, aAddress)); sl@0: if(KErrNone != err || KErrNone != r) sl@0: { sl@0: LOG_MSG3("Error from DebugSupport::RestoreCode: r: %d, err: %d", r, err); sl@0: } sl@0: err = (KErrNone == r) ? err : r; sl@0: threadObj->Close(NULL); sl@0: } sl@0: else sl@0: { sl@0: err = KErrBadHandle; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Public member function which enables previously disabled breakpoints within a given thread. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoEnableDisabledBreak sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aThreadId Thread in which to enable all previously disabled breakpoints sl@0: @return KErrNone if successful, one of the system wide error codes otherwise. sl@0: */ sl@0: TInt D_RMD_Breakpoints::DoEnableDisabledBreak(TUint64 aThreadId) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TInt err = priv_DoEnableDisabledBreak(aThreadId); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Private member function which enables previously disabled breakpoints within a given thread. sl@0: sl@0: @see DoEnableDisabledBreak sl@0: sl@0: @param aThreadId Thread in which to enable all previously disabled breakpoints sl@0: @return KErrNone if successful, one of the system wide error codes otherwise. sl@0: */ sl@0: TInt D_RMD_Breakpoints::priv_DoEnableDisabledBreak(TUint64 aThreadId) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::priv_DoEnableDisabledBreak()"); sl@0: DThread* thread = DebugUtils::OpenThreadHandle(aThreadId); sl@0: if(!thread) sl@0: { sl@0: LOG_MSG2("Thread: 0x%08x does not exist", aThreadId); sl@0: return KErrNotFound; sl@0: } sl@0: TUint64 processId = thread->iOwningProcess->iId; sl@0: thread->Close(NULL); sl@0: sl@0: for (TInt i = NUMBER_OF_TEMP_BREAKPOINTS; i < iBreakPointList.Count(); i++) sl@0: { sl@0: TBool needsEnabling = EFalse; sl@0: if(iBreakPointList[i].iDisabledForStep) sl@0: { sl@0: if(iBreakPointList[i].iThreadSpecific) sl@0: { sl@0: needsEnabling = (aThreadId == iBreakPointList[i].iId); sl@0: } sl@0: else sl@0: { sl@0: needsEnabling = (processId == iBreakPointList[i].iId); sl@0: } sl@0: } sl@0: if (needsEnabling) sl@0: { sl@0: LOG_MSG2("Re-enabling breakpoint at address %x", iBreakPointList[i].iAddress); sl@0: TInt err = priv_DoEnableBreak(iBreakPointList[i], EFalse); sl@0: if(KErrNone != err) sl@0: { sl@0: LOG_MSG2("Error returned from DoEnableBreak: %d", err); sl@0: iBreakPointList[i].iDisabledForStep = EFalse; sl@0: } sl@0: return err; sl@0: } sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: Public member function which removes all the breakpoints within a given thread. sl@0: sl@0: Note 1: sl@0: This function ensures exclusive access to the breakpoint data structures sl@0: by using a semaphore to serialise access. sl@0: sl@0: @see priv_DoRemoveThreadBreaks sl@0: sl@0: Note 2: sl@0: As implied by Note 1, the caller must have previously called Init() or this sl@0: function will return KErrNotReady; sl@0: sl@0: @param aThreadId Thread from which to remove all existing breakpoints sl@0: @return KErrNone if successful, one of the system wide error codes otherwise. sl@0: */ sl@0: void D_RMD_Breakpoints::DoRemoveThreadBreaks(TUint64 aThreadId) sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: priv_DoRemoveThreadBreaks(aThreadId); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: sl@0: /** sl@0: Private member function which removes all the breakpoints particular to a particular thread sl@0: sl@0: @see DoRemoveThreadBreaks sl@0: sl@0: @param aThreadId Thread from which to remove all existing breakpoints sl@0: @return KErrNone if successful, one of the system wide error codes otherwise. sl@0: */ sl@0: void D_RMD_Breakpoints::priv_DoRemoveThreadBreaks(TUint64 aThreadId) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoRemoveThreadBreaks(aThreadId = 0x%016lx)\n",aThreadId); sl@0: sl@0: TInt err = KErrNone; sl@0: TUint64 threadId; sl@0: sl@0: for (TInt i=iBreakPointList.Count()-1; i >= 0; i--) sl@0: { sl@0: if ((iBreakPointList[i].iAddress != 0) && !iBreakPointList[i].iObsoleteLibraryBreakpoint) sl@0: { sl@0: threadId = iBreakPointList[i].iId + (iBreakPointList[i].iThreadSpecific ? 0 : 1); sl@0: if (threadId == aThreadId) sl@0: { sl@0: LOG_MSG4("D_RMD_Breakpoints::priv_DoRemoveThreadBreaks() - Clearing breakpoint at address 0x%08x for thread id 0x%016lx with id 0x%08x", iBreakPointList[i].iAddress, iBreakPointList[i].iId, iBreakPointList[i].iBreakId); sl@0: sl@0: err = priv_DoClearBreak(iBreakPointList[i].iBreakId, EFalse); sl@0: sl@0: if (err != KErrNone) sl@0: { sl@0: LOG_MSG2("D_RMD_Breakpoints::priv_DoRemoveThreadBreaks() - failed to remove break id 0x%08x\n",iBreakPointList[i].iBreakId); sl@0: return; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Remove the process breakpoints for process with PID aProcessId in the range [aCodeAddress, aCodeAddress + aCodeSize) sl@0: void D_RMD_Breakpoints::RemoveBreaksForProcess(TUint64 aProcessId, TUint32 aCodeAddress, TUint32 aCodeSize) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::RemoveBreaksForProcess()"); sl@0: for (TInt i=iBreakPointList.Count() - 1; i>=0; i--) sl@0: { sl@0: TBreakEntry& breakEntry = iBreakPointList[i]; sl@0: if(!breakEntry.iThreadSpecific && breakEntry.iId == aProcessId) sl@0: { sl@0: if ((breakEntry.iAddress >= aCodeAddress) && (breakEntry.iAddress < (aCodeAddress + aCodeSize))) sl@0: { sl@0: LOG_MSG2("Removing process breakpoint at address %x", (TUint32)breakEntry.iAddress); sl@0: TInt err = DoClearBreak(breakEntry.iBreakId, ETrue); sl@0: if(KErrNone != err) sl@0: { sl@0: LOG_MSG2("Error removing process breakpoint: %d", err); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: // mark the breakpoints in the range [aCodeAddress, aCodeAddress + aCodeSize) sl@0: void D_RMD_Breakpoints::InvalidateLibraryBreakPoints(TUint32 aCodeAddress, TUint32 aCodeSize) sl@0: { sl@0: LOG_MSG("D_RMD_Breakpoints::InvalidateLibraryBreakPoints()"); sl@0: for (TInt i=0; i= aCodeAddress) && (iBreakPointList[i].iAddress < (aCodeAddress + aCodeSize))) sl@0: { sl@0: LOG_EVENT_MSG2("Disabling library breakpoint at address %x", iBreakPointList[i].iAddress); sl@0: iBreakPointList[i].iObsoleteLibraryBreakpoint = ETrue; sl@0: } sl@0: } sl@0: } sl@0: sl@0: TInt D_RMD_Breakpoints::BreakPointCount() const sl@0: { sl@0: return iBreakPointList.Count(); sl@0: } sl@0: sl@0: /** sl@0: Gets next breakpoint in list. sl@0: @param aBreakEntry The break entry to get the successor of. If NULL then returns the first entry. sl@0: @return A pointer to the next break entry, or NULL if the end of the list has been reached sl@0: */ sl@0: TBreakEntry* D_RMD_Breakpoints::GetNextBreak(const TBreakEntry* aBreakEntry) const sl@0: { sl@0: if(!aBreakEntry) sl@0: { sl@0: return (TBreakEntry*)&(iBreakPointList[0]); sl@0: } sl@0: TInt index = iBreakPointList.FindInSignedKeyOrder(*aBreakEntry) + 1; sl@0: return (index < BreakPointCount()) ? (TBreakEntry*)&(iBreakPointList[index]) : NULL; sl@0: } sl@0: sl@0: TBool D_RMD_Breakpoints::IsTemporaryBreak(const TBreakEntry& aBreakEntry) const sl@0: { sl@0: // Ensure we have a valid semaphore sl@0: if (!iInitialised || !iLock) sl@0: { sl@0: return EFalse; sl@0: } sl@0: sl@0: // Acquire the lock sl@0: NKern::ThreadEnterCS(); sl@0: Kern::SemaphoreWait(*iLock); sl@0: sl@0: // Really do the work sl@0: TBool tempBreak = priv_IsTemporaryBreak(aBreakEntry); sl@0: sl@0: // Release the lock sl@0: Kern::SemaphoreSignal(*iLock); sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: return tempBreak; sl@0: } sl@0: sl@0: /** sl@0: Private member function which tells us if a breakpoint is temporary sl@0: sl@0: @see IsTemporaryBreak sl@0: sl@0: @param aBreakEntry sl@0: @return TBool indicating if the break is temporary or not sl@0: */ sl@0: TBool D_RMD_Breakpoints::priv_IsTemporaryBreak(const TBreakEntry& aBreakEntry) const sl@0: { sl@0: return aBreakEntry.iBreakId < NUMBER_OF_TEMP_BREAKPOINTS; sl@0: } sl@0: sl@0: sl@0: // End of file - d_rmd_breakpoints.cpp