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 #include <arm.h> |
|
23 |
|
24 #include "GppSamplerImpl.h" |
|
25 |
|
26 extern TUint* IntStackPtr(); |
|
27 #define TAG(obj) (*(TUint32*)&(obj.iAsyncDeleteNext)) |
|
28 |
|
29 // properties for ISA task parsing |
|
30 const TUid KIsaPropertyCat={0x2001E5AD}; |
|
31 enum TIsaPropertyKeys |
|
32 { |
|
33 EIsaPropertyIsaTaskParserStatus = 1, |
|
34 EIsaPropertyIsaTaskAddressStart, |
|
35 EIsaPropertyIsaTaskAddressEnd, |
|
36 EIsaPropertyIsaTaskAddress, |
|
37 EIsaPropertyIsaOsTaskRunningAddress, |
|
38 EIsaPropertyIsaTaskParsedName |
|
39 }; |
|
40 |
|
41 |
|
42 DGppSamplerImpl::DGppSamplerImpl() |
|
43 { |
|
44 LOGTEXT("GppSamplerImpl::GppSamplerImpl"); |
|
45 iInterruptStack = (TUint*)IntStackPtr(); |
|
46 |
|
47 LOGTEXT("GppSamplerImpl::GppSamplerImpl - attaching to properties"); |
|
48 |
|
49 TInt err = iIsaStartAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressStart); |
|
50 if(err != KErrNone) |
|
51 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressStart not available"); |
|
52 err = iIsaEndAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressEnd); |
|
53 if(err != KErrNone) |
|
54 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressEnd not available"); |
|
55 err = iIsaPluginStatus.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskParserStatus); |
|
56 if(err != KErrNone) |
|
57 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskParserStatus not available"); |
|
58 err = iIsaOsTaskRunning.Attach(KIsaPropertyCat, EIsaPropertyIsaOsTaskRunningAddress); |
|
59 if(err != KErrNone) |
|
60 LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaOsTaskRunningAddress not available"); |
|
61 |
|
62 PROFILER_ISA_TASK_NAMES |
|
63 |
|
64 Reset(); |
|
65 } |
|
66 |
|
67 DGppSamplerImpl::~DGppSamplerImpl() |
|
68 { |
|
69 iIsaStartAddr.Close(); |
|
70 iIsaEndAddr.Close(); |
|
71 iIsaPluginStatus.Close(); |
|
72 iIsaOsTaskRunning.Close(); |
|
73 } |
|
74 |
|
75 void DGppSamplerImpl::Reset() |
|
76 { |
|
77 LOGTEXT("GppSamplerImpl::Reset"); |
|
78 iLastPc = 0; |
|
79 iLastThread = 0xfffffffe; |
|
80 iRepeat = 0; |
|
81 iIsaStatus = 0; |
|
82 iIsaStart = 0; |
|
83 iIsaEnd = 0; |
|
84 // isaOsTaskRunningAddr = 0; |
|
85 |
|
86 // in SMP start time common with all CPUs, provided by DGeneralsDriver class |
|
87 #ifndef __SMP__ |
|
88 iStartTime = ( NKern::TickCount() & 0xfffffffc ); |
|
89 #endif |
|
90 |
|
91 TPropertyStatus status; |
|
92 TInt osAddr = 0; |
|
93 |
|
94 LOGTEXT("GppSamplerImpl::Reset - getting status"); |
|
95 |
|
96 // get status of ISA plug-in |
|
97 if(iIsaPluginStatus.GetStatus(status)) |
|
98 { |
|
99 iIsaPluginStatus.Get(iIsaStatus); |
|
100 LOGSTRING2("GppSamplerImpl::Reset - ISA plug-in status %d", iIsaStatus); |
|
101 } |
|
102 |
|
103 if(iIsaStatus > 0) |
|
104 { |
|
105 LOGTEXT("GppSamplerImpl::Reset - get isa start address"); |
|
106 iIsaStartAddr.Get(iIsaStart); |
|
107 LOGTEXT("GppSamplerImpl::Reset - get isa end address"); |
|
108 iIsaEndAddr.Get(iIsaEnd); |
|
109 LOGTEXT("GppSamplerImpl::Reset - get isa os_task_running address"); |
|
110 iIsaOsTaskRunning.Get(osAddr); |
|
111 isaOsTaskRunningAddr = reinterpret_cast<TInt*>(osAddr); |
|
112 LOGSTRING2("GppSamplerImpl::Reset - got isa os_task_running address 0x%X", osAddr); |
|
113 } |
|
114 |
|
115 LOGTEXT("GppSamplerImpl::Reset - initializing isa task list"); |
|
116 |
|
117 iIsaSample = false; |
|
118 |
|
119 for(TInt i=0;i<256;i++) |
|
120 knownIsaTasks[i] = -1; |
|
121 |
|
122 knownIsaTaskCount = 0; |
|
123 |
|
124 iCpuSelector = 0x3; |
|
125 #ifndef __SMP__ |
|
126 iMask = 0xfffffffc; |
|
127 #else |
|
128 iMask = 0xfffffff0; |
|
129 switch(iCpuNumber) |
|
130 { |
|
131 case 0: |
|
132 iCpuSelector = 0x1; |
|
133 break; |
|
134 case 1: |
|
135 iCpuSelector = 0x2; |
|
136 break; |
|
137 case 2: |
|
138 iCpuSelector = 0x4; |
|
139 break; |
|
140 case 3: |
|
141 iCpuSelector = 0x8; |
|
142 break; |
|
143 } |
|
144 #endif |
|
145 } |
|
146 |
|
147 TUint8* DGppSamplerImpl::EncodeTag(TUint8* aPtr) |
|
148 // |
|
149 // Encode a tag and version to the trace data. This allows the offline analyser to |
|
150 // identify the sample data. |
|
151 // |
|
152 { |
|
153 _LIT(KGppSamplerVersion,"Bappea_GPP_V"); |
|
154 _LIT(KProfilerVersion,"#Prof#"); |
|
155 _LIT(KSamplerVersion,"#Samp#"); |
|
156 #ifdef __SMP__ |
|
157 _LIT(KCPUNumberText,"#CPU#"); |
|
158 #endif |
|
159 |
|
160 TBuf<64> buf; |
|
161 buf.Zero(); |
|
162 buf.Append(KGppSamplerVersion); |
|
163 buf.Append(PROFILER_GPP_SAMPLER_VERSION); |
|
164 buf.Append(KProfilerVersion); |
|
165 buf.Append(PROFILER_VERSION_SHORT); |
|
166 buf.Append(KSamplerVersion); |
|
167 buf.Append(PROFILER_SAMPLER_VERSION); |
|
168 #ifdef __SMP__ |
|
169 buf.Append(KCPUNumberText); |
|
170 buf.AppendNum(iCpuNumber); |
|
171 #endif |
|
172 aPtr = EncodeText(aPtr, buf); |
|
173 return aPtr; |
|
174 } |
|
175 |
|
176 TUint8* DGppSamplerImpl::EncodeInt(TUint8* aPtr,TInt aValue) |
|
177 { |
|
178 LOGSTRING2("Encoding int 0x%x",aPtr); |
|
179 |
|
180 LOGSTRING2("TIint = 0x%x",aValue); |
|
181 |
|
182 TUint byte; |
|
183 for (;;) |
|
184 { |
|
185 byte = aValue & 0x7f; |
|
186 if ((aValue >> 6) == (aValue >> 7)) |
|
187 break; |
|
188 aValue >>= 7; |
|
189 *aPtr++ = byte; |
|
190 } |
|
191 *aPtr++ = byte | 0x80; |
|
192 |
|
193 LOGSTRING2("Encoded int 0x%x",aPtr); |
|
194 |
|
195 return aPtr; |
|
196 } |
|
197 |
|
198 TUint8* DGppSamplerImpl::EncodeUint(TUint8* aPtr,TUint aValue) |
|
199 { |
|
200 LOGSTRING2("Encoding Uint 0x%x",aPtr); |
|
201 |
|
202 LOGSTRING2("TUint = 0x%x",aValue); |
|
203 |
|
204 |
|
205 TUint byte; |
|
206 for (;;) |
|
207 { |
|
208 byte = aValue & 0x7f; |
|
209 aValue >>= 7; |
|
210 if (aValue == 0) |
|
211 break; |
|
212 *aPtr++ = byte; |
|
213 } |
|
214 *aPtr++ = byte | 0x80; |
|
215 |
|
216 LOGSTRING2("Encoded Uint 0x%x",aPtr); |
|
217 |
|
218 return aPtr; |
|
219 } |
|
220 |
|
221 TUint8* DGppSamplerImpl::EncodeText(TUint8* aPtr, const TDesC& aDes) |
|
222 // |
|
223 // Encode a descriptor into the data stream |
|
224 // This is currently limited to a descriptor that is up to 255 characters in length, |
|
225 // and Unicode characters are truncated to 8 bits |
|
226 // |
|
227 { |
|
228 LOGSTRING2("Encoding text 0x%x",aPtr); |
|
229 TInt len=aDes.Length(); |
|
230 *aPtr++ = TUint8(len); |
|
231 const TText* p = aDes.Ptr(); |
|
232 while (--len >= 0) |
|
233 { |
|
234 *aPtr++ = TUint8(*p++); |
|
235 } |
|
236 |
|
237 LOGSTRING2("Encoded text 0x%x",aPtr); |
|
238 return aPtr; |
|
239 } |
|
240 |
|
241 |
|
242 TUint8* DGppSamplerImpl::EncodeName(TUint8* aPtr, DObject& aObject,TUint32 id) |
|
243 // |
|
244 // Encode the name of a kernel object |
|
245 // |
|
246 { |
|
247 LOGSTRING2("Encoding name 0x%x",aPtr); |
|
248 TBuf8<0x5f> name; |
|
249 aObject.TraceAppendName(name,false); |
|
250 |
|
251 if(id != 0xffffffff) |
|
252 { |
|
253 name.Append('['); |
|
254 name.AppendNum(id,EHex); |
|
255 name.Append(']'); |
|
256 } |
|
257 else |
|
258 { |
|
259 name.Append('['); |
|
260 name.AppendNum((TUint32)((void*)&(((DThread*)&aObject)->iNThread)),EHex); |
|
261 name.Append(']'); |
|
262 } |
|
263 |
|
264 aPtr = EncodeText(aPtr,name); |
|
265 LOGSTRING2("Encoded name 0x%x",aPtr); |
|
266 return aPtr; |
|
267 } |
|
268 |
|
269 TUint8* DGppSamplerImpl::EncodeThread(TUint8* aPtr, DThread& aThread) |
|
270 // |
|
271 // Encode a thread name in the data stream. |
|
272 // The thread is identified by its name, and the identity of its owning process. |
|
273 // If the process has not been identified in the data stream already, it's name is |
|
274 // also encoded. |
|
275 // |
|
276 { |
|
277 LOGSTRING2("Encoding thread 0x%x",aPtr); |
|
278 |
|
279 DProcess& p = *aThread.iOwningProcess; |
|
280 |
|
281 aPtr = EncodeUint(aPtr, p.iId); |
|
282 |
|
283 #ifdef __SMP__ |
|
284 // check if first time founding |
|
285 if ((TAG(p) & iMask) != iStartTime) |
|
286 { |
|
287 // mark tagged for this CPU |
|
288 TAG(p) = (iStartTime | iCpuSelector); |
|
289 |
|
290 // The thread is 'unknown' to this sample, so encode the thread name |
|
291 aPtr = EncodeName(aPtr, p, p.iId); |
|
292 } |
|
293 // check if thread appeared already on this CPU |
|
294 else if((TAG(p) & iCpuSelector) != iCpuSelector) |
|
295 { |
|
296 TAG(p) = (TAG(p) | iCpuSelector); |
|
297 // The thread is 'unknown' to this sample, so encode the thread name |
|
298 aPtr = EncodeName(aPtr, p, p.iId); |
|
299 } |
|
300 #else |
|
301 if (TAG(p) != iStartTime) |
|
302 { |
|
303 TAG(p) = iStartTime; |
|
304 // Provide the name matching this process ID |
|
305 aPtr = EncodeName(aPtr, p, p.iId); |
|
306 } |
|
307 #endif |
|
308 aPtr = EncodeName(aPtr, aThread,0xffffffff); |
|
309 |
|
310 LOGSTRING2("Encoded thread 0x%x",aPtr); |
|
311 |
|
312 return aPtr; |
|
313 } |
|
314 |
|
315 TUint8* DGppSamplerImpl::EncodeRepeat(TUint8* aPtr) |
|
316 // |
|
317 // Encode a repeated sequence of samples |
|
318 // |
|
319 { |
|
320 LOGSTRING2("Encoding repeat, 0x%x",iRepeat); |
|
321 |
|
322 aPtr = EncodeInt(aPtr, 0); |
|
323 aPtr = EncodeUint(aPtr, iRepeat); |
|
324 iRepeat = 0; |
|
325 |
|
326 LOGSTRING2("Encoded repeat, 0x%x",iRepeat); |
|
327 |
|
328 return aPtr; |
|
329 } |
|
330 |
|
331 TInt DGppSamplerImpl::CreateFirstSample() |
|
332 { |
|
333 LOGTEXT("GppSamplerImpl::CreateFirstSample"); |
|
334 Reset(); |
|
335 |
|
336 TUint8* w = this->tempBuf; |
|
337 w = EncodeTag(w); |
|
338 |
|
339 TInt length = w-tempBuf; |
|
340 |
|
341 LOGSTRING2("TAG encoded, length %d",length); |
|
342 return length; |
|
343 } |
|
344 |
|
345 TBool DGppSamplerImpl::IsaTaskKnown(TUint8 task) |
|
346 { |
|
347 for(TInt i=0;i<256;i++) |
|
348 { |
|
349 if(knownIsaTasks[i] == -1) |
|
350 { |
|
351 knownIsaTasks[i] = task; |
|
352 knownIsaTaskCount++; |
|
353 return false; |
|
354 } |
|
355 else if(knownIsaTasks[i] == task) |
|
356 { |
|
357 return true; |
|
358 } |
|
359 } |
|
360 |
|
361 return false; |
|
362 } |
|
363 |
|
364 TUint8* DGppSamplerImpl::EncodeIsaTask(TUint8* aPtr, TUint task) |
|
365 |
|
366 { |
|
367 LOGSTRING2("Encoding ISA task 0x%x",aPtr); |
|
368 |
|
369 aPtr = EncodeUint(aPtr,task); |
|
370 // use the task name as the process name |
|
371 aPtr = EncodeIsaName(aPtr,task,true); |
|
372 // then encode the task name |
|
373 aPtr = EncodeIsaName(aPtr,task,false); |
|
374 |
|
375 LOGSTRING2("Encoded ISA task 0x%x",aPtr); |
|
376 |
|
377 return aPtr; |
|
378 } |
|
379 |
|
380 TUint8* DGppSamplerImpl::EncodeIsaName(TUint8* aPtr, TUint task,TBool process) |
|
381 // |
|
382 // Encode a descriptor into the data stream |
|
383 // This is currently limited to a descriptor that is up to 255 characters in length, |
|
384 // and Unicode characters are truncated to 8 bits |
|
385 // |
|
386 { |
|
387 TBuf8<256> aDes; |
|
388 |
|
389 // #ifdef NCP_COMMON_PROFILER_ISA_TASKS |
|
390 if(iIsaStatus > 0) |
|
391 { |
|
392 // resolve the isa task name from the task name array |
|
393 if((task-100000) < PROFILER_ISA_OS_TASK_AMOUNT && process == false) |
|
394 { |
|
395 aDes.Append(isaTaskNames[(task-100000)]); |
|
396 } |
|
397 else |
|
398 { |
|
399 aDes.Append(_L8("NativeOS_Task")); |
|
400 } |
|
401 } |
|
402 else |
|
403 { |
|
404 aDes.Append(_L8("NativeOS_Task")); |
|
405 } |
|
406 |
|
407 aDes.Append('['); |
|
408 aDes.AppendNum((task-100000),EHex); |
|
409 aDes.Append(']'); |
|
410 |
|
411 LOGSTRING2("Encoding ISA name 0x%x",aPtr); |
|
412 TInt len=aDes.Length(); |
|
413 *aPtr++ = TUint8(len); |
|
414 const TText* p = aDes.Ptr(); |
|
415 while (--len >= 0) |
|
416 { |
|
417 *aPtr++ = TUint8(*p++); |
|
418 } |
|
419 |
|
420 LOGSTRING2("Encoded ISA name 0x%x",aPtr); |
|
421 return aPtr; |
|
422 } |
|
423 |
|
424 |
|
425 TInt DGppSamplerImpl::SampleImpl() |
|
426 // |
|
427 // ISR for the profile timer |
|
428 // This extracts the thread and PC that was current when the interrupt went off and |
|
429 // encodes it into the sample data buffer. If enough data has been generated, the |
|
430 // DFC is triggered to complete a read request |
|
431 // |
|
432 { |
|
433 TUint8* w(this->tempBuf); |
|
434 |
|
435 // Kern::Printf(("Got thread 0x%08x"), &t); |
|
436 #ifdef __SMP__ |
|
437 // get the program counter of irq mode |
|
438 TUint32 pc = (TUint32)Arm::IrqReturnAddress(); |
|
439 #else |
|
440 // get program counter of irq mode |
|
441 TUint32 pc = iInterruptStack[-1]; |
|
442 #endif |
|
443 //LOGSTRING3("pc value 0x%x sp 0x%x",pc,iInterruptStack); |
|
444 |
|
445 // ignore the low bit being set for THUMB mode - we use for something else |
|
446 pc &= ~1; |
|
447 TInt diff = pc - iLastPc; |
|
448 iLastPc = pc; |
|
449 |
|
450 if(iIsaStatus > 0) |
|
451 { |
|
452 if((TUint32)pc > (TUint32)iIsaStart && (TUint32)pc < (TUint32)iIsaEnd) |
|
453 { |
|
454 LOGSTRING2("Identified ISA execution at 0x%x",pc); |
|
455 iIsaSample = true; |
|
456 } |
|
457 else |
|
458 { |
|
459 LOGSTRING2("Normal sample at 0x%x",pc); |
|
460 iIsaSample = false; |
|
461 } |
|
462 } |
|
463 |
|
464 // request for current thread from kernel |
|
465 DThread& t = ((DThread&)*Kern::NThreadToDThread(NKern::CurrentThread())); |
|
466 |
|
467 TUint tid; |
|
468 TUint8 isaTask = 0; |
|
469 if(iIsaSample) |
|
470 { |
|
471 LOGSTRING2("Reading ISA task number from 0x%x",isaOsTaskRunningAddr); |
|
472 |
|
473 // if we don't get reasonable ISA address to read, skip ISA task handling |
|
474 if(isaOsTaskRunningAddr == 0) |
|
475 { |
|
476 tid = 100000; // to tell the difference from SOS threads |
|
477 iIsaSample = false; |
|
478 } |
|
479 else // normal ISA task parsing process |
|
480 { |
|
481 isaTask = *isaOsTaskRunningAddr; |
|
482 LOGSTRING2("ISA task = %d",isaTask); |
|
483 tid = isaTask; |
|
484 // this will make sure we don't mix ISA tasks and normal tasks |
|
485 tid += 100000; |
|
486 } |
|
487 |
|
488 } |
|
489 else |
|
490 { |
|
491 tid = t.iId; |
|
492 } |
|
493 |
|
494 if (tid != iLastThread) |
|
495 { |
|
496 // Change of thread is marked in the low bit of the PC difference |
|
497 diff |= 1; |
|
498 } |
|
499 TUint rp = iRepeat; |
|
500 if (diff == 0) |
|
501 { |
|
502 // Identical sample, bump up the repeat count |
|
503 iRepeat = rp + 1; |
|
504 } |
|
505 else |
|
506 { |
|
507 if (rp) |
|
508 { |
|
509 // Encode the repeat data |
|
510 w = EncodeRepeat(w); |
|
511 } |
|
512 // Encode the PC difference |
|
513 w = EncodeInt(w, diff); |
|
514 if (diff & 1) |
|
515 { |
|
516 // Encode the new thread ID |
|
517 if(iIsaSample) |
|
518 { |
|
519 iLastThread = tid; |
|
520 w = EncodeUint(w,tid); |
|
521 |
|
522 if(!this->IsaTaskKnown(isaTask)) |
|
523 { |
|
524 w = EncodeIsaTask(w,iLastThread); |
|
525 } |
|
526 //LOGSTRING2("Sample total length: %d",w-tempBuf); |
|
527 TInt length = w-tempBuf; |
|
528 // encoded isa task, return here |
|
529 return length; |
|
530 } |
|
531 |
|
532 iLastThread = tid; |
|
533 w = EncodeUint(w, tid); |
|
534 |
|
535 #ifdef __SMP__ |
|
536 // iStartTime format: 0xXXXXXXX0, the last byte set to zero |
|
537 // iMask = 0xfffffff0(0b111....1110000) |
|
538 // iCpuSelector = 0x1(0b0001), 0x2(0b0010), 0x4(0b0100) or 0x8(0b1000) |
|
539 |
|
540 // check first time founding |
|
541 if ((TAG(t) & iMask) != iStartTime) |
|
542 { |
|
543 // mark tagged for this CPU |
|
544 TAG(t) = (iStartTime | iCpuSelector); |
|
545 |
|
546 // The thread is 'unknown' to this sample, so encode the thread name |
|
547 w = EncodeThread(w, t); |
|
548 } |
|
549 // check if thread appeared on this CPU |
|
550 else if((TAG(t) & iCpuSelector) != iCpuSelector) |
|
551 { |
|
552 TAG(t) = (TAG(t) | iCpuSelector); |
|
553 // The thread is 'unknown' to this sample, so encode the thread name |
|
554 w = EncodeThread(w, t); |
|
555 } |
|
556 #else |
|
557 // check if tag has not been set, neither original nor |
|
558 if ((TAG(t) & 0xfffffffc) != iStartTime) |
|
559 { |
|
560 TAG(t) = ((TAG(t) & 0x3) | iStartTime); |
|
561 // The thread is 'unknown' to this sample, so encode the thread name |
|
562 w = EncodeThread(w, t); |
|
563 } |
|
564 #endif |
|
565 } |
|
566 } |
|
567 LOGSTRING2("Sample total length: %d",w-tempBuf); |
|
568 TInt length = w-tempBuf; |
|
569 |
|
570 return length; |
|
571 } |
|
572 |
|