--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debugsrv/runmodedebug/rmdriver/src/rm_debug_kerneldriver.cpp Thu Sep 02 22:05:40 2010 +0300
@@ -0,0 +1,3530 @@
+// 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:
+// Device driver for kernel side debug assist
+//
+
+#ifdef __WINS__
+#error - this driver cannot be built for emulation
+#endif
+
+#include <e32def.h>
+#include <e32def_private.h>
+#include <e32cmn.h>
+#include <e32cmn_private.h>
+#include <e32ldr.h>
+#include <u32std.h>
+#include <kernel/kernel.h>
+#include <kernel/kern_priv.h>
+#include <nk_trace.h>
+#include <arm.h>
+#include <kernel/cache.h>
+#include <platform.h>
+#include <nkern.h>
+#include <u32hal.h>
+#include <rm_debug_api.h>
+
+#include "debug_logging.h"
+#include "d_rmd_breakpoints.h" // moved breakpoints code lives here
+#include "d_rmd_stepping.h" // moved stepping code lives here
+#include "rm_debug_kerneldriver.h"
+#include "d_list_manager.h"
+#include "rm_debug_driver.h"
+#include "rm_debug_eventhandler.h"
+#include "d_debug_functionality.h"
+#include "d_process_tracker.h"
+#include "debug_utils.h"
+
+using namespace Debug;
+
+/////////////////////////////////////////////////////////////////////////
+//
+// DRM_DebugDriverFactory implementation
+//
+/////////////////////////////////////////////////////////////////////////
+
+//
+// DRM_DebugDriverFactory constructor
+//
+DRM_DebugDriverFactory::DRM_DebugDriverFactory()
+ {
+ iVersion = TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
+ }
+
+//
+// DRM_DebugDriverFactory::Create
+//
+TInt DRM_DebugDriverFactory::Create(DLogicalChannelBase*& aChannel)
+ {
+ if (iOpenChannels != 0)
+ return KErrInUse; // a channel is already open
+
+ aChannel = new DRM_DebugChannel(this);
+
+ return aChannel ? KErrNone : KErrNoMemory;
+ }
+
+//
+// DRM_DebugDriverFactory::Install
+//
+TInt DRM_DebugDriverFactory::Install()
+ {
+ return(SetName(&KRM_DebugDriverName));
+ }
+
+//
+// DRM_DebugDriverFactory::Install
+//
+void DRM_DebugDriverFactory::GetCaps(TDes8& aDes) const
+ {
+ TCapsRM_DebugDriver b;
+ b.iVersion = TVersion(KMajorVersionNumber, KMinorVersionNumber, KBuildVersionNumber);
+
+ Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
+ }
+
+/////////////////////////////////////////////////////////////////////////
+//
+// DRM_DebugChannel implementation
+//
+/////////////////////////////////////////////////////////////////////////
+
+//
+// DRM_DebugChannel constructor
+//
+DRM_DebugChannel::DRM_DebugChannel(DLogicalDevice* aLogicalDevice)
+ : iExcludedROMAddressStart(ROM_LINEAR_BASE),
+ iExcludedROMAddressEnd(0),
+ iPageSize(0x1000),
+ iBreakManager(0),
+ iStepper(0),
+ iStepLock(0),
+ iDfcQ(NULL),
+ iInitialisedCodeModifier(0),
+ iAsyncGetValueRequest(NULL)
+ {
+ LOG_MSG("DRM_DebugChannel::DRM_DebugChannel()");
+
+ iDevice = aLogicalDevice;
+
+ iClientThread = &Kern::CurrentThread();
+
+ // Opening handle to current thread, so no need to check for return-value
+ (void)iClientThread->Open();
+
+ LOG_MSG3("DRM_DebugChannel::DRM_DebugChannel() clientThread = 0x%08x, id=%d",
+ iClientThread, iClientThread->iId );
+
+
+ iPageSize = Kern::RoundToPageSize(1);
+ }
+
+//
+// DRM_DebugChannel destructor
+//
+DRM_DebugChannel::~DRM_DebugChannel()
+ {
+ LOG_MSG("DRM_DebugChannel::~DRM_DebugChannel()");
+
+ if (iAsyncGetValueRequest)
+ {
+ Kern::QueueRequestComplete(iClientThread, iAsyncGetValueRequest, KErrCancel); // does nothing if request not pending
+ Kern::DestroyClientRequest(iAsyncGetValueRequest);
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::SafeClose((DObject*&)iClientThread, NULL);
+ NKern::ThreadLeaveCS();
+
+ // Close breakpoint manager
+ if (iBreakManager)
+ {
+ NKern::ThreadEnterCS();
+ delete iBreakManager;
+ NKern::ThreadLeaveCS();
+ }
+
+ // Close stepping manager
+ if (iStepper)
+ {
+ NKern::ThreadEnterCS();
+ delete iStepper;
+ NKern::ThreadLeaveCS();
+ }
+
+ //close the debug process list
+ iDebugProcessList.Close();
+
+ DestroyDfcQ();
+
+ //close the code modifier
+ if (iInitialisedCodeModifier)
+ {
+ DebugSupport::CloseCodeModifier();
+ }
+ }
+
+void DRM_DebugChannel::DestroyDfcQ()
+ {
+ LOG_MSG("DRM_DebugChannel::DestroyDfcQ()");
+ if (iDfcQ)
+ {
+ NKern::ThreadEnterCS();
+ iDfcQ->Destroy();
+ NKern::ThreadLeaveCS();
+ }
+ }
+
+//
+// DRM_DebugChannel::DoCreate
+//
+TInt DRM_DebugChannel::DoCreate(TInt /*aUnit*/, const TDesC* anInfo, const TVersion& aVer)
+ {
+ LOG_MSG("DRM_DebugChannel::DoCreate()");
+ TInt err = Kern::CreateClientDataRequest(iAsyncGetValueRequest);
+ if(err != KErrNone)
+ return err;
+
+ if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber, KMinorVersionNumber, KBuildVersionNumber), aVer))
+ return KErrNotSupported;
+
+ // Do the security check here so that any arbitrary application doesn't make
+ // use of Trk kernel driver.
+ if (!DoSecurityCheck())
+ {
+ LOG_MSG("DRM_DebugChannel::DoCreate() - permission denied!");
+ return KErrPermissionDenied;
+ }
+
+ if (anInfo)
+ {
+ // this is the end address of the user library.
+ // this doesn't seem to be valid for EKA2.
+ // right now we dont need this for EKA2 since we are not worried
+ // about kernel being stopped as kernel is multithreaded.
+ // just retaining this for future use.
+ TBuf8<32> buf;
+ TInt err = Kern::ThreadRawRead(iClientThread, anInfo, &buf, 32);
+ if(err != KErrNone)
+ return err;
+ }
+
+ // Allocate a D_RMD_Breakpoints class as a breakpoint manager
+ NKern::ThreadEnterCS();
+ iBreakManager = new D_RMD_Breakpoints(this);
+ NKern::ThreadLeaveCS();
+ if (iBreakManager == NULL)
+ {
+ LOG_MSG("DRM_DebugChannel::DRM_DebugChannel - could not construct breakpoint manager");
+ return KErrNoMemory;
+ }
+
+ // Initialise the new breakpoint manager object
+ iBreakManager->Init();
+
+ // Allocate a DRMDStepping class as the stepping manager
+ NKern::ThreadEnterCS();
+ iStepper = new DRMDStepping(this);
+ NKern::ThreadLeaveCS();
+ if (iStepper == NULL)
+ {
+ LOG_MSG("DRM_DebugChannel::DRM_DebugChannel - could not construct stepper manager");
+ return KErrNoMemory;
+ }
+
+ // Initialize the code modifier for managing breakpoints.
+ TUint caps; //ignored for now
+ err = DebugSupport::InitialiseCodeModifier(caps, NUMBER_OF_MAX_BREAKPOINTS);
+ //if code modifier initializer failed,
+ //return here, since we can't set an breakpoints
+ if(err != KErrNone)
+ {
+ return err;
+ }
+ else
+ {
+ iInitialisedCodeModifier = ETrue;
+ }
+
+ //create and set the driver's Dfc queue
+ err = CreateDfcQ();
+ if(err != KErrNone)
+ {
+ LOG_MSG("DRM_DebugChannel::DoCreate() Creating Dfc queue failed.");
+ }
+ SetDfcQ(iDfcQ);
+
+ iMsgQ.Receive();
+
+ iEventHandler = new DRM_DebugEventHandler;
+ if (!iEventHandler)
+ return KErrNoMemory;
+ err = iEventHandler->Create(iDevice, this, iClientThread);
+ if (err != KErrNone)
+ return err;
+
+ //return KErrNone;
+ return iEventHandler->Start();
+ }
+
+/**
+Forward call to either synch or asynch methods while serialising all calls via lock.
+
+Protect access via a the event handler lock to
+serialise all calls and protect concurrent access to data structures
+
+@param aMsg pointer to a TMessageBase object
+
+@return error returned by called methods
+
+@see DRM_DebugEventHandler::HandleSpecificEvent where lock is also used
+@see DRM_DebugEventHandler::iProtectionLock
+
+*/
+TInt DRM_DebugChannel::SendMsg(TMessageBase* aMsg)
+ {
+ DThread * currThread = &Kern::CurrentThread();
+
+ iEventHandler->LockDataAccess();
+ LOG_MSG3("DRM_DebugChannel::SendMsg() currThread = 0x%08x, iClientThread=0x%08x", currThread, iClientThread );
+
+ TThreadMessage& m = *(TThreadMessage*)aMsg;
+ TInt id = m.iValue;
+ TInt err = KErrNone;
+
+ if (id != (TInt)ECloseMsg && id != KMaxTInt && id < 0)
+ {
+ // DoRequest
+ TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0();
+ err = SendRequest(aMsg);
+ if (err != KErrNone)
+ Kern::RequestComplete(pStatus,err);
+ }
+ else
+ {
+ err = DLogicalChannel::SendMsg(aMsg);
+ }
+
+ iEventHandler->ReleaseDataAccess();
+ return err;
+ }
+
+//
+// DRM_DebugChannel::SendRequest
+//
+TInt DRM_DebugChannel::SendRequest(TMessageBase* aMsg)
+ {
+ LOG_MSG("DRM_DebugChannel::SendRequest()");
+
+ TThreadMessage& m = *(TThreadMessage*)aMsg;
+ TInt function = ~m.iValue;
+ TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0();
+ TAny* a1 = m.Ptr1();
+
+ TInt err = KErrNotSupported;
+ switch(function)
+ {
+ case RRM_DebugDriver::ERequestGetEvent:
+ err = PreAsyncGetValue((TEventInfo*)a1,pStatus);
+ break;
+ }
+ if (err == KErrNone)
+ err = DLogicalChannel::SendMsg(aMsg);
+ return err;
+ }
+
+//
+// DRM_DebugChannel::PreAsyncGetValue
+//
+TInt DRM_DebugChannel::PreAsyncGetValue(TEventInfo* aValue, TRequestStatus* aStatus)
+ {
+ LOG_MSG3("DRM_DebugChannel::PreAsyncGetValue() TEventInfo=0x%08x, TRequestStatus=0x%08x",
+ aValue, aStatus );
+
+ iAsyncGetValueRequest->Reset();
+
+ TInt err = iAsyncGetValueRequest->SetStatus(aStatus);
+ if (err != KErrNone)
+ return err;
+
+ iAsyncGetValueRequest->SetDestPtr(aValue);
+ return KErrNone;
+ }
+
+/**
+ Create the Dfc queue for receiving messages
+ */
+TInt DRM_DebugChannel::CreateDfcQ()
+ {
+ LOG_MSG("DRM_DebugChannel::CreateDfcQ()");
+ TInt r = Kern::DynamicDfcQCreate(iDfcQ, KRmDebugDriverThreadPriority, KRM_DebugDriverName);
+
+ if (r == KErrNone)
+ iDfcQ->SetRealtimeState(ERealtimeStateOff);
+ return r;
+ }
+
+//
+// DRM_DebugChannel::DoCancel
+//
+// New: The cancel call does not take an enum parameter describing
+// the request to be cancelled. Rather it supplies a pointer
+// to a user-side struct defining the cancellation
+//
+void DRM_DebugChannel::DoCancel(TInt aReqNo)
+ {
+ LOG_MSG("DRM_DebugChannel::DoCancel()");
+
+ TRMD_DebugCancelInfo info;
+
+ TInt err = Kern::ThreadRawRead(iClientThread,(TAny*)aReqNo,(TAny*)&info,sizeof(info));
+ if (err != KErrNone)
+ {
+ // How do we cancel something we know nothing about???
+ LOG_MSG("DRM_DebugChannel::DoCancel - bad arguments");
+ return;
+ }
+
+ DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId(info.iProcessName, info.iAgentId);
+ if (debugAgent == NULL)
+ {
+ // Bad agent means there is no tracking agent
+ LOG_MSG2("Cannot locate debug agent with pid 0x%x", info.iAgentId);
+ return;
+ }
+
+ // Agent completes/pends the request as appropriate.
+ debugAgent->CancelGetEvent();
+
+ }
+
+//
+// DRM_DebugChannel::DoRequest
+//
+void DRM_DebugChannel::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2)
+ {
+ LOG_MSG4("DRM_DebugChannel::DoRequest(), iClientThread=0x%08x, tid=0x%08x, TRequestStatus=0x%08x",
+ iClientThread, I64LOW(iClientThread->iId), aStatus);
+
+ switch(aReqNo)
+ {
+ case RRM_DebugDriver::ERequestGetEvent:
+ {
+ TEventMetaData eventMetaData;
+ TInt err = Kern::ThreadRawRead(iClientThread, a2, (TUint8 *)&eventMetaData, sizeof(TEventMetaData) );
+ if (err != KErrNone)
+ {
+ LOG_MSG("Error: could not read argument data from the DSS (TEventMetaData)");
+
+ // We could not read information from the user, so the a2 argument is probably wrong
+ Kern::RequestComplete(iClientThread, aStatus, KErrArgument);
+ return;
+ }
+
+ DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId(eventMetaData.iTargetProcessName, eventMetaData.iDebugAgentProcessId);
+ if (debugAgent == NULL)
+ {
+ // Bad agent means there is no tracking agent
+ LOG_MSG2("Cannot locate debug agent with pid 0x%x", eventMetaData.iDebugAgentProcessId);
+ Kern::RequestComplete(iClientThread, aStatus, KErrNotFound);
+ return;
+ }
+ // Agent completes/pends the request as appropriate.
+ debugAgent->GetEvent(iAsyncGetValueRequest, iClientThread);
+
+ break;
+ }
+ default:
+ {
+ // Should not get here!
+ LOG_MSG2("DRM_DebugChannel::DoRequest was passed unsupported request aReqNo=%d", aReqNo );
+ Kern::RequestComplete(iClientThread, aStatus, KErrNotSupported);
+ }
+ }
+ }
+
+//
+// DRM_DebugChannel::DoControl
+//
+TInt DRM_DebugChannel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
+ {
+ LOG_MSG("DRM_DebugChannel::DoControl()");
+
+ LOG_MSG2("DoControl Function %d", aFunction);
+
+ TInt err = KErrNone;
+ DThread* threadObj = NULL;
+ // Open a thread handle for the operations that need one
+ switch (aFunction)
+ {
+ case RRM_DebugDriver::EControlSuspendThread:
+ case RRM_DebugDriver::EControlResumeThread:
+ case RRM_DebugDriver::EControlStepRange:
+ case RRM_DebugDriver::EControlReadMemory:
+ case RRM_DebugDriver::EControlWriteMemory:
+ case RRM_DebugDriver::EControlReadRegistersLegacy:
+ case RRM_DebugDriver::EControlWriteRegistersLegacy:
+ case RRM_DebugDriver::EControlReadRegisters:
+ case RRM_DebugDriver::EControlWriteRegisters:
+ {
+ NKern::ThreadEnterCS();
+ threadObj = DebugUtils::OpenThreadHandle((TUint32)a1);
+ if (!threadObj)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrBadHandle;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch(aFunction)
+ {
+ /* Security first */
+ case RRM_DebugDriver::EControlIsDebuggable:
+ {
+ err = IsDebuggable((TUint32)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlSetBreak:
+ {
+ err = SetBreak((TSetBreakInfo*)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlClearBreak:
+ {
+ err = iBreakManager->DoClearBreak((TInt32)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlModifyBreak:
+ {
+ err = iBreakManager->DoModifyBreak((TModifyBreakInfo*)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlModifyProcessBreak:
+ {
+ err = iBreakManager->DoModifyProcessBreak((TModifyProcessBreakInfo*)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlBreakInfo:
+ {
+ err = iBreakManager->DoBreakInfo((TGetBreakInfo*)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlSuspendThread:
+ {
+ err = DoSuspendThread(threadObj);
+ break;
+ }
+ case RRM_DebugDriver::EControlResumeThread:
+ {
+ err = DoResumeThread(threadObj);
+ break;
+ }
+ case RRM_DebugDriver::EControlStepRange:
+ {
+ err = StepRange(threadObj, (TRM_DebugStepInfo*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlReadMemory:
+ {
+ err = ReadMemory(threadObj, (TRM_DebugMemoryInfo*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlWriteMemory:
+ {
+ err = WriteMemory(threadObj, (TRM_DebugMemoryInfo*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlReadRegistersLegacy:
+ {
+ err = ReadRegistersLegacy(threadObj, (TRM_DebugRegisterInfo*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlWriteRegistersLegacy:
+ {
+ err = WriteRegistersLegacy(threadObj, (TRM_DebugRegisterInfo*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlReadRegisters:
+ {
+ err = ReadRegisters(threadObj, (TRM_DebugRegisterInformation*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlWriteRegisters:
+ {
+ err = WriteRegisters(threadObj, (TRM_DebugRegisterInformation*)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlGetDebugFunctionalityBufSize:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlGetDebugFunctionalityBufSize\n");
+
+ TDebugFunctionality df;
+
+ TUint size = df.GetDebugFunctionalityBufSize();
+
+ // Return size to user-side in a safe manner
+ err = Kern::ThreadRawWrite(iClientThread, a1, (TUint8*)&size, sizeof(TUint), iClientThread);
+ break;
+ }
+ case RRM_DebugDriver::EControlGetDebugFunctionality:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlGetDebugFunctionality\n");
+
+ TDebugFunctionality df;
+
+ TUint32 dfsize = df.GetDebugFunctionalityBufSize();
+
+ // Alloc tmp buffer for Debug Functionality data
+ NKern::ThreadEnterCS();
+ TUint8* dfbuffer = (TUint8*)Kern::AllocZ(dfsize);
+ if (dfbuffer==NULL)
+ {
+ LOG_MSG2("Could not allocate memory for %d bytes\n",dfsize);
+ NKern::ThreadLeaveCS();
+ // could not allocate memory
+ return KErrNoMemory;
+ }
+
+ // Temporary descriptor to hold DF data
+ TPtr8 tmpPtr(dfbuffer,0,dfsize);
+
+ // Obtain the DF data
+ if (df.GetDebugFunctionality(tmpPtr) )
+ {
+ // Return the DF data to the user-side
+ err = Kern::ThreadDesWrite(iClientThread, a1, tmpPtr, 0, KChunkShiftBy0, iClientThread);
+ }
+ else
+ {
+ // Failed.
+ err = KErrGeneral;
+ }
+
+ // Free tmp buffer
+ Kern::Free(dfbuffer);
+ NKern::ThreadLeaveCS();
+ break;
+ }
+ case RRM_DebugDriver::EControlAttachProcess:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlAttachProcess");
+
+ err = AttachProcess(a1,a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlDetachProcess:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlDetachProcess");
+
+ err = DetachProcess(a1,a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlDetachAgent:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlDetachAgent");
+
+ err = DetachAgent(a1,a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlSetEventAction:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlSetEventAction");
+
+ err = SetEventAction(a1,a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlGetMemoryOperationMaxBlockSize:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlGetMemoryOperationMaxBlockSize\n");
+
+ TUint32 maxSize = TDebugFunctionality::GetMemoryOperationMaxBlockSize();
+
+ // Return size to user-side in a safe manner
+ err = Kern::ThreadRawWrite(iClientThread, a1, (TUint8*)&maxSize, sizeof(TUint32), iClientThread);
+ break;
+ }
+ case RRM_DebugDriver::EControlGetList:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlGetList\n");
+ err = GetList((TListInformation*)a1);
+ break;
+ }
+ case RRM_DebugDriver::EControlStep:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlStep\n");
+
+ err = Step((TUint32)a1,(TUint32)a2);
+ break;
+ }
+ case RRM_DebugDriver::EControlKillProcess:
+ {
+ LOG_MSG("RRM_DebugDriver::EControlKillProcess\n");
+
+ err = KillProcess((TUint32)a1,(TUint32)a2);
+ break;
+ }
+ default:
+ {
+ err = KErrGeneral;
+ }
+ }
+
+ if (KErrNone != err)
+ {
+ LOG_MSG2("Error %d from control function", err);
+ }
+
+ if (threadObj)
+ {
+ // Close the thread handle which has been opened by DebugUtils::OpenThreadHandle
+ threadObj->Close(NULL);
+ NKern::ThreadLeaveCS();
+ }
+
+ return err;
+ }
+
+void DRM_DebugChannel::HandleMsg(TMessageBase* aMsg)
+ {
+ LOG_MSG("DRM_DebugChannel::HandleMsg()");
+
+ TThreadMessage& m = *(TThreadMessage*)aMsg;
+ TInt id = m.iValue;
+
+ if (id == (TInt)ECloseMsg)
+ {
+ if (iEventHandler)
+ {
+ iEventHandler->Stop();
+ iEventHandler->Close();
+ iEventHandler = NULL;
+ }
+ m.Complete(KErrNone, EFalse);
+ return;
+ }
+
+ if (id == KMaxTInt)
+ {
+ // DoCancel
+ DoCancel(m.Int0());
+ m.Complete(KErrNone, ETrue);
+ return;
+ }
+
+ if (id < 0)
+ {
+ // DoRequest
+ TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0();
+ DoRequest(~id, pStatus, m.Ptr1(), m.Ptr2());
+ m.Complete(KErrNone, ETrue);
+ }
+ else
+ {
+ // DoControl
+ TInt err = DoControl(id, m.Ptr0(), m.Ptr1());
+ m.Complete(err, ETrue);
+ }
+ }
+
+//
+// DRM_DebugChannel::RemoveProcess
+//
+TBool DRM_DebugChannel::RemoveProcess(TAny* a1, TAny* a2)
+ {
+ LOG_MSG("DRM_DebugChannel::RemoveProcess()");
+
+ DProcess *aProcess = (DProcess*)a1;
+
+ // Sanity check
+ if (!aProcess)
+ {
+ // No process was specified!
+ LOG_MSG("DRM_DebugChannel::RemoveProcess was called with an invalid process ID");
+ return EFalse;
+ }
+
+ // this is called when a process dies. we want to mark any breakpoints in this
+ // process space as obsolete. the main reason for this is so we don't return
+ // an error when the host debugger tries to clear breakpoints for the process
+
+ TUint32 codeAddress = 0;
+ TUint32 codeSize = 0;
+
+ LOG_EVENT_MSG2("Process being removed, Name %S", aProcess->iName);
+
+ DCodeSeg* codeSeg = aProcess->iCodeSeg;
+
+ if (codeSeg)
+ {
+ TModuleMemoryInfo processMemoryInfo;
+ TInt err = codeSeg->GetMemoryInfo(processMemoryInfo, aProcess);
+ if (err != KErrNone)
+ {
+ codeAddress = processMemoryInfo.iCodeBase;
+ codeSize = processMemoryInfo.iCodeSize;
+ }
+ else
+ {
+ LOG_MSG2("Error in getting memory info: %d", err);
+ }
+ }
+
+ if (!codeAddress || !codeSize)
+ {
+ LOG_EVENT_MSG2("Code segment not available for process %d", aProcess->iId);
+ // make sure there is not already a breakpoint at this address
+ for (TInt i = 0; i < iDebugProcessList.Count(); i++)
+ {
+ if (iDebugProcessList[i].iId == aProcess->iId)
+ {
+ codeAddress = iDebugProcessList[i].iCodeAddress;
+ codeSize = iDebugProcessList[i].iCodeSize;
+
+ //now remove from the list
+ iDebugProcessList.Remove(i);
+ break;
+ }
+ }
+ }
+
+ if (!codeAddress || !codeSize)
+ {
+ return EFalse;
+ }
+
+ iBreakManager->RemoveBreaksForProcess(aProcess->iId, codeAddress, codeSize);
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::StartThread
+//
+TBool DRM_DebugChannel::StartThread(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::StartThread()");
+
+ DThread *aThread = (DThread*)a1;
+ if(!aThread)
+ {
+ LOG_MSG("Error getting DThread object");
+ __NK_ASSERT_DEBUG(aThread);
+ return EFalse;
+ }
+
+ //a2 points to the thread creating the new thread.
+ //We have no use for it at the moment so just ignore it for now
+
+ TDriverEventInfo info;
+ info.iEventType = EEventsStartThread;
+ info.iThreadId = aThread->iId;
+ info.iThreadIdValid = ETrue;
+ DProcess* owningProcess = aThread->iOwningProcess;
+ if(owningProcess)
+ {
+ info.iProcessId = owningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ DCodeSeg* p = owningProcess->iCodeSeg;
+ if(p && p->iFileName)
+ {
+ info.iFileName.Copy(*(p->iFileName));
+ TheDProcessTracker.NotifyAgentsForProcessEvent(*p->iFileName, info);
+ }
+ else
+ {
+ if(p)
+ {
+ LOG_EVENT_MSG("\tCode segment name missing");
+ }
+ else
+ {
+ LOG_EVENT_MSG("\tCode segment is NULL");
+ }
+ }
+ }
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandleAddProcessEvent
+//
+TBool DRM_DebugChannel::HandleAddProcessEvent(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddProcess()");
+
+ DProcess *aProcess = (DProcess*)a1;
+ // a2 points to the thread creating the new process.
+ DThread *aThread = (DThread*)a2;
+
+ if(!aProcess)
+ {
+ LOG_MSG("Error getting DProcess object");
+ __NK_ASSERT_DEBUG(aProcess);
+ return EFalse;
+ }
+
+ TDriverEventInfo info;
+ info.iEventType = EEventsAddProcess;
+ info.iProcessId = aProcess->iId;
+
+ info.iCreatorThreadId = aThread ? aThread->iId : 0;
+ info.iProcessIdValid = ETrue;
+
+ // Copy TUids
+ info.iUids = aProcess->iUids;
+
+ info.iUidsValid = ETrue;
+
+ // copy name of the process
+ if (aProcess->iName)
+ {
+ // copy the name of the process
+ info.iFileName.Copy(*aProcess->iName);
+ // AddProcess event does not have fully-qualified path, it has "filename.exe"
+ // So we allow a less-precise match by passing in ETrue
+ TheDProcessTracker.NotifyAgentsForProcessEvent(*aProcess->iName, info, ETrue);
+ }
+ else
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddProcess - No iName for this process");
+ }
+
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandleRemoveProcessEvent
+//
+TBool DRM_DebugChannel::HandleRemoveProcessEvent(TAny* a1, TAny* a2)
+ {
+ LOG_MSG("DRM_DebugChannel::HandleRemoveProcessEvent()");
+
+ DProcess *aProcess = (DProcess*)a1;
+ if(!aProcess)
+ {
+ LOG_MSG("Error getting DProcess object");
+ __NK_ASSERT_DEBUG(aProcess);
+ return EFalse;
+ }
+
+ // a2 points to the thread creating the new process.
+ // We have no use for it at the moment so just ignore it for now
+ // Also, it may not be known and therefore NULL
+
+ TDriverEventInfo info;
+ info.iEventType = EEventsRemoveProcess;
+ info.iProcessId = aProcess->iId;
+ info.iProcessIdValid = ETrue;
+
+ // copy name of the process
+ if (aProcess->iName)
+ {
+ info.iFileName.Copy(*aProcess->iName);
+
+ // RemoveProcess event does not have fully-qualified path, it has "filename.exe"
+ // So we allow a less-precise match by passing in ETrue
+ TheDProcessTracker.NotifyAgentsForProcessEvent(*aProcess->iName, info, ETrue);
+ }
+ else
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddProcess - No iName for this process");
+ }
+
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::AddLibrary
+//
+TBool DRM_DebugChannel::AddLibrary(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary()");
+
+ DLibrary *aLibrary = (DLibrary*)a1;
+ DThread *aThread = (DThread*)a2;
+
+ // sanity check
+ if (!aLibrary)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary called with no library specified");
+ return EFalse;
+ }
+
+ if (!aThread)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary called with no thread specified");
+ return EFalse;
+ }
+
+#ifdef __LOG_EVENTS__
+ TFullName threadName;
+ aThread->FullName(threadName);
+ LOG_EVENT_MSG3(("Lib %S loaded by %S"), aLibrary->iName, &threadName);
+#endif
+
+ if (aThread)
+ {
+ // make sure this is not the debugger thread
+ if ((aThread != iClientThread) && (aThread->iOwningProcess->iId != iClientThread->iOwningProcess->iId))
+ {
+ TDriverEventInfo info;
+
+ info.iEventType = EEventsAddLibrary;
+ info.iProcessId = aThread->iOwningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = aThread->iId;
+ info.iThreadIdValid = ETrue;
+
+ //get the code address
+ DCodeSeg* codeSeg = aLibrary->iCodeSeg;
+ if (!codeSeg)
+ {
+ LOG_EVENT_MSG2("Code segment not available for library %S", aLibrary->iName);
+ return EFalse;
+ }
+
+ // Uid3
+ info.iUids = codeSeg->iUids;
+ info.iUidsValid = ETrue;
+
+ TModuleMemoryInfo memoryInfo;
+ TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL); //NULL for DProcess should be ok;
+ if (err != KErrNone)
+ {
+ LOG_EVENT_MSG2("Error in getting memory info: %d", err);
+ return EFalse;
+ }
+
+ info.iCodeAddress = memoryInfo.iCodeBase;
+ info.iDataAddress = memoryInfo.iInitialisedDataBase;
+
+ info.iFileName.Copy(*(aLibrary->iName)); //just the name, without uid info.
+
+ //queue up or complete the event
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+ NotifyAgentsFromEventPid(info);
+ }
+
+ }
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::RemoveLibrary
+//
+TBool DRM_DebugChannel::RemoveLibrary(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::RemoveLibrary()");
+ DLibrary *aLibrary = (DLibrary*)a1;
+
+ // sanity check
+ if (!aLibrary)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::RemoveLibrary called with no library specified");
+ return EFalse;
+ }
+
+ LOG_EVENT_MSG2(("Lib unloaded: %S"), aLibrary->iName);
+
+ // this is called when all handles to this library have been closed. this can happen when a process dies, or when a dll is
+ // unloaded while the process lives on. in former case, we don't need to notify the host debugger because that process is
+ // dying anyway. for the latter case, we do need to notify the host so it can unload the symbolics, etc.
+
+ DThread* aThread = &Kern::CurrentThread();
+
+ if ((aThread) &&
+ (aThread != iClientThread) &&
+ (aThread->iOwningProcess->iId != iClientThread->iOwningProcess->iId))
+ {
+ //the library gets unloaded only when the mapcount is 0.
+ if (aLibrary->iMapCount != 0)
+ return EFalse;
+
+ DCodeSeg* codeSeg = aLibrary->iCodeSeg;
+ if (!codeSeg)
+ {
+ LOG_EVENT_MSG2("Code segment not available for library %S", aLibrary->iName);
+ return EFalse;
+ }
+
+ TModuleMemoryInfo processMemoryInfo;
+ TInt err = codeSeg->GetMemoryInfo(processMemoryInfo, NULL); //passing NULL for the DProcess argument should be ok;
+ if (err != KErrNone)
+ {
+ LOG_EVENT_MSG2("Error in getting memory info: %d", err);
+ return EFalse;
+ }
+
+ TUint32 codeAddress = processMemoryInfo.iCodeBase;
+ TUint32 codeSize = processMemoryInfo.iCodeSize;
+
+ // first invalidate all breakpoints that were set in the library code
+ iBreakManager->InvalidateLibraryBreakPoints(codeAddress, codeSize);
+ DProcess *process = &Kern::CurrentProcess();
+ RArray<SCodeSegEntry>* dynamicCode = &(process->iDynamicCode);
+
+ for (TInt j=0; j<dynamicCode->Count(); j++)
+ {
+ if ((*dynamicCode)[j].iLib == aLibrary)
+ {
+ TDriverEventInfo info;
+
+ info.iEventType = EEventsRemoveLibrary;
+ info.iFileName.Copy(*(aLibrary->iName)); //lib name without uid info
+ //info.iFileName.ZeroTerminate();
+ info.iProcessId = process->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = 0xFFFFFFFF; // don't care!
+ info.iThreadIdValid = EFalse;
+ // Uid3
+ info.iUids = codeSeg->iUids;
+ info.iUidsValid = ETrue;
+
+ //queue up or complete the event
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+ NotifyAgentsFromEventPid(info);
+ }
+ }
+ }
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandleEventKillThread
+//
+TBool DRM_DebugChannel::HandleEventKillThread(TAny* a1, TAny* a2)
+ {
+ DThread* currentThread = &Kern::CurrentThread();
+
+ // a1 should point to the current thread, check this to make sure it does
+ __NK_ASSERT_DEBUG((DThread*)a1 == currentThread);
+
+ TDriverEventInfo info;
+
+ LOG_MSG5(" HandleEventKillThread for thread 0x%x, CritScount=%d, suspCnt=%d, waitObj=0x%x",
+ currentThread->iId,
+ currentThread->iNThread.iCsCount,
+ currentThread->iNThread.iSuspendCount,
+ currentThread->iWaitObj);
+
+ info.iProcessId = currentThread->iOwningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = currentThread->iId;
+ info.iThreadIdValid = ETrue;
+
+ TInt err = ReadKernelRegisterValue(currentThread, 14, info.iCurrentPC);
+ if(err != KErrNone)
+ {
+ LOG_EVENT_MSG2("DRM_DebugChannel::HandleEventKillThread - Non-zero error code discarded: %d", err);
+ }
+
+ LOG_MSG5(" HandleEventKillThread for thread exit category=%S reason=%d, exitType=0x%x, PC=0x%x",
+ ¤tThread->iExitCategory,
+ currentThread->iExitReason,
+ currentThread->iExitType,
+ info.iCurrentPC);
+
+ if (currentThread->iExitType == EExitPanic)
+ {
+ info.iPanicCategory.Copy(currentThread->iExitCategory);
+ }
+ info.iExceptionNumber = currentThread->iExitReason;
+ info.iExitType = currentThread->iExitType;
+ info.iEventType = EEventsKillThread;
+ info.iThreadFlags = currentThread->iFlags;
+
+ // remove all the breakpoints in this thread, whether we are debugging it or not.
+ iBreakManager->DoRemoveThreadBreaks(info.iThreadId);
+
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+ NotifyAgentsFromEventPid(info);
+
+ return ETrue;
+ }
+
+//
+// DRM_DebugChannel::HandleSwException
+//
+TBool DRM_DebugChannel::HandleSwException(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::HandleSwException");
+ TExcType aExcType = (TExcType)(TInt)a1;
+
+ TDriverEventInfo info;
+
+ DThread* currentThread = &Kern::CurrentThread();
+ if (!currentThread)
+ {
+ LOG_MSG("Error getting current thread");
+ __NK_ASSERT_DEBUG(currentThread);
+ return EFalse;
+ }
+
+ info.iProcessId = currentThread->iOwningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = currentThread->iId;
+ info.iThreadIdValid = ETrue;
+ TInt err = ReadKernelRegisterValue(currentThread, PC_REGISTER, info.iCurrentPC);
+ if(err != KErrNone)
+ {
+ LOG_EVENT_MSG2("DRM_DebugChannel::HandleSwException - Non-zero error code discarded: %d", err);
+ }
+ info.iExceptionNumber = aExcType;
+ info.iEventType = EEventsSwExc;
+ info.iThreadFlags = currentThread->iFlags;
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+
+ NotifyAgentsFromEventPid(info);
+
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandleHwException
+//
+TBool DRM_DebugChannel::HandleHwException(TAny* a1, TAny* a2)
+ {
+ TArmExcInfo* aExcInfo = (TArmExcInfo*)a1;
+
+ // sanity check
+ if (!aExcInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::HandleHwException called with no aExcInfo");
+ __NK_ASSERT_DEBUG(aExcInfo);
+ return EFalse;
+ }
+
+ TDriverEventInfo info;
+
+ DThread* currentThread = &Kern::CurrentThread();
+
+ if (!currentThread)
+ {
+ LOG_MSG("Error getting current thread");
+ __NK_ASSERT_DEBUG(currentThread);
+ return EFalse;
+ }
+
+ info.iProcessId = currentThread->iOwningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = currentThread->iId;
+ info.iThreadIdValid = ETrue;
+ info.iRmdArmExcInfo.iFaultAddress= aExcInfo->iFaultAddress;
+ info.iRmdArmExcInfo.iFaultStatus= aExcInfo->iFaultStatus;
+
+ LOG_MSG5("DRM_DebugChannel::HandleHwException current thread = 0x%08x, CritSect count=%d,\n"
+ " iFaultAddress=0x%08x, iFaultStatus=0x%08x",
+ currentThread, currentThread->iNThread.iCsCount, aExcInfo->iFaultAddress, aExcInfo->iFaultStatus);
+
+
+ LOG_MSG3(" HandleHwException CsFunc=%d, suspCount=%d",
+ currentThread->iNThread.iCsFunction, currentThread->iNThread.iSuspendCount );
+
+ info.iRmdArmExcInfo.iR0= aExcInfo->iR0;
+ info.iRmdArmExcInfo.iR1= aExcInfo->iR1;
+ info.iRmdArmExcInfo.iR2= aExcInfo->iR2;
+ info.iRmdArmExcInfo.iR3= aExcInfo->iR3;
+
+ info.iRmdArmExcInfo.iR4= aExcInfo->iR4;
+ info.iRmdArmExcInfo.iR5= aExcInfo->iR5;
+ info.iRmdArmExcInfo.iR6= aExcInfo->iR6;
+ info.iRmdArmExcInfo.iR7= aExcInfo->iR7;
+ info.iRmdArmExcInfo.iR8= aExcInfo->iR8;
+ info.iRmdArmExcInfo.iR9= aExcInfo->iR9;
+ info.iRmdArmExcInfo.iR10= aExcInfo->iR10;
+ info.iRmdArmExcInfo.iR11= aExcInfo->iR11;
+ info.iRmdArmExcInfo.iR12= aExcInfo->iR12;
+
+ info.iRmdArmExcInfo.iR13= aExcInfo->iR13;
+ info.iRmdArmExcInfo.iR14= aExcInfo->iR14;
+ info.iRmdArmExcInfo.iR15= aExcInfo->iR15;
+
+ info.iRmdArmExcInfo.iCpsr= aExcInfo->iCpsr;
+ info.iRmdArmExcInfo.iR13Svc= aExcInfo->iR13Svc;
+ info.iRmdArmExcInfo.iR14Svc= aExcInfo->iR14Svc;
+ info.iRmdArmExcInfo.iSpsrSvc= aExcInfo->iSpsrSvc;
+ LOG_MSG5(" iCpsr=0x%x, iExcCode=0x%x, R14=0x%x, R15=0x%x",
+ aExcInfo->iCpsr, aExcInfo->iExcCode, aExcInfo->iR14, aExcInfo->iR15);
+
+ switch (aExcInfo->iExcCode)
+ {
+ case 0:
+ info.iExceptionNumber = EExcCodeAbort;
+ LOG_MSG(" iExcCode == 0 => EExcCodeAbort");
+ break;
+ case 1:
+ info.iExceptionNumber = EExcDataAbort;
+ LOG_MSG(" iExcCode == 1 => EExcDataAbort");
+ break;
+ case 2:
+ info.iExceptionNumber = EExcInvalidOpCode;
+ LOG_MSG(" iExcCode == 2 => EExcInvalidOpCode");
+ break;
+ default:
+ // new event? Something gone wrong?
+ __NK_ASSERT_DEBUG(EFalse);
+ return EFalse;
+ }
+
+ info.iEventType = EEventsHwExc;
+ info.iThreadFlags = currentThread->iFlags;
+
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+
+ if(EExcInvalidOpCode == info.iExceptionNumber)
+ {
+ return HandleInvalidOpCodeException(info, currentThread);
+ }
+
+ NotifyAgentsFromEventPid(info);
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandUserTrace
+//
+TBool DRM_DebugChannel::HandleUserTrace(TAny* a1, TAny* a2)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::HandleUserTrace()");
+
+ DThread* currentThread = &Kern::CurrentThread();
+ if (!currentThread)
+ {
+ LOG_EVENT_MSG("Error getting current thread");
+ __NK_ASSERT_DEBUG(currentThread);
+ return EFalse;
+ }
+
+ TDriverEventInfo info;
+ info.iProcessId = currentThread->iOwningProcess->iId;
+ info.iProcessIdValid = ETrue;
+ info.iThreadId = currentThread->iId;
+ info.iThreadIdValid = ETrue;
+ info.iEventType = EEventsUserTrace;
+ info.iArg1 = a1;
+ info.iArg2 = a2;
+
+ TInt err = KErrNone;
+
+ //User Trace info
+ XTRAP(err, XT_DEFAULT, kumemget(info.iUserTraceText, info.iArg1, (TInt)a2));
+ if(KErrNone != err)
+ {
+ return EFalse;
+ }
+
+ info.iMessageStatus = ESingleMessage;
+
+ NotifyAgentsFromEventPid(info);
+
+ return EFalse;
+ }
+
+//
+// DRM_DebugChannel::HandleException
+//
+TBool DRM_DebugChannel::HandleInvalidOpCodeException(TDriverEventInfo& aEventInfo, DThread* aCurrentThread)
+ {
+ LOG_MSG("DRM_DebugChannel::HandleInvalidOpCodeException()");
+
+ TInt err = KErrNone;
+
+ TUint32 inst = KArmBreakPoint;
+ TInt instSize = 4;
+
+ // change these for thumb mode
+ TUint32 regValue;
+ err = ReadKernelRegisterValue(aCurrentThread, STATUS_REGISTER, regValue);
+ if(err != KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::HandleInvalidOpCodeException - Non-zero error code discarded: %d", err);
+ }
+
+ if (regValue & ECpuThumb)
+ {
+ inst = KThumbBreakPoint;
+ instSize = 2;
+ }
+
+ TUint32 instruction = 0;
+ err = Kern::ThreadRawRead(aCurrentThread, (TUint32 *)aEventInfo.iRmdArmExcInfo.iR15, (TUint8 *)&instruction, instSize);
+
+ if (KErrNone != err)
+ LOG_MSG2("Error reading instruction at currentpc: %d", err);
+
+ if (!memcompare((TUint8 *)&inst, instSize, (TUint8 *)&instruction, instSize))
+ {
+ TInt err = DoSuspendThread(aCurrentThread);
+ if(! ((KErrNone == err) || (KErrAlreadyExists == err)) )
+ {
+ LOG_MSG2("DRM_DebugChannel::HandleInvalidOpCodeException() Thread with id 0x%08x could not be suspended.", aCurrentThread->iId);
+ return EFalse;
+ }
+
+ // the exception was a breakpoint instruction. see if we have a breakpoint at that address
+ TBreakEntry* breakEntry = NULL;
+ do
+ {
+ breakEntry = iBreakManager->GetNextBreak(breakEntry);
+ if (breakEntry && ((breakEntry->iThreadSpecific && breakEntry->iId == aEventInfo.iThreadId) || (!breakEntry->iThreadSpecific && breakEntry->iId == aEventInfo.iProcessId)) && breakEntry->iAddress == aEventInfo.iRmdArmExcInfo.iR15)
+ {
+ LOG_MSG2("Breakpoint with Id %d has been hit", breakEntry->iBreakId);
+
+ TBreakEntry tempBreakEntry = *breakEntry;
+
+ //change the event type to breakpoint type
+ aEventInfo.iEventType = breakEntry->iThreadSpecific ? EEventsBreakPoint : EEventsProcessBreakPoint;
+
+ // enable any breakpoints we had to disable for this thread
+ err = iBreakManager->DoEnableDisabledBreak(aEventInfo.iThreadId);
+ if (KErrNone != err)
+ LOG_MSG2("Error %d enabling disabled breakpoints", err);
+
+ // see if this is a temp breakpoint
+ if (iBreakManager->IsTemporaryBreak(*breakEntry))
+ {
+ // this was a temp breakpoint, so we need to clear it now
+ err = iBreakManager->DoClearBreak(breakEntry->iBreakId);
+ if (KErrNone != err)
+ LOG_MSG2("Error %d clearing temp breakpoint", err);
+
+ // Find out how many steps remain to be done
+
+ // reduce the number of steps to complete by 1
+ tempBreakEntry.iNumSteps--;
+
+ LOG_MSG2("There are %d steps remaining\n", tempBreakEntry.iNumSteps);
+
+ // New. If we have not finished do all the steps, continue stepping and don't notify event
+ if (tempBreakEntry.iNumSteps)
+ {
+ LOG_MSG("Continuing stepping...not telling the agent yet\n");
+ err = DoStepRange(aCurrentThread, aEventInfo.iRmdArmExcInfo.iR15, aEventInfo.iRmdArmExcInfo.iR15, ETrue, tempBreakEntry.iResumeOnceOutOfRange /*EFalse*/, tempBreakEntry.iNumSteps, ETrue);
+ if (err != KErrNone)
+ {
+ LOG_EVENT_MSG("Failed to continue stepping\n");
+
+ // what do we do? might as well stop here and tell the user
+ NotifyAgentsFromEventPid(aEventInfo);
+
+ return ETrue;
+ }
+
+ // continue as though no event occured. No need to suspend/resume anything...
+ LOG_MSG("Continuing to step\n");
+ return ETrue;
+ }
+
+ // Is this a case where we just want to continue?
+ if (tempBreakEntry.iResumeOnceOutOfRange)
+ {
+ LOG_MSG("PC is out of range, continuing thread");
+ DoResumeThread(aCurrentThread);
+
+ return ETrue;
+ }
+ }
+
+ // if the breakpoint is thread specific, make sure it's the right thread
+ // if not, just continue the thread. take special care if it's the debugger
+ // thread. if it hits a regular breakpoint, we NEVER want to stop at it. if
+ // it hits a temp breakpoint, we're probably just stepping past a real breakpoint
+ // and we do need to handle it.
+ TBool needToResume = (tempBreakEntry.iThreadSpecific && tempBreakEntry.iId != aEventInfo.iThreadId) ||
+ (!tempBreakEntry.iThreadSpecific && tempBreakEntry.iId != aEventInfo.iProcessId);
+
+ if (needToResume)
+ {
+ LOG_MSG("breakpoint does not match threadId, calling DoResumeThread");
+ err = DoResumeThread(aCurrentThread);
+ if (KErrNone != err)
+ LOG_MSG2("Error in DoResumeThread: %d", err);
+
+ return EFalse;
+ }
+
+ //normal user break point, just notify the event
+ break;
+ }
+ } while(breakEntry);
+ }
+
+ NotifyAgentsFromEventPid(aEventInfo);
+
+ return (aEventInfo.iEventType == EEventsBreakPoint) || (aEventInfo.iEventType == EEventsProcessBreakPoint);
+ }
+
+//
+// DRM_DebugChannel::SetBreak
+//
+TInt DRM_DebugChannel::SetBreak(TSetBreakInfo* aBreakInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::SetBreak()");
+
+ TInt err = KErrNone;
+
+ if (!aBreakInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::SetBreak() was passed a NULL argument");
+ return KErrArgument;
+ }
+
+ //User side memory is not accessible directly
+ TSetBreakInfo info;
+ err = Kern::ThreadRawRead(iClientThread, aBreakInfo, (TUint8*)&info, sizeof(TSetBreakInfo));
+ if (err != KErrNone)
+ {
+ LOG_MSG("DRM_DebugChannel::SetBreak() was passed a bad argument");
+ return err;
+ }
+
+ DProcess* process = NULL;
+ NKern::ThreadEnterCS();
+ if(info.iThreadSpecific)
+ {
+ DThread* thread = DebugUtils::OpenThreadHandle(info.iId);
+ if(!thread)
+ {
+ LOG_MSG2("DRM_DebugChannel::SetBreak() Thread with id 0x%08x not found", info.iId);
+ }
+ else
+ {
+ process = DebugUtils::OpenProcessHandle(thread->iOwningProcess->iId);
+ thread->Close(NULL);
+ }
+ }
+ else
+ {
+ process = DebugUtils::OpenProcessHandle(info.iId);
+ if(!process)
+ {
+ LOG_MSG2("DRM_DebugChannel::SetBreak() Process with id 0x%08x not found", info.iId);
+ }
+ }
+
+ if (process == NULL)
+ {
+ NKern::ThreadLeaveCS();
+ return KErrNotFound;
+ }
+
+ TBool found = EFalse;
+ for(TInt i=0; i<iDebugProcessList.Count(); i++)
+ {
+ if(process->iId == iDebugProcessList[i].iId)
+ {
+ found = ETrue;
+ }
+ }
+
+ if(!found)
+ {
+ DCodeSeg* codeSeg = process->iCodeSeg;
+ if (!codeSeg)
+ {
+ LOG_MSG2("DRM_DebugChannel::SetBreak() Code seg for process with id 0x%08x not found", process->iId);
+ err = KErrNotFound;
+ }
+
+ TModuleMemoryInfo memoryInfo;
+ if (!err)
+ {
+ err = codeSeg->GetMemoryInfo(memoryInfo, process);
+ if (err != KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::SetBreak() Error getting memory info for process with id 0x%08x", process->iId);
+ }
+ }
+
+ if (!err)
+ {
+ //add this process to the list of processes that we are debugging
+ TProcessInfo processInfo(process->iId, memoryInfo.iCodeBase, memoryInfo.iCodeSize, memoryInfo.iInitialisedDataBase);
+ iDebugProcessList.Append(processInfo);
+ }
+ }
+
+ process->Close(NULL);
+ NKern::ThreadLeaveCS();
+
+ if (!info.iBreakId) //first check if the iId address is valid
+ return KErrArgument;
+
+ if (err == KErrNone)
+ {
+ TInt32 iBreakId;
+
+ err = iBreakManager->DoSetBreak(iBreakId, info.iId, info.iThreadSpecific, info.iAddress, info.iMode );
+
+ if (err == KErrNone)
+ {
+ err = Kern::ThreadRawWrite(iClientThread, (TUint8 *)info.iBreakId, &iBreakId, sizeof(TInt32), iClientThread);
+ }
+ }
+
+ return err;
+ }
+
+//
+// DRM_DebugChannel::StepRange
+//
+TInt DRM_DebugChannel::StepRange(DThread* aThread, TRM_DebugStepInfo* aStepInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::StepRange()");
+
+ TInt err = KErrNone;
+
+ if (!aStepInfo)
+ return KErrArgument;
+
+ TRM_DebugStepInfo info(0, 0, 0);
+ err = Kern::ThreadRawRead(iClientThread, aStepInfo, (TUint8*)&info, sizeof(TRM_DebugStepInfo));
+
+ if (err != KErrNone)
+ return err;
+
+ err = DoStepRange(aThread, info.iStartAddress, info.iStopAddress, info.iStepInto, EFalse, ETrue);
+
+ return err;
+ }
+
+/**
+Read memory from a target thread and return the data to the client. If the
+memory block has breakpoints in it then the correct values are placed in the
+returned data
+
+@param aThread pointer to thread whose memory space the memory is to be read from
+@param aMemoryInfo information about what memory to read
+
+@return KErrNone if memory read successfully,
+ KErrArgument if aMemoryInfo is not initialised correctly,
+ KErrNoMemory if a temporary buffer could not be allocated,
+ KErrBadHandle if aThread is invalid,
+ or another of the system wide error codes
+*/
+TInt DRM_DebugChannel::ReadMemory(DThread* aThread, TRM_DebugMemoryInfo* aMemoryInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::ReadMemory()");
+
+ TInt err = KErrNone;
+
+ if (!aMemoryInfo)
+ return KErrArgument;
+
+ TRM_DebugMemoryInfo info(0, 0, 0);
+ err = Kern::ThreadRawRead(iClientThread, aMemoryInfo, (TUint8*)&info, sizeof(TRM_DebugMemoryInfo));
+ if (err != KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::ReadMemory returning error %d after Kern::ThreadRawRead()", err);
+ return err;
+ }
+
+ if (!info.iData)
+ return KErrArgument;
+
+ NKern::ThreadEnterCS();
+ TUint8 *data = (TUint8*)Kern::Alloc(info.iLength);
+ NKern::ThreadLeaveCS();
+ if (!data)
+ {
+ return KErrNoMemory;
+ }
+
+ TPtr8 dataDes(data, info.iLength);
+
+ err = DoReadMemory(aThread, info.iAddress, info.iLength, dataDes);
+ if (err == KErrNone)
+ {
+ err = Kern::ThreadDesWrite(iClientThread, info.iData, dataDes, 0, KChunkShiftBy0, iClientThread);
+ if (err)
+ {
+ LOG_MSG2("DRM_DebugChannel::ReadMemory - Kern::ThreadDesWrite() returned error %d", err);
+ }
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free(data);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+Attempt to write memory to aThread's address space
+
+@param aThread thread to whose address space memory is to be written
+@param aMemoryInfo memory info object representing the data to write
+
+@return KErrNone if memory written successfully,
+ KErrNoMemory if memory could not be allocated
+ KErrArgument if aMemoryInfo is NULL, if aMemoryInfo.iData is NULL,
+ if aMemoryInfo.iLength is greater than than the length of the passed
+ in descrptor
+ KErrBadHandle if aThread is invalid,
+ or another of the system wide error codes
+*/
+TInt DRM_DebugChannel::WriteMemory(DThread* aThread, TRM_DebugMemoryInfo* aMemoryInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::WriteMemory()");
+
+ TInt err = KErrNone;
+
+ if (!aMemoryInfo)
+ return KErrArgument;
+
+ TRM_DebugMemoryInfo info(0, 0, 0);
+ err = Kern::ThreadRawRead(iClientThread, aMemoryInfo, (TUint8*)&info, sizeof(TRM_DebugMemoryInfo));
+ if (err != KErrNone)
+ return err;
+
+ if (!info.iData)
+ return KErrArgument;
+
+ NKern::ThreadEnterCS();
+ TUint8 *data = (TUint8*)Kern::Alloc(info.iLength);
+ NKern::ThreadLeaveCS();
+ if (!data)
+ {
+ return KErrNoMemory;
+ }
+
+ TPtr8 dataDes(data, info.iLength);
+
+ err = Kern::ThreadDesRead(iClientThread, info.iData, dataDes, 0);
+ if (err == KErrNone)
+ {
+ err = DoWriteMemory(aThread, info.iAddress, info.iLength, dataDes);
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free(data);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+//
+// DRM_DebugChannel::ReadRegisters
+//
+TInt DRM_DebugChannel::ReadRegistersLegacy(DThread* aThread, TRM_DebugRegisterInfo* aRegisterInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::ReadRegistersLegacy()");
+
+ TInt err = KErrNone;
+
+ if (!aRegisterInfo)
+ return KErrArgument;
+
+ TRM_DebugRegisterInfo info(0, 0, 0);
+ err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInfo));
+ if (err != KErrNone)
+ return err;
+
+ if (!info.iValues)
+ return KErrArgument;
+
+ TUint length = (info.iLastRegister - info.iFirstRegister + 1) * 4;
+
+ NKern::ThreadEnterCS();
+ TUint8 *values = (TUint8*)Kern::Alloc(length);
+ NKern::ThreadLeaveCS();
+ if (!values)
+ {
+ return KErrNoMemory;
+ }
+
+ TPtr8 valuesDes(values, length);
+
+ err = DoReadRegisters(aThread, info.iFirstRegister, info.iLastRegister, valuesDes);
+ if (err == KErrNone)
+ {
+ err = Kern::ThreadDesWrite(iClientThread, info.iValues, valuesDes, 0, KChunkShiftBy0, iClientThread);
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free(values);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+Get listing information.
+
+@param aListInformation pointer to a TListInformation object containing the
+ user specified listings information
+
+@return KErrNone on success,
+ KErrTooBig if the kernel's data is too big to fit in the passed buffer,
+ KErrArgument if aListInformation is NULL,
+ or one of the other system-wide error codes
+*/
+TInt DRM_DebugChannel::GetList(TListInformation* aListInformation) const
+ {
+ LOG_MSG("DRM_DebugChannel::GetList()");
+
+ TInt err = KErrNone;
+
+ if(aListInformation == NULL)
+ {
+ return KErrArgument;
+ }
+
+ //read DSS' data into local structure
+ TListInformation info;
+ err = Kern::ThreadRawRead(iClientThread, aListInformation, (TUint8*)&info, sizeof(TListInformation));
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ //check arguments
+ TPtr8 buffer(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iBuffer, buffer);
+ if(err != KErrNone)
+ {
+ //need to free the buffer if it was allocated
+ if(err != KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)buffer.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ return err;
+ }
+
+ //get the list
+ TUint32 dataSize = 0;
+ TListManager manager;
+ err = KErrArgument;
+ switch(info.iType)
+ {
+ case EXipLibraries:
+ if(Debug::EScopeGlobal == info.iListScope)
+ {
+ err = manager.GetXipLibrariesList(buffer, dataSize);
+ }
+ break;
+
+ case EThreads:
+ if(Debug::EScopeGlobal == info.iListScope)
+ {
+ err = manager.GetGlobalThreadList(buffer, dataSize);
+ }
+ else if(Debug::EScopeProcessSpecific == info.iListScope)
+ {
+ err = manager.GetThreadListForProcess(buffer, dataSize, info.iTargetId);
+ }
+ else if(Debug::EScopeThreadSpecific == info.iListScope)
+ {
+ err = manager.GetThreadListForThread(buffer, dataSize, info.iTargetId);
+ }
+ break;
+
+ case EProcesses:
+ if(Debug::EScopeGlobal == info.iListScope)
+ {
+ err = manager.GetProcessList(buffer, dataSize);
+ }
+ break;
+
+ case ECodeSegs:
+ if(Debug::EScopeGlobal == info.iListScope)
+ {
+ err = manager.GetGlobalCodeSegList(buffer, dataSize);
+ }
+ else if(Debug::EScopeProcessSpecific == info.iListScope)
+ {
+ err = manager.GetCodeSegListForProcess(buffer, dataSize, info.iTargetId);
+ }
+ else if(Debug::EScopeThreadSpecific == info.iListScope)
+ {
+ err = manager.GetCodeSegListForThread(buffer, dataSize, info.iTargetId);
+ }
+ break;
+
+ default:
+ err = KErrNotSupported;
+ }
+
+ if(err == KErrNone)
+ {
+ //if no error then write the buffer back
+ err = Kern::ThreadDesWrite(iClientThread, info.iBuffer, buffer, 0, KChunkShiftBy0, iClientThread);
+ }
+
+ //write back the size of the data regardless of any error
+ TInt writeErr = Kern::ThreadRawWrite(iClientThread, info.iDataSize, (TUint8*)&dataSize, sizeof(TUint32), iClientThread);
+ if(writeErr != KErrNone)
+ {
+ //if there was an error writing the size return that error instead
+ err = writeErr;
+ }
+
+ //free the buffer
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)buffer.Ptr());
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+Read registers and store register data in aRegisterInfo
+
+@param aThread thread to read registers from
+@param aRegisterInfo structure specifying which registers to read and providing
+ descriptors to write the register data into
+
+@return KErrNone if registers were read successfully. Note that this does not
+ mean that all the registers could be read, the
+ aRegisterInfo.iRegisterFlags array should be checked as to whether each
+ individual register could be read,
+ KErrArgument if aRegisterInfo is NULL, or if any of the pointers that
+ are members of aRegisterInfo are NULL, if an unknown register is
+ specified or if the passed in register values buffer is too small
+ KErrNoMemory if there is insufficient memory,
+ KErrDied, if the thread with thread ID aThreadId is dead
+*/
+TInt DRM_DebugChannel::ReadRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) const
+ {
+ LOG_MSG("DRM_DebugChannel::ReadRegisters()");
+
+ TInt err = KErrNone;
+
+ if (!aRegisterInfo)
+ return KErrArgument;
+
+ TRM_DebugRegisterInformation info;
+ err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInformation));
+ if (err != KErrNone)
+ return err;
+
+ if ((!info.iRegisterIds) || (!info.iRegisterValues) || (!info.iRegisterFlags))
+ return KErrArgument;
+
+ //read ids from client thread
+ TPtr8 ids(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterIds, ids);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ return err;
+ }
+
+ //read values from client thread
+ TPtr8 values(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterValues, values, EFalse);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)values.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+ //read flags from client thread
+ TPtr8 flags(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterFlags, flags, EFalse);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)flags.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ Kern::Free((TAny*)values.Ptr());
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+ err = DoReadRegisters(aThread, ids, values, flags);
+ if (err == KErrNone)
+ {
+ err = Kern::ThreadDesWrite(iClientThread, info.iRegisterValues, values, 0, KChunkShiftBy0, iClientThread);
+ if(err == KErrNone)
+ {
+ err = Kern::ThreadDesWrite(iClientThread, info.iRegisterFlags, flags, 0, KChunkShiftBy0, iClientThread);
+ }
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ Kern::Free((TAny*)values.Ptr());
+ Kern::Free((TAny*)flags.Ptr());
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+@deprecated use DRM_DebugChannel::WriteRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) instead
+*/
+TInt DRM_DebugChannel::WriteRegistersLegacy(DThread* aThread, const TRM_DebugRegisterInfo* aRegisterInfo)
+ {
+ LOG_MSG("DRM_DebugChannel::WriteRegistersLegacy()");
+
+ TInt err = KErrNone;
+
+ if (!aRegisterInfo)
+ return KErrArgument;
+
+ TRM_DebugRegisterInfo info(0, 0, 0);
+ err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInfo));
+ if (err != KErrNone)
+ return err;
+
+ if (!info.iValues)
+ return KErrArgument;
+
+ TUint length = (info.iLastRegister - info.iFirstRegister + 1) * 4;
+
+ NKern::ThreadEnterCS();
+ TUint8 *values = (TUint8*)Kern::Alloc(length);
+ NKern::ThreadLeaveCS();
+ if (!values)
+ {
+ return KErrNoMemory;
+ }
+
+ TPtr8 valuesDes(values, length);
+
+ err = Kern::ThreadDesRead(iClientThread, info.iValues, valuesDes, 0);
+ if (err == KErrNone)
+ {
+ err = DoWriteRegisters(aThread, info.iFirstRegister, info.iLastRegister, valuesDes);
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free(values);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+Write registers and store flags data in aRegisterInfo
+
+@param aThread thread to write registers to
+@param aRegisterInfo structure specifying which registers to write and providing
+ descriptors to write the register flags data into
+
+@return KErrNone if registers were written successfully. Note that this does not
+ mean that all the registers could be written, the flags array
+ should be checked as to whether each individual register could be read,
+ KErrArgument if aRegisterInfo is NULL, or if any of the pointers that
+ are members of aRegisterInfo are NULL, if an unknown register is
+ specified or if the passed in register values buffer is too small, or
+ if aThread is NULL,
+ KErrGeneral if there was a problem initialising the register set,
+ KErrNoMemory if there is insufficient memory,
+ KErrDied, if the thread with thread ID aThreadId is dead
+*/
+TInt DRM_DebugChannel::WriteRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) const
+ {
+ LOG_MSG("DRM_DebugChannel::WriteRegisters()");
+
+ TInt err = KErrNone;
+
+ if (!aRegisterInfo)
+ return KErrArgument;
+
+ TRM_DebugRegisterInformation info;
+ err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInformation));
+ if (err != KErrNone)
+ return err;
+
+ if ((!info.iRegisterIds) || (!info.iRegisterValues) ||(!info.iRegisterFlags))
+ return KErrArgument;
+
+ //read ids from client thread
+ TPtr8 ids(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterIds, ids);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ return err;
+ }
+
+ //read values from client thread
+ TPtr8 values(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterValues, values);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)values.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+ //read flags from client thread
+ TPtr8 flags(NULL, 0);
+ err = AllocAndReadDes(iClientThread, *info.iRegisterFlags, flags, EFalse);
+ if(err != KErrNone)
+ {
+ if(err == KErrNoMemory)
+ {
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)flags.Ptr());
+ NKern::ThreadLeaveCS();
+ }
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ Kern::Free((TAny*)values.Ptr());
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+ err = DoWriteRegisters(aThread, ids, values, flags);
+ if(err == KErrNone)
+ {
+ err = Kern::ThreadDesWrite(iClientThread, info.iRegisterFlags, flags, 0, KChunkShiftBy0, iClientThread);
+ }
+
+ NKern::ThreadEnterCS();
+ Kern::Free((TAny*)ids.Ptr());
+ Kern::Free((TAny*)values.Ptr());
+ Kern::Free((TAny*)flags.Ptr());
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/**
+Suspends execution of the specified thread.
+
+@param aThread thread to resume
+
+@return KErrNone if there were no problems or KErrArgument if aThread is NULL
+*/
+TInt DRM_DebugChannel::DoSuspendThread(DThread *aThread)
+ {
+ LOG_MSG("DRM_DebugChannel::DoSuspendThread()");
+
+ if (!aThread)
+ {
+ LOG_MSG("Invalid dthread object");
+ return KErrArgument;
+ }
+
+ return TheDProcessTracker.SuspendThread(aThread);
+ }
+
+/**
+Resumes execution of the specified thread.
+
+@param aThread thread to resume
+
+@return KErrNone if there were no problems, KErrArgument if aThread is NULL
+ or an error value returned from DoStepRange()
+*/
+TInt DRM_DebugChannel::DoResumeThread(DThread *aThread)
+ {
+ if (!aThread)
+ return KErrArgument;
+
+ // get the current PC
+ TUint32 currentPC;
+ TInt err = ReadKernelRegisterValue(aThread, PC_REGISTER, currentPC);
+ if(err != KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::DoResumeThread : Read PC reg error %d.", err);
+ // Set to this value because 0 is dangerous since structures are usually 0-initialised,
+ // and could thus lead to a false positive in tmp break loop below
+ currentPC = 0xFFFFFFFF;
+ }
+ else
+ {
+ LOG_MSG2("DRM_DebugChannel::DoResumeThread(), pc=0x%x", currentPC);
+ }
+
+ // if there is a breakpoint at the current PC, we need to single step past it
+ TBreakEntry* breakEntry = NULL;
+ do
+ {
+ breakEntry = iBreakManager->GetNextBreak(breakEntry);
+ if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry))
+ {
+ if (breakEntry->iAddress == currentPC)
+ {
+ LOG_MSG2("DRM_DebugChannel::DoResumeThread : > DoStepRange(pc+1)=0x%x, resume once out of range", currentPC+1 );
+ return DoStepRange(aThread, currentPC, currentPC+1, ETrue, 1, ETrue);
+ }
+ }
+ } while(breakEntry);
+
+ return TheDProcessTracker.ResumeThread(aThread);
+ }
+
+//
+// DRM_DebugChannel::DoStepRange
+//
+TInt DRM_DebugChannel::DoStepRange(DThread *aThread, const TUint32 aStartAddress, const TUint32 aStopAddress, TBool aStepInto, TBool aResumeOnceOutOfRange, const TUint32 aNumSteps, TBool aUserRequest)
+ {
+ LOG_MSG("DRM_DebugChannel::DoStepRange()");
+
+ if (!aThread)
+ return KErrArgument;
+
+
+ TUint32 startAddress = (aStartAddress & 0x1) ? aStartAddress + 1 : aStartAddress;
+ TUint32 stopAddress = (aStopAddress & 0x1) ? aStopAddress + 1 : aStopAddress;;
+
+ // don't allow the user to step in the excluded ROM region. this could be called
+ // internally however. for example, the the special breakpoints we set to handle
+ // panics, exceptions, and library loaded events are in the user library, and we
+ // will need to step past the breakpoint before continuing the thread.
+ //if (aUserRequest && (startAddress >= iExcludedROMAddressStart) && (startAddress < iExcludedROMAddressEnd))
+ //{
+ // return KErrNotSupported;
+ //}
+
+ // set the temp breakpoint, and disable the breakpoint at the current PC if necessary
+ // if its not a user request, and we are just trying to resume from a breakpoint,
+ // then we don't need to check for stubs. The last parameter aUserRequest tells
+ // ModifyBreaksForStep to check for stubs or not. In some cases, the check for stubs
+ // is true even if its not a user request.For example, this is true in cases where
+ // we are doing a step range and the instruction in the range modified PC.
+ // in this case, DoStepRange will be called from the exception handler where
+ // we need to check for the stubs for the valid behavior. So truly, we don't need to check
+ // for stubs only when resuming from a breakpoint.
+ ReturnIfError(iStepper->ModifyBreaksForStep(aThread, startAddress, stopAddress, aResumeOnceOutOfRange, aUserRequest, aNumSteps));
+
+ LOG_MSG("DRM_DebugChannel::DoStepRange() - resuming thread\n");
+
+ return TheDProcessTracker.ResumeThread(aThread);
+ }
+
+/**
+Read memory from the specified addres into the aData descriptor. If there is a
+breakpoint set in the region of memory returned then the correct data value is
+inserted into the descriptor
+
+@param aThread pointer to thread whose address space memory is to be read from
+@param aAddress address to start reading memory from
+@param aLength length of memory block to read
+@param aData descriptor to read memory into
+
+@return KErrNone if memory read successfully,
+ KErrNotSupported if reading from the rom section is not supported,
+ KErrBadHandle if aThread is invalid,
+ or one of the other system wide error codes
+*/
+TInt DRM_DebugChannel::DoReadMemory(const DThread *aThread, const TUint32 aAddress, const TUint32 aLength, TDes8 &aData) const
+ {
+ LOG_MSG("DRM_DebugChannel::DoReadMemory()");
+
+ // make sure the parameters are valid
+ if (aLength > aData.MaxSize())
+ return KErrArgument;
+
+ TInt err = KErrNone;
+
+ // trap exceptions in case the address is invalid
+ XTRAPD(r, XT_DEFAULT, err = TryToReadMemory(aThread, (TAny *)aAddress, (TAny *)aData.Ptr(), aLength));
+
+ err = (KErrNone == r) ? err : r;
+
+ if (KErrNone == err)
+ {
+ aData.SetLength(aLength);
+
+ TPtr8 data((TUint8 *)aData.Ptr(), aLength, aLength);
+
+ // if we have any breakpoints in this range, put the actual instruction in the buffer
+ TBreakEntry* breakEntry = NULL;
+ do
+ {
+ breakEntry = iBreakManager->GetNextBreak(breakEntry);
+ if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry))
+ {
+ if ((breakEntry->iAddress >= aAddress) && (breakEntry->iAddress < (aAddress + aLength)))
+ {
+ TInt instSize;
+
+ switch(breakEntry->iMode)
+ {
+ case EArmMode:
+ instSize = 4;
+ break;
+
+ case EThumbMode:
+ instSize = 2;
+ break;
+
+ case EThumb2EEMode:
+ default:
+ LOG_MSG("DRM_DebugChannel::DoReadMemory() cannot fixup breakpoints with unsupported architecture");
+ return KErrNotSupported;
+ }
+ memcpy((TAny*)&data[breakEntry->iAddress - aAddress], (TAny *)breakEntry->iInstruction.Ptr(), instSize);
+ }
+ }
+ } while(breakEntry);
+ }
+
+ return err;
+ }
+
+/**
+Attempt to write memory to aThread's address space
+
+@param aThread thread to whose address space memory is to be written
+@param aAddress memory location to write memory to
+@param aLength number of bytes of data to write
+@param aData descriptor containing memory to write
+
+@return KErrNone if memory written successfully,
+ KErrArgument if aLength is greater than than the length of the aData
+ KErrBadHandle if aThread is invalid,
+ or another of the system wide error codes
+*/
+TInt DRM_DebugChannel::DoWriteMemory(DThread *aThread, const TUint32 aAddress, const TUint32 aLength, TDes8 &aData)
+ {
+ LOG_MSG("DRM_DebugChannel::DoWriteMemory()");
+
+ // make sure the parameters are valid
+ if (aLength > aData.Length())
+ return KErrArgument;
+
+ TInt err = KErrNone;
+
+ // trap exceptions in case the address is invalid
+ XTRAPD(r, XT_DEFAULT, err = TryToWriteMemory(aThread, (TAny *)aAddress, (TAny *)aData.Ptr(), aLength));
+
+ err = (KErrNone == r) ? err : r;
+
+ // reset any breakpoints we may have just overwritten
+ if (KErrNone == err)
+ {
+ TPtr8 data((TUint8 *)aData.Ptr(), aLength, aLength);
+
+ TBreakEntry* breakEntry = NULL;
+ do
+ {
+ breakEntry = iBreakManager->GetNextBreak(breakEntry);
+ if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry))
+ {
+ if ((breakEntry->iAddress >= aAddress) && (breakEntry->iAddress < (aAddress + aLength)))
+ {
+ // default to arm mode
+ TUint32 inst;
+ TInt instSize;
+
+ switch (breakEntry->iMode)
+ {
+ case EArmMode:
+ inst = KArmBreakPoint;
+ instSize = 4;
+ break;
+
+ case EThumbMode:
+ inst = KThumbBreakPoint;
+ instSize = 2;
+ break;
+
+ case EThumb2EEMode:
+ default:
+ LOG_MSG("DRM_DebugChannel::DoWriteMemory() cannot fixup breakpoints of unsupported architecture type");
+
+ return KErrNotSupported;
+ }
+
+ breakEntry->iInstruction.Copy(&data[breakEntry->iAddress - aAddress], instSize);
+ memcpy((TAny*)breakEntry->iAddress, (TAny *)&inst, instSize);
+ }
+ }
+
+ } while(breakEntry);
+ }
+ return err;
+ }
+
+//
+// DRM_DebugChannel::DoReadRegisters
+//
+TInt DRM_DebugChannel::DoReadRegisters(DThread *aThread, const TInt16 aFirstRegister, const TInt16 aLastRegister, TDes8 &aValues)
+ {
+ LOG_EVENT_MSG("DRM_DebugChannel::DoReadRegisters()");
+
+ // make sure the parameters are valid
+ if (!aThread || (aFirstRegister < 0) || (aLastRegister >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg))))
+ return KErrArgument;
+
+ // make sure the descriptor is big enough to hold the requested data
+ if ((TInt)((aLastRegister - aFirstRegister + 1) * sizeof(TArmReg)) > (aValues.MaxSize()))
+ return KErrArgument;
+
+ TArmRegSet regSet;
+ TUint32 unused;
+
+ NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused);
+
+ LOG_MSG2( "DRM_DebugChannel::DoReadRegistersLegacy() : unused = 0x%X\n", unused );
+
+ TArmReg *reg = ®Set.iR0;
+
+ if (!reg)
+ return KErrGeneral;
+
+ for (TInt16 i = aFirstRegister; i <= aLastRegister; i++)
+ aValues.Append((TUint8 *)®[i], sizeof(TArmReg));
+
+ return KErrNone;
+}
+
+/**
+ @prototype
+
+ Experimental function for determining whether a thread is suspended.
+
+ @param aThread thread to check if suspended
+
+ @return ETrue if the thread is suspended, EFalse if it isn't or does not exist
+ */
+TBool DRM_DebugChannel::CheckSuspended(const DThread *aThread) const
+ {
+ if(!aThread)
+ {
+ return EFalse;
+ }
+
+ if( (aThread->iNThread.iCsCount>0) && (aThread->iNThread.iCsFunction>0) )
+ {
+ return ETrue;
+ }
+
+ if(aThread->iNThread.iSuspendCount > 0)
+ {
+ return ETrue;
+ }
+ return EFalse;
+ }
+
+/**
+Read registers and store register values in aRegisterValues and the flags
+indicating which registers could be read in aRegisterFlags
+
+@param aThread thread to read registers from
+@param aRegisterIds array containing register IDs to read
+@param aRegisterValues array to store register values in
+@param aRegisterFlags array to store flags in
+
+@return KErrNone if registers were read successfully. Note that this does not
+ mean that all the registers could be read, the aRegisterFlags array
+ should be checked as to whether each individual register could be read,
+ KErrArgument if aThread is NULL, if an unknown register is specified in
+ aRegisterValues or if aRegisterValues is too small
+ KErrGeneral if there was a problem initialising the register set
+*/
+TInt DRM_DebugChannel::DoReadRegisters(DThread *aThread, const TDesC8 &aRegisterIds, TDes8 &aRegisterValues, TDes8& aRegisterFlags) const
+ {
+ LOG_MSG("DRM_DebugChannel::DoReadRegisters()");
+
+ // make sure the parameters are valid
+ if (!aThread)
+ return KErrArgument;
+
+ //Need to revisit this to determine whether there is a way to validate this
+#if 0
+ if ( !CheckSuspended(aThread) )
+ {
+ LOG_MSG2("DRM_DebugChannel::DoReadRegisters() thread with id 0x%08x is not suspended", aThread->iId);
+ return KErrInUse;
+ }
+#endif
+
+ //set lengths of output descriptors to 0 prior to filling
+ aRegisterValues.SetLength(0);
+ aRegisterFlags.SetLength(0);
+
+ TArmRegSet regSet;
+ TUint32 flags;
+
+ NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, flags);
+
+ LOG_MSG2( "DRM_DebugChannel::DoReadRegisters() : flags = 0x%X\n", flags );
+
+ TArmReg *regPtr = ®Set.iR0;
+
+ if (!regPtr)
+ return KErrGeneral;
+
+ TUint numberOfRegisters = aRegisterIds.Length() / sizeof(TRegisterInfo);
+
+ //iterate through registers setting the relevant aFlags value
+ for(TUint i=0; i<numberOfRegisters; i++)
+ {
+ //get current register id
+ TRegisterInfo reg;
+ TInt err = GetTRegisterInfo(aRegisterIds, i, reg);
+ //exit with the error value if there was an error
+ if(err != KErrNone)
+ return err;
+
+ //if unknown register then exit as can't know how many bytes this entry will
+ //represent in aRegisterValues
+ TTag registerTag;
+ TDebugFunctionality::GetRegister(reg, registerTag);
+ if(registerTag.iValue == EAccessUnknown)
+ {
+ return KErrArgument;
+ }
+
+ //get the current register id as a kernel register
+ TArmReg armReg;
+ err = GetKernelRegisterId(reg, armReg);
+ if((err == KErrNotSupported) || (registerTag.iValue == EAccessNone) || (registerTag.iValue == EAccessWriteOnly))
+ {
+ //reading this register is not supported
+ aRegisterFlags.Append(ENotSupported);
+ //just skip over this entry in the values buffer
+ if(aRegisterValues.Length() + registerTag.iSize > aRegisterValues.MaxLength())
+ {
+ //writing this value would cause overflow so exit
+ return KErrArgument;
+ }
+ aRegisterValues.SetLength(aRegisterValues.Length() + registerTag.iSize);
+ }
+ else
+ {
+ if(registerTag.iSize == sizeof(TArmReg))
+ {
+ if(GetFlagAtOffset(flags, armReg))
+ {
+ //set flag as valid
+ aRegisterFlags.Append(EValid);
+ }
+ else
+ {
+ // Even though the flag is invalid, we can return the value of the register
+ // and let the user decide what to do
+ aRegisterFlags.Append(EInValid);
+ }
+
+ if(aRegisterValues.Length() + sizeof(TArmReg) > aRegisterValues.MaxLength())
+ {
+ //writing this value would cause overflow so exit
+ return KErrArgument;
+ }
+ //write value into register into regSet
+ aRegisterValues.Append((TUint8 *)®Ptr[armReg], registerTag.iSize);
+ }
+ else
+ {
+ //currently all kernel supported registers are 4 bytes so
+ //return EBadSize. Would need updating if/when other register
+ //value sizes are supported
+ aRegisterFlags.Append(EBadSize);
+ aRegisterValues.SetLength(aRegisterValues.Length() + registerTag.iSize);
+ }
+ }
+ }
+ return KErrNone;
+ }
+
+//
+// DRM_DebugChannel::DoWriteRegisters
+//
+TInt DRM_DebugChannel::DoWriteRegisters(DThread *aThread, const TInt16 aFirstRegister, const TInt16 aLastRegister, TDesC8 &aValues)
+ {
+ LOG_MSG("DRM_DebugChannel::DoWriteRegisters()");
+
+ // make sure the parameters are valid
+ if (!aThread || (aFirstRegister < 0) || (aLastRegister >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg))))
+ return KErrArgument;
+
+ // make sure the descriptor is big enough to hold the data to write
+ if ((TInt)((aLastRegister - aFirstRegister + 1) * sizeof(TArmReg)) > (aValues.Length()))
+ return KErrArgument;
+
+ TArmRegSet regSet;
+ TUint32 unused;
+
+ NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused);
+
+ TArmReg *reg = ®Set.iR0;
+
+ for (TInt16 i = aFirstRegister; i <= aLastRegister; i++)
+ reg[i] = *(TUint32 *)&aValues[(i-aFirstRegister)*sizeof(TArmReg)];
+
+ NKern::ThreadSetUserContext(&aThread->iNThread, ®Set);
+
+ return KErrNone;
+ }
+
+/**
+Write registers and store flags indicating which registers could be read in
+aRegisterFlags
+
+@param aThread thread to write registers to
+@param aRegisterIds array containing register IDs to write
+@param aRegisterValues array containing register values to write
+@param aRegisterFlags array to store flags in
+
+@return KErrNone if registers were written successfully. Note that this does not
+ mean that all the registers could be written, the aRegisterFlags array
+ should be checked as to whether each individual register could be read,
+ KErrArgument if aThread is NULL, if the buffer passed in as
+ aRegisterValue is too small, or if an unknown register is requested,
+ KErrGeneral if there was a problem initialising the register set
+*/
+TInt DRM_DebugChannel::DoWriteRegisters(DThread *aThread, const TDesC8 &aRegisterIds, TDesC8 &aRegisterValues, TDes8 &aRegisterFlags) const
+ {
+ LOG_MSG("DRM_DebugChannel::DoWriteRegisters()");
+
+ // make sure the parameters are valid
+ if (!aThread)
+ return KErrArgument;
+
+
+ //get register values from kernel
+ TArmRegSet regSet;
+ TUint32 flags;
+ NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, flags);
+
+ //set lengths of output descriptors to 0 prior to filling
+ aRegisterFlags.SetLength(0);
+
+ //pointer to first kernel register
+ TArmReg *regPtr = ®Set.iR0;
+
+ if (!regPtr)
+ return KErrGeneral;
+
+ //calculate number of registers
+ TUint numberOfRegisters = aRegisterIds.Length() / sizeof(TRegisterInfo);
+
+ //iterate through registers setting the relevant aRegisterFlags value and
+ //setting the necessary value in regSet ready to write to kernel
+ for(TUint i=0, offset = 0; i<numberOfRegisters; i++)
+ {
+ //get current register id
+ TRegisterInfo reg;
+ TInt err = GetTRegisterInfo(aRegisterIds, i, reg);
+ //exit with the error value if there was an error
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ //if unknown register then exit as can't know how many bytes this entry will
+ //represent in aRegisterValues
+ TTag registerTag;
+ TDebugFunctionality::GetRegister(reg, registerTag);
+ if(registerTag.iValue == EAccessUnknown)
+ {
+ return KErrArgument;
+ }
+
+ //get the current register id as a kernel register
+ TArmReg armReg;
+ err = GetKernelRegisterId(reg, armReg);
+ if((err == KErrNotSupported) || (registerTag.iValue == EAccessNone) || (registerTag.iValue == EAccessReadOnly))
+ {
+ //writing to this register is not supported
+ aRegisterFlags.Append(ENotSupported);
+ }
+ else if(GetFlagAtOffset(flags, armReg))
+ {
+ if(registerTag.iSize == sizeof(TArmReg))
+ {
+ //set flag as valid
+ aRegisterFlags.Append(EValid);
+ if(offset + sizeof(TArmReg) > aRegisterValues.Length())
+ {
+ //getting this value would cause overflow so exit
+ return KErrArgument;
+ }
+ //write value into register into regSet
+ regPtr[armReg] = *(TUint32 *)&aRegisterValues[offset];
+ }
+ else
+ {
+ //currently all kernel supported registers are 4 bytes so
+ //return EBadSize. Would need updating if/when other register
+ //value sizes are supported
+ aRegisterFlags.Append(EBadSize);
+ }
+
+ }
+ else
+ {
+ //set flag as invalid as register value couldn't be read
+ aRegisterFlags.Append(EInValid);
+ }
+ offset+=registerTag.iSize;
+ }
+
+ //write the input data into the registers
+ NKern::ThreadSetUserContext(&aThread->iNThread, ®Set);
+
+ //return normally
+ return KErrNone;
+ }
+
+//
+// DRM_DebugChannel::DoSecurityCheck
+//
+TBool DRM_DebugChannel::DoSecurityCheck()
+ {
+ LOG_MSG("DRM_DebugChannel::DoSecurityCheck");
+ DProcess* clientProcess = iClientThread->iOwningProcess;
+ if (clientProcess)
+ {
+ SSecurityInfo secureInfo = clientProcess->iS;
+
+ LOG_MSG2("DoSecurityCheck - client secure id is 0x%08x",secureInfo.iSecureId);
+
+ // Ensure we really are communicating with the Debug Security Server
+ if (secureInfo.iSecureId == KUidDebugSecurityServer.iUid )
+ {
+ return ETrue;
+ }
+ }
+ return EFalse;
+ }
+
+/**
+Attempt to read memory from aThread's address space
+
+@param aThread thread from whose address space memory is to be read
+@param aSrc pointer to memory location to read memory from
+@param aDest pointer to memory location to write memory to
+@param aLength number of bytes of data to read
+
+@return KErrNone if memory read successfully,
+ or another of the system wide error codes
+*/
+TInt DRM_DebugChannel::TryToReadMemory(const DThread *aThread, const TAny *aSrc, TAny *aDest, const TUint32 aLength) const
+ {
+ LOG_MSG("DRM_DebugChannel::TryToReadMemory()");
+
+ // make sure the parameters are valid
+ if (!aThread)
+ return KErrArgument;
+
+ //Need to revisit this to determine whether there is a way to validate this
+#if 0
+ //check that the thread is suspended before reading the memory
+ if ( !CheckSuspended(aThread) )
+ {
+ LOG_MSG2("DRM_DebugChannel::TryToReadMemory() thread with id 0x%08x is not suspended", aThread->iId);
+ return KErrInUse;
+ }
+#endif
+
+ LOG_MSG2("Using Kern::ThreadRawRead to read memory at address %x", aSrc);
+ return Kern::ThreadRawRead((DThread *)aThread, aSrc, aDest, aLength);
+ }
+
+/**
+Attempt to write memory to aThread's address space
+
+@param aThread thread to whose address space memory is to be written
+@param aDest pointer to memory location to write memory to
+@param aSrc pointer to memory location to read memory from
+@param aLength number of bytes of data to write
+
+@return KErrNone if memory written successfully, or another of the system wide
+ error codes
+*/
+TInt DRM_DebugChannel::TryToWriteMemory(const DThread *aThread, TAny *aDest, const TAny *aSrc, const TUint32 aLength)
+ {
+ LOG_MSG("DRM_DebugChannel::TryToWriteMemory()");
+
+
+ LOG_MSG2("Using Kern::ThreadRawWrite to write memory at address %x", (TUint32)aDest);
+ return Kern::ThreadRawWrite((DThread *)aThread, aDest, aSrc, aLength, iClientThread);
+ }
+
+/**
+@deprecated use DRM_DebugChannel::ReadKernelRegisterValue(DThread *aThread, const TArmReg aKernelRegisterId, T4ByteRegisterValue &aValue) instead
+*/
+TInt32 DRM_DebugChannel::ReadRegister(DThread *aThread, TInt aNum)
+ {
+ LOG_MSG("DRM_DebugChannel::ReadRegister()");
+
+ if (!aThread || (aNum < 0) || (aNum >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg))))
+ {
+ LOG_MSG2("Invalid register number (%d) passed to ReadRegister", aNum);
+ return 0;
+ }
+
+ TArmRegSet regSet;
+ TUint32 unused;
+
+ NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused);
+
+ TArmReg *reg = ®Set.iR0;
+
+ return ((TUint32 *)reg)[aNum];
+ }
+
+/**
+Given a TArmReg register ID, read the value of the register. The register value
+will be stored in aValue if the register could be read.
+
+@param aThread thread to read register from
+@param aKernelRegisterId ID of register to read from
+@param aValue value read from register
+
+@return KErrNone if value was successfully stored in aValue,
+ KErrNotSupported if aKernelRegister is not supported by the debug
+ security server,
+ or a return value from DRM_DebugChannel::ReadDebugRegisterValue()
+*/
+TInt32 DRM_DebugChannel::ReadKernelRegisterValue(DThread *aThread, const TArmReg aKernelRegisterId, T4ByteRegisterValue &aValue) const
+ {
+ //get register ID as a TRegisterInfo ID
+ TRegisterInfo regId;
+ TInt err = GetDebugRegisterId(aKernelRegisterId, regId);
+ if(err != KErrNone)
+ return err;
+
+ //get the value for the register
+ err = ReadDebugRegisterValue(aThread, regId, aValue);
+ return err;
+ }
+
+/**
+Given a TRegisterInfo register ID, read the value of this register. The
+register value will be stored in aValue if the register could be read.
+
+@param aThread thread to read register from
+@param aDebugRegisterId ID of register to read from
+@param aValue value read from register
+
+@return KErrNone if value was successfully stored in aValue,
+ TRegisterFlag::EInValid if value could not be read from the register,
+ TRegisterFlag::ENotSupported if the register is not supported,
+ KErrNoMemory if temporary memory could not be allocated,
+ or a return value from DRM_DebugChannel::DoReadRegisters
+*/
+TInt32 DRM_DebugChannel::ReadDebugRegisterValue(DThread *aThread, const TRegisterInfo aDebugRegisterId, T4ByteRegisterValue &aValue) const
+ {
+ //allocate temporary buffers to store data
+ NKern::ThreadEnterCS();
+ TUint8* id = (TUint8*)Kern::Alloc(sizeof(TRegisterInfo));
+ NKern::ThreadLeaveCS();
+ if(id == NULL)
+ {
+ return KErrNoMemory;
+ }
+
+ TPtr8 idPtr(id, sizeof(TRegisterInfo));
+
+ NKern::ThreadEnterCS();
+ TUint8* value = (TUint8*)Kern::Alloc(sizeof(T4ByteRegisterValue));
+ NKern::ThreadLeaveCS();
+ if(value == NULL)
+ {
+ return KErrNoMemory;
+ }
+ TPtr8 valuePtr(value, sizeof(T4ByteRegisterValue));
+
+ NKern::ThreadEnterCS();
+ TUint8* flag = (TUint8*)Kern::Alloc(sizeof(TUint8));
+ NKern::ThreadLeaveCS();
+ if(flag == NULL)
+ {
+ return KErrNoMemory;
+ }
+ TPtr8 flagPtr(flag, sizeof(TUint8));
+
+ //store register id in buffer
+ idPtr.Append((TUint8*)&aDebugRegisterId, sizeof(TRegisterInfo));
+
+ //read registers
+ TInt err = DoReadRegisters(aThread, idPtr, valuePtr, flagPtr);
+ if(err == KErrNone)
+ {
+ if(*flag == EValid)
+ {
+ //register could be read so store value
+ aValue = *(T4ByteRegisterValue*)value;
+ }
+ else
+ {
+ //register couldn't be read for some reason
+ err = *flag;
+ }
+ }
+
+ //free memory
+ NKern::ThreadEnterCS();
+ Kern::Free(id);
+ Kern::Free(value);
+ Kern::Free(flag);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+//
+// DRM_DebugChannel::NotifyAgentsFromEventPid
+//
+TBool DRM_DebugChannel::NotifyAgentsFromEventPid(const TDriverEventInfo& aEventInfo)
+ {
+
+ // Look for the relevant DTargetProcess
+ // We can find out the relevant process id from aEventInfo
+ TUint32 pid = aEventInfo.iProcessId;
+
+#ifdef _DEBUG
+ if (aEventInfo.iEventType != EEventsUserTrace)
+ {
+ LOG_MSG3(" NotifyAgentsFromEventPid - pid = 0x%x, evType=%d", pid, aEventInfo.iEventType);
+ }
+#endif
+
+ //opening handle to process
+ DProcess* targetProcess = DebugUtils::OpenProcessHandle(pid);
+
+ if(!targetProcess)
+ {
+ LOG_EVENT_MSG("process does not exist!");
+ return EFalse;
+ }
+
+ // Are we debugging this process - decide based on iFileName
+ DCodeSeg* p = targetProcess->iCodeSeg;
+ DTargetProcess* foundProcess;
+ if (p)
+ {
+ foundProcess = TheDProcessTracker.FindProcess(*(p->iFileName));
+ }
+ else
+ {
+ // special case: may not have a code seg in some cases. in which case we tell everyone!
+ if (targetProcess->iName)
+ {
+ // copy the name of the process
+ foundProcess = TheDProcessTracker.FindProcess(*(targetProcess->iName));
+ }
+ else
+ {
+ foundProcess = NULL;
+ }
+ }
+
+ //close the handle
+ targetProcess->Close(NULL);
+
+ if (foundProcess)
+ {
+ foundProcess->NotifyEvent(aEventInfo);
+ return ETrue;
+ }
+ else
+ {
+ // Check if there's an attach-to-all handler
+ DDebugAgent* agent = TheDProcessTracker.GetCurrentAgentAttachedToAll();
+ if (agent)
+ {
+ LOG_EVENT_MSG2(" NotifyAgentsFromEventPid - found attach all agent 0x%lx", agent->Id());
+ agent->NotifyEvent(aEventInfo);
+ return ETrue;
+ }
+ }
+
+ // we are not debugging this process
+ return EFalse;
+ }
+
+DECLARE_STANDARD_LDD()
+ {
+ return new DRM_DebugDriverFactory;
+ }
+
+/**
+Helper function
+
+Allocates memory in current thread with a max length the same as aSrcDes. If
+aReadFromClient is true (as it is by default) then the data from aSrdDes is
+copied into the allocated aDestDes buffer.
+
+Use of this function should be followed at a later time by a call such as
+Kern::Free(aDestDes.Ptr())
+
+@param aThread pointer to thread to read data from
+@param aSrcDes descriptor in aThread to read data from
+@param aDestDes location to read data to. Memory is allocated at this location,
+ if memory is already allocated at this location then the function will
+ return KErrArgument
+@param aReadFromClient if false then data is not actually read from the
+ client, the memory is simply allocated
+@param aOffest offset into aSrcDes to start reading from. Default is 0.
+
+@return KErrNone if there were no problems,
+ KErrArgument if aDestDes.Ptr() != NULL or aSrcDes has max length 0,
+ KErrNoMemory if could not allocate memory,
+ or one of the other system wide error codes
+*/
+TInt DRM_DebugChannel::AllocAndReadDes(DThread *aThread, const TDesC8& aSrcDes, TPtr8& aDestDes, const TBool aReadFromClient, const TUint aOffset) const
+ {
+
+ //check thread is not null
+ if(!aThread)
+ {
+ return KErrArgument;
+ }
+
+ //check aDestDes is empty
+ if(aDestDes.Ptr() != NULL)
+ {
+ return KErrArgument;
+ }
+
+ //get the source descriptor's max length and exit if 0
+ TUint srcMaxLength = Kern::ThreadGetDesMaxLength(aThread, &aSrcDes);
+ if(srcMaxLength == 0)
+ {
+ return KErrNone;
+ }
+
+ //allocate memory and return if none available
+ NKern::ThreadEnterCS();
+ TUint8 *destPtr = (TUint8*)Kern::Alloc(srcMaxLength);
+ NKern::ThreadLeaveCS();
+ if (!destPtr)
+ {
+ return KErrNoMemory;
+ }
+
+ //point the TPtr8 at the target memory
+ aDestDes.Set(destPtr, srcMaxLength, srcMaxLength);
+
+ if(aReadFromClient)
+ {
+ //read data from the client thread and return status code
+ return Kern::ThreadDesRead(aThread, &aSrcDes, aDestDes, aOffset);
+ }
+ else
+ {
+ return KErrNone;
+ }
+ }
+
+/**
+Helper function to extract a TRegisterInfo value from a descriptor containing
+binary data.
+
+@param aRegisterIds descriptor containing register IDs
+@param aOffset offset in bytes into the descriptor to start reading data from.
+ If this value is not a multiple of sizeof(TRegisterInfo) then a
+ KErrArgument error is returned.
+@param aValue will contain the returned value
+
+@return KErrNone if aValue was set correctly, KErrArgument if bad arguments
+ were passed in
+*/
+TInt DRM_DebugChannel::GetTRegisterInfo(const TDesC8 &aRegisterIds, const TUint aIndex, TRegisterInfo &aValue) const
+ {
+ TUint length = aRegisterIds.Length();
+
+ TUint size = sizeof(TRegisterInfo);
+
+ //check that not trying to read past end of descriptor
+ if((aIndex + 1) * size > length)
+ return KErrArgument;
+
+ //get pointer to descriptor's data
+ const TUint8 *dataPtr = aRegisterIds.Ptr();
+ const TRegisterInfo *registerId = reinterpret_cast<const TRegisterInfo*>(dataPtr + (aIndex * size));
+
+ aValue = *registerId;
+
+ return KErrNone;
+ }
+
+/**
+Helper function to get the kernel register ID of the TRegisterInfo defined register.
+
+@param aDebugRegister the debug register ID to return the kernel ID for
+@param aKernelRegister corresponding value of register aDebugRegister
+
+@return KErrNone if translation occurred without problems
+ KErrNotSupported if aDebugRegister is not supported by the kernel
+*/
+TInt DRM_DebugChannel::GetKernelRegisterId(const TRegisterInfo aDebugRegister, TArmReg& aKernelRegister) const
+ {
+ if(Register::IsCoreReg(aDebugRegister))
+ {
+ TUint id = Register::GetCoreRegId(aDebugRegister);
+ //first 17 registers match the first 17 kernel registers
+ if(id < 17)
+ {
+ aKernelRegister = id;
+ }
+ else
+ {
+ return KErrNotSupported;
+ }
+ }
+ else if(Register::IsCoproReg(aDebugRegister))
+ {
+ TUint32 crn = Register::GetCRn(aDebugRegister);
+ TUint32 crm = Register::GetCRm(aDebugRegister);
+ TUint32 opcode1 = Register::GetOpcode1(aDebugRegister);
+ TUint32 opcode2 = Register::GetOpcode2(aDebugRegister);
+ TUint32 coproNum = Register::GetCoproNum(aDebugRegister);
+
+ //each coprocessor register has potentially different characteristics
+ //so need to identify each individually
+
+ //this is the DACR, the ARM ARM specifies that the CRn and the
+ //Opcodes are not relevant, section B3-24, point 3.7.3
+ if((coproNum == 15) && (crm == 3))
+ {
+ aKernelRegister = EArmDacr;
+ }
+ else
+ {
+ return KErrNotSupported;
+ }
+ }
+ else // might be supported at a later date
+ {
+ return KErrNotSupported;
+ }
+
+ return KErrNone;
+ }
+
+/**
+Helper function to get the debug register ID of the kernel defined register.
+
+@param aKernelRegister the kernel register ID to return the debug ID for
+@param aDebugRegister corresponding value of register aKernelRegister
+
+@return KErrNone if translation occured without problems
+ KErrNotSupported if aKernelRegister is not supported by the debug
+ security server
+*/
+TInt DRM_DebugChannel::GetDebugRegisterId(const TArmReg aKernelRegister, TRegisterInfo &aDebugRegister) const
+ {
+
+ // registers 0 - 15 and the CPSR share the same values as with the debug enums
+ if(aKernelRegister < 17)
+ {
+ TUint32 id = aKernelRegister;
+ aDebugRegister = id << 8;
+ }
+ //the DACR value is special and corresponds to EDF_Register_DACR
+ else if(aKernelRegister == EArmDacr)
+ {
+ aDebugRegister = 0x00300f01;
+ }
+ // must be an unsupported register, return an error as such
+ else
+ {
+ return KErrNotSupported;
+ }
+
+ //found a supported register so return KErrNone
+ return KErrNone;
+ }
+
+/**
+Helper function to find out whether the aIndex flag is set. This is equivalent
+to the aIndex bit of aFlags being non-zero.
+
+@param aFlags set of flags
+@param aIndex offset into aFlags to get flag from
+
+@return ETrue if bit is set, EFalse if not
+*/
+TBool DRM_DebugChannel::GetFlagAtOffset(const TUint32 aFlags, const TArmReg aIndex) const
+ {
+ return aFlags & (1<<aIndex);
+ }
+
+/* Register the attachment of a debug agent to a process to be debugged
+ *
+ * @param a1 - TDes8 target process name
+ * @param a2 - &TUint64 - Debug Agent Id
+ *
+ * @return - KErrNone if successful. KErrArgument if the filepath is not a valid size.
+ * KErrOutOfMemory if there is insufficient memory. Or one of the other system wide error codes
+ * if appropriate.
+ */
+TInt DRM_DebugChannel::AttachProcess(TAny* a1, TAny* a2)
+ {
+ LOG_MSG("DRM_DebugChannel::AttachProcess()");
+
+ // Validate the supplied TDes8 target process name in a1
+ TInt length = Kern::ThreadGetDesLength(iClientThread, a1);
+ if (length < 0) return length;
+
+ // Check the processname is a valid size for a filepath
+ if (length < 1 || length >= KMaxPath)
+ {
+ return KErrArgument;
+ }
+
+ // Allocate space to store the target process name in a kernel-side TPtr8
+ NKern::ThreadEnterCS();
+ TUint8* buffer = (TUint8*)Kern::AllocZ(length);
+ NKern::ThreadLeaveCS();
+ if (buffer==NULL)
+ {
+ // Out of memory
+ return KErrNoMemory;
+ }
+
+ // A temporary descriptor to store the target process name
+ TPtr8 targetProcessName(buffer,length,length);
+
+ // Read the user-side data into targetProcessName
+ TInt err = Kern::ThreadDesRead(iClientThread,a1,targetProcessName,0,KChunkShiftBy0);
+ if (err != KErrNone)
+ {
+ // Could not read the user-side descriptor containing the target process name
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+ // Obtain the Debug Agent Id
+ TUint64 debugAgentId = 0;
+
+ err = Kern::ThreadRawRead(iClientThread,a2,&debugAgentId,sizeof(debugAgentId));
+ if (err != KErrNone)
+ {
+ // Something bad happened so free the memory and return
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+ return err;
+ }
+
+ // Add the target process to our list of tracked processes
+ err = TheDProcessTracker.AttachProcess(targetProcessName, debugAgentId);
+
+ // Free the kernel-side memory containing targetProcessName data
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+/* Register the detachment of a debug agent to a process to be debugged.
+ *
+ * @param - a1 TDes8 target process name in a1
+ * @param a2 - &TUint64 - Debug Agent Id
+ *
+ * @return - KErrNone if successful. KErrArgument if the filepath is not a valid size.
+ * KErrOutOfMemory if there is insufficient memory. Or one of the other system wide error codes
+ * if appropriate.
+ */
+TInt DRM_DebugChannel::DetachProcess(TAny* a1, TAny* a2)
+ {
+ TInt length = Kern::ThreadGetDesLength(iClientThread, a1);
+ if (length < 0)
+ {
+ return length;
+ }
+
+ if (length < 1 || length >= KMaxPath)
+ {
+ return KErrArgument;
+ }
+
+ HBuf* targetProcessName = HBuf::New(length);
+ if (targetProcessName == NULL)
+ {
+ // Out of memory
+ return KErrNoMemory;
+ }
+
+ // Read the user-side data into buf
+ TInt err = Kern::ThreadDesRead(iClientThread,a1,*targetProcessName,0,KChunkShiftBy0);
+ if (err != KErrNone)
+ {
+ // Something bad happened so free the memory and return
+ delete targetProcessName;
+ return err;
+ }
+
+ // Obtain the AgentId
+ TUint64 debugAgentId = 0;
+
+ err = Kern::ThreadRawRead(iClientThread,a2,&debugAgentId,sizeof(debugAgentId));
+ if (err == KErrNone)
+ {
+ // Remove the process from our list of tracked processes
+ err = TheDProcessTracker.DetachProcess(*targetProcessName, debugAgentId);
+ }
+
+ // Free the kernel-side memory containing targetProcessName data
+ delete targetProcessName;
+ return err;
+ }
+
+/* Register the detachment of a debug agent from all processes being debugged.
+ *
+ * @param - a1 - &TUint64 Debug Agent Id.
+ * @return - KErrNone if successful. One of the system-wide error codes otherwise.
+ */
+TInt DRM_DebugChannel::DetachAgent(TAny* a1, TAny* a2)
+ {
+ // Obtain the AgentId
+ TUint64 debugAgentId = 0;
+
+ TInt err = Kern::ThreadRawRead(iClientThread,a1,&debugAgentId,sizeof(debugAgentId));
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ // Remove the process from our list of tracked processes
+ return TheDProcessTracker.DetachAgent(debugAgentId);
+ }
+
+/* Set the action associated with a particular kernel event for a given agent and target process
+ *
+ * @param - a1 TDes8 target process name in a1
+ * @param - a2 &TRM_DebugEventActionInfo
+ * @return - KErrNone if successful. KErrArgument if the filepath is an invalid size. Or one of
+ * the other system wide error codes if appropriate.
+ */
+TInt DRM_DebugChannel::SetEventAction(TAny* a1, TAny* a2)
+ {
+ // Validate the supplied TDes8 target process name in a1
+ TInt length, maxLength;
+ TUint8* aPtr;
+
+ TInt err = Kern::ThreadGetDesInfo(iClientThread,\
+ a1,\
+ length,\
+ maxLength,\
+ aPtr,\
+ EFalse);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ if (length < 1 || length >= KMaxPath)
+ {
+ return KErrArgument;
+ }
+
+ if (maxLength < 1 || maxLength >= KMaxPath)
+ {
+ return KErrArgument;
+ }
+
+ // Allocate space to store the target process name in a kernelspace TPtr8
+ NKern::ThreadEnterCS();
+ TUint8* buffer = (TUint8*)Kern::AllocZ(length);
+ NKern::ThreadLeaveCS();
+ if (buffer==NULL)
+ {
+ // Out of memory
+ return KErrNoMemory;
+ }
+ TPtr8 targetProcessName(buffer,length,length);
+
+ // Read the user-side data into targetProcessName
+ err = Kern::ThreadDesRead(iClientThread,a1,targetProcessName,0,KChunkShiftBy0);
+ if (err != KErrNone)
+ {
+ // Something bad happened so free the memory and return
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+ // Read the Event and Action from the user-side
+ TRM_DebugEventActionInfo info(0,0,0);
+
+ err = Kern::ThreadRawRead(iClientThread, a2, &info, sizeof(info));
+ if (err != KErrNone)
+ {
+ // Could not read event action data from the user-side
+
+ // Free memory used for targetProcessName
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+ DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId( targetProcessName, info.iAgentId );
+ if (debugAgent != NULL)
+ {
+ // Set the event action
+ err = debugAgent->SetEventAction((TEventType)info.iEvent,(TKernelEventAction)info.iAction);
+ }
+ else
+ {
+ // Bad agent means there is no tracking agent
+ LOG_MSG2("Cannot locate debug agent with pid 0x%0xlx",info.iAgentId);
+ err = KErrNotFound;
+ }
+
+ // Free memory used for targetProcessName
+ NKern::ThreadEnterCS();
+ Kern::Free(buffer);
+ NKern::ThreadLeaveCS();
+
+ return err;
+
+ }
+
+TInt DRM_DebugChannel::Step(const TUint32 aThreadId, const TUint32 aNumSteps)
+ {
+ LOG_MSG3("DRM_DebugChannel::Step(aThreadId = 0x%08x, aNumSteps = 0x%08x)\n",aThreadId,aNumSteps);
+
+ DThread* thread = DebugUtils::OpenThreadHandle(aThreadId);
+
+ if (thread == NULL)
+ {
+ // The thread terminated before we could open it.
+ LOG_MSG2("DRM_DebugChannel::Step - Could not open thread %u", aThreadId);
+
+ return KErrArgument;
+ }
+
+ // We simply repeat this for desired number of steps
+ TInt err = KErrNone;
+
+ // Need to step from the current location for 'n' steps
+ TUint32 startAddress;
+
+ // We always step from the current PC.
+ err = ReadKernelRegisterValue(thread, PC_REGISTER, startAddress);
+ if(err != KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::Step - Could not read the PC: %d", err);
+
+ // Close the handle
+ thread->Close(NULL);
+
+ return err;
+ }
+
+ err = DoStepRange(thread, startAddress, startAddress, ETrue, EFalse, aNumSteps, ETrue);
+
+ if (err != KErrNone)
+ {
+ // There was a problem, return straightaway
+ LOG_MSG("DRM_DebugChannel::Step - failed to step");
+ }
+
+ // Close the handle
+ thread->Close(NULL);
+
+ return err;
+ }
+
+TInt DRM_DebugChannel::KillProcess(const TUint32 aProcessId, const TInt aReason)
+ {
+ LOG_MSG3("DRM_DebugChannel::KillProcess(aProcessId = 0x%08x, aReason = 0x%08x)\n",aProcessId,aReason);
+
+ DProcess* process = DebugUtils::OpenProcessHandle(aProcessId);
+
+ if (process == NULL)
+ {
+ // The process terminated before we could open it to kill it ourselves.
+ LOG_MSG2("DRM_DebugChannel::KillProcess - Could not open process %u", aProcessId);
+
+ return KErrArgument;
+ }
+
+ TInt err = KErrNone;
+
+ DebugSupport::TerminateProcess(process,aReason);
+
+ // Close the handle
+ process->Close(NULL);
+
+ return err;
+ }
+
+/* Security critical - this checks whether the specified process is debuggable or not
+ *
+ * @param aProcessId - The process id of the process to check
+ * @return KErrNone if debuggable, KErrPermissionDenied if not debuggable.
+ */
+TInt DRM_DebugChannel::IsDebuggable(const TUint32 aProcessId)
+ {
+ /* In order to ensure that only processes which are debuggable
+ * can be debugged, this function enables the security server
+ * to read the DProcess.iDebugAttributes field and ensure
+ * the process was created from a debuggable executable.
+ */
+ LOG_MSG2("DRM_DebugChannel::IsDebuggable(aProcessId 0x%08x)\n",aProcessId);
+
+ TInt err = KErrPermissionDenied;
+
+ DProcess* process = DebugUtils::OpenProcessHandle(aProcessId);
+ if (process)
+ {
+ if (process->iDebugAttributes & TProcessCreateInfo::EDebugAllowed)
+ {
+ // Yes this process exists and is debuggable
+ err = KErrNone;
+ }
+ process->Close(NULL);
+ }
+
+ if (err == KErrNone)
+ {
+ LOG_MSG2("DRM_DebugChannel::IsDebuggable(aProcessId 0x%08x) - Yes it is debuggable\n",aProcessId);
+ }
+
+ return err;
+ }