sl@0: // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Purpose: The DProcessTracker object tracks which processes are being sl@0: // debugged. The DProcessTracker class uses a DTargetProcess object for sl@0: // each process being debugged. sl@0: // Note: Although TheDProcessTracker object is a global, it should be unique sl@0: // as only the Debug Security Server should load and use this driver. sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "nk_priv.h" sl@0: #include sl@0: sl@0: #include "d_target_process.h" sl@0: #include "debug_logging.h" sl@0: #include "debug_utils.h" sl@0: sl@0: // ctor sl@0: DTargetProcess::DTargetProcess() sl@0: :iProcessName(0,0,0) sl@0: { sl@0: } sl@0: sl@0: // dtor sl@0: DTargetProcess::~DTargetProcess() sl@0: { sl@0: // Delete the space allocated for the name if any sl@0: if (iProcessName.Ptr() != 0) sl@0: { sl@0: NKern::ThreadEnterCS(); sl@0: Kern::Free((TAny*)iProcessName.Ptr()); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: //Reset the array and delete the objects that its members point to sl@0: NKern::ThreadEnterCS(); sl@0: iAgentList.ResetAndDestroy(); sl@0: NKern::ThreadLeaveCS(); sl@0: } sl@0: sl@0: // Compare two DTargetProcess items. They are the same if they have the same name. sl@0: TInt DTargetProcess::Compare(const DTargetProcess& aFirst, const DTargetProcess& aSecond) sl@0: { sl@0: return aFirst.iProcessName.Compare(aSecond.iProcessName); sl@0: } sl@0: sl@0: // Set the name of the process we are tracking sl@0: TInt DTargetProcess::SetProcessName(const TDesC8& aProcessName) sl@0: { sl@0: // Argument checking sl@0: if (aProcessName.Length() < 1) sl@0: { sl@0: return KErrArgument; sl@0: } sl@0: sl@0: // Allocate some memory to store the name sl@0: TUint length = aProcessName.Length(); sl@0: sl@0: NKern::ThreadEnterCS(); sl@0: TUint8* buffer = (TUint8*)Kern::AllocZ(length); sl@0: NKern::ThreadLeaveCS(); sl@0: if (buffer==NULL) sl@0: { sl@0: // Out of memory sl@0: return KErrNoMemory; sl@0: } sl@0: sl@0: // Set iProcessName to use the alloc'd buffer sl@0: iProcessName.Set(buffer,length,length); sl@0: sl@0: // Store aProcessName within this object sl@0: iProcessName.Copy(aProcessName); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: // Obtain the name of the process being tracked sl@0: const TPtr8& DTargetProcess::ProcessName(void) sl@0: { sl@0: return iProcessName; sl@0: } sl@0: sl@0: // Returns a pointer to the DDebugAgent with aAgentId. sl@0: // If the agent is not in the list, it returns NULL. sl@0: DDebugAgent* DTargetProcess::Agent(TUint64 aAgentId) sl@0: { sl@0: for(TInt i = 0; i < iAgentList.Count(); i++) sl@0: { sl@0: if (iAgentList[i]->Id() == aAgentId) sl@0: { sl@0: return iAgentList[i]; sl@0: } sl@0: } sl@0: sl@0: // what do we return if we don't have any agents? sl@0: return NULL; sl@0: } sl@0: sl@0: // Adds aAgentId as a tracking agent for this process. sl@0: TInt DTargetProcess::AddAgent(TUint64 aAgentId) sl@0: { sl@0: LOG_MSG("DTargetProcess::AddAgent()"); sl@0: DDebugAgent* agent = DDebugAgent::New(aAgentId); sl@0: if(agent == NULL) sl@0: { sl@0: LOG_MSG("DTargetProcess::AddAgent() couldn't allocate memory for DDebugAgent"); sl@0: return KErrNoMemory; sl@0: } sl@0: return iAgentList.Insert(agent,0); sl@0: } sl@0: sl@0: // Stops tracking the process with this agent sl@0: TInt DTargetProcess::RemoveAgent(TUint64 aAgentId) sl@0: { sl@0: // We need to find and then remove the agent sl@0: for(TUint i = 0; i < iAgentList.Count(); i++) sl@0: { sl@0: if (iAgentList[i]->Id() == aAgentId) sl@0: { sl@0: delete iAgentList[i]; sl@0: iAgentList.Remove(i); sl@0: return KErrNone; sl@0: } sl@0: } sl@0: sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: // Index through the agents by position sl@0: DDebugAgent* DTargetProcess::operator[](TInt aIndex) sl@0: { sl@0: return iAgentList[aIndex]; sl@0: } sl@0: sl@0: // returns the number of agents tracking this process. sl@0: TInt DTargetProcess::AgentCount(void) sl@0: { sl@0: return iAgentList.Count(); sl@0: } sl@0: sl@0: /** sl@0: Resume the specified thread sl@0: sl@0: @param aThread thread to resume sl@0: sl@0: @return KErrNone if the thread has previously been suspended and is resumed, sl@0: KErrNotFound if the thread has not previously been suspended sl@0: */ sl@0: TInt DTargetProcess::ResumeThread(DThread* aThread) sl@0: { sl@0: LOG_MSG2("DTargetProcess::ResumeSuspendedThread(): thread=0x%08x", aThread); sl@0: TInt err1 = ResumeSuspendedThread(aThread); sl@0: LOG_MSG2("DTargetProcess::ResumeSuspendedThread(): ret=%d)", err1); sl@0: TInt err2 = ResumeFrozenThread(aThread->iNThread); sl@0: LOG_MSG2("DTargetProcess::ResumeFrozenThread(): ret=%d)", err2); sl@0: //if resuming the suspended thread failed for an obscure reason return it sl@0: if((err1 != KErrNotFound) && (err1 != KErrNone)) sl@0: { sl@0: LOG_MSG2("DTargetProcess::ResumeThread() unexpected exit, err1: %d", err1); sl@0: return err1; sl@0: } sl@0: //if resuming the frozen thread failed for an obscure reason return it sl@0: if((err2 != KErrNotFound) && (err2 != KErrNone)) sl@0: { sl@0: LOG_MSG2("DTargetProcess::ResumeThread() unexpected exit, err2: %d", err2); sl@0: return err2; sl@0: } sl@0: // if resuming the suspended thread succeeded in both cases, we have a consistency problem sl@0: if ((err1 == KErrNone) && (err2 == KErrNone)) sl@0: { sl@0: LOG_MSG("DTargetProcess::ResumeThread() unexpected exit, err1 == err2 == KErrNone"); sl@0: } sl@0: sl@0: //if the thread was in neither list return KErrNotFound, otherwise KErrNone sl@0: return ((err1 == KErrNone) || (err2 == KErrNone)) ? KErrNone : KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: Resume the specified frozen thread sl@0: sl@0: @param aThread thread to resume sl@0: sl@0: @return KErrNone if the thread has previously been suspended and is resumed, sl@0: KErrNotFound if the thread has not previously been suspended sl@0: */ sl@0: TInt DTargetProcess::ResumeFrozenThread(NThread& aThread) sl@0: { sl@0: for(TInt i=0; iiOwningThread == &aThread) sl@0: { sl@0: NKern::FSSignal(iFrozenThreadSemaphores[i]); sl@0: NKern::ThreadEnterCS(); sl@0: delete iFrozenThreadSemaphores[i]; sl@0: NKern::ThreadLeaveCS(); sl@0: iFrozenThreadSemaphores.Remove(i); sl@0: return KErrNone; sl@0: } sl@0: } sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: Resume the specified suspended thread sl@0: sl@0: @param aThread thread to resume sl@0: sl@0: @return KErrNone if the thread has previously been suspended and is resumed, sl@0: KErrNotFound if the thread has not previously been suspended sl@0: */ sl@0: TInt DTargetProcess::ResumeSuspendedThread(DThread* aThread) sl@0: { sl@0: TUint64 threadId = (TUint64)aThread->iId; sl@0: for(TInt i=0; i Kern::ThreadResume() 0x%x", aThread); sl@0: Kern::ThreadResume(*aThread); sl@0: return KErrNone; sl@0: } sl@0: } sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: Suspend the specified thread sl@0: sl@0: @param aThread thread to suspend sl@0: sl@0: @param aFreezeThread suspend the thread on a Fast Semaphore if sl@0: ETrue. EFalse means suspend by calling Kern::Suspend. sl@0: sl@0: @return KErrNone if the thread is successfully suspended, sl@0: KErrAlreadyExists if the agent has already suspended the thread, sl@0: or one of the other system wide error codes sl@0: sl@0: This function suspends a thread by calling Kern::Thread Suspend. sl@0: sl@0: An alternative means of suspending the _current_ thread only sl@0: is by call DTargetProcess::FreezeThread. This will ensure that sl@0: the current thread is suspended when exception processing for this sl@0: thread completes (see rm_debug_eventhandler.cpp) sl@0: sl@0: */ sl@0: TInt DTargetProcess::SuspendThread(DThread* aThread, TBool aFreezeThread) sl@0: { sl@0: // should check if this thread is already suspended/frozen and return if so sl@0: // but just warn for the moment. sl@0: if (CheckSuspended(aThread)) sl@0: { sl@0: // thread was already suspended, don't bother doing it again sl@0: LOG_MSG2("DTargetProcess::SuspendThread - Thread Id 0x%08x already suspended\n",aThread->iId); sl@0: //return KErrAlreadyExists; sl@0: } sl@0: sl@0: return aFreezeThread ? FreezeThread() : DoSuspendThread(aThread); sl@0: } sl@0: sl@0: /** sl@0: Freeze the current thread sl@0: sl@0: @return KErrNone if the thread is successfully suspended, sl@0: KErrAlreadyExists if the agent has already suspended the thread, sl@0: or one of the other system wide error codes sl@0: sl@0: This marks the current thread for waiting on a Fast Semaphore sl@0: when exception handling for this thread has completed - see sl@0: rm_debug_eventhandler.cpp for details. sl@0: */ sl@0: TInt DTargetProcess::FreezeThread() sl@0: { sl@0: // create and store a fast semaphore to stop the thread on sl@0: NKern::ThreadEnterCS(); sl@0: NFastSemaphore* sem = new NFastSemaphore(); sl@0: NKern::ThreadLeaveCS(); sl@0: sem->iOwningThread = &(Kern::CurrentThread().iNThread); sl@0: LOG_EVENT_MSG2("DTargetProcess::FreezeThread(): new NFastSemaphore() curr thread=0x%x", sem->iOwningThread); sl@0: return iFrozenThreadSemaphores.Append(sem); sl@0: } sl@0: sl@0: /** sl@0: Suspend the specified thread sl@0: sl@0: @param aThread thread to suspend sl@0: sl@0: @return KErrNone if the thread is successfully suspended, sl@0: KErrAlreadyExists if the agent has already suspended the thread, sl@0: or one of the other system wide error codes sl@0: */ sl@0: TInt DTargetProcess::DoSuspendThread(DThread* aThread) sl@0: { sl@0: TUint64 threadId = (TUint64)aThread->iId; sl@0: sl@0: // Don't suspend if this thread is already suspended (by FSWait or sl@0: // Kern::ThreadSuspend sl@0: if (CheckSuspended(aThread)) sl@0: { sl@0: // thread was already suspended, don't bother doing it again sl@0: LOG_MSG2("DTargetProcess::SuspendThread - Thread Id 0x%08x already suspended\n",threadId); sl@0: return KErrAlreadyExists; sl@0: } sl@0: sl@0: // Add thread to the suspend list sl@0: TInt err = iSuspendedThreads.Append(threadId); sl@0: if(err == KErrNone) sl@0: { sl@0: Kern::ThreadSuspend(*aThread, 1); sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Waits the current thread on a Fast Semaphore. sl@0: sl@0: This is useful for situations where the current thread sl@0: has hit a breakpoint within a critical section, and sl@0: otherwise could not be suspended at this point. sl@0: sl@0: Note that the Fast Semaphore structure on which the thread sl@0: waits must be a member data item of this class instance, sl@0: as it needs to be FSSignal()'d by another thread to resume sl@0: again. sl@0: */ sl@0: void DTargetProcess::FSWait() sl@0: { sl@0: LOG_MSG2("DTargetProcess::NotifyEvent(): number of attached agents: %d", AgentCount()); sl@0: NThread* currentThread = &(Kern::CurrentThread().iNThread); sl@0: for(TInt i=0; iiOwningThread == currentThread) sl@0: { sl@0: LOG_MSG3("DTargetProcess::FSWait(): > FSWait frozen sem %d, curr thread=0x%x", i, currentThread); sl@0: NKern::FSWait(iFrozenThreadSemaphores[i]); sl@0: return; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Checks that the thread has been suspended sl@0: sl@0: @param aThread thread to check suspended sl@0: sl@0: @return ETrue if the thread has been suspended, sl@0: EFalse if the thread has not been suspended sl@0: */ sl@0: TBool DTargetProcess::CheckSuspended(DThread* aThread) const sl@0: { sl@0: if(!aThread) sl@0: { sl@0: return EFalse; sl@0: } sl@0: //check if the thread is in the suspended threads list sl@0: for(TInt i=0; iiId) sl@0: { sl@0: return ETrue; sl@0: } sl@0: } sl@0: // not in the suspended threads list so check in the frozen threads list sl@0: NThread* nThread = &(aThread->iNThread); sl@0: for(TInt i=0; iiOwningThread == nThread) sl@0: { sl@0: return ETrue; sl@0: } sl@0: } sl@0: return EFalse; sl@0: } sl@0: sl@0: /* sl@0: @return ETrue if the debug driver has suspended any of the process' threads, EFalse otherwise sl@0: */ sl@0: TBool DTargetProcess::HasSuspendedThreads() const sl@0: { sl@0: return (iSuspendedThreads.Count() > 0) || (iFrozenThreadSemaphores.Count() > 0); sl@0: } sl@0: sl@0: void DTargetProcess::NotifyEvent(const TDriverEventInfo& aEventInfo) sl@0: { sl@0: // Stuff the event info into all the tracking agents event queues sl@0: LOG_MSG2("DTargetProcess::NotifyEvent(): number of attached agents: %d", AgentCount()); sl@0: sl@0: for(TInt i = 0; i < AgentCount(); i++) sl@0: { sl@0: // Index through all the relevant debug agents sl@0: DDebugAgent* debugAgent = iAgentList[i]; sl@0: if(debugAgent != NULL) sl@0: { sl@0: debugAgent->NotifyEvent(aEventInfo); sl@0: } sl@0: } sl@0: } sl@0: