--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/debug/rmdebug/d_debug_agent.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,368 @@
+// 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"
+
+using namespace Debug;
+
+#define NUMBER_OF_EVENTS_TO_QUEUE 100
+#define CRITICAL_BUFFER_SIZE (NUMBER_OF_EVENTS_TO_QUEUE - 50)
+
+// ctor
+DDebugAgent::DDebugAgent(TUint64 aId)
+: iId(aId),
+ iEventInfo(NULL),
+ iEventQueue(NUMBER_OF_EVENTS_TO_QUEUE, 0),
+ iRequestGetEventStatus(NULL),
+ iClientThread(0),
+ iHead(0),
+ iTail(0),
+ iIgnoringTrace(EFalse)
+ {
+ LOG_MSG("DDebugAgent::DDebugAgent() ");
+
+ // Initialize all the Event Actions to Ignore
+ for(TInt i=0; i<EEventsLast; i++)
+ {
+ iEventActions[i] = EActionIgnore;
+ }
+ }
+
+DDebugAgent* DDebugAgent::New(TUint64 aId)
+ {
+ LOG_MSG("DDebugAgent::New()");
+ DDebugAgent* agent = new DDebugAgent(aId);
+ if(agent == NULL)
+ {
+ return (NULL);
+ }
+ if(KErrNone != agent->Construct())
+ {
+ delete agent;
+ return (NULL);
+ }
+ return agent;
+ }
+
+TInt DDebugAgent::Construct()
+ {
+ // Empty the event queue
+ LOG_MSG("DDebugAgent::Construct()");
+ TDriverEventInfo emptyEvent;
+
+ for (TInt i=0; i<NUMBER_OF_EVENTS_TO_QUEUE; i++)
+ {
+ TInt err = iEventQueue.Append(emptyEvent);
+ if (KErrNone != err)
+ {
+ LOG_MSG("Error appending blank event entry");
+ return err;
+ }
+ }
+ return KErrNone;
+ }
+
+
+// dtor
+DDebugAgent::~DDebugAgent()
+ {
+ iEventQueue.Reset();
+ }
+
+// 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
+ *
+ * Returns : 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 aEventInfo - Address of TEventInfo structure to place event data when available
+// @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS.
+void DDebugAgent::GetEvent(TClientDataRequest<TEventInfo>* aAsyncGetValueRequest, TEventInfo* aEventInfo, DThread* aClientThread)
+ {
+ iClientThread = aClientThread;
+
+ if (BufferEmpty())
+ {
+ LOG_MSG("no events available");
+
+ // store the pointer so we can modify it later
+ iEventInfo = (TEventInfo *)aEventInfo;
+ iRequestGetEventStatus = aAsyncGetValueRequest;
+ return;
+ }
+
+ LOG_MSG("Event available");
+
+ // returning the event to the client
+ TInt err = iEventQueue[iTail].WriteEventToClientThread(aAsyncGetValueRequest,iClientThread);
+ if (KErrNone != err)
+ {
+ LOG_MSG2("Error writing event info: %d", err);
+ return;
+ }
+
+ // signal the DSS thread
+ Kern::QueueRequestComplete(iClientThread, aAsyncGetValueRequest, KErrNone);
+
+ iEventQueue[iTail].Reset();
+
+ // move to the next slot
+ IncrementPosition(iTail);
+ }
+
+// Stop waiting for an event to occur. This means events will be placed in the iEventQueue
+// until GetEvent is called.
+TInt DDebugAgent::CancelGetEvent(void)
+ {
+ Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel);
+ iEventInfo = NULL;
+ iRequestGetEventStatus = 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)
+ {
+ LOG_MSG("DDebugAgent::NotifyEvent()");
+ // Action depends on the TKernelEvent type in aEventInfo.iType
+
+ // Added to fix the pass by value issue seen in Coverity.
+ // Function is changed to pass by reference but temp object is explicitly created.
+ TDriverEventInfo eventInfo = aEventInfo;
+
+ if(aEventInfo.iEventType >= EEventsLast)
+ {
+ // unknown event type so return
+ return;
+ }
+
+ TKernelEventAction action = iEventActions[eventInfo.iEventType];
+
+ switch (action)
+ {
+ case EActionSuspend:
+ {
+ LOG_MSG("DDebugAgent::NotifyEvent() Suspend thread");
+ DThread* currentThread = &Kern::CurrentThread();
+ switch(eventInfo.iEventType)
+ {
+ case EEventsAddLibrary:
+ case EEventsRemoveLibrary:
+ currentThread = DebugUtils::OpenThreadHandle(eventInfo.iThreadId);
+ if(currentThread)
+ {
+ currentThread->Close(NULL);
+ }
+ break;
+ default:
+ break;
+ }
+ TInt err = TheDProcessTracker.SuspendThread(currentThread, eventInfo.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:
+ LOG_MSG("DDebugAgent::NotifyEvent() Continue");
+
+ // Tell the user about this event
+ if (iEventInfo && iClientThread)
+ {
+ LOG_MSG("Completing event\r\n");
+
+ // returning the event to the client
+ TInt err = eventInfo.WriteEventToClientThread(iRequestGetEventStatus,iClientThread);
+ if (KErrNone != err)
+ {
+ LOG_MSG2("Error writing event info: %d", err);
+ }
+
+ // clear this since we've completed the request
+ iEventInfo = NULL;
+
+ // signal the debugger thread
+ Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone);
+ }
+ else
+ {
+ LOG_MSG("Queuing event\r\n");
+
+ QueueEvent(eventInfo);
+
+ }
+ break;
+
+ case EActionIgnore:
+ default:
+ LOG_MSG("DDebugAgent::NotifyEvent() fallen through to default case");
+ // Ignore everything we don't understand.
+ return;
+ }
+
+ }
+
+// 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
+void DDebugAgent::QueueEvent(TDriverEventInfo& aEventInfo)
+ {
+ // Have we caught the tail?
+ if(BufferFull())
+ {
+ return;
+ }
+
+ //check to see if we wish to ignore this event - we dump trace events as they are lower priority than the other events
+ if(BufferAtCriticalLevel())
+ {
+ if(aEventInfo.iEventType == EEventsUserTrace)
+ {
+ if(!iIgnoringTrace)
+ {
+ //if this is the first time we are ignoring trace events, we need to issue a EEventsUserTracesLost event
+ aEventInfo.Reset();
+ aEventInfo.iEventType = EEventsUserTracesLost;
+
+ iIgnoringTrace = ETrue;
+ }
+ else
+ {
+ //otherwise, ignore this event
+ return;
+ }
+ }
+ }
+ else
+ {
+ //reset the iIgnoringTrace flag as we are not at critical level
+ iIgnoringTrace = EFalse;
+ }
+
+ // only one space left so store a EEventsBufferFull event
+ if(!BufferCanStoreEvent())
+ {
+ aEventInfo.Reset();
+ aEventInfo.iEventType = EEventsBufferFull;
+ }
+
+ __NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown); // we think there is space but the slot is not marked empty
+
+ // Insert the event into the ring buffer at iHead
+ iEventQueue[iHead] = aEventInfo;
+ IncrementPosition(iHead);
+ }
+
+// Checks whether the event queue is empty
+TBool DDebugAgent::BufferEmpty() const
+ {
+ return (NumberOfEmptySlots() == NUMBER_OF_EVENTS_TO_QUEUE);
+ }
+
+// Checks whether the event queue is full
+TBool DDebugAgent::BufferFull() const
+ {
+ return (NumberOfEmptySlots() == 0);
+ }
+
+// Checks whether there is room in the event queue to store an event (i.e. at least two free slots)
+TBool DDebugAgent::BufferCanStoreEvent() const
+ {
+ return (NumberOfEmptySlots() > 1);
+ }
+
+//This looks to see if the buffer is close to being full and should only accept higher priority debug events (user trace is the only low priority event)
+TBool DDebugAgent::BufferAtCriticalLevel() const
+ {
+ return (NumberOfEmptySlots() < NUMBER_OF_EVENTS_TO_QUEUE - CRITICAL_BUFFER_SIZE);
+ }
+
+// increments aPosition, wrapping at NUMBER_OF_EVENTS_TO_QUEUE if necessary
+void DDebugAgent::IncrementPosition(TInt& aPosition)
+ {
+ aPosition = (aPosition + 1) % NUMBER_OF_EVENTS_TO_QUEUE;
+ }
+
+// finds the number of empty slots in the event queue
+TInt DDebugAgent::NumberOfEmptySlots() const
+ {
+ if(iHead < iTail)
+ {
+ return (iTail - iHead) - 1;
+ }
+ // iHead >= iTail
+ return NUMBER_OF_EVENTS_TO_QUEUE - (iHead - iTail);
+ }
+