diff -r 07b41fa8d1dd -r ca8a1b6995f6 debugsrv/runmodedebug/rmdriver/src/d_process_tracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debugsrv/runmodedebug/rmdriver/src/d_process_tracker.cpp Tue Aug 31 16:45:49 2010 +0300 @@ -0,0 +1,664 @@ +// Copyright (c) 2006-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: +// Purpose: The DProcessTracker object tracks which processes are being +// debugged. The DProcessTracker class uses a DTargetProcess object for +// each process being debugged. +// Note: Although TheDProcessTracker object is a global, it will be unique +// as only the Debug Security Server can load and use rm_debug.ldd. +// +// + +#include +#include +#include +#include +#include +#include + +#include +#include "debug_logging.h" +#include "d_process_tracker.h" +#include "debug_utils.h" + +// Global Run-mode debugged process tracking object +DProcessTracker TheDProcessTracker; + +// ctor +DProcessTracker::DProcessTracker() + { + } + +/** + * dtor + * Go through forzen thread list and resume each one before clearing our structures + * @internalTechnology + */ +DProcessTracker::~DProcessTracker() + { + + for(TInt i=0; iiOwningThread ); + NKern::FSSignal(iFrozenThreadSemaphores[i]); + } + + NKern::ThreadEnterCS(); + // The ResetAndDestroy() will call the individual deletes for all objects in the containers + iFrozenThreadSemaphores.ResetAndDestroy(); + iProcesses.ResetAndDestroy(); + iAgentsAttachedToAll.ResetAndDestroy(); + NKern::ThreadLeaveCS(); + } + +/** + * @internalTechnology + * + * Creates and stores an internal mapping of debug agent to debugged process. + * Note that an individual process may be mapped to a number of debug agents. + * + * @param aProcessName - The fullly qualified path of the debugged process. E.g. z:\sys\bin\hello_world.exe + * @param aAgentId - The process id of the debug agent which is attaching to aProcessName, as returned by RProcess.Id() + * @return KErrNone if there are no errors. KErrArgument if the processname is too long/short for a valid filepath. + * KErrNoMemory if there is insufficient memory. + */ +TInt DProcessTracker::AttachProcess(const TDesC8& aProcessName,TUint64 aAgentId) + { + LOG_MSG3("DProcessTracker::AttachProcess name=%S agentId=0x%lx", + &aProcessName, aAgentId); + + // Valid ProcessName? + if (aProcessName.Length() < 1 || aProcessName.Length() >= KMaxPath) + { + return KErrArgument; + } + + if (aProcessName == _L8("*")) + { + DDebugAgent* agent = FindAgentForProcessAndId( aProcessName, aAgentId ); + if(agent != NULL) + { + LOG_MSG("Found agent already attached to all"); + return KErrAlreadyExists; + } + + agent = DDebugAgent::New(aAgentId); + if(agent == NULL) + { + LOG_MSG("DProcessTracker::AttachProcess() couldn't allocate memory for DDebugAgent"); + return KErrNoMemory; + } + + return iAgentsAttachedToAll.Append(agent); + + } + + // Not attach all, but for a specific process/exe + + // Create an DTargetProcess to store + DTargetProcess* tmpProcess = new DTargetProcess; + if (tmpProcess == 0) + { + return KErrNoMemory; + } + LOG_MSG2(" AttachProcess: < new DTargetProcess=0x%08x", tmpProcess ); + + // Set the name + TInt err = KErrNone; + err = tmpProcess->SetProcessName(aProcessName); + if (err != KErrNone) + { + LOG_MSG2(" AttachProcess: < SetProcessName returned %d", err ); + return err; + } + + // Is this process being debugged (ie already attached?) + TInt found = KErrNotFound; + const TInt numberOfProcesses = iProcesses.Count(); + for (TInt index = 0; index < numberOfProcesses; index++) + { + if (iProcesses[index]->ProcessName().CompareF(aProcessName) == 0) + { + LOG_MSG3(" Proc count=%d, found proc in iProcesses at %d. Count=%d", + index, iProcesses.Count() ); + found = index; + break; + } + } + + if (found != KErrNotFound) + { + // Yes, it is being debugged + + // Add the agent to the list of agents for this process + LOG_MSG3(" > AddAgent(agent id %d) to existing iProcesses[%d]", I64LOW(aAgentId), found); + + iProcesses[found]->AddAgent(aAgentId); + + return KErrNone; + } + else + { + // No, it is not being debugged + + // Add the agent to the list of agents for this process + LOG_MSG2(" > AddAgent(agent %d) to new proc at index 0", I64LOW(aAgentId) ); + + tmpProcess->AddAgent(aAgentId); + + // Add the process to the list of processes being debugged + return iProcesses.Append(tmpProcess); + } + } + +/** + * @internalTechnology + * + * Removes a previously created mapping between a debug agent and a debugged process, + * as created by AttachProcess. + * + * @param aProcessName - The fully qualified path of the debugged process. E.g. z:\sys\bin\hello_world.exe + * @param aAgentId - The process id of the debug agent which is attaching to aProcessName, as returned by RProcess.Id() + * @return KErrNone if there are no problems. KErrArgument if the processname is too long/short for a valid filepath. + * KErrNotFound if the mapping does not exist (and therefore cannot be removed). + */ +TInt DProcessTracker::DetachProcess(const TDesC8& aProcessName, TUint64 aAgentId) + { + LOG_MSG3("DProcessTracker::DetachProcess name=%S agentId=0x%lx", + &aProcessName, aAgentId); + + // Valid ProcessName? + if (aProcessName.Length() < 1 || aProcessName.Length() >= KMaxPath) + { + return KErrArgument; + } + + if (aProcessName == _L8("*")) + { + TInt const agentCount = iAgentsAttachedToAll.Count(); + LOG_MSG2(" iAgentsAttachedToAll size=%d", agentCount ); + + for (TInt i = 0; i < agentCount; i++) + { + if (iAgentsAttachedToAll[i]->Id() == aAgentId) + { + LOG_MSG2(" Agent id found at index %d, deleting it", i); + delete iAgentsAttachedToAll[i]; + iAgentsAttachedToAll.Remove(i); + return KErrNone; + } + } + + //Not found, so error condition + return KErrNotFound; + } + + // Are we debugging this process? + const TInt numberOfProcesses = iProcesses.Count(); + TInt foundIdx = KErrNotFound; + for(TInt i = 0; i < numberOfProcesses; i++) + { + if (iProcesses[i]->ProcessName().CompareF(aProcessName) == 0) + { + foundIdx = i; + break; + } + } + + if (foundIdx == KErrNotFound) + { + return KErrNotFound; + } + + // remove the agent from the process + iProcesses[foundIdx]->RemoveAgent(aAgentId); + + // Found it, are there any more attached agents, or suspended threads in the process? + + + if (iProcesses[foundIdx]->AgentCount() == 0) + { + // Delete the process as no more agents are still attached + delete iProcesses[foundIdx]; + + // Remove the now obsolete pointer from our array. + iProcesses.Remove(foundIdx); + } + + return KErrNone; + } + +/** + * @internalTechnology + * + * Detachs a debug agent from every process being debugged. Used when a debug agent is being detached + * from the debug security server and has not supplied a specific process name from which to detach. + */ +TInt DProcessTracker::DetachAgent(const TUint64 aAgentId) + { + + LOG_MSG2("DProcessTracker::DetachAgent 0x%lx", aAgentId); + + // Remove this agent from all the processes being tracked. + TInt numberOfProcesses = iProcesses.Count(); + for(TInt i=0; iRemoveAgent(aAgentId); + } + + // Increment down through the array as we then don't have to worry about + // missing entries which have been shifted after deletes. + // The initial value of i correspnds to the index of the final element + // in the array. + for(TInt i = iProcesses.Count()-1; i>=0; i--) + { + if (iProcesses[i]->AgentCount() == 0) + { + // No agents remain for this process. Delete the + // process object and remove the pointer from the array + delete iProcesses[i]; + iProcesses.Remove(i); + } + } + + TInt const agentCount = iAgentsAttachedToAll.Count(); + for (TInt i = 0; i < agentCount; i++) + { + if (iAgentsAttachedToAll[i]->Id() == aAgentId) + { + LOG_MSG2(" Agent id found at index %d, deleting it", i); + delete iAgentsAttachedToAll[i]; + iAgentsAttachedToAll.Remove(i); + } + } + + return KErrNone; + } + +/** + * @internalTechnology + * + * Returns a pointer to a DTargetProcess object representing the mapping of a debugged process + * with all the relevant debug agents interested in that process, as determined + * by AttachProcess. + * + * @param aProcessName - The fully qualified path of the debugged process. E.g. z:\sys\bin\hello_world.exe + * @return DTargetProcess* pointer to an object representing the internal mapping of a process to all associated + * debug agents. Returns 0 if the mapping cannot be found or the aProcessName is invalid. + */ +DTargetProcess* DProcessTracker::FindProcess(const TDesC8& aProcessName) const + { + // Valid ProcessName? + if (aProcessName.Length() < 1 || aProcessName.Length() >= KMaxPath) + { + return NULL; + } + + // Can we find this in the array? + + // Are we debugging this process? + const TInt numberOfProcesses = iProcesses.Count(); + DTargetProcess* found = NULL; + for(TInt i = 0; i < numberOfProcesses; i++) + { + if (iProcesses[i]->ProcessName().CompareF(aProcessName) == 0) + { + found = iProcesses[i]; + LOG_EVENT_MSG3("DProcessTracker::FindProcess(%S) found at list pos %i", + &aProcessName, i); + break; + } + } + + if (found == NULL) + { + LOG_EVENT_MSG2("DProcessTracker::FindProcess(%S), not found", &aProcessName); + } + + return found; + } + +/** + * @internalTechnology + * + * Returns a pointer to a DTargetProcess object representing the mapping of a debugged process + * with all the relevant debug agents interested in that process, as determined + * by AttachProcess. + * + * Note: This does not attempt an exact match, because the AddProcess event does not provide + * a fully-qualified path, it provides something like [t_rmdebug_security0.exe]. + * + * So for the purposes of dealing with this event, we need a "fuzzier" match which does not use the complete + * path. + * + * @param aProcessName - The fully qualified path of the debugged process. E.g. z:\sys\bin\hello_world.exe + * @return DTargetProcess* pointer to an object representing the internal mapping of a process to all associated + * debug agents. Returns 0 if the mapping cannot be found or the aProcessName is invalid. + */ +DTargetProcess* DProcessTracker::FuzzyFindProcess(const TDesC8& aProcessName) + { + // Valid ProcessName? + if (aProcessName.Length() < 1 || aProcessName.Length() >= KMaxPath) + { + return 0; // not found + } + + // Can we find this in the array? + TBool found = EFalse; + DTargetProcess* foundProcess = 0; + const TChar KBackSlash('\\'); + + TInt numberOfProcesses = iProcesses.Count(); + for(TInt i=0; i < numberOfProcesses; i++) + { + foundProcess = iProcesses[i]; + + TInt procListBackSlash = foundProcess->ProcessName().LocateReverse( KBackSlash ); + if( procListBackSlash == KErrNotFound ) + { + procListBackSlash = 0; + } + else + { + //Now move to the char after the backlash + procListBackSlash++; + } + + TInt eventBackSlash = aProcessName.LocateReverse( KBackSlash ); + if( eventBackSlash == KErrNotFound ) + { + eventBackSlash = 0; + } + else + { + //Now move to the char after the backlash + eventBackSlash++; + } + + if( ( procListBackSlash == 0 ) && ( eventBackSlash == 0 ) ) + { + //There were no backslashes on either name, so no point in continuing + break; + } + + TPtrC8 eventCleanName( aProcessName.Mid( eventBackSlash ) ); + TPtrC8 procListCleanName( foundProcess->ProcessName().Mid( procListBackSlash ) ); + + if ( eventCleanName.CompareF( procListCleanName ) == 0 ) + { + LOG_MSG2("DProcessTracker::FuzzyFindProcess() found a match : process list[%d]", i ); + found = ETrue; + break; + } + } + + if (found == EFalse) + { + return 0; // not found + } + + return foundProcess; + } + +/** + Freeze the current thread + + @return KErrNone if the thread is successfully suspended, + KErrAlreadyExists if the agent has already suspended the thread, + or one of the other system wide error codes + + This marks the current thread for waiting on a Fast Semaphore + when exception handling for this thread has completed - see + rm_debug_eventhandler.cpp for details. + */ +TInt DProcessTracker::FreezeThread() + { + // create and store a fast semaphore to stop the thread on + TInt err = KErrGeneral; + NKern::ThreadEnterCS(); + NFastSemaphore* sem = new NFastSemaphore( &(Kern::CurrentThread().iNThread) ); + if( sem != NULL ) + { + LOG_MSG3("DProcessTracker::FreezeThread(): new NFastSemaphore(curr NThread=0x%08x), DThread=0x%08x", + sem->iOwningThread, &(Kern::CurrentThread()) ); + err = iFrozenThreadSemaphores.Append(sem); + } + else + { + LOG_MSG("DProcessTracker::FreezeThread(): Error : could not allocate NFastSemaphore"); + err = KErrNoMemory; + } + + NKern::ThreadLeaveCS(); + return err; + } + +/** + Waits the current thread on a Fast Semaphore. + + This is useful for situations where the current thread + has hit a breakpoint within a critical section, and + otherwise could not be suspended at this point. + + Note that the Fast Semaphore structure on which the thread + waits must be a member data item of this class instance, + as it needs to be FSSignal()'d by another thread to resume + again. + */ +void DProcessTracker::FSWait() + { + NThread* currentNThread = &(Kern::CurrentThread().iNThread); + for(TInt i=0; iiOwningThread == currentNThread) + { + LOG_MSG4("DProcessTracker::FSWait(): > FSWait frozen sem %d, currentNThread=0x%08x, id=0x%x", + i, currentNThread, Kern::CurrentThread().iId ); + NKern::FSWait(iFrozenThreadSemaphores[i]); + return; + } + } + } + +/** + Resume the specified frozen thread + + @param aThread thread to resume + + @return KErrNone if the thread has previously been suspended and is resumed, + KErrNotFound if the thread has not previously been suspended + */ +TInt DProcessTracker::ResumeFrozenThread(DThread* aThread) + { + for(TInt i=0; iiOwningThread == &(aThread->iNThread)) + { + LOG_MSG2("DProcessTracker::ResumeFrozenThread 0x%08x, signalling then deleting this FastSem", aThread->iId ); + NKern::FSSignal(iFrozenThreadSemaphores[i]); + NKern::ThreadEnterCS(); + delete iFrozenThreadSemaphores[i]; + NKern::ThreadLeaveCS(); + iFrozenThreadSemaphores.Remove(i); + return KErrNone; + } + } + return KErrNotFound; + } + +TInt DProcessTracker::SuspendThread(DThread* aTargetThread, TBool aFreezeThread) + { + LOG_MSG5("DProcessTracker::SuspendThread() id 0x%08x, iCsCount=%d, , iCsFunction=%d, iSuspendCount=%d ", + aTargetThread->iId, aTargetThread->iNThread.iCsCount, aTargetThread->iNThread.iCsFunction, aTargetThread->iNThread.iSuspendCount ); + if( !aFreezeThread ) + { + if(!aTargetThread) + { + LOG_MSG("DProcessTracker::SuspendThread() > Kern::ThreadSuspend NullThrd Ptr!!"); + return KErrBadHandle; + } + + Kern::ThreadSuspend(*aTargetThread, 1); + return KErrNone; + } + + if( Kern::CurrentThread().iId != aTargetThread->iId ) + { + LOG_MSG2("DProcessTracker::SuspendThread() Error: Freeze for thread 0x%08x, but different from current thread", + aTargetThread->iId); + return KErrBadHandle; + } + + return FreezeThread(); + } + + +TInt DProcessTracker::ResumeThread(DThread* aTargetThread) + { + LOG_MSG5("DProcessTracker::ResumeThread() id 0x%08x, iCsCount=%d, , iCsFunction=%d, iSuspendCount=%d ", + aTargetThread->iId, aTargetThread->iNThread.iCsCount, aTargetThread->iNThread.iCsFunction, aTargetThread->iNThread.iSuspendCount ); + + TInt err = ResumeFrozenThread( aTargetThread ); + if( err == KErrNotFound ) + { + LOG_MSG(" ResumeThread() : not found in frozen list. Using Kern::ThreadResume" ); + Kern::ThreadResume(*aTargetThread); + return KErrNone; + } + + return err; + } + +/** + Get a thread's originating file name + + @param aThread the thread to get the file name for + + @return a pointer to the thread's file name, if there are problems accessing + the file name then NULL will be returned + */ +const TDesC* DProcessTracker::GetFileName(DThread* aThread) const + { + //check if the thread is NULL and return if so + if(!aThread) + { + return NULL; + } + + //get the owning process and return if it is NULL + DProcess* process = aThread->iOwningProcess; + if(!process) + { + return NULL; + } + + //get the process' code seg and return if it is NULL + DCodeSeg* codeSeg = process->iCodeSeg; + if(!codeSeg) + { + return NULL; + } + + //return the code seg's stored file name (which could theoretically be NULL) + return codeSeg->iFileName; + } + +/** +If any agent has called AttachToAll, return the most recently attached one. +*/ +DDebugAgent* DProcessTracker::GetCurrentAgentAttachedToAll() const + { + if (iAgentsAttachedToAll.Count() > 0) + { + return iAgentsAttachedToAll[iAgentsAttachedToAll.Count()-1]; + } + else + { + return NULL; + } + } + +/** +Returns ETrue if at least one agent was found for this process (either a specifically-attached +one or a current attached to all). Search specifically attached first, since these have +priority over attach all. +*/ +TBool DProcessTracker::NotifyAgentsForProcessEvent(const TDesC8& aProcessName, const TDriverEventInfo& aEvent, TBool aAllowFuzzy) + { + TBool foundAgent = EFalse; + + DTargetProcess* process = FindProcess(aProcessName); + if (process == NULL && aAllowFuzzy) + { + process = FuzzyFindProcess(aProcessName); + } + + if (process) + { + LOG_MSG3("DProcessTracker::NotifyAgentsForProcessEvent name=%S eventtype=%d", + &aProcessName, aEvent.iEventType); + process->NotifyEvent(aEvent); + return ETrue; + } + + // Since no specifically attached agents were found, try the attach all + + DDebugAgent* currentAll = GetCurrentAgentAttachedToAll(); + if (currentAll) + { + foundAgent = ETrue; + LOG_MSG4("DProcessTracker::NotifyAgentsForProcessEvent via AttachAll name=%S eventtype=%d, agent 0x%lx", + &aProcessName, aEvent.iEventType, currentAll->Id()); + currentAll->NotifyEvent(aEvent); + } + + return foundAgent; + } + +/** + * Find the agent that matches this exe/proc name. Name could be the attachall indicator "*", + * in which case it returns the agent that matched the pid from the attach all list + */ +DDebugAgent* DProcessTracker::FindAgentForProcessAndId(const TDesC8& aProcessName, TUint64 aAgentId) const + { + + if (aProcessName == _L8("*")) + { + TInt const agentCount = iAgentsAttachedToAll.Count(); + + LOG_MSG3("FindAgentForProcessAndId : Searching for agent id 0x%lx, iAgentsAttachedToAll size=%d", + aAgentId, agentCount ); + + // Then check the attached to all list. Should not have more than one entry + // for each agent, but just in case we search backwards to match the append + // + for (TInt i = agentCount - 1 ; i >= 0; i--) + { + DDebugAgent* agent = iAgentsAttachedToAll[i]; + if (agent->Id() == aAgentId) + { + return agent; + } + } + } + else + { + DTargetProcess* process = FindProcess(aProcessName); + if (process) + { + return process->Agent(aAgentId); + } + } + return NULL; + }