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 |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
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 |
|