|
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_MSG("DDebugAgent::New()"); |
|
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 LOG_MSG("DDebugAgent::Construct()"); |
|
90 TDriverEventInfo emptyEvent; |
|
91 TInt err = KErrNone; |
|
92 |
|
93 for (TInt i=0; i<KNumberOfEventsToQueue; i++) |
|
94 { |
|
95 err = iEventQueue.Append(emptyEvent); |
|
96 if (err != KErrNone) |
|
97 { |
|
98 LOG_MSG("Error appending blank event entry"); |
|
99 return err; |
|
100 } |
|
101 } |
|
102 |
|
103 err = Kern::CreateClientDataRequest(iRequestGetEventStatus); |
|
104 if(err != KErrNone) |
|
105 { |
|
106 LOG_MSG("Error creating TClientDataRequest"); |
|
107 return err; |
|
108 } |
|
109 |
|
110 LOG_MSG2("DDebugAgent::Construct() iRequestGetEventStatus=0x%08x", iRequestGetEventStatus); |
|
111 |
|
112 return err; |
|
113 } |
|
114 |
|
115 // dtor |
|
116 DDebugAgent::~DDebugAgent() |
|
117 { |
|
118 iEventQueue.Reset(); |
|
119 |
|
120 if (iEventQueueLock) |
|
121 iEventQueueLock->Close(NULL); |
|
122 |
|
123 if(iRequestGetEventStatus) |
|
124 Kern::DestroyClientRequest(iRequestGetEventStatus); |
|
125 |
|
126 } |
|
127 |
|
128 // Associate an action with a particular kernel event |
|
129 TInt DDebugAgent::SetEventAction(TEventType aEvent, TKernelEventAction aEventAction) |
|
130 { |
|
131 // Valid Event? |
|
132 if (aEvent >= EEventsLast) |
|
133 { |
|
134 LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); |
|
135 return KErrArgument; |
|
136 } |
|
137 |
|
138 iEventActions[aEvent] = aEventAction; |
|
139 |
|
140 return KErrNone; |
|
141 } |
|
142 |
|
143 /** Get the aEventAction associated with aEvent |
|
144 * |
|
145 * @return : aEventAction (always +ve), or KErrArgument. |
|
146 */ |
|
147 TInt DDebugAgent::EventAction(TEventType aEvent) |
|
148 { |
|
149 // Validate the Event id |
|
150 if (aEvent >= EEventsLast) |
|
151 { |
|
152 LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); |
|
153 return KErrArgument; |
|
154 } |
|
155 |
|
156 // Return the action associated with this event |
|
157 return iEventActions[aEvent]; |
|
158 } |
|
159 |
|
160 /** Obtain the details of the latest kernel event (if it exists) and place the details in aEventInfo |
|
161 * If there is no event in the queue for this process+agent combination, store the details |
|
162 * so that it can be notified later when an event actually occurs. |
|
163 * |
|
164 * @param aAsyncGetValueRequest - TClientDataRequest object used for pinning user memory |
|
165 * @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS. |
|
166 */ |
|
167 void DDebugAgent::GetEvent(TClientDataRequest<TEventInfo>* aAsyncGetValueRequest, DThread* aClientThread) |
|
168 { |
|
169 LockEventQueue(); |
|
170 |
|
171 iRequestGetEventStatus->Reset(); |
|
172 TInt err = iRequestGetEventStatus->SetStatus( aAsyncGetValueRequest->StatusPtr() ); |
|
173 if (err != KErrNone) |
|
174 { |
|
175 LOG_MSG2("Error :iRequestGetEventStatus->SetStatus ret %d", err); |
|
176 return; |
|
177 } |
|
178 |
|
179 iRequestGetEventStatus->SetDestPtr( aAsyncGetValueRequest->DestPtr() ); |
|
180 |
|
181 iEventBalance++; |
|
182 |
|
183 LOG_MSG4("DDebugAgent::GetEvent: this=0x%08x, iRequestGetEventStatus=0x%08x, iEventBalance=%d", |
|
184 this, iRequestGetEventStatus, iEventBalance ); |
|
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_MSG2("Event already available at queue pos=%d", iTail); |
|
196 |
|
197 // returning the event to the client |
|
198 err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread); |
|
199 if (err != KErrNone) |
|
200 { |
|
201 LOG_MSG2("Error writing event info: %d", err); |
|
202 UnlockEventQueue(); |
|
203 return; |
|
204 } |
|
205 |
|
206 // signal the DSS thread |
|
207 Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); |
|
208 iEventBalance--; |
|
209 |
|
210 iEventQueue[iTail].Reset(); |
|
211 |
|
212 // move to the next slot |
|
213 IncrementTailPosition(); |
|
214 |
|
215 UnlockEventQueue(); |
|
216 } |
|
217 |
|
218 /** |
|
219 * Stop waiting for an event to occur. This means events will be placed |
|
220 * in the iEventQueue (by setting iEventBalance to 0) until GetEvent is called. |
|
221 */ |
|
222 TInt DDebugAgent::CancelGetEvent(void) |
|
223 { |
|
224 LOG_MSG2("DDebugAgent::CancelGetEvent. iEventBalance=%d. > QueueRequestComplete", iEventBalance); |
|
225 Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel); |
|
226 iEventBalance=0; |
|
227 iClientThread = 0; |
|
228 return KErrNone; |
|
229 } |
|
230 |
|
231 /** Signal a kernel event to the user-side DSS when it occurs, or queue it for later |
|
232 * if the user-side has not called GetEvent (see above). |
|
233 * |
|
234 * @param aEventInfo - the details of the event to queue. |
|
235 */ |
|
236 void DDebugAgent::NotifyEvent(const TDriverEventInfo& aEventInfo) |
|
237 { |
|
238 |
|
239 if(aEventInfo.iEventType >= EEventsLast) |
|
240 { |
|
241 LOG_MSG3("DDebugAgent::NotifyEvent(),iEventType %d, this=0x%x. Ignoring since > EEventsLast", aEventInfo.iEventType, this); |
|
242 return; |
|
243 } |
|
244 |
|
245 LockEventQueue(); |
|
246 |
|
247 DThread* currentThread = &Kern::CurrentThread(); |
|
248 |
|
249 LOG_MSG5("DDebugAgent::NotifyEvent(), iEventType %d, this=0x%x currThrd=0x%08x, iEventBalance=%d", |
|
250 aEventInfo.iEventType, this, currentThread, iEventBalance ); |
|
251 TKernelEventAction action = iEventActions[aEventInfo.iEventType]; |
|
252 |
|
253 switch (action) |
|
254 { |
|
255 case EActionSuspend: |
|
256 { |
|
257 LOG_MSG("DDebugAgent::NotifyEvent() Suspend thread"); |
|
258 |
|
259 switch(aEventInfo.iEventType) |
|
260 { |
|
261 case EEventsAddLibrary: |
|
262 case EEventsRemoveLibrary: |
|
263 currentThread = DebugUtils::OpenThreadHandle(aEventInfo.iThreadId); |
|
264 if(currentThread) |
|
265 { |
|
266 currentThread->Close(NULL); |
|
267 } |
|
268 break; |
|
269 default: |
|
270 break; |
|
271 } |
|
272 TInt err = TheDProcessTracker.SuspendThread(currentThread, aEventInfo.FreezeOnSuspend()); |
|
273 if((err != KErrNone) && (err != KErrAlreadyExists)) |
|
274 { |
|
275 // Is there anything we can do in the future to deal with this error having happened? |
|
276 LOG_MSG2("DDebugAgent::NotifyEvent() Problem while suspending thread: %d", err); |
|
277 } |
|
278 |
|
279 // now drop through to the continue case, which typically notifies |
|
280 // the debug agent of the event |
|
281 } |
|
282 case EActionContinue: |
|
283 { |
|
284 // Queue this event |
|
285 QueueEvent(aEventInfo); |
|
286 |
|
287 // Tell the user about the oldest event in the queue |
|
288 if ( iClientThread ) |
|
289 { |
|
290 if( iRequestGetEventStatus && (iEventBalance > 0) ) |
|
291 { |
|
292 // Fill the event data |
|
293 TInt err = iEventQueue[iTail].WriteEventToClientThread(iRequestGetEventStatus,iClientThread); |
|
294 if (err != KErrNone) |
|
295 { |
|
296 LOG_MSG2("Error writing event info: %d", err); |
|
297 } |
|
298 |
|
299 // signal the debugger thread |
|
300 LOG_MSG4("> QueueRequestComplete iRequestGetEventStatus=0x%08x, iEventBalance=%d, iTail=%d", |
|
301 iRequestGetEventStatus->iStatus, iEventBalance, iTail ); |
|
302 Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); |
|
303 |
|
304 iEventBalance--; |
|
305 |
|
306 iEventQueue[iTail].Reset(); |
|
307 |
|
308 // move to the next slot |
|
309 IncrementTailPosition(); |
|
310 } |
|
311 else |
|
312 { |
|
313 if( !iRequestGetEventStatus ) |
|
314 { |
|
315 LOG_MSG("iRequestGetEventStatus is NULL so not signalling client" ); |
|
316 } |
|
317 else |
|
318 { |
|
319 LOG_MSG2("Queued event. iEventBalance=%d (unbalanced event requests vs notifications)", |
|
320 iEventBalance ); |
|
321 } |
|
322 } |
|
323 } |
|
324 else |
|
325 { |
|
326 LOG_MSG("DDebugAgent::NotifyEvent() : Not informing client since its thread is NULL"); |
|
327 } |
|
328 break; |
|
329 } |
|
330 case EActionIgnore: |
|
331 default: |
|
332 LOG_EVENT_MSG("DDebugAgent::NotifyEvent() fallen through to default case"); |
|
333 // Ignore everything we don't understand. |
|
334 |
|
335 } |
|
336 |
|
337 UnlockEventQueue(); |
|
338 |
|
339 } |
|
340 |
|
341 // Used to identify which Debug Agent this DDebugAgent is associated with. |
|
342 TUint64 DDebugAgent::Id(void) |
|
343 { |
|
344 return iId; |
|
345 } |
|
346 |
|
347 /** |
|
348 * Used to add an event to the event queue for this debug agent if event |
|
349 * queue is not at critical level. If it is at critical and it is trace event, |
|
350 * we start ignoring trace events and insert a lost trace event. |
|
351 * If the buffer cannot store an event, only insert a buffer full event. |
|
352 * @see EEventsBufferFull |
|
353 * @see EEventsUserTracesLost |
|
354 * @see TDriverEventInfo |
|
355 * @see iEventQueue |
|
356 */ |
|
357 void DDebugAgent::QueueEvent(const TDriverEventInfo& aEventInfo) |
|
358 { |
|
359 // Have we caught the tail? |
|
360 if(BufferFull()) |
|
361 { |
|
362 LOG_MSG("DDebugAgent::QueueEvent : BufferFull. Not queueing"); |
|
363 return; |
|
364 } |
|
365 |
|
366 // Assert if we think there is space but the slot is not marked empty |
|
367 __NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown); |
|
368 |
|
369 const TBool bufferAtCritical = BufferAtCriticalLevel(); |
|
370 |
|
371 if(!bufferAtCritical) |
|
372 { |
|
373 //reset the iIgnoringTrace flag as we are not at |
|
374 //critical level and can store event |
|
375 iIgnoringTrace = EFalse; |
|
376 |
|
377 // Insert the event into the ring buffer at iHead |
|
378 iEventQueue[iHead] = aEventInfo; |
|
379 IncrementHeadPosition(); |
|
380 } |
|
381 else if(bufferAtCritical && BufferCanStoreEvent()) |
|
382 { |
|
383 LOG_MSG("DDebugAgent::QueueEvent : BufferCritical"); |
|
384 if(aEventInfo.iEventType == EEventsUserTrace) |
|
385 { |
|
386 if(!iIgnoringTrace) |
|
387 { |
|
388 //if this is the first time we are ignoring trace events, |
|
389 //we need to issue a EEventsUserTracesLost event |
|
390 iEventQueue[iHead].Reset(); |
|
391 iEventQueue[iHead].iEventType = EEventsUserTracesLost; |
|
392 IncrementHeadPosition(); |
|
393 |
|
394 iIgnoringTrace = ETrue; |
|
395 } |
|
396 else |
|
397 { |
|
398 //otherwise, ignore this event |
|
399 LOG_MSG("DDebugAgent::QueueEvent : Ignore EEventsUserTrace event"); |
|
400 } |
|
401 } |
|
402 else |
|
403 { |
|
404 // Store the event since its not a trace event |
|
405 iEventQueue[iHead] = aEventInfo; |
|
406 IncrementHeadPosition(); |
|
407 } |
|
408 } |
|
409 else |
|
410 { |
|
411 //At critical level and cannot store new events, so |
|
412 //only one space left. Store a EEventsBufferFull event |
|
413 LOG_MSG("DDebugAgent::QueueEvent : Event Buffer Full, ignoring event"); |
|
414 iEventQueue[iHead].Reset(); |
|
415 iEventQueue[iHead].iEventType = EEventsBufferFull; |
|
416 IncrementHeadPosition(); |
|
417 } |
|
418 } |
|
419 |
|
420 // End of file - d_debug_agent.cpp |