diff -r 07b41fa8d1dd -r ca8a1b6995f6 debugsrv/runmodedebug/rmdriver/src/d_debug_agent.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debugsrv/runmodedebug/rmdriver/src/d_debug_agent.cpp Tue Aug 31 16:45:49 2010 +0300 @@ -0,0 +1,468 @@ +// Copyright (c) 2006-2009 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: Kernel-side tracking of debug agent information associated +// with each process being debugged. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "d_process_tracker.h" +#include "debug_logging.h" + +#include "d_debug_agent.h" +#include "debug_utils.h" + +#include "d_debug_agent.inl" + +using namespace Debug; + +// ctor +DDebugAgent::DDebugAgent(TUint64 aId) : + iId(aId), + iRequestGetEventStatus(NULL), + iClientThread(0), + iEventQueue(KNumberOfEventsToQueue, 0), + iHead(0), + iTail(0), + iEventQueueLock(NULL), + iFreeSlots(KNumberOfEventsToQueue), + iIgnoringTrace(EFalse), + iEventBalance(0) + { + LOG_MSG2("DDebugAgent::DDebugAgent(), this=0x%x ", this); + + // Initialize all the Event Actions to Ignore + for(TInt i=0; iConstruct()) + { + delete agent; + return (NULL); + } + + // Use a semaphore to serialise access + TInt err = Kern::SemaphoreCreate(agent->iEventQueueLock, _L("RM_DebugAgentQueueLock"), 1 /* Initial count */); + if (err != KErrNone) + return NULL; + + return agent; + } + +/** Standard contructor. + * Fills event queue with empty events + * @return : standard system error code + */ +TInt DDebugAgent::Construct() + { + // Empty the event queue + TDriverEventInfo emptyEvent; + TInt err = KErrNone; + + for (TInt i=0; iClose(NULL); + + if(iRequestGetEventStatus) + Kern::DestroyClientRequest(iRequestGetEventStatus); + + } + +// Associate an action with a particular kernel event +TInt DDebugAgent::SetEventAction(TEventType aEvent, TKernelEventAction aEventAction) + { + // Valid Event? + if (aEvent >= EEventsLast) + { + LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); + return KErrArgument; + } + + iEventActions[aEvent] = aEventAction; + + return KErrNone; + } + +/** Get the aEventAction associated with aEvent + * + * @return : aEventAction (always +ve), or KErrArgument. + */ +TInt DDebugAgent::EventAction(TEventType aEvent) + { + // Validate the Event id + if (aEvent >= EEventsLast) + { + LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); + return KErrArgument; + } + + // Return the action associated with this event + return iEventActions[aEvent]; + } + +/** Obtain the details of the latest kernel event (if it exists) and place the details in aEventInfo + * If there is no event in the queue for this process+agent combination, store the details + * so that it can be notified later when an event actually occurs. + * + * @param aAsyncGetValueRequest - TClientDataRequest object used for pinning user memory + * @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS. + */ +void DDebugAgent::GetEvent(TClientDataRequest* aAsyncGetValueRequest, DThread* aClientThread) + { + LockEventQueue(); + + iRequestGetEventStatus->Reset(); + TInt err = iRequestGetEventStatus->SetStatus( aAsyncGetValueRequest->StatusPtr() ); + if (err != KErrNone) + { + LOG_MSG2("Error :iRequestGetEventStatus->SetStatus ret %d", err); + UnlockEventQueue(); + return; + } + + iRequestGetEventStatus->SetDestPtr( aAsyncGetValueRequest->DestPtr() ); + + iEventBalance++; + + LOG_MSG5("DDebugAgent::GetEvent: this=0x%08x, iRequestGetEventStatus=0x%08x, iEventBalance=%d, destPrt=0x%08x", + this, iRequestGetEventStatus, iEventBalance, aAsyncGetValueRequest->DestPtr() ); + + iClientThread = aClientThread; + + if (BufferEmpty()) + { + LOG_MSG2("Event buffer empty, iEventBalance=%d", iEventBalance); + UnlockEventQueue(); + return; + } + + LOG_MSG5("Event already available at queue pos (tail)=%d, evType=%d, threadId=0x%x, actionTaken=%d", + iTail, iEventQueue[iTail].iEventType, + iEventQueue[iTail].iThreadId, iEventQueue[iTail].iActionTaken ); + + // returning the event to the client + err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread); + if (err != KErrNone) + { + LOG_MSG2("Error writing event info: %d", err); + UnlockEventQueue(); + return; + } + + // signal the DSS thread + Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); + iEventBalance--; + + iEventQueue[iTail].Reset(); + + // move to the next slot + IncrementTailPosition(); + + UnlockEventQueue(); + } + +/** + * Stop waiting for an event to occur. This means events will be placed + * in the iEventQueue (by setting iEventBalance to 0) until GetEvent is called. + */ +TInt DDebugAgent::CancelGetEvent(void) + { + LOG_MSG2("DDebugAgent::CancelGetEvent. iEventBalance=%d. > QueueRequestComplete", iEventBalance); + Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel); + iEventBalance=0; + iClientThread = 0; + return KErrNone; + } + +/** Signal a kernel event to the user-side DSS when it occurs, or queue it for later + * if the user-side has not called GetEvent (see above). + * + * @param aEventInfo - the details of the event to queue. + */ +void DDebugAgent::NotifyEvent(const TDriverEventInfo& aEventInfo) + { + + if(aEventInfo.iEventType >= EEventsLast) + { + LOG_MSG3("DDebugAgent::NotifyEvent(),iEventType %d, this=0x%x. Ignoring since > EEventsLast", aEventInfo.iEventType, this); + return; + } + + LockEventQueue(); + + DThread* currentThread = &Kern::CurrentThread(); + + + TKernelEventAction action = iEventActions[aEventInfo.iEventType]; + + if (aEventInfo.iProcessId == Id() && + (aEventInfo.iEventType == EEventsSwExc || aEventInfo.iEventType == EEventsHwExc || aEventInfo.iEventType == EEventsKillThread)) + { + + // It might be nice not to deliver *any* events about the debug agent to the agent itself, but this is a bit too drastic a change to make. + // There's a risk it might completely break TRK or similar, and at a more practical level it would require major rewriting of the t_rmdebug2 + // tests. + // + // So instead, we only don't suspend&deliver events about the debug agent IF it's a thread crash event AND the thread is process + // critical/permanent AND (in the case of a critical thread) it's an abnormal exit. We're not worrying (yet) about the case where the entire + // process is set as system critical + // This fixes the original problem with CDS's worker thread crashing, and doesn't wreck the t_rmdebug2 tests. + + TBool problematic = ( + (aEventInfo.iThreadFlags & (KThreadFlagProcessCritical|KThreadFlagSystemCritical) && (aEventInfo.iEventType != EEventsKillThread || aEventInfo.iExitType != EExitKill)) // process or system critical, and either an exception (not a EEventsKillThread) or a non EExitKill exit + || (aEventInfo.iThreadFlags & (KThreadFlagProcessPermanent|KThreadFlagSystemPermanent)) + ); + + if (problematic) + { + LOG_MSG("Agent is dying - no further events will be delivered to it"); + iAgentDying = ETrue; + } + + } + + if (iAgentDying && action == EActionSuspend) + { + LOG_MSG("Not delivering this event or suspending the thread because agent is dying"); + action = EActionIgnore; + } + + switch (action) + { + case EActionSuspend: + { + LOG_MSG5("DDebugAgent::NotifyEvent(), Suspend thread, iEventType %d, this=0x%x currThrd=0x%08x, iEventBalance=%d", + aEventInfo.iEventType, this, currentThread, iEventBalance ); + + switch(aEventInfo.iEventType) + { + case EEventsAddLibrary: + case EEventsRemoveLibrary: + // TomS: Anybody want to explain what is going on here?? + currentThread = DebugUtils::OpenThreadHandle(aEventInfo.iThreadId); + if(currentThread) + { + currentThread->Close(NULL); + } + break; + default: + break; + } + + // Do not call suspend for breakpoints, since the breakpoint code that runs when deciding if an exception + // is a breakpoint will itself suspend the thread + if( (aEventInfo.iEventType != EEventsBreakPoint) && (aEventInfo.iEventType != EEventsProcessBreakPoint) ) + { + TInt err = TheDProcessTracker.SuspendThread(currentThread, aEventInfo.FreezeOnSuspend()); + if((err != KErrNone) && (err != KErrAlreadyExists)) + { + // Is there anything we can do in the future to deal with this error having happened? + LOG_MSG2("DDebugAgent::NotifyEvent() Problem while suspending thread: %d", err); + } + } + + // now drop through to the continue case, which typically notifies + // the debug agent of the event + } + case EActionContinue: + { + if( action == EActionContinue ) + { + LOG_MSG5("DDebugAgent::NotifyEvent(), Action continue, iEventType %d, this=0x%x currThrd=0x%08x, iEventBalance=%d", + aEventInfo.iEventType, this, currentThread, iEventBalance ); + } + + // Queue this event + TDriverEventInfo eventInfo = aEventInfo; + eventInfo.iActionTaken = action; + QueueEvent(eventInfo); + + // Tell the user about the oldest event in the queue + if ( iClientThread ) + { + if( iRequestGetEventStatus && (iEventBalance > 0) ) + { + // Fill the event data + TInt err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread); + if (err != KErrNone) + { + LOG_MSG2("Error writing event info: %d", err); + } + + // signal the debugger thread + LOG_MSG4("> QueueRequestComplete iRequestGetEventStatus=0x%08x, iEventBalance=%d, iTail=%d", + iRequestGetEventStatus->iStatus, iEventBalance, iTail ); + Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); + + iEventBalance--; + + iEventQueue[iTail].Reset(); + + // move to the next slot + IncrementTailPosition(); + } + else + { + if( !iRequestGetEventStatus ) + { + LOG_MSG("iRequestGetEventStatus is NULL so not signalling client" ); + } + else + { + LOG_MSG2("Queued event. iEventBalance=%d (unbalanced event requests vs notifications)", + iEventBalance ); + } + } + } + else + { + LOG_MSG("DDebugAgent::NotifyEvent() : Not informing client since its thread is NULL"); + } + break; + } + case EActionIgnore: + default: + // Ignore everything we don't understand. + break; + } + + UnlockEventQueue(); + + } + +// Used to identify which Debug Agent this DDebugAgent is associated with. +TUint64 DDebugAgent::Id(void) + { + return iId; + } + +/** + * Used to add an event to the event queue for this debug agent if event + * queue is not at critical level. If it is at critical and it is trace event, + * we start ignoring trace events and insert a lost trace event. + * If the buffer cannot store an event, only insert a buffer full event. + * @see EEventsBufferFull + * @see EEventsUserTracesLost + * @see TDriverEventInfo + * @see iEventQueue + */ +void DDebugAgent::QueueEvent(const TDriverEventInfo& aEventInfo) + { + // Have we caught the tail? + if(BufferFull()) + { + LOG_MSG("DDebugAgent::QueueEvent : BufferFull. Not queueing"); + return; + } + + // Assert if we think there is space but the slot is not marked empty + __NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown); + + const TBool bufferAtCritical = BufferAtCriticalLevel(); + + if(!bufferAtCritical) + { + //reset the iIgnoringTrace flag as we are not at + //critical level and can store event + iIgnoringTrace = EFalse; + + // Insert the event into the ring buffer at iHead + iEventQueue[iHead] = aEventInfo; + IncrementHeadPosition(); + } + else if(bufferAtCritical && BufferCanStoreEvent()) + { + LOG_MSG("DDebugAgent::QueueEvent : BufferCritical"); + if(aEventInfo.iEventType == EEventsUserTrace) + { + if(!iIgnoringTrace) + { + //if this is the first time we are ignoring trace events, + //we need to issue a EEventsUserTracesLost event + iEventQueue[iHead].Reset(); + iEventQueue[iHead].iEventType = EEventsUserTracesLost; + IncrementHeadPosition(); + + iIgnoringTrace = ETrue; + } + else + { + //otherwise, ignore this event + LOG_MSG("DDebugAgent::QueueEvent : Ignore EEventsUserTrace event"); + } + } + else + { + // Store the event since its not a trace event + iEventQueue[iHead] = aEventInfo; + IncrementHeadPosition(); + } + } + else + { + //At critical level and cannot store new events, so + //only one space left. Store a EEventsBufferFull event + LOG_MSG("DDebugAgent::QueueEvent : Event Buffer Full, ignoring event"); + iEventQueue[iHead].Reset(); + iEventQueue[iHead].iEventType = EEventsBufferFull; + IncrementHeadPosition(); + } + } + +// End of file - d_debug_agent.cpp