|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <piprofiler/ProfilerVersion.h> |
|
20 #include <piprofiler/ProfilerTraces.h> |
|
21 #include <kern_priv.h> |
|
22 |
|
23 #include "GppSamplerImpl.h" |
|
24 |
|
25 extern TUint* IntStackPtr(); |
|
26 #define TAG(obj) (*(TUint32*)&(obj.iAsyncDeleteNext)) |
|
27 |
|
28 // properties for ISA task parsing |
|
29 const TUid KIsaPropertyCat={0x2001E5AD}; |
|
30 enum TIsaPropertyKeys |
|
31 { |
|
32 EIsaPropertyIsaTaskParserStatus = 1, |
|
33 EIsaPropertyIsaTaskAddressStart, |
|
34 EIsaPropertyIsaTaskAddressEnd, |
|
35 EIsaPropertyIsaTaskAddress, |
|
36 EIsaPropertyIsaOsTaskRunningAddress, |
|
37 EIsaPropertyIsaTaskParsedName |
|
38 }; |
|
39 |
|
40 |
|
41 GppSamplerImpl::GppSamplerImpl() |
|
42 { |
|
43 LOGTEXT("GppSamplerImpl::GppSamplerImpl"); |
|
44 iInterruptStack = (TUint*)IntStackPtr(); |
|
45 |
|
46 LOGTEXT("GppSamplerImpl::GppSamplerImpl - attaching to properties"); |
|
47 |
|
48 TInt err = iIsaStartAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressStart); |
|
49 if(err != KErrNone) |
|
50 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressStart not available"); |
|
51 err = iIsaEndAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressEnd); |
|
52 if(err != KErrNone) |
|
53 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressEnd not available"); |
|
54 err = iIsaPluginStatus.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskParserStatus); |
|
55 if(err != KErrNone) |
|
56 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskParserStatus not available"); |
|
57 err = iIsaOsTaskRunning.Attach(KIsaPropertyCat, EIsaPropertyIsaOsTaskRunningAddress); |
|
58 if(err != KErrNone) |
|
59 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaOsTaskRunningAddress not available"); |
|
60 |
|
61 PROFILER_ISA_TASK_NAMES |
|
62 |
|
63 Reset(); |
|
64 } |
|
65 |
|
66 GppSamplerImpl::~GppSamplerImpl() |
|
67 { |
|
68 iIsaStartAddr.Close(); |
|
69 iIsaEndAddr.Close(); |
|
70 iIsaPluginStatus.Close(); |
|
71 iIsaOsTaskRunning.Close(); |
|
72 } |
|
73 |
|
74 void GppSamplerImpl::Reset() |
|
75 { |
|
76 LOGTEXT("GppSamplerImpl::Reset"); |
|
77 iLastPc = 0; |
|
78 iLastThread = 0; |
|
79 iRepeat = 0; |
|
80 iIsaStatus = 0; |
|
81 iIsaStart = 0; |
|
82 iIsaEnd = 0; |
|
83 // isaOsTaskRunningAddr = 0; |
|
84 iStartTime=( NKern::TickCount() & 0xfffffffc ); |
|
85 |
|
86 TPropertyStatus status; |
|
87 TInt osAddr = 0; |
|
88 |
|
89 LOGTEXT("GppSamplerImpl::Reset - getting status"); |
|
90 |
|
91 // get status of ISA plug-in |
|
92 if(iIsaPluginStatus.GetStatus(status)) |
|
93 { |
|
94 iIsaPluginStatus.Get(iIsaStatus); |
|
95 LOGSTRING2("GppSamplerImpl::Reset - ISA plug-in status %d", iIsaStatus); |
|
96 } |
|
97 |
|
98 if(iIsaStatus > 0) |
|
99 { |
|
100 LOGTEXT("GppSamplerImpl::Reset - get isa start address"); |
|
101 iIsaStartAddr.Get(iIsaStart); |
|
102 LOGTEXT("GppSamplerImpl::Reset - get isa end address"); |
|
103 iIsaEndAddr.Get(iIsaEnd); |
|
104 LOGTEXT("GppSamplerImpl::Reset - get isa os_task_running address"); |
|
105 iIsaOsTaskRunning.Get(osAddr); |
|
106 isaOsTaskRunningAddr = reinterpret_cast<TInt*>(osAddr); |
|
107 LOGSTRING2("GppSamplerImpl::Reset - got isa os_task_running address 0x%X", osAddr); |
|
108 } |
|
109 |
|
110 LOGTEXT("GppSamplerImpl::Reset - initializing isa task list"); |
|
111 |
|
112 iIsaSample = false; |
|
113 |
|
114 for(TInt i=0;i<256;i++) |
|
115 knownIsaTasks[i] = -1; |
|
116 |
|
117 knownIsaTaskCount = 0; |
|
118 |
|
119 } |
|
120 |
|
121 TUint8* GppSamplerImpl::EncodeTag(TUint8* aPtr) |
|
122 // |
|
123 // Encode a tag and version to the trace data. This allows the offline analyser to |
|
124 // identify the sample data. |
|
125 // |
|
126 { |
|
127 _LIT(KGppSamplerVersion,"Bappea_GPP_V"); |
|
128 _LIT(KProfilerVersion,"#Prof#"); |
|
129 _LIT(KSamplerVersion,"#Samp#"); |
|
130 |
|
131 TBuf<64> buf; |
|
132 buf.Zero(); |
|
133 buf.Append(KGppSamplerVersion); |
|
134 buf.Append(PROFILER_GPP_SAMPLER_VERSION); |
|
135 buf.Append(KProfilerVersion); |
|
136 buf.Append(PROFILER_VERSION_SHORT); |
|
137 buf.Append(KSamplerVersion); |
|
138 buf.Append(PROFILER_SAMPLER_VERSION); |
|
139 aPtr = EncodeText(aPtr, buf); |
|
140 return aPtr; |
|
141 } |
|
142 |
|
143 TUint8* GppSamplerImpl::EncodeInt(TUint8* aPtr,TInt aValue) |
|
144 { |
|
145 LOGSTRING2("Encoding int 0x%x",aPtr); |
|
146 |
|
147 LOGSTRING2("TIint = 0x%x",aValue); |
|
148 |
|
149 TUint byte; |
|
150 for (;;) |
|
151 { |
|
152 byte = aValue & 0x7f; |
|
153 if ((aValue >> 6) == (aValue >> 7)) |
|
154 break; |
|
155 aValue >>= 7; |
|
156 *aPtr++ = byte; |
|
157 } |
|
158 *aPtr++ = byte | 0x80; |
|
159 |
|
160 LOGSTRING2("Encoded int 0x%x",aPtr); |
|
161 |
|
162 return aPtr; |
|
163 } |
|
164 |
|
165 TUint8* GppSamplerImpl::EncodeUint(TUint8* aPtr,TUint aValue) |
|
166 { |
|
167 LOGSTRING2("Encoding Uint 0x%x",aPtr); |
|
168 |
|
169 LOGSTRING2("TUint = 0x%x",aValue); |
|
170 |
|
171 |
|
172 TUint byte; |
|
173 for (;;) |
|
174 { |
|
175 byte = aValue & 0x7f; |
|
176 aValue >>= 7; |
|
177 if (aValue == 0) |
|
178 break; |
|
179 *aPtr++ = byte; |
|
180 } |
|
181 *aPtr++ = byte | 0x80; |
|
182 |
|
183 LOGSTRING2("Encoded Uint 0x%x",aPtr); |
|
184 |
|
185 return aPtr; |
|
186 } |
|
187 |
|
188 TUint8* GppSamplerImpl::EncodeText(TUint8* aPtr, const TDesC& aDes) |
|
189 // |
|
190 // Encode a descriptor into the data stream |
|
191 // This is currently limited to a descriptor that is up to 255 characters in length, |
|
192 // and Unicode characters are truncated to 8 bits |
|
193 // |
|
194 { |
|
195 LOGSTRING2("Encoding text 0x%x",aPtr); |
|
196 TInt len=aDes.Length(); |
|
197 *aPtr++ = TUint8(len); |
|
198 const TText* p = aDes.Ptr(); |
|
199 while (--len >= 0) |
|
200 { |
|
201 *aPtr++ = TUint8(*p++); |
|
202 } |
|
203 |
|
204 LOGSTRING2("Encoded text 0x%x",aPtr); |
|
205 return aPtr; |
|
206 } |
|
207 |
|
208 |
|
209 TUint8* GppSamplerImpl::EncodeName(TUint8* aPtr, DObject& aObject,TUint32 id) |
|
210 // |
|
211 // Encode the name of a kernel object |
|
212 // |
|
213 { |
|
214 LOGSTRING2("Encoding name 0x%x",aPtr); |
|
215 TBuf8<0x5f> name; |
|
216 aObject.TraceAppendName(name,false); |
|
217 |
|
218 if(id != 0xffffffff) |
|
219 { |
|
220 name.Append('['); |
|
221 name.AppendNum(id,EHex); |
|
222 name.Append(']'); |
|
223 } |
|
224 else |
|
225 { |
|
226 name.Append('['); |
|
227 name.AppendNum((TUint32)((void*)&(((DThread*)&aObject)->iNThread)),EHex); |
|
228 name.Append(']'); |
|
229 } |
|
230 |
|
231 aPtr = EncodeText(aPtr,name); |
|
232 LOGSTRING2("Encoded name 0x%x",aPtr); |
|
233 return aPtr; |
|
234 } |
|
235 |
|
236 TUint8* GppSamplerImpl::EncodeThread(TUint8* aPtr, DThread& aThread) |
|
237 // |
|
238 // Encode a thread name in the data stream. |
|
239 // The thread is identified by its name, and the identity of its owning process. |
|
240 // If the process has not been identified in the data stream already, it's name is |
|
241 // also encoded. |
|
242 // |
|
243 { |
|
244 LOGSTRING2("Encoding thread 0x%x",aPtr); |
|
245 |
|
246 DProcess& p = *aThread.iOwningProcess; |
|
247 |
|
248 aPtr = EncodeUint(aPtr, p.iId); |
|
249 if (TAG(p) != iStartTime) |
|
250 { |
|
251 TAG(p) = iStartTime; |
|
252 // Provide the name matching this process ID |
|
253 aPtr = EncodeName(aPtr, p, p.iId); |
|
254 } |
|
255 aPtr = EncodeName(aPtr, aThread,0xffffffff); |
|
256 |
|
257 LOGSTRING2("Encoded thread 0x%x",aPtr); |
|
258 |
|
259 return aPtr; |
|
260 } |
|
261 |
|
262 TUint8* GppSamplerImpl::EncodeRepeat(TUint8* aPtr) |
|
263 // |
|
264 // Encode a repeated sequence of samples |
|
265 // |
|
266 { |
|
267 LOGSTRING2("Encoding repeat, 0x%x",iRepeat); |
|
268 |
|
269 aPtr = EncodeInt(aPtr, 0); |
|
270 aPtr = EncodeUint(aPtr, iRepeat); |
|
271 iRepeat = 0; |
|
272 |
|
273 LOGSTRING2("Encoded repeat, 0x%x",iRepeat); |
|
274 |
|
275 return aPtr; |
|
276 } |
|
277 |
|
278 TInt GppSamplerImpl::CreateFirstSample() |
|
279 { |
|
280 LOGTEXT("GppSamplerImpl::CreateFirstSample"); |
|
281 Reset(); |
|
282 |
|
283 TUint8* w = this->tempBuf; |
|
284 w = EncodeTag(w); |
|
285 |
|
286 TInt length = w-tempBuf; |
|
287 |
|
288 LOGSTRING2("TAG encoded, length %d",length); |
|
289 return length; |
|
290 } |
|
291 |
|
292 TBool GppSamplerImpl::IsaTaskKnown(TUint8 task) |
|
293 { |
|
294 for(TInt i=0;i<256;i++) |
|
295 { |
|
296 if(knownIsaTasks[i] == -1) |
|
297 { |
|
298 knownIsaTasks[i] = task; |
|
299 knownIsaTaskCount++; |
|
300 return false; |
|
301 } |
|
302 else if(knownIsaTasks[i] == task) |
|
303 { |
|
304 return true; |
|
305 } |
|
306 } |
|
307 |
|
308 return false; |
|
309 } |
|
310 |
|
311 TUint8* GppSamplerImpl::EncodeIsaTask(TUint8* aPtr, TUint task) |
|
312 |
|
313 { |
|
314 LOGSTRING2("Encoding ISA task 0x%x",aPtr); |
|
315 |
|
316 aPtr = EncodeUint(aPtr,task); |
|
317 // use the task name as the process name |
|
318 aPtr = EncodeIsaName(aPtr,task,true); |
|
319 // then encode the task name |
|
320 aPtr = EncodeIsaName(aPtr,task,false); |
|
321 |
|
322 LOGSTRING2("Encoded ISA task 0x%x",aPtr); |
|
323 |
|
324 return aPtr; |
|
325 } |
|
326 |
|
327 TUint8* GppSamplerImpl::EncodeIsaName(TUint8* aPtr, TUint task,TBool process) |
|
328 // |
|
329 // Encode a descriptor into the data stream |
|
330 // This is currently limited to a descriptor that is up to 255 characters in length, |
|
331 // and Unicode characters are truncated to 8 bits |
|
332 // |
|
333 { |
|
334 TBuf8<256> aDes; |
|
335 |
|
336 // #ifdef NCP_COMMON_PROFILER_ISA_TASKS |
|
337 if(iIsaStatus > 0) |
|
338 { |
|
339 // resolve the isa task name from the task name array |
|
340 if((task-100000) < PROFILER_ISA_OS_TASK_AMOUNT && process == false) |
|
341 { |
|
342 aDes.Append(isaTaskNames[(task-100000)]); |
|
343 } |
|
344 else |
|
345 { |
|
346 aDes.Append(_L8("NativeOS_Task")); |
|
347 } |
|
348 } |
|
349 else |
|
350 { |
|
351 aDes.Append(_L8("NativeOS_Task")); |
|
352 } |
|
353 |
|
354 aDes.Append('['); |
|
355 aDes.AppendNum((task-100000),EHex); |
|
356 aDes.Append(']'); |
|
357 |
|
358 LOGSTRING2("Encoding ISA name 0x%x",aPtr); |
|
359 TInt len=aDes.Length(); |
|
360 *aPtr++ = TUint8(len); |
|
361 const TText* p = aDes.Ptr(); |
|
362 while (--len >= 0) |
|
363 { |
|
364 *aPtr++ = TUint8(*p++); |
|
365 } |
|
366 |
|
367 LOGSTRING2("Encoded ISA name 0x%x",aPtr); |
|
368 return aPtr; |
|
369 } |
|
370 |
|
371 |
|
372 TInt GppSamplerImpl::SampleImpl() |
|
373 // |
|
374 // ISR for the profile timer |
|
375 // This extracts the thread and PC that was current when the interrupt went off and |
|
376 // encodes it into the sample data buffer. If enough data has been generated, the |
|
377 // DFC is triggered to complete a read request |
|
378 // |
|
379 { |
|
380 TUint8* w = this->tempBuf; |
|
381 |
|
382 TUint32 pc = iInterruptStack[-1]; |
|
383 LOGSTRING3("pc value 0x%x sp 0x%x",pc,iInterruptStack); |
|
384 |
|
385 // ignore the low bit being set for THUMB mode - we use for something else |
|
386 pc &= ~1; |
|
387 TInt diff = pc - iLastPc; |
|
388 iLastPc = pc; |
|
389 |
|
390 if(iIsaStatus > 0) |
|
391 { |
|
392 if((TUint32)pc > (TUint32)iIsaStart && (TUint32)pc < (TUint32)iIsaEnd) |
|
393 { |
|
394 LOGSTRING2("Identified ISA execution at 0x%x",pc); |
|
395 iIsaSample = true; |
|
396 } |
|
397 else |
|
398 { |
|
399 LOGSTRING2("Normal sample at 0x%x",pc); |
|
400 iIsaSample = false; |
|
401 } |
|
402 } |
|
403 |
|
404 DThread& t = Kern::CurrentThread(); |
|
405 |
|
406 TUint tid; |
|
407 TUint8 isaTask = 0; |
|
408 if(iIsaSample) |
|
409 { |
|
410 LOGSTRING2("Reading ISA task number from 0x%x",isaOsTaskRunningAddr); |
|
411 |
|
412 // if we don't get reasonable ISA address to read, skip ISA task handling |
|
413 if(isaOsTaskRunningAddr == 0) |
|
414 { |
|
415 tid = 100000; // to tell the difference from SOS threads |
|
416 iIsaSample = false; |
|
417 } |
|
418 else // normal ISA task parsing process |
|
419 { |
|
420 isaTask = *isaOsTaskRunningAddr; |
|
421 LOGSTRING2("ISA task = %d",isaTask); |
|
422 tid = isaTask; |
|
423 // this will make sure we don't mix ISA tasks and normal tasks |
|
424 tid += 100000; |
|
425 } |
|
426 |
|
427 } |
|
428 else |
|
429 { |
|
430 tid = t.iId; |
|
431 } |
|
432 |
|
433 if (tid != iLastThread) |
|
434 { |
|
435 // Change of thread is marked in the low bit of the PC difference |
|
436 diff |= 1; |
|
437 } |
|
438 TUint rp = iRepeat; |
|
439 if (diff == 0) |
|
440 { |
|
441 // Identical sample, bump up the repeat count |
|
442 iRepeat = rp + 1; |
|
443 } |
|
444 else |
|
445 { |
|
446 if (rp) |
|
447 { |
|
448 // Encode the repeat data |
|
449 w = EncodeRepeat(w); |
|
450 } |
|
451 // Encode the PC difference |
|
452 w = EncodeInt(w, diff); |
|
453 if (diff & 1) |
|
454 { |
|
455 // Encode the new thread ID |
|
456 if(iIsaSample) |
|
457 { |
|
458 iLastThread = tid; |
|
459 w = EncodeUint(w,tid); |
|
460 |
|
461 if(!this->IsaTaskKnown(isaTask)) |
|
462 { |
|
463 w = EncodeIsaTask(w,iLastThread); |
|
464 } |
|
465 //LOGSTRING2("Sample total length: %d",w-tempBuf); |
|
466 TInt length = w-tempBuf; |
|
467 // encoded isa task, return here |
|
468 return length; |
|
469 } |
|
470 |
|
471 iLastThread = tid; |
|
472 w = EncodeUint(w, tid); |
|
473 |
|
474 if ((TAG(t) & 0xfffffffc) != iStartTime) |
|
475 { |
|
476 |
|
477 TAG(t) = ((TAG(t) & 0x3) | iStartTime); |
|
478 // The thread is 'unknown' to this sample, so encode the thread name |
|
479 w = EncodeThread(w, t); |
|
480 } |
|
481 } |
|
482 } |
|
483 LOGSTRING2("Sample total length: %d",w-tempBuf); |
|
484 TInt length = w-tempBuf; |
|
485 |
|
486 return length; |
|
487 } |
|
488 |