sl@0: // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Purpose: Kernel-side tracking of debug agent information associated sl@0: // with each process being debugged. sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #include "d_process_tracker.h" sl@0: #include "debug_logging.h" sl@0: sl@0: #include "d_debug_agent.h" sl@0: #include "debug_utils.h" sl@0: sl@0: using namespace Debug; sl@0: sl@0: #define NUMBER_OF_EVENTS_TO_QUEUE 100 sl@0: #define CRITICAL_BUFFER_SIZE (NUMBER_OF_EVENTS_TO_QUEUE - 50) sl@0: sl@0: // ctor sl@0: DDebugAgent::DDebugAgent(TUint64 aId) sl@0: : iId(aId), sl@0: iEventInfo(NULL), sl@0: iEventQueue(NUMBER_OF_EVENTS_TO_QUEUE, 0), sl@0: iRequestGetEventStatus(NULL), sl@0: iClientThread(0), sl@0: iHead(0), sl@0: iTail(0), sl@0: iIgnoringTrace(EFalse) sl@0: { sl@0: LOG_MSG("DDebugAgent::DDebugAgent() "); sl@0: sl@0: // Initialize all the Event Actions to Ignore sl@0: for(TInt i=0; iConstruct()) sl@0: { sl@0: delete agent; sl@0: return (NULL); sl@0: } sl@0: return agent; sl@0: } sl@0: sl@0: TInt DDebugAgent::Construct() sl@0: { sl@0: // Empty the event queue sl@0: LOG_MSG("DDebugAgent::Construct()"); sl@0: TDriverEventInfo emptyEvent; sl@0: sl@0: for (TInt i=0; i= EEventsLast) sl@0: { sl@0: LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: iEventActions[aEvent] = aEventAction; sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /* Get the aEventAction associated with aEvent sl@0: * sl@0: * Returns : aEventAction (always +ve), or KErrArgument. sl@0: */ sl@0: TInt DDebugAgent::EventAction(TEventType aEvent) sl@0: { sl@0: // Validate the Event id sl@0: if (aEvent >= EEventsLast) sl@0: { sl@0: LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: // Return the action associated with this event sl@0: return iEventActions[aEvent]; sl@0: } sl@0: sl@0: // Obtain the details of the latest kernel event (if it exists) and place the details in aEventInfo sl@0: // If there is no event in the queue for this process+agent combination, store the details sl@0: // so that it can be notified later when an event actually occurs. sl@0: // sl@0: // @param aAsyncGetValueRequest - TClientDataRequest object used for pinning user memory sl@0: // @param aEventInfo - Address of TEventInfo structure to place event data when available sl@0: // @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS. sl@0: void DDebugAgent::GetEvent(TClientDataRequest* aAsyncGetValueRequest, TEventInfo* aEventInfo, DThread* aClientThread) sl@0: { sl@0: iClientThread = aClientThread; sl@0: sl@0: if (BufferEmpty()) sl@0: { sl@0: LOG_MSG("no events available"); sl@0: sl@0: // store the pointer so we can modify it later sl@0: iEventInfo = (TEventInfo *)aEventInfo; sl@0: iRequestGetEventStatus = aAsyncGetValueRequest; sl@0: return; sl@0: } sl@0: sl@0: LOG_MSG("Event available"); sl@0: sl@0: // returning the event to the client sl@0: TInt err = iEventQueue[iTail].WriteEventToClientThread(aAsyncGetValueRequest,iClientThread); sl@0: if (KErrNone != err) sl@0: { sl@0: LOG_MSG2("Error writing event info: %d", err); sl@0: return; sl@0: } sl@0: sl@0: // signal the DSS thread sl@0: Kern::QueueRequestComplete(iClientThread, aAsyncGetValueRequest, KErrNone); sl@0: sl@0: iEventQueue[iTail].Reset(); sl@0: sl@0: // move to the next slot sl@0: IncrementPosition(iTail); sl@0: } sl@0: sl@0: // Stop waiting for an event to occur. This means events will be placed in the iEventQueue sl@0: // until GetEvent is called. sl@0: TInt DDebugAgent::CancelGetEvent(void) sl@0: { sl@0: Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel); sl@0: iEventInfo = NULL; sl@0: iRequestGetEventStatus = 0; sl@0: iClientThread = 0; sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: // Signal a kernel event to the user-side DSS when it occurs, or queue it for later sl@0: // if the user-side has not called GetEvent (see above). sl@0: // sl@0: // @param aEventInfo - the details of the event to queue. sl@0: void DDebugAgent::NotifyEvent(const TDriverEventInfo& aEventInfo) sl@0: { sl@0: LOG_MSG("DDebugAgent::NotifyEvent()"); sl@0: // Action depends on the TKernelEvent type in aEventInfo.iType sl@0: sl@0: // Added to fix the pass by value issue seen in Coverity. sl@0: // Function is changed to pass by reference but temp object is explicitly created. sl@0: TDriverEventInfo eventInfo = aEventInfo; sl@0: sl@0: if(aEventInfo.iEventType >= EEventsLast) sl@0: { sl@0: // unknown event type so return sl@0: return; sl@0: } sl@0: sl@0: TKernelEventAction action = iEventActions[eventInfo.iEventType]; sl@0: sl@0: switch (action) sl@0: { sl@0: case EActionSuspend: sl@0: { sl@0: LOG_MSG("DDebugAgent::NotifyEvent() Suspend thread"); sl@0: DThread* currentThread = &Kern::CurrentThread(); sl@0: switch(eventInfo.iEventType) sl@0: { sl@0: case EEventsAddLibrary: sl@0: case EEventsRemoveLibrary: sl@0: currentThread = DebugUtils::OpenThreadHandle(eventInfo.iThreadId); sl@0: if(currentThread) sl@0: { sl@0: currentThread->Close(NULL); sl@0: } sl@0: break; sl@0: default: sl@0: break; sl@0: } sl@0: TInt err = TheDProcessTracker.SuspendThread(currentThread, eventInfo.FreezeOnSuspend()); sl@0: if(!( (err == KErrNone) || (err == KErrAlreadyExists) )) sl@0: { sl@0: // Is there anything we can do in the future to deal with this error having happened? sl@0: LOG_MSG2("DDebugAgent::NotifyEvent() Problem while suspending thread: %d", err); sl@0: } sl@0: sl@0: // now drop through to the continue case, which typically notifies sl@0: // the debug agent of the event sl@0: } sl@0: case EActionContinue: sl@0: LOG_MSG("DDebugAgent::NotifyEvent() Continue"); sl@0: sl@0: // Tell the user about this event sl@0: if (iEventInfo && iClientThread) sl@0: { sl@0: LOG_MSG("Completing event\r\n"); sl@0: sl@0: // returning the event to the client sl@0: TInt err = eventInfo.WriteEventToClientThread(iRequestGetEventStatus,iClientThread); sl@0: if (KErrNone != err) sl@0: { sl@0: LOG_MSG2("Error writing event info: %d", err); sl@0: } sl@0: sl@0: // clear this since we've completed the request sl@0: iEventInfo = NULL; sl@0: sl@0: // signal the debugger thread sl@0: Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); sl@0: } sl@0: else sl@0: { sl@0: LOG_MSG("Queuing event\r\n"); sl@0: sl@0: QueueEvent(eventInfo); sl@0: sl@0: } sl@0: break; sl@0: sl@0: case EActionIgnore: sl@0: default: sl@0: LOG_MSG("DDebugAgent::NotifyEvent() fallen through to default case"); sl@0: // Ignore everything we don't understand. sl@0: return; sl@0: } sl@0: sl@0: } sl@0: sl@0: // Used to identify which Debug Agent this DDebugAgent is associated with. sl@0: TUint64 DDebugAgent::Id(void) sl@0: { sl@0: return iId; sl@0: } sl@0: sl@0: // Used to add an event to the event queue for this debug agent sl@0: void DDebugAgent::QueueEvent(TDriverEventInfo& aEventInfo) sl@0: { sl@0: // Have we caught the tail? sl@0: if(BufferFull()) sl@0: { sl@0: return; sl@0: } sl@0: sl@0: //check to see if we wish to ignore this event - we dump trace events as they are lower priority than the other events sl@0: if(BufferAtCriticalLevel()) sl@0: { sl@0: if(aEventInfo.iEventType == EEventsUserTrace) sl@0: { sl@0: if(!iIgnoringTrace) sl@0: { sl@0: //if this is the first time we are ignoring trace events, we need to issue a EEventsUserTracesLost event sl@0: aEventInfo.Reset(); sl@0: aEventInfo.iEventType = EEventsUserTracesLost; sl@0: sl@0: iIgnoringTrace = ETrue; sl@0: } sl@0: else sl@0: { sl@0: //otherwise, ignore this event sl@0: return; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: //reset the iIgnoringTrace flag as we are not at critical level sl@0: iIgnoringTrace = EFalse; sl@0: } sl@0: sl@0: // only one space left so store a EEventsBufferFull event sl@0: if(!BufferCanStoreEvent()) sl@0: { sl@0: aEventInfo.Reset(); sl@0: aEventInfo.iEventType = EEventsBufferFull; sl@0: } sl@0: sl@0: __NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown); // we think there is space but the slot is not marked empty sl@0: sl@0: // Insert the event into the ring buffer at iHead sl@0: iEventQueue[iHead] = aEventInfo; sl@0: IncrementPosition(iHead); sl@0: } sl@0: sl@0: // Checks whether the event queue is empty sl@0: TBool DDebugAgent::BufferEmpty() const sl@0: { sl@0: return (NumberOfEmptySlots() == NUMBER_OF_EVENTS_TO_QUEUE); sl@0: } sl@0: sl@0: // Checks whether the event queue is full sl@0: TBool DDebugAgent::BufferFull() const sl@0: { sl@0: return (NumberOfEmptySlots() == 0); sl@0: } sl@0: sl@0: // Checks whether there is room in the event queue to store an event (i.e. at least two free slots) sl@0: TBool DDebugAgent::BufferCanStoreEvent() const sl@0: { sl@0: return (NumberOfEmptySlots() > 1); sl@0: } sl@0: sl@0: //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) sl@0: TBool DDebugAgent::BufferAtCriticalLevel() const sl@0: { sl@0: return (NumberOfEmptySlots() < NUMBER_OF_EVENTS_TO_QUEUE - CRITICAL_BUFFER_SIZE); sl@0: } sl@0: sl@0: // increments aPosition, wrapping at NUMBER_OF_EVENTS_TO_QUEUE if necessary sl@0: void DDebugAgent::IncrementPosition(TInt& aPosition) sl@0: { sl@0: aPosition = (aPosition + 1) % NUMBER_OF_EVENTS_TO_QUEUE; sl@0: } sl@0: sl@0: // finds the number of empty slots in the event queue sl@0: TInt DDebugAgent::NumberOfEmptySlots() const sl@0: { sl@0: if(iHead < iTail) sl@0: { sl@0: return (iTail - iHead) - 1; sl@0: } sl@0: // iHead >= iTail sl@0: return NUMBER_OF_EVENTS_TO_QUEUE - (iHead - iTail); sl@0: } sl@0: