--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debugsrv/runmodedebug/rmdriver/src/d_debug_agent.cpp Thu Sep 02 22:05:40 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 <e32def.h>
+#include <e32def_private.h>
+#include <e32cmn.h>
+#include <e32cmn_private.h>
+#include <kernel/kernel.h>
+#include <kernel/kern_priv.h>
+#include <nk_trace.h>
+#include <arm.h>
+
+#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; i<EEventsLast; i++)
+ {
+ iEventActions[i] = EActionIgnore;
+ }
+ }
+
+DDebugAgent* DDebugAgent::New(TUint64 aId)
+ {
+ LOG_MSG2("DDebugAgent::New(id=0x%lx)", aId);
+ DDebugAgent* agent = new DDebugAgent(aId);
+ if(agent == NULL)
+ {
+ return (NULL);
+ }
+ if(KErrNone != agent->Construct())
+ {
+ 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; i<KNumberOfEventsToQueue; i++)
+ {
+ err = iEventQueue.Append(emptyEvent);
+ if (err != KErrNone)
+ {
+ LOG_MSG("Error appending blank event entry");
+ return err;
+ }
+ }
+
+ err = Kern::CreateClientDataRequest(iRequestGetEventStatus);
+ if(err != KErrNone)
+ {
+ LOG_MSG("Error creating TClientDataRequest");
+ return err;
+ }
+
+ LOG_MSG2("DDebugAgent::Construct() iRequestGetEventStatus=0x%08x", iRequestGetEventStatus);
+
+ return err;
+ }
+
+// dtor
+DDebugAgent::~DDebugAgent()
+ {
+ iEventQueue.Reset();
+
+ if (iEventQueueLock)
+ iEventQueueLock->Close(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<TEventInfo>* 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