42
|
1 |
// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
2 |
// All rights reserved.
|
|
3 |
// This component and the accompanying materials are made available
|
56
|
4 |
// under the terms of "Eclipse Public License v1.0"
|
42
|
5 |
// which accompanies this distribution, and is available
|
|
6 |
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
7 |
//
|
|
8 |
// Initial Contributors:
|
|
9 |
// Nokia Corporation - initial contribution.
|
|
10 |
//
|
|
11 |
// Contributors:
|
|
12 |
//
|
|
13 |
// Description:
|
|
14 |
// Purpose: Kernel-side tracking of debug agent information associated
|
|
15 |
// with each process being debugged.
|
|
16 |
//
|
|
17 |
//
|
|
18 |
|
|
19 |
#include <e32def.h>
|
|
20 |
#include <e32def_private.h>
|
|
21 |
#include <e32cmn.h>
|
|
22 |
#include <e32cmn_private.h>
|
|
23 |
#include <kernel/kernel.h>
|
|
24 |
#include <kernel/kern_priv.h>
|
|
25 |
#include <nk_trace.h>
|
|
26 |
#include <arm.h>
|
|
27 |
|
|
28 |
#include "d_process_tracker.h"
|
|
29 |
#include "debug_logging.h"
|
|
30 |
|
|
31 |
#include "d_debug_agent.h"
|
|
32 |
#include "debug_utils.h"
|
|
33 |
|
|
34 |
#include "d_debug_agent.inl"
|
|
35 |
|
|
36 |
using namespace Debug;
|
|
37 |
|
|
38 |
// ctor
|
|
39 |
DDebugAgent::DDebugAgent(TUint64 aId) :
|
|
40 |
iId(aId),
|
|
41 |
iRequestGetEventStatus(NULL),
|
|
42 |
iClientThread(0),
|
|
43 |
iEventQueue(KNumberOfEventsToQueue, 0),
|
|
44 |
iHead(0),
|
|
45 |
iTail(0),
|
|
46 |
iEventQueueLock(NULL),
|
|
47 |
iFreeSlots(KNumberOfEventsToQueue),
|
|
48 |
iIgnoringTrace(EFalse),
|
|
49 |
iEventBalance(0)
|
|
50 |
{
|
|
51 |
LOG_MSG2("DDebugAgent::DDebugAgent(), this=0x%x ", this);
|
|
52 |
|
|
53 |
// Initialize all the Event Actions to Ignore
|
|
54 |
for(TInt i=0; i<EEventsLast; i++)
|
|
55 |
{
|
|
56 |
iEventActions[i] = EActionIgnore;
|
|
57 |
}
|
|
58 |
}
|
|
59 |
|
|
60 |
DDebugAgent* DDebugAgent::New(TUint64 aId)
|
|
61 |
{
|
|
62 |
LOG_MSG2("DDebugAgent::New(id=0x%lx)", aId);
|
|
63 |
DDebugAgent* agent = new DDebugAgent(aId);
|
|
64 |
if(agent == NULL)
|
|
65 |
{
|
|
66 |
return (NULL);
|
|
67 |
}
|
|
68 |
if(KErrNone != agent->Construct())
|
|
69 |
{
|
|
70 |
delete agent;
|
|
71 |
return (NULL);
|
|
72 |
}
|
|
73 |
|
|
74 |
// Use a semaphore to serialise access
|
|
75 |
TInt err = Kern::SemaphoreCreate(agent->iEventQueueLock, _L("RM_DebugAgentQueueLock"), 1 /* Initial count */);
|
|
76 |
if (err != KErrNone)
|
|
77 |
return NULL;
|
|
78 |
|
|
79 |
return agent;
|
|
80 |
}
|
|
81 |
|
|
82 |
/** Standard contructor.
|
|
83 |
* Fills event queue with empty events
|
|
84 |
* @return : standard system error code
|
|
85 |
*/
|
|
86 |
TInt DDebugAgent::Construct()
|
|
87 |
{
|
|
88 |
// Empty the event queue
|
|
89 |
TDriverEventInfo emptyEvent;
|
|
90 |
TInt err = KErrNone;
|
|
91 |
|
|
92 |
for (TInt i=0; i<KNumberOfEventsToQueue; i++)
|
|
93 |
{
|
|
94 |
err = iEventQueue.Append(emptyEvent);
|
|
95 |
if (err != KErrNone)
|
|
96 |
{
|
|
97 |
LOG_MSG("Error appending blank event entry");
|
|
98 |
return err;
|
|
99 |
}
|
|
100 |
}
|
|
101 |
|
|
102 |
err = Kern::CreateClientDataRequest(iRequestGetEventStatus);
|
|
103 |
if(err != KErrNone)
|
|
104 |
{
|
|
105 |
LOG_MSG("Error creating TClientDataRequest");
|
|
106 |
return err;
|
|
107 |
}
|
|
108 |
|
|
109 |
LOG_MSG2("DDebugAgent::Construct() iRequestGetEventStatus=0x%08x", iRequestGetEventStatus);
|
|
110 |
|
|
111 |
return err;
|
|
112 |
}
|
|
113 |
|
|
114 |
// dtor
|
|
115 |
DDebugAgent::~DDebugAgent()
|
|
116 |
{
|
|
117 |
iEventQueue.Reset();
|
|
118 |
|
|
119 |
if (iEventQueueLock)
|
|
120 |
iEventQueueLock->Close(NULL);
|
|
121 |
|
|
122 |
if(iRequestGetEventStatus)
|
|
123 |
Kern::DestroyClientRequest(iRequestGetEventStatus);
|
|
124 |
|
|
125 |
}
|
|
126 |
|
|
127 |
// Associate an action with a particular kernel event
|
|
128 |
TInt DDebugAgent::SetEventAction(TEventType aEvent, TKernelEventAction aEventAction)
|
|
129 |
{
|
|
130 |
// Valid Event?
|
|
131 |
if (aEvent >= EEventsLast)
|
|
132 |
{
|
|
133 |
LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent);
|
|
134 |
return KErrArgument;
|
|
135 |
}
|
|
136 |
|
|
137 |
iEventActions[aEvent] = aEventAction;
|
|
138 |
|
|
139 |
return KErrNone;
|
|
140 |
}
|
|
141 |
|
|
142 |
/** Get the aEventAction associated with aEvent
|
|
143 |
*
|
|
144 |
* @return : aEventAction (always +ve), or KErrArgument.
|
|
145 |
*/
|
|
146 |
TInt DDebugAgent::EventAction(TEventType aEvent)
|
|
147 |
{
|
|
148 |
// Validate the Event id
|
|
149 |
if (aEvent >= EEventsLast)
|
|
150 |
{
|
|
151 |
LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent);
|
|
152 |
return KErrArgument;
|
|
153 |
}
|
|
154 |
|
|
155 |
// Return the action associated with this event
|
|
156 |
return iEventActions[aEvent];
|
|
157 |
}
|
|
158 |
|
|
159 |
/** Obtain the details of the latest kernel event (if it exists) and place the details in aEventInfo
|
|
160 |
* If there is no event in the queue for this process+agent combination, store the details
|
|
161 |
* so that it can be notified later when an event actually occurs.
|
|
162 |
*
|
|
163 |
* @param aAsyncGetValueRequest - TClientDataRequest object used for pinning user memory
|
|
164 |
* @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS.
|
|
165 |
*/
|
|
166 |
void DDebugAgent::GetEvent(TClientDataRequest<TEventInfo>* aAsyncGetValueRequest, DThread* aClientThread)
|
|
167 |
{
|
|
168 |
LockEventQueue();
|
|
169 |
|
|
170 |
iRequestGetEventStatus->Reset();
|
|
171 |
TInt err = iRequestGetEventStatus->SetStatus( aAsyncGetValueRequest->StatusPtr() );
|
|
172 |
if (err != KErrNone)
|
|
173 |
{
|
|
174 |
LOG_MSG2("Error :iRequestGetEventStatus->SetStatus ret %d", err);
|
|
175 |
UnlockEventQueue();
|
|
176 |
return;
|
|
177 |
}
|
|
178 |
|
|
179 |
iRequestGetEventStatus->SetDestPtr( aAsyncGetValueRequest->DestPtr() );
|
|
180 |
|
|
181 |
iEventBalance++;
|
|
182 |
|
|
183 |
LOG_MSG5("DDebugAgent::GetEvent: this=0x%08x, iRequestGetEventStatus=0x%08x, iEventBalance=%d, destPrt=0x%08x",
|
|
184 |
this, iRequestGetEventStatus, iEventBalance, aAsyncGetValueRequest->DestPtr() );
|
|
185 |
|
|
186 |
iClientThread = aClientThread;
|
|
187 |
|
|
188 |
if (BufferEmpty())
|
|
189 |
{
|
|
190 |
LOG_MSG2("Event buffer empty, iEventBalance=%d", iEventBalance);
|
|
191 |
UnlockEventQueue();
|
|
192 |
return;
|
|
193 |
}
|
|
194 |
|
|
195 |
LOG_MSG5("Event already available at queue pos (tail)=%d, evType=%d, threadId=0x%x, actionTaken=%d",
|
|
196 |
iTail, iEventQueue[iTail].iEventType,
|
|
197 |
iEventQueue[iTail].iThreadId, iEventQueue[iTail].iActionTaken );
|
|
198 |
|
|
199 |
// returning the event to the client
|
|
200 |
err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread);
|
|
201 |
if (err != KErrNone)
|
|
202 |
{
|
|
203 |
LOG_MSG2("Error writing event info: %d", err);
|
|
204 |
UnlockEventQueue();
|
|
205 |
return;
|
|
206 |
}
|
|
207 |
|
|
208 |
// signal the DSS thread
|
|
209 |
Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone);
|
|
210 |
iEventBalance--;
|
|
211 |
|
|
212 |
iEventQueue[iTail].Reset();
|
|
213 |
|
|
214 |
// move to the next slot
|
|
215 |
IncrementTailPosition();
|
|
216 |
|
|
217 |
UnlockEventQueue();
|
|
218 |
}
|
|
219 |
|
|
220 |
/**
|
|
221 |
* Stop waiting for an event to occur. This means events will be placed
|
|
222 |
* in the iEventQueue (by setting iEventBalance to 0) until GetEvent is called.
|
|
223 |
*/
|
|
224 |
TInt DDebugAgent::CancelGetEvent(void)
|
|
225 |
{
|
|
226 |
LOG_MSG2("DDebugAgent::CancelGetEvent. iEventBalance=%d. > QueueRequestComplete", iEventBalance);
|
|
227 |
Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel);
|
|
228 |
iEventBalance=0;
|
|
229 |
iClientThread = 0;
|
|
230 |
return KErrNone;
|
|
231 |
}
|
|
232 |
|
|
233 |
/** Signal a kernel event to the user-side DSS when it occurs, or queue it for later
|
|
234 |
* if the user-side has not called GetEvent (see above).
|
|
235 |
*
|
|
236 |
* @param aEventInfo - the details of the event to queue.
|
|
237 |
*/
|
|
238 |
void DDebugAgent::NotifyEvent(const TDriverEventInfo& aEventInfo)
|
|
239 |
{
|
|
240 |
|
|
241 |
if(aEventInfo.iEventType >= EEventsLast)
|
|
242 |
{
|
|
243 |
LOG_MSG3("DDebugAgent::NotifyEvent(),iEventType %d, this=0x%x. Ignoring since > EEventsLast", aEventInfo.iEventType, this);
|
|
244 |
return;
|
|
245 |
}
|
|
246 |
|
|
247 |
LockEventQueue();
|
|
248 |
|
|
249 |
DThread* currentThread = &Kern::CurrentThread();
|
|
250 |
|
|
251 |
|
|
252 |
TKernelEventAction action = iEventActions[aEventInfo.iEventType];
|
|
253 |
|
|
254 |
if (aEventInfo.iProcessId == Id() &&
|
|
255 |
(aEventInfo.iEventType == EEventsSwExc || aEventInfo.iEventType == EEventsHwExc || aEventInfo.iEventType == EEventsKillThread))
|
|
256 |
{
|
|
257 |
|
|
258 |
// 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.
|
|
259 |
// 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
|
|
260 |
// tests.
|
|
261 |
//
|
|
262 |
// 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
|
|
263 |
// 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
|
|
264 |
// process is set as system critical
|
|
265 |
// This fixes the original problem with CDS's worker thread crashing, and doesn't wreck the t_rmdebug2 tests.
|
|
266 |
|
|
267 |
TBool problematic = (
|
|
268 |
(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
|
|
269 |
|| (aEventInfo.iThreadFlags & (KThreadFlagProcessPermanent|KThreadFlagSystemPermanent))
|
|
270 |
);
|
|
271 |
|
|
272 |
if (problematic)
|
|
273 |
{
|
|
274 |
LOG_MSG("Agent is dying - no further events will be delivered to it");
|
|
275 |
iAgentDying = ETrue;
|
|
276 |
}
|
|
277 |
|
|
278 |
}
|
|
279 |
|
|
280 |
if (iAgentDying && action == EActionSuspend)
|
|
281 |
{
|
|
282 |
LOG_MSG("Not delivering this event or suspending the thread because agent is dying");
|
|
283 |
action = EActionIgnore;
|
|
284 |
}
|
|
285 |
|
|
286 |
switch (action)
|
|
287 |
{
|
|
288 |
case EActionSuspend:
|
|
289 |
{
|
|
290 |
LOG_MSG5("DDebugAgent::NotifyEvent(), Suspend thread, iEventType %d, this=0x%x currThrd=0x%08x, iEventBalance=%d",
|
|
291 |
aEventInfo.iEventType, this, currentThread, iEventBalance );
|
|
292 |
|
|
293 |
switch(aEventInfo.iEventType)
|
|
294 |
{
|
|
295 |
case EEventsAddLibrary:
|
|
296 |
case EEventsRemoveLibrary:
|
|
297 |
// TomS: Anybody want to explain what is going on here??
|
|
298 |
currentThread = DebugUtils::OpenThreadHandle(aEventInfo.iThreadId);
|
|
299 |
if(currentThread)
|
|
300 |
{
|
|
301 |
currentThread->Close(NULL);
|
|
302 |
}
|
|
303 |
break;
|
|
304 |
default:
|
|
305 |
break;
|
|
306 |
}
|
|
307 |
|
|
308 |
// Do not call suspend for breakpoints, since the breakpoint code that runs when deciding if an exception
|
|
309 |
// is a breakpoint will itself suspend the thread
|
|
310 |
if( (aEventInfo.iEventType != EEventsBreakPoint) && (aEventInfo.iEventType != EEventsProcessBreakPoint) )
|
|
311 |
{
|
|
312 |
TInt err = TheDProcessTracker.SuspendThread(currentThread, aEventInfo.FreezeOnSuspend());
|
|
313 |
if((err != KErrNone) && (err != KErrAlreadyExists))
|
|
314 |
{
|
|
315 |
// Is there anything we can do in the future to deal with this error having happened?
|
|
316 |
LOG_MSG2("DDebugAgent::NotifyEvent() Problem while suspending thread: %d", err);
|
|
317 |
}
|
|
318 |
}
|
|
319 |
|
|
320 |
// now drop through to the continue case, which typically notifies
|
|
321 |
// the debug agent of the event
|
|
322 |
}
|
|
323 |
case EActionContinue:
|
|
324 |
{
|
|
325 |
if( action == EActionContinue )
|
|
326 |
{
|
|
327 |
LOG_MSG5("DDebugAgent::NotifyEvent(), Action continue, iEventType %d, this=0x%x currThrd=0x%08x, iEventBalance=%d",
|
|
328 |
aEventInfo.iEventType, this, currentThread, iEventBalance );
|
|
329 |
}
|
|
330 |
|
|
331 |
// Queue this event
|
|
332 |
TDriverEventInfo eventInfo = aEventInfo;
|
|
333 |
eventInfo.iActionTaken = action;
|
|
334 |
QueueEvent(eventInfo);
|
|
335 |
|
|
336 |
// Tell the user about the oldest event in the queue
|
|
337 |
if ( iClientThread )
|
|
338 |
{
|
|
339 |
if( iRequestGetEventStatus && (iEventBalance > 0) )
|
|
340 |
{
|
|
341 |
// Fill the event data
|
|
342 |
TInt err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread);
|
|
343 |
if (err != KErrNone)
|
|
344 |
{
|
|
345 |
LOG_MSG2("Error writing event info: %d", err);
|
|
346 |
}
|
|
347 |
|
|
348 |
// signal the debugger thread
|
|
349 |
LOG_MSG4("> QueueRequestComplete iRequestGetEventStatus=0x%08x, iEventBalance=%d, iTail=%d",
|
|
350 |
iRequestGetEventStatus->iStatus, iEventBalance, iTail );
|
|
351 |
Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone);
|
|
352 |
|
|
353 |
iEventBalance--;
|
|
354 |
|
|
355 |
iEventQueue[iTail].Reset();
|
|
356 |
|
|
357 |
// move to the next slot
|
|
358 |
IncrementTailPosition();
|
|
359 |
}
|
|
360 |
else
|
|
361 |
{
|
|
362 |
if( !iRequestGetEventStatus )
|
|
363 |
{
|
|
364 |
LOG_MSG("iRequestGetEventStatus is NULL so not signalling client" );
|
|
365 |
}
|
|
366 |
else
|
|
367 |
{
|
|
368 |
LOG_MSG2("Queued event. iEventBalance=%d (unbalanced event requests vs notifications)",
|
|
369 |
iEventBalance );
|
|
370 |
}
|
|
371 |
}
|
|
372 |
}
|
|
373 |
else
|
|
374 |
{
|
|
375 |
LOG_MSG("DDebugAgent::NotifyEvent() : Not informing client since its thread is NULL");
|
|
376 |
}
|
|
377 |
break;
|
|
378 |
}
|
|
379 |
case EActionIgnore:
|
|
380 |
default:
|
|
381 |
// Ignore everything we don't understand.
|
|
382 |
break;
|
|
383 |
}
|
|
384 |
|
|
385 |
UnlockEventQueue();
|
|
386 |
|
|
387 |
}
|
|
388 |
|
|
389 |
// Used to identify which Debug Agent this DDebugAgent is associated with.
|
|
390 |
TUint64 DDebugAgent::Id(void)
|
|
391 |
{
|
|
392 |
return iId;
|
|
393 |
}
|
|
394 |
|
|
395 |
/**
|
|
396 |
* Used to add an event to the event queue for this debug agent if event
|
|
397 |
* queue is not at critical level. If it is at critical and it is trace event,
|
|
398 |
* we start ignoring trace events and insert a lost trace event.
|
|
399 |
* If the buffer cannot store an event, only insert a buffer full event.
|
|
400 |
* @see EEventsBufferFull
|
|
401 |
* @see EEventsUserTracesLost
|
|
402 |
* @see TDriverEventInfo
|
|
403 |
* @see iEventQueue
|
|
404 |
*/
|
|
405 |
void DDebugAgent::QueueEvent(const TDriverEventInfo& aEventInfo)
|
|
406 |
{
|
|
407 |
// Have we caught the tail?
|
|
408 |
if(BufferFull())
|
|
409 |
{
|
|
410 |
LOG_MSG("DDebugAgent::QueueEvent : BufferFull. Not queueing");
|
|
411 |
return;
|
|
412 |
}
|
|
413 |
|
|
414 |
// Assert if we think there is space but the slot is not marked empty
|
|
415 |
__NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown);
|
|
416 |
|
|
417 |
const TBool bufferAtCritical = BufferAtCriticalLevel();
|
|
418 |
|
|
419 |
if(!bufferAtCritical)
|
|
420 |
{
|
|
421 |
//reset the iIgnoringTrace flag as we are not at
|
|
422 |
//critical level and can store event
|
|
423 |
iIgnoringTrace = EFalse;
|
|
424 |
|
|
425 |
// Insert the event into the ring buffer at iHead
|
|
426 |
iEventQueue[iHead] = aEventInfo;
|
|
427 |
IncrementHeadPosition();
|
|
428 |
}
|
|
429 |
else if(bufferAtCritical && BufferCanStoreEvent())
|
|
430 |
{
|
|
431 |
LOG_MSG("DDebugAgent::QueueEvent : BufferCritical");
|
|
432 |
if(aEventInfo.iEventType == EEventsUserTrace)
|
|
433 |
{
|
|
434 |
if(!iIgnoringTrace)
|
|
435 |
{
|
|
436 |
//if this is the first time we are ignoring trace events,
|
|
437 |
//we need to issue a EEventsUserTracesLost event
|
|
438 |
iEventQueue[iHead].Reset();
|
|
439 |
iEventQueue[iHead].iEventType = EEventsUserTracesLost;
|
|
440 |
IncrementHeadPosition();
|
|
441 |
|
|
442 |
iIgnoringTrace = ETrue;
|
|
443 |
}
|
|
444 |
else
|
|
445 |
{
|
|
446 |
//otherwise, ignore this event
|
|
447 |
LOG_MSG("DDebugAgent::QueueEvent : Ignore EEventsUserTrace event");
|
|
448 |
}
|
|
449 |
}
|
|
450 |
else
|
|
451 |
{
|
|
452 |
// Store the event since its not a trace event
|
|
453 |
iEventQueue[iHead] = aEventInfo;
|
|
454 |
IncrementHeadPosition();
|
|
455 |
}
|
|
456 |
}
|
|
457 |
else
|
|
458 |
{
|
|
459 |
//At critical level and cannot store new events, so
|
|
460 |
//only one space left. Store a EEventsBufferFull event
|
|
461 |
LOG_MSG("DDebugAgent::QueueEvent : Event Buffer Full, ignoring event");
|
|
462 |
iEventQueue[iHead].Reset();
|
|
463 |
iEventQueue[iHead].iEventType = EEventsBufferFull;
|
|
464 |
IncrementHeadPosition();
|
|
465 |
}
|
|
466 |
}
|
|
467 |
|
|
468 |
// End of file - d_debug_agent.cpp
|