|
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 using namespace Debug; |
|
35 |
|
36 #define NUMBER_OF_EVENTS_TO_QUEUE 100 |
|
37 #define CRITICAL_BUFFER_SIZE (NUMBER_OF_EVENTS_TO_QUEUE - 50) |
|
38 |
|
39 // ctor |
|
40 DDebugAgent::DDebugAgent(TUint64 aId) |
|
41 : iId(aId), |
|
42 iEventInfo(NULL), |
|
43 iEventQueue(NUMBER_OF_EVENTS_TO_QUEUE, 0), |
|
44 iRequestGetEventStatus(NULL), |
|
45 iClientThread(0), |
|
46 iHead(0), |
|
47 iTail(0), |
|
48 iIgnoringTrace(EFalse) |
|
49 { |
|
50 LOG_MSG("DDebugAgent::DDebugAgent() "); |
|
51 |
|
52 // Initialize all the Event Actions to Ignore |
|
53 for(TInt i=0; i<EEventsLast; i++) |
|
54 { |
|
55 iEventActions[i] = EActionIgnore; |
|
56 } |
|
57 } |
|
58 |
|
59 DDebugAgent* DDebugAgent::New(TUint64 aId) |
|
60 { |
|
61 LOG_MSG("DDebugAgent::New()"); |
|
62 DDebugAgent* agent = new DDebugAgent(aId); |
|
63 if(agent == NULL) |
|
64 { |
|
65 return (NULL); |
|
66 } |
|
67 if(KErrNone != agent->Construct()) |
|
68 { |
|
69 delete agent; |
|
70 return (NULL); |
|
71 } |
|
72 return agent; |
|
73 } |
|
74 |
|
75 TInt DDebugAgent::Construct() |
|
76 { |
|
77 // Empty the event queue |
|
78 LOG_MSG("DDebugAgent::Construct()"); |
|
79 TDriverEventInfo emptyEvent; |
|
80 |
|
81 for (TInt i=0; i<NUMBER_OF_EVENTS_TO_QUEUE; i++) |
|
82 { |
|
83 TInt err = iEventQueue.Append(emptyEvent); |
|
84 if (KErrNone != err) |
|
85 { |
|
86 LOG_MSG("Error appending blank event entry"); |
|
87 return err; |
|
88 } |
|
89 } |
|
90 return KErrNone; |
|
91 } |
|
92 |
|
93 |
|
94 // dtor |
|
95 DDebugAgent::~DDebugAgent() |
|
96 { |
|
97 iEventQueue.Reset(); |
|
98 } |
|
99 |
|
100 // Associate an action with a particular kernel event |
|
101 TInt DDebugAgent::SetEventAction(TEventType aEvent, TKernelEventAction aEventAction) |
|
102 { |
|
103 // Valid Event? |
|
104 if (aEvent >= EEventsLast) |
|
105 { |
|
106 LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); |
|
107 return KErrArgument; |
|
108 } |
|
109 |
|
110 iEventActions[aEvent] = aEventAction; |
|
111 |
|
112 return KErrNone; |
|
113 } |
|
114 |
|
115 /* Get the aEventAction associated with aEvent |
|
116 * |
|
117 * Returns : aEventAction (always +ve), or KErrArgument. |
|
118 */ |
|
119 TInt DDebugAgent::EventAction(TEventType aEvent) |
|
120 { |
|
121 // Validate the Event id |
|
122 if (aEvent >= EEventsLast) |
|
123 { |
|
124 LOG_MSG2("DDebugAgent::EventAction: Bad Event number %d",aEvent); |
|
125 return KErrArgument; |
|
126 } |
|
127 |
|
128 // Return the action associated with this event |
|
129 return iEventActions[aEvent]; |
|
130 } |
|
131 |
|
132 // Obtain the details of the latest kernel event (if it exists) and place the details in aEventInfo |
|
133 // If there is no event in the queue for this process+agent combination, store the details |
|
134 // so that it can be notified later when an event actually occurs. |
|
135 // |
|
136 // @param aAsyncGetValueRequest - TClientDataRequest object used for pinning user memory |
|
137 // @param aEventInfo - Address of TEventInfo structure to place event data when available |
|
138 // @param aClientThread - The ThreadId of the requesting user-side process. In this case the DSS. |
|
139 void DDebugAgent::GetEvent(TClientDataRequest<TEventInfo>* aAsyncGetValueRequest, TEventInfo* aEventInfo, DThread* aClientThread) |
|
140 { |
|
141 iClientThread = aClientThread; |
|
142 |
|
143 if (BufferEmpty()) |
|
144 { |
|
145 LOG_MSG("no events available"); |
|
146 |
|
147 // store the pointer so we can modify it later |
|
148 iEventInfo = (TEventInfo *)aEventInfo; |
|
149 iRequestGetEventStatus = aAsyncGetValueRequest; |
|
150 return; |
|
151 } |
|
152 |
|
153 LOG_MSG("Event available"); |
|
154 |
|
155 // returning the event to the client |
|
156 TInt err = iEventQueue[iTail].WriteEventToClientThread(aAsyncGetValueRequest,iClientThread); |
|
157 if (KErrNone != err) |
|
158 { |
|
159 LOG_MSG2("Error writing event info: %d", err); |
|
160 return; |
|
161 } |
|
162 |
|
163 // signal the DSS thread |
|
164 Kern::QueueRequestComplete(iClientThread, aAsyncGetValueRequest, KErrNone); |
|
165 |
|
166 iEventQueue[iTail].Reset(); |
|
167 |
|
168 // move to the next slot |
|
169 IncrementPosition(iTail); |
|
170 } |
|
171 |
|
172 // Stop waiting for an event to occur. This means events will be placed in the iEventQueue |
|
173 // until GetEvent is called. |
|
174 TInt DDebugAgent::CancelGetEvent(void) |
|
175 { |
|
176 Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrCancel); |
|
177 iEventInfo = NULL; |
|
178 iRequestGetEventStatus = 0; |
|
179 iClientThread = 0; |
|
180 |
|
181 return KErrNone; |
|
182 } |
|
183 |
|
184 // Signal a kernel event to the user-side DSS when it occurs, or queue it for later |
|
185 // if the user-side has not called GetEvent (see above). |
|
186 // |
|
187 // @param aEventInfo - the details of the event to queue. |
|
188 void DDebugAgent::NotifyEvent(const TDriverEventInfo& aEventInfo) |
|
189 { |
|
190 LOG_MSG("DDebugAgent::NotifyEvent()"); |
|
191 // Action depends on the TKernelEvent type in aEventInfo.iType |
|
192 |
|
193 // Added to fix the pass by value issue seen in Coverity. |
|
194 // Function is changed to pass by reference but temp object is explicitly created. |
|
195 TDriverEventInfo eventInfo = aEventInfo; |
|
196 |
|
197 if(aEventInfo.iEventType >= EEventsLast) |
|
198 { |
|
199 // unknown event type so return |
|
200 return; |
|
201 } |
|
202 |
|
203 TKernelEventAction action = iEventActions[eventInfo.iEventType]; |
|
204 |
|
205 switch (action) |
|
206 { |
|
207 case EActionSuspend: |
|
208 { |
|
209 LOG_MSG("DDebugAgent::NotifyEvent() Suspend thread"); |
|
210 DThread* currentThread = &Kern::CurrentThread(); |
|
211 switch(eventInfo.iEventType) |
|
212 { |
|
213 case EEventsAddLibrary: |
|
214 case EEventsRemoveLibrary: |
|
215 currentThread = DebugUtils::OpenThreadHandle(eventInfo.iThreadId); |
|
216 if(currentThread) |
|
217 { |
|
218 currentThread->Close(NULL); |
|
219 } |
|
220 break; |
|
221 default: |
|
222 break; |
|
223 } |
|
224 TInt err = TheDProcessTracker.SuspendThread(currentThread, eventInfo.FreezeOnSuspend()); |
|
225 if(!( (err == KErrNone) || (err == KErrAlreadyExists) )) |
|
226 { |
|
227 // Is there anything we can do in the future to deal with this error having happened? |
|
228 LOG_MSG2("DDebugAgent::NotifyEvent() Problem while suspending thread: %d", err); |
|
229 } |
|
230 |
|
231 // now drop through to the continue case, which typically notifies |
|
232 // the debug agent of the event |
|
233 } |
|
234 case EActionContinue: |
|
235 LOG_MSG("DDebugAgent::NotifyEvent() Continue"); |
|
236 |
|
237 // Tell the user about this event |
|
238 if (iEventInfo && iClientThread) |
|
239 { |
|
240 LOG_MSG("Completing event\r\n"); |
|
241 |
|
242 // returning the event to the client |
|
243 TInt err = eventInfo.WriteEventToClientThread(iRequestGetEventStatus,iClientThread); |
|
244 if (KErrNone != err) |
|
245 { |
|
246 LOG_MSG2("Error writing event info: %d", err); |
|
247 } |
|
248 |
|
249 // clear this since we've completed the request |
|
250 iEventInfo = NULL; |
|
251 |
|
252 // signal the debugger thread |
|
253 Kern::QueueRequestComplete(iClientThread, iRequestGetEventStatus, KErrNone); |
|
254 } |
|
255 else |
|
256 { |
|
257 LOG_MSG("Queuing event\r\n"); |
|
258 |
|
259 QueueEvent(eventInfo); |
|
260 |
|
261 } |
|
262 break; |
|
263 |
|
264 case EActionIgnore: |
|
265 default: |
|
266 LOG_MSG("DDebugAgent::NotifyEvent() fallen through to default case"); |
|
267 // Ignore everything we don't understand. |
|
268 return; |
|
269 } |
|
270 |
|
271 } |
|
272 |
|
273 // Used to identify which Debug Agent this DDebugAgent is associated with. |
|
274 TUint64 DDebugAgent::Id(void) |
|
275 { |
|
276 return iId; |
|
277 } |
|
278 |
|
279 // Used to add an event to the event queue for this debug agent |
|
280 void DDebugAgent::QueueEvent(TDriverEventInfo& aEventInfo) |
|
281 { |
|
282 // Have we caught the tail? |
|
283 if(BufferFull()) |
|
284 { |
|
285 return; |
|
286 } |
|
287 |
|
288 //check to see if we wish to ignore this event - we dump trace events as they are lower priority than the other events |
|
289 if(BufferAtCriticalLevel()) |
|
290 { |
|
291 if(aEventInfo.iEventType == EEventsUserTrace) |
|
292 { |
|
293 if(!iIgnoringTrace) |
|
294 { |
|
295 //if this is the first time we are ignoring trace events, we need to issue a EEventsUserTracesLost event |
|
296 aEventInfo.Reset(); |
|
297 aEventInfo.iEventType = EEventsUserTracesLost; |
|
298 |
|
299 iIgnoringTrace = ETrue; |
|
300 } |
|
301 else |
|
302 { |
|
303 //otherwise, ignore this event |
|
304 return; |
|
305 } |
|
306 } |
|
307 } |
|
308 else |
|
309 { |
|
310 //reset the iIgnoringTrace flag as we are not at critical level |
|
311 iIgnoringTrace = EFalse; |
|
312 } |
|
313 |
|
314 // only one space left so store a EEventsBufferFull event |
|
315 if(!BufferCanStoreEvent()) |
|
316 { |
|
317 aEventInfo.Reset(); |
|
318 aEventInfo.iEventType = EEventsBufferFull; |
|
319 } |
|
320 |
|
321 __NK_ASSERT_DEBUG(iEventQueue[iHead].iEventType == EEventsUnknown); // we think there is space but the slot is not marked empty |
|
322 |
|
323 // Insert the event into the ring buffer at iHead |
|
324 iEventQueue[iHead] = aEventInfo; |
|
325 IncrementPosition(iHead); |
|
326 } |
|
327 |
|
328 // Checks whether the event queue is empty |
|
329 TBool DDebugAgent::BufferEmpty() const |
|
330 { |
|
331 return (NumberOfEmptySlots() == NUMBER_OF_EVENTS_TO_QUEUE); |
|
332 } |
|
333 |
|
334 // Checks whether the event queue is full |
|
335 TBool DDebugAgent::BufferFull() const |
|
336 { |
|
337 return (NumberOfEmptySlots() == 0); |
|
338 } |
|
339 |
|
340 // Checks whether there is room in the event queue to store an event (i.e. at least two free slots) |
|
341 TBool DDebugAgent::BufferCanStoreEvent() const |
|
342 { |
|
343 return (NumberOfEmptySlots() > 1); |
|
344 } |
|
345 |
|
346 //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) |
|
347 TBool DDebugAgent::BufferAtCriticalLevel() const |
|
348 { |
|
349 return (NumberOfEmptySlots() < NUMBER_OF_EVENTS_TO_QUEUE - CRITICAL_BUFFER_SIZE); |
|
350 } |
|
351 |
|
352 // increments aPosition, wrapping at NUMBER_OF_EVENTS_TO_QUEUE if necessary |
|
353 void DDebugAgent::IncrementPosition(TInt& aPosition) |
|
354 { |
|
355 aPosition = (aPosition + 1) % NUMBER_OF_EVENTS_TO_QUEUE; |
|
356 } |
|
357 |
|
358 // finds the number of empty slots in the event queue |
|
359 TInt DDebugAgent::NumberOfEmptySlots() const |
|
360 { |
|
361 if(iHead < iTail) |
|
362 { |
|
363 return (iTail - iHead) - 1; |
|
364 } |
|
365 // iHead >= iTail |
|
366 return NUMBER_OF_EVENTS_TO_QUEUE - (iHead - iTail); |
|
367 } |
|
368 |