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