debugsrv/runmodedebug/rmdriver/src/d_debug_agent.cpp
branchRCL_3
changeset 20 ca8a1b6995f6
--- /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 <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