|
1 // Copyright (c) 1999-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 // e32utils\profiler\sampler.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "platform.h" |
|
19 |
|
20 #include "sampler.h" |
|
21 #include <kernel/kern_priv.h> //temporary |
|
22 #include <memmodel/epoc/plat_priv.h> // needed for DEpocCodeSeg |
|
23 _LIT(KLddName,"Sampler"); |
|
24 |
|
25 TKName KiDFCThread = _L("Running from iDFC"); |
|
26 TKName KiDFCProcess = _L("N/A"); |
|
27 TUint KiDFCId = (TUint)-1; //both process and thread assigned to iDFC will have 'fake' id=-1 |
|
28 |
|
29 const TInt KMajorVersionNumber=2; |
|
30 const TInt KMinorVersionNumber=0; |
|
31 const TInt KBuildVersionNumber=0; |
|
32 |
|
33 const TInt KMinRate=10; |
|
34 const TInt KMaxRate=1000; |
|
35 const TInt KRawBufSize=256; |
|
36 const TInt KCookedBufSize=0x2000; |
|
37 const TInt KCodeBufSize=0x2000; |
|
38 |
|
39 const TInt KMaxCreateCodeSegRecordSize = 300; //Max size of the encoded CodeSegCreate record. |
|
40 const TInt KMaxErrorReportRecordSize = 18; //Max size of the encoded ErrorReport record. (3 zeros and 3 integers) |
|
41 const TInt KRequiredFreeSpace=512; |
|
42 |
|
43 //Bit mask in report |
|
44 const TInt KNonXIPModeActive=1; |
|
45 const TInt KNoDebugSupport=2; |
|
46 |
|
47 |
|
48 #define PUT(p,x,e,s) {*(p)++=(x); if ((p)==(e)) (p)-=(s);} |
|
49 #define GET_A_BYTE(p,x,e,s) {*(x)++=*(p)++; if ((p)==(e)) (p)-=(s);} |
|
50 |
|
51 #define TAG(obj) (*(TUint32*)&(obj->iAsyncDeleteNext)) |
|
52 |
|
53 #define CODESEGBUFEND (iCodeSegBuffer+KCodeBufSize) |
|
54 #define COOKEDBUFEND (iCookedBuf+KCookedBufSize) |
|
55 |
|
56 extern TUint IntStackPtr(); |
|
57 extern TUint32 SPSR(); |
|
58 extern TUint IDFCRunning(); |
|
59 |
|
60 // global Dfc Que |
|
61 TDynamicDfcQue* gDfcQ; |
|
62 |
|
63 class DDeviceSampler : public DLogicalDevice |
|
64 { |
|
65 public: |
|
66 DDeviceSampler(); |
|
67 ~DDeviceSampler(); |
|
68 virtual TInt Install(); |
|
69 virtual void GetCaps(TDes8& aDes) const; |
|
70 virtual TInt Create(DLogicalChannelBase*& aChannel); |
|
71 }; |
|
72 |
|
73 struct SRawSample |
|
74 { |
|
75 TLinAddr iPC; |
|
76 TUint32 iSampleCounter; |
|
77 TUint32 iThreadId; |
|
78 }; |
|
79 |
|
80 class DProfile : public DLogicalChannel |
|
81 { |
|
82 public: |
|
83 DProfile(); |
|
84 ~DProfile(); |
|
85 protected: |
|
86 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); |
|
87 virtual void HandleMsg(TMessageBase* aMsg); |
|
88 private: |
|
89 TInt GetSegments(TDes8* aDes); |
|
90 TInt StartSampling(TInt aRate); |
|
91 TInt StopSampling(); |
|
92 TInt Reset(TBool aXIPOnly); |
|
93 TInt ResetSegments(); |
|
94 TInt Drain(TDes8* aDes); |
|
95 TInt GetErrors(TDes8* aDes); |
|
96 TInt ProcessReadRequest(); |
|
97 TInt DoDrainCooked(); |
|
98 TInt Cook(); |
|
99 void Complete(TInt aResult); |
|
100 inline TBool Running() |
|
101 {return iTimer.iState!=NTimer::EIdle;} |
|
102 private: |
|
103 static void Sample(TAny*); |
|
104 static void Dfc(TAny*); |
|
105 static TUint KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData); |
|
106 void LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg); |
|
107 |
|
108 private: |
|
109 static TUint8* EncodeTag(TUint8* p, TUint8* e); |
|
110 static TUint8* EncodeInt(TUint8* p, TUint8* e, TInt aValue); |
|
111 static TUint8* EncodeUint(TUint8* p, TUint8* e, TUint aValue); |
|
112 static TUint8* EncodeText(TUint8* p, TUint8* e, const TDesC& aDes); |
|
113 static TUint8* EncodeRepeat(TUint8* p, TUint8* e, DProfile* aProfile); |
|
114 TUint8* EncodeThread(TUint8* p, TUint8* e, DThread* aThread); |
|
115 TUint8* EncodeIDFC(TUint8* p, TUint8* e); |
|
116 |
|
117 TUint8* PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize); |
|
118 TUint8* GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize); |
|
119 TBool CookCodeSeg(TBool aPutAll, TInt aSampleCounter); |
|
120 |
|
121 private: |
|
122 TUint32 iStartTime; |
|
123 TInt iRepeat; |
|
124 SRawSample iLast; |
|
125 TInt iPeriod; |
|
126 NTimer iTimer; |
|
127 TDfc iDfc; |
|
128 TUint* iIntStackTop; |
|
129 DThread* iClient; |
|
130 TRequestStatus* iReqStatus; |
|
131 TInt iPos; // client des pos |
|
132 TInt iRemain; // space left in client des |
|
133 TDes8* iDes; // client des pointer |
|
134 TUint8 iRPut; // raw buffer put index |
|
135 TUint8 iRGet; // raw buffer get index |
|
136 TUint8* iCPut; // cooked buffer put |
|
137 TUint8* iCGet; // cooked buffer get |
|
138 SRawSample iRawBuf[KRawBufSize]; |
|
139 TUint8 iCookedBuf[KCookedBufSize]; |
|
140 |
|
141 DKernelEventHandler* iKernelEvHandler; |
|
142 |
|
143 TInt iNextSampleCounter; |
|
144 TBool iXIPOnly; |
|
145 TBool iMarkedOnlySegments; // True during GettingSegments phase in which event handler... |
|
146 // ... collects only the events from marked segments. |
|
147 TUint8 iCodeSegBuffer[KCodeBufSize]; |
|
148 TUint8* iCSPut; // CodeSeg buffer put |
|
149 TUint8* iCSGet; // CodeSeg buffer get |
|
150 TUint iIDFCSeenBefore; |
|
151 struct TReport |
|
152 { |
|
153 TUint iRowBufferErrCounter; |
|
154 TUint iCodeSegErrCounter; |
|
155 TInt iReportMask; |
|
156 } iReport; |
|
157 }; |
|
158 |
|
159 DECLARE_STANDARD_LDD() |
|
160 { |
|
161 return new DDeviceSampler; |
|
162 } |
|
163 |
|
164 DDeviceSampler::DDeviceSampler() |
|
165 // |
|
166 // Constructor |
|
167 // |
|
168 { |
|
169 //iParseMask=0; |
|
170 //iUnitsMask=0; |
|
171 iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); |
|
172 } |
|
173 |
|
174 const TInt KDSamplerThreadPriority = 27; |
|
175 _LIT(KDSamplerThread,"DSamplerThread"); |
|
176 |
|
177 TInt DDeviceSampler::Install() |
|
178 // |
|
179 // Install the device driver. |
|
180 // |
|
181 { |
|
182 // Allocate a kernel thread to run the DFC |
|
183 TInt r = Kern::DynamicDfcQCreate(gDfcQ, KDSamplerThreadPriority, KDSamplerThread); |
|
184 |
|
185 if (r != KErrNone) |
|
186 return r; |
|
187 |
|
188 r=SetName(&KLddName); |
|
189 return r; |
|
190 } |
|
191 |
|
192 void DDeviceSampler::GetCaps(TDes8& aDes) const |
|
193 // |
|
194 // Return the capabilities. |
|
195 // |
|
196 { |
|
197 } |
|
198 |
|
199 /** |
|
200 Destructor |
|
201 */ |
|
202 DDeviceSampler::~DDeviceSampler() |
|
203 { |
|
204 if (gDfcQ) |
|
205 gDfcQ->Destroy(); |
|
206 } |
|
207 |
|
208 TInt DDeviceSampler::Create(DLogicalChannelBase*& aChannel) |
|
209 // |
|
210 // Create a channel on the device. |
|
211 // |
|
212 { |
|
213 aChannel=new DProfile; |
|
214 return aChannel?KErrNone:KErrNoMemory; |
|
215 } |
|
216 |
|
217 DProfile::DProfile() |
|
218 : iTimer(Sample,this), |
|
219 iDfc(Dfc,this,NULL,7) |
|
220 // |
|
221 // Constructor |
|
222 // |
|
223 { |
|
224 } |
|
225 |
|
226 DProfile::~DProfile() |
|
227 // |
|
228 // Destructor |
|
229 // |
|
230 { |
|
231 Kern::SafeClose((DObject*&)iClient, NULL); |
|
232 } |
|
233 |
|
234 TInt DProfile::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer) |
|
235 // |
|
236 // Create the channel from the passed info. |
|
237 // |
|
238 { |
|
239 if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer)) |
|
240 return KErrNotSupported; |
|
241 iClient=&Kern::CurrentThread(); |
|
242 iClient->Open(); |
|
243 Kern::SetThreadPriority(24); |
|
244 iIntStackTop=(TUint*)IntStackPtr(); |
|
245 SetDfcQ(gDfcQ); |
|
246 iDfc.SetDfcQ(iDfcQ); |
|
247 iMsgQ.Receive(); |
|
248 return KErrNone; |
|
249 } |
|
250 |
|
251 void DProfile::Complete(TInt aResult) |
|
252 //Completes user request |
|
253 { |
|
254 DEBUG_PROFILER(Kern::Printf("C");) |
|
255 Kern::RequestComplete(iClient,iReqStatus,aResult); |
|
256 } |
|
257 |
|
258 TInt DProfile::StartSampling(TInt aRate) |
|
259 { |
|
260 DEBUG_PROFILER(Kern::Printf("START");) |
|
261 //Activate timer |
|
262 aRate=Min(KMaxRate, Max(KMinRate, aRate)); |
|
263 iPeriod=1000/aRate; |
|
264 if (!Running()) |
|
265 iTimer.OneShot(iPeriod); |
|
266 |
|
267 DEBUG_PROFILER(Kern::Printf("START end");) |
|
268 return KErrNone; |
|
269 } |
|
270 |
|
271 TInt DProfile::GetSegments(TDes8* aDes) |
|
272 // |
|
273 // Collects and marks all non-XIP segments. |
|
274 // |
|
275 { |
|
276 DEBUG_PROFILER(Kern::Printf("GS");) |
|
277 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes); |
|
278 Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//Set length to zero |
|
279 TInt current = 0; |
|
280 |
|
281 Kern::AccessCode(); |
|
282 |
|
283 // Take all records that are collected by event handler first. They may be only Delete CodeSeg |
|
284 // events of tagged(marked) segments. On the first GetSegments call, cooked buffer also contains Profile Tag. |
|
285 |
|
286 CookCodeSeg(ETrue, 0); // Transfer/encode from CodeSeg buffer into cooked buffer |
|
287 current = iCPut-iCGet; |
|
288 if (current) |
|
289 { |
|
290 if (current < max) |
|
291 {//Copy data into user side descriptor |
|
292 TPtrC8 aPtr(iCGet, current); |
|
293 Kern::ThreadDesWrite(iClient,aDes,aPtr,0,KChunkShiftBy0,iClient); |
|
294 } |
|
295 else |
|
296 { |
|
297 //This is very unlikely as in this stage we collect only CodeSeg Delete events of the marked segments. |
|
298 //It cannot happen on the first call, as there are no marked segments - which means that Profiler Tag is OK. |
|
299 iReport.iCodeSegErrCounter++; |
|
300 } |
|
301 } |
|
302 iCGet = iCPut = iCookedBuf; //Reset the cooked buffer |
|
303 |
|
304 //Collect all non-XIP segments that are not already marked. |
|
305 |
|
306 SDblQue* p = Kern::CodeSegList(); |
|
307 SDblQueLink* anchor=&p->iA; |
|
308 SDblQueLink* a=anchor->iNext; |
|
309 for (; a!=anchor; a=a->iNext) |
|
310 { |
|
311 DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink); |
|
312 if (pSeg->iXIP || pSeg->iMark&DCodeSeg::EMarkProfilerTAG) |
|
313 continue; |
|
314 if (current > (max-KMaxCreateCodeSegRecordSize)) |
|
315 break;//No more space. Finish now and wait for another GetSegments request. |
|
316 |
|
317 pSeg->iMark |= DCodeSeg::EMarkProfilerTAG; //Mark this segment |
|
318 LogCodeSegEvent(EEventAddCodeSeg, pSeg); //Place this record into CodeSeg buffer ... |
|
319 CookCodeSeg(ETrue, 0); //...and encode it into cooked buffer |
|
320 TPtrC8 aPtr(iCGet, iCPut-iCGet); |
|
321 Kern::ThreadDesWrite(iClient,aDes,aPtr,current,KChunkShiftBy0,iClient);//Copy record into user desc. |
|
322 current += iCPut-iCGet; |
|
323 iCPut = iCGet = iCookedBuf; //Reset cooked buffer |
|
324 } |
|
325 |
|
326 if (!current)//This will be the last GetSegments call. From now on, all events have to be recorded. |
|
327 iMarkedOnlySegments = EFalse; |
|
328 |
|
329 Kern::EndAccessCode(); |
|
330 DEBUG_PROFILER(Kern::Printf("GS end %d",current);) |
|
331 return KErrNone; |
|
332 } |
|
333 |
|
334 TInt DProfile::ResetSegments() |
|
335 // |
|
336 // Unmarks all non-XIP segments |
|
337 // Sets device into GettingSegments mode in which only the events of the marked Code Segments will be recorder |
|
338 // |
|
339 { |
|
340 DEBUG_PROFILER(Kern::Printf("RS");) |
|
341 if (iXIPOnly) |
|
342 return KErrGeneral; |
|
343 |
|
344 Kern::AccessCode(); |
|
345 SDblQue* p = Kern::CodeSegList(); |
|
346 SDblQueLink* anchor=&p->iA; |
|
347 SDblQueLink* a=anchor->iNext; |
|
348 for (; a!=anchor; a=a->iNext) |
|
349 { |
|
350 DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink); |
|
351 if (!pSeg->iXIP) |
|
352 pSeg->iMark &= ~DCodeSeg::EMarkProfilerTAG; |
|
353 } |
|
354 |
|
355 if (DKernelEventHandler::DebugSupportEnabled()) |
|
356 { |
|
357 DEBUG_PROFILER(Kern::Printf("RS add handler");) |
|
358 iKernelEvHandler->Add(); |
|
359 iReport.iReportMask|= KNonXIPModeActive; |
|
360 } |
|
361 else |
|
362 iReport.iReportMask|= KNoDebugSupport; |
|
363 |
|
364 iMarkedOnlySegments = ETrue; |
|
365 Kern::EndAccessCode(); |
|
366 DEBUG_PROFILER(Kern::Printf("RS end");) |
|
367 return KErrNone; |
|
368 } |
|
369 |
|
370 TInt DProfile::Reset(TBool aXIPOnly) |
|
371 { |
|
372 // |
|
373 // Resets the device. It is the first message sent by profiler application. |
|
374 // |
|
375 if (Running()) |
|
376 return KErrGeneral; |
|
377 |
|
378 DEBUG_PROFILER(Kern::Printf("RST %d", aXIPOnly);) |
|
379 |
|
380 iXIPOnly = aXIPOnly; |
|
381 |
|
382 iTimer.Cancel(); |
|
383 iDfc.Cancel(); |
|
384 iLast.iPC=0; |
|
385 iLast.iSampleCounter=0; |
|
386 iLast.iThreadId=0; |
|
387 iRepeat=0; |
|
388 iPeriod=1; |
|
389 iReqStatus=NULL; |
|
390 iRPut=0; // raw buffer put index |
|
391 iRGet=0; // raw buffer get index |
|
392 iCPut=EncodeTag(iCookedBuf,COOKEDBUFEND); //cooked buffer put |
|
393 iCGet=iCookedBuf; // cooked buffer get |
|
394 iPos=0; // client des pos |
|
395 iDes=NULL; // client des pointer |
|
396 iStartTime=NKern::TickCount(); |
|
397 |
|
398 iReport.iRowBufferErrCounter = 0; |
|
399 iReport.iCodeSegErrCounter = 0; |
|
400 iReport.iReportMask = 0; |
|
401 iNextSampleCounter = 0; |
|
402 iCSPut=iCodeSegBuffer; // CodeSeg buffer put |
|
403 iCSGet=iCodeSegBuffer; // CodeSeg buffer get |
|
404 iMarkedOnlySegments = EFalse; |
|
405 iIDFCSeenBefore = EFalse; |
|
406 if (!iXIPOnly) |
|
407 iKernelEvHandler = new DKernelEventHandler(KernelEventHandler, this); |
|
408 |
|
409 DEBUG_PROFILER(Kern::Printf("RST end");) |
|
410 return KErrNone; |
|
411 } |
|
412 |
|
413 TInt DProfile::StopSampling() |
|
414 // |
|
415 // Stops sampling |
|
416 // |
|
417 { |
|
418 DEBUG_PROFILER(Kern::Printf("STOP");) |
|
419 if (Running()) |
|
420 { |
|
421 iTimer.Cancel(); |
|
422 Dfc(this); |
|
423 } |
|
424 if (iReqStatus) |
|
425 Complete(KErrNone); |
|
426 |
|
427 DEBUG_PROFILER(Kern::Printf("STOP end");) |
|
428 return KErrNone; |
|
429 } |
|
430 |
|
431 TInt DProfile::GetErrors(TDes8* aDes) |
|
432 // |
|
433 // Returns error report and closes event handler |
|
434 // |
|
435 { |
|
436 TInt r = KErrNone; |
|
437 TBuf8<KMaxErrorReportRecordSize> localBuf; //Enough space to encode 3 zeros and 3 integers |
|
438 DEBUG_PROFILER(Kern::Printf("GE");) |
|
439 |
|
440 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes); |
|
441 if (max<KMaxErrorReportRecordSize) |
|
442 return KErrArgument; |
|
443 |
|
444 Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//set zero length |
|
445 |
|
446 TUint8* p = (TUint8*)localBuf.Ptr(); |
|
447 TUint8* e = p+KMaxErrorReportRecordSize; |
|
448 p = EncodeInt (p, e, 0); |
|
449 p = EncodeUint(p, e, 0); |
|
450 p = EncodeUint(p, e, 0); |
|
451 |
|
452 p = EncodeUint(p, e, iReport.iRowBufferErrCounter); |
|
453 p = EncodeUint(p, e, iReport.iCodeSegErrCounter); |
|
454 p = EncodeUint(p, e, iReport.iReportMask); |
|
455 |
|
456 localBuf.SetLength(p-localBuf.Ptr()); |
|
457 r=Kern::ThreadDesWrite(iClient,aDes,localBuf,0,KChunkShiftBy0,iClient); |
|
458 |
|
459 if(iKernelEvHandler && iKernelEvHandler->IsQueued()) |
|
460 iKernelEvHandler->Close(); |
|
461 |
|
462 DEBUG_PROFILER(Kern::Printf("GE end %d %d %d", iReport.iRowBufferErrCounter, iReport.iCodeSegErrCounter, iReport.iReportMask);) |
|
463 return r; |
|
464 } |
|
465 |
|
466 |
|
467 TInt DProfile::Drain(TDes8* aDes) |
|
468 // |
|
469 // Collects any remaining data |
|
470 // |
|
471 { |
|
472 DEBUG_PROFILER(Kern::Printf("D");) |
|
473 if (Running()) |
|
474 return KErrGeneral; |
|
475 // we can assume read request is not pending |
|
476 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes); |
|
477 if (max<0) |
|
478 return max; |
|
479 if (max==0) |
|
480 return KErrArgument; |
|
481 TInt r=Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient); // set client descriptor length to zero |
|
482 if (r!=KErrNone) |
|
483 return r; |
|
484 iDes=aDes; |
|
485 iRemain=max; |
|
486 iPos=0; |
|
487 iReqStatus=NULL; |
|
488 TInt n=-1; |
|
489 while (n) |
|
490 { |
|
491 r=DoDrainCooked(); // drain any cooked data if possible |
|
492 if (r<0 && r!=KErrUnderflow) |
|
493 return r; // error writing client buffer |
|
494 n=Cook(); // cook the samples, return number cooked |
|
495 } |
|
496 |
|
497 // there might still be data left over |
|
498 DEBUG_PROFILER(Kern::Printf("D end");) |
|
499 return KErrNone; |
|
500 } |
|
501 |
|
502 TInt DProfile::ProcessReadRequest() |
|
503 { |
|
504 // If the profiler is stopped and there is available data, return it immediately and complete the request |
|
505 // If the profiler is stopped and there is no data, wait. |
|
506 // If the profiler is running, retrieve any data available now, if more is req'd set the trigger |
|
507 DEBUG_PROFILER(Kern::Printf("READ");) |
|
508 TInt max=Kern::ThreadGetDesMaxLength(iClient,iDes); |
|
509 if (max<0) |
|
510 return max; |
|
511 if (max==0) |
|
512 return KErrArgument; |
|
513 TInt r=Kern::ThreadDesWrite(iClient,iDes,KNullDesC8,0,0,iClient); // set client descriptor length to zero |
|
514 if (r!=KErrNone) |
|
515 return r; |
|
516 iRemain=max; |
|
517 iPos=0; |
|
518 TInt n=-1; |
|
519 TBool read=EFalse; |
|
520 while (n) |
|
521 { |
|
522 r=DoDrainCooked(); // drain any cooked data if possible |
|
523 if (r!=KErrUnderflow) |
|
524 read=ETrue; // we've got something |
|
525 if (r>0) |
|
526 return KErrNone; // request completed, so finish |
|
527 if (r!=KErrNone && r!=KErrUnderflow) |
|
528 return r; // error writing client buffer |
|
529 n=Cook(); // cook the samples, return number cooked |
|
530 } |
|
531 if (!Running() && read) |
|
532 return KErrCompletion; // if stopped and data read, return it |
|
533 return KErrNone; // wait |
|
534 } |
|
535 |
|
536 TInt DProfile::DoDrainCooked() |
|
537 // |
|
538 // Copies encoded data from Cook buffer into user side descriptor (iDes). |
|
539 // Returns: |
|
540 // KErrUnderflow if all the data was already transfered or the desciptor was already full before the call. |
|
541 // KErrNone if there is still remaining space available in the descriptor. |
|
542 // 1 - descriptor is full and user request is completed. |
|
543 // Error code other then KErrNone if writing to the user memory fails |
|
544 // |
|
545 { |
|
546 TInt avail=iCPut-iCGet; |
|
547 if (avail<0) |
|
548 avail+=KCookedBufSize; |
|
549 TInt len=Min(avail,iRemain); |
|
550 if (len) |
|
551 { |
|
552 TUint8* pE=iCookedBuf+KCookedBufSize; |
|
553 TInt len1=Min(len,pE-iCGet); |
|
554 TPtrC8 local(iCGet,len1); |
|
555 TInt r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient); |
|
556 if (r!=KErrNone) |
|
557 return r; |
|
558 len-=len1; |
|
559 TUint8* pG=iCGet+len1; |
|
560 if (pG==pE) |
|
561 pG=iCookedBuf; |
|
562 iCGet=pG; |
|
563 iRemain-=len1; |
|
564 iPos+=len1; |
|
565 if (len) // will be > 0 if there are remaining data at the beginning of Cooked buffer to be copied. |
|
566 { |
|
567 TPtrC8 local(iCGet,len); |
|
568 r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient); |
|
569 if (r!=KErrNone) |
|
570 return r; |
|
571 iCGet+=len; |
|
572 iRemain-=len; |
|
573 iPos+=len; |
|
574 } |
|
575 if (iRemain==0 && iReqStatus) |
|
576 { |
|
577 Complete(KErrNone); |
|
578 return 1; |
|
579 } |
|
580 return KErrNone; |
|
581 } |
|
582 return KErrUnderflow; |
|
583 } |
|
584 |
|
585 void DProfile::HandleMsg(TMessageBase* aMsg) |
|
586 // |
|
587 // Client requests |
|
588 // |
|
589 { |
|
590 TInt r=KErrNone; |
|
591 TThreadMessage& m=*(TThreadMessage*)aMsg; |
|
592 TInt id=m.iValue; |
|
593 // Allow the client thread to send a message or system critical thread |
|
594 // to send a close message as this is probably the supervisor thread doing clean up |
|
595 if (m.Client()!=iClient && |
|
596 !((m.Client()->iFlags&KThreadFlagSystemCritical) && id==(TInt)ECloseMsg)) |
|
597 { |
|
598 m.PanicClient(_L("SAMPLER"),EAccessDenied); |
|
599 return; |
|
600 } |
|
601 if (id==(TInt)ECloseMsg) |
|
602 { |
|
603 DEBUG_PROFILER(Kern::Printf("CLOSE");) |
|
604 iTimer.Cancel(); |
|
605 iDfc.Cancel(); |
|
606 m.Complete(KErrNone,EFalse); |
|
607 iMsgQ.CompleteAll(KErrServerTerminated); |
|
608 DEBUG_PROFILER(Kern::Printf("CLOSE end");) |
|
609 return; |
|
610 } |
|
611 else if (id<0) |
|
612 { |
|
613 if (id!=~RSampler::ERequestRead) |
|
614 { |
|
615 TRequestStatus* pS=(TRequestStatus*)m.Ptr0(); |
|
616 Kern::RequestComplete(iClient,pS,KErrNotSupported); |
|
617 } |
|
618 if (iReqStatus) |
|
619 { |
|
620 m.PanicClient(_L("SAMPLER"),ERequestAlreadyPending); |
|
621 return; |
|
622 } |
|
623 iReqStatus=(TRequestStatus*)m.Ptr0(); |
|
624 iDes=(TDes8*)m.Ptr1(); |
|
625 r=ProcessReadRequest(); |
|
626 if (r!=KErrNone) |
|
627 { |
|
628 if (r==KErrCompletion) |
|
629 r=KErrNone; |
|
630 Complete(r); |
|
631 } |
|
632 r=KErrNone; |
|
633 } |
|
634 else if (id==KMaxTInt) |
|
635 { |
|
636 TInt mask=m.Int0(); |
|
637 if (mask & (1<<RSampler::ERequestRead)) |
|
638 { |
|
639 Complete(KErrCancel); |
|
640 } |
|
641 } |
|
642 else |
|
643 { |
|
644 switch(id) |
|
645 { |
|
646 case RSampler::EControlGetSegments: |
|
647 r=GetSegments((TDes8*)m.Ptr0()); |
|
648 break; |
|
649 case RSampler::EControlStartProfile: |
|
650 r=StartSampling(m.Int0()); |
|
651 break; |
|
652 case RSampler::EControlStopProfile: |
|
653 r=StopSampling(); |
|
654 break; |
|
655 case RSampler::EControlResetProfile: |
|
656 r=Reset((TBool)m.Ptr0()); |
|
657 break; |
|
658 case RSampler::EControlResetSegments: |
|
659 r=ResetSegments(); |
|
660 break; |
|
661 case RSampler::EControlDrain: |
|
662 r=Drain((TDes8*)m.Ptr0()); |
|
663 break; |
|
664 case RSampler::EControlGetErrors: |
|
665 r=GetErrors((TDes8*)m.Ptr0()); |
|
666 break; |
|
667 default: |
|
668 r=KErrNotSupported; |
|
669 break; |
|
670 } |
|
671 } |
|
672 m.Complete(r,ETrue); |
|
673 } |
|
674 |
|
675 TUint8* DProfile::EncodeTag(TUint8* p, TUint8* e) |
|
676 // |
|
677 // Encode a tag and version to the trace data. This allows the offline analyser to |
|
678 // identify the sample data. |
|
679 // |
|
680 { |
|
681 _LIT(KTraceTag,"profile"); |
|
682 p=EncodeText(p,e,KTraceTag); |
|
683 p=EncodeUint(p,e,KMajorVersionNumber); |
|
684 return p; |
|
685 } |
|
686 |
|
687 TUint8* DProfile::EncodeInt(TUint8* p, TUint8* e, TInt aValue) |
|
688 // |
|
689 // Encode a 32 bit signed integer into the data stream |
|
690 // This has to deal with wrap around at the end of the buffer |
|
691 // |
|
692 { |
|
693 TUint byte; |
|
694 for (;;) |
|
695 { |
|
696 byte = aValue & 0x7f; |
|
697 if ((aValue >> 6) == (aValue >> 7)) |
|
698 break; |
|
699 aValue >>= 7; |
|
700 PUT(p,(TUint8)byte,e,KCookedBufSize); |
|
701 } |
|
702 PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize); |
|
703 return p; |
|
704 } |
|
705 |
|
706 TUint8* DProfile::EncodeUint(TUint8* p, TUint8* e, TUint aValue) |
|
707 // |
|
708 // Encode a 32 bit unsigned integer into the data stream |
|
709 // This has to deal with wrap around at the end of the buffer |
|
710 // |
|
711 { |
|
712 TUint byte; |
|
713 for (;;) |
|
714 { |
|
715 byte = aValue & 0x7f; |
|
716 aValue >>= 7; |
|
717 if (aValue == 0) |
|
718 break; |
|
719 PUT(p,(TUint8)byte,e,KCookedBufSize); |
|
720 } |
|
721 PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize); |
|
722 return p; |
|
723 } |
|
724 |
|
725 TUint8* DProfile::EncodeText(TUint8* p, TUint8* e, const TDesC& aDes) |
|
726 // |
|
727 // Encode a descriptor into the data stream |
|
728 // This is currently limited to a descriptor that is up to 255 characters in length, |
|
729 // and Unicode characters are truncated to 8 bits |
|
730 // |
|
731 { |
|
732 TInt len=aDes.Length(); |
|
733 PUT(p,(TUint8)len,e,KCookedBufSize); |
|
734 const TText* s = aDes.Ptr(); |
|
735 while (--len >= 0) |
|
736 PUT(p,*s++,e,KCookedBufSize); |
|
737 return p; |
|
738 } |
|
739 |
|
740 TUint8* DProfile::EncodeIDFC(TUint8* p, TUint8* e) |
|
741 // |
|
742 // iDFC samples do not really belong to any thread. |
|
743 // However, the profiler protocol requires each sample to be associated to a particular thread. |
|
744 // This method will encode 'fake' process ID & name and thread name for iDFC sample in the data stream. |
|
745 // It will be embedded only for the very first sample from iDFCs. |
|
746 // (For the rest of iDFCs samples, threadID is sufficient - as for the real threads.) |
|
747 // |
|
748 { |
|
749 p=EncodeUint(p,e,KiDFCId); //processID for iDFC |
|
750 p=EncodeText(p,e,KiDFCProcess);//process name for iDFC |
|
751 p=EncodeText(p,e,KiDFCThread); //thread name for iDFC |
|
752 return p; |
|
753 } |
|
754 |
|
755 TUint8* DProfile::EncodeThread(TUint8* p, TUint8* e, DThread* aThread) |
|
756 // |
|
757 // Encode a thread name in the data stream. |
|
758 // The thread is identified by its name, and the identity of its owning process. |
|
759 // If the process has not been identified in the data stream already, it's name is |
|
760 // also encoded. |
|
761 // |
|
762 { |
|
763 DProcess* pP=aThread->iOwningProcess; |
|
764 TKName n; |
|
765 p=EncodeUint(p,e,pP->iId); |
|
766 if (TAG(pP)!=iStartTime) // not seen this before |
|
767 { |
|
768 TAG(pP)=iStartTime; |
|
769 // Provide the name matching this process ID |
|
770 pP->Name(n); |
|
771 p=EncodeText(p,e,n); |
|
772 } |
|
773 aThread->Name(n); |
|
774 p=EncodeText(p,e,n); |
|
775 return p; |
|
776 } |
|
777 |
|
778 TUint8* DProfile::EncodeRepeat(TUint8* p, TUint8* e, DProfile* aP) |
|
779 // |
|
780 // Encode a repeated sequence of samples |
|
781 // |
|
782 { |
|
783 p=EncodeInt(p,e,0); |
|
784 p=EncodeUint(p,e,aP->iRepeat); |
|
785 aP->iRepeat = 0; |
|
786 return p; |
|
787 } |
|
788 |
|
789 TInt DProfile::CookCodeSeg(TBool aPutAll, TInt aSampleCounter) |
|
790 // |
|
791 // Transfers and encodes CodeSeg Create/Delete records from CodeSeg buffer into Cooked buffer. |
|
792 // If aPutAll = Etrue, all records will be transferred. |
|
793 // If aPutAll = EFalse, only records at the beginning of the queue that matches aSampleCounter will be transferred. |
|
794 // It stopps if there is less then KRequiredFreeSpace bytes available in cooked buffer. |
|
795 // Returns the number of the transferred records. |
|
796 // |
|
797 { |
|
798 if (iXIPOnly) |
|
799 return 0; |
|
800 |
|
801 TInt n = 0; |
|
802 TInt codeSampleCounter;//Will hold the sample counter of the record |
|
803 TInt runAddress; |
|
804 TInt codeSize; |
|
805 TInt8 textLen; |
|
806 TBuf<255> text; |
|
807 TUint8* localCSGet = iCSGet; |
|
808 |
|
809 FOREVER |
|
810 { |
|
811 //Check if there is any Code Seg record left. |
|
812 if (iCSGet==iCSPut) |
|
813 return n;//No records left |
|
814 |
|
815 //Check if the next record is due to be encoded. Both Create & Delete CodeSeg records start with sample counter. |
|
816 localCSGet = iCSGet; |
|
817 localCSGet = GetStream(localCSGet, CODESEGBUFEND, (TInt8*)(&codeSampleCounter), sizeof(TInt)); |
|
818 if (!aPutAll && codeSampleCounter!=aSampleCounter) |
|
819 return n; //Still too early to insert the record into Cook Buffer |
|
820 |
|
821 //Check for the space in cook buffer |
|
822 TInt cookAvailable = (TInt)iCGet - (TInt)iCPut; |
|
823 if (cookAvailable <= 0) |
|
824 cookAvailable+=KCookedBufSize; |
|
825 if (cookAvailable < KRequiredFreeSpace) |
|
826 return n;//No space in Cook Buffer. |
|
827 |
|
828 //At this point it is for sure that we have to transfer some record to cook buffer |
|
829 |
|
830 n++; |
|
831 iCSGet = localCSGet; |
|
832 //The next field for both Create & Delete CodeSeg records is run address: |
|
833 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&runAddress), sizeof(TInt)); |
|
834 |
|
835 if (runAddress & 1)//LSB in run address idenifies the type of the record |
|
836 {//CodeSegment Delete record. To be encoded as Int(0), UInt(0), UInt(RunAddress | 1) |
|
837 iCPut = EncodeInt (iCPut, COOKEDBUFEND, 0); |
|
838 iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0); |
|
839 iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress); |
|
840 } |
|
841 else |
|
842 {//CodeSegment Create record. |
|
843 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&codeSize), sizeof(TInt)); |
|
844 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&textLen), sizeof(TInt8)); |
|
845 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(text.Ptr()), textLen); |
|
846 text.SetLength(textLen); |
|
847 //To be encoded as Int(0), UInt(0), UInt(RunAddress), UInt(SegmentSize), Text(FileNeme) |
|
848 iCPut = EncodeInt(iCPut, COOKEDBUFEND, 0); |
|
849 iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0); |
|
850 iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress); |
|
851 iCPut = EncodeUint(iCPut, COOKEDBUFEND, codeSize); |
|
852 iCPut = EncodeText(iCPut, COOKEDBUFEND, text); |
|
853 } |
|
854 } |
|
855 } |
|
856 |
|
857 TInt DProfile::Cook() |
|
858 // |
|
859 // Transfers/encodes row data and code segments record into cooked buffer. |
|
860 // Returns the number of records (incl. both samples and codeSeg records) cooked. |
|
861 // |
|
862 { |
|
863 TUint8* p=iCPut; |
|
864 TUint8* e=iCookedBuf+KCookedBufSize; |
|
865 TInt n=0; |
|
866 |
|
867 FOREVER |
|
868 { |
|
869 iCPut=p; //update iCPut before calling CookCodeSeg |
|
870 if ((iRGet==iRPut)) |
|
871 {//No more samples. |
|
872 n+=CookCodeSeg(ETrue, 0); //Cook the remaining content of CodeSeg buffer. |
|
873 break; |
|
874 } |
|
875 |
|
876 SRawSample* s=iRawBuf+iRGet; // pointer to the next sample to be cooked |
|
877 |
|
878 n+=CookCodeSeg(EFalse, s->iSampleCounter);//cook all codeSeg records than matches this sample counter |
|
879 p=iCPut; //CookCodeSeg might have changed iCPut |
|
880 |
|
881 TInt space=iCGet-p; |
|
882 if (space<=0) |
|
883 space+=KCookedBufSize; // space remaining in cooked buffer |
|
884 if (space<KRequiredFreeSpace) |
|
885 break; // if insufficient, finish |
|
886 |
|
887 //Cook the next sample record from Row buffer |
|
888 ++iRGet; |
|
889 ++n; |
|
890 TBool newthread=s->iPC & 1; // bit 0 of PC means so far unknown thread |
|
891 TLinAddr pc=s->iPC &~ 1; |
|
892 TUint rp = iRepeat; |
|
893 TInt diff=TInt(pc-iLast.iPC); |
|
894 if (!newthread) |
|
895 { |
|
896 if (s->iThreadId!=iLast.iThreadId) |
|
897 diff|=1; |
|
898 if (diff == 0) |
|
899 { |
|
900 iRepeat = rp + 1; // Identical sample, bump up the repeat count |
|
901 continue; |
|
902 } |
|
903 if (rp) |
|
904 { |
|
905 // Encode the repeat data |
|
906 p = EncodeRepeat(p,e,this); |
|
907 } |
|
908 // Encode the PC difference |
|
909 p = EncodeInt(p, e, diff); |
|
910 if (diff & 1) |
|
911 { |
|
912 // Encode the new thread ID |
|
913 iLast.iThreadId = s->iThreadId; |
|
914 p = EncodeUint(p, e, s->iThreadId); |
|
915 } |
|
916 } |
|
917 else |
|
918 { |
|
919 if (rp) |
|
920 { |
|
921 // Encode the repeat data |
|
922 p = EncodeRepeat(p,e,this); |
|
923 } |
|
924 // Encode the PC difference |
|
925 p = EncodeInt(p, e, diff|1); |
|
926 |
|
927 if (s->iThreadId == KiDFCId) |
|
928 { |
|
929 // This is the first sample from iDFC. Encode 'threadID' |
|
930 iLast.iThreadId = KiDFCId; |
|
931 p = EncodeUint(p, e, KiDFCId); |
|
932 // and encode processID, processName & threadName for this sample |
|
933 p = EncodeIDFC(p, e); |
|
934 } |
|
935 else |
|
936 { |
|
937 // Encode the new thread ID |
|
938 DThread* pT=(DThread*)s->iThreadId; |
|
939 iLast.iThreadId = pT->iId; |
|
940 p = EncodeUint(p, e, pT->iId); |
|
941 // The thread is 'unknown' to this sample, so encode the thread name |
|
942 p = EncodeThread(p, e, pT); |
|
943 } |
|
944 } |
|
945 iLast.iPC=pc; |
|
946 } |
|
947 return n; |
|
948 } |
|
949 |
|
950 void DProfile::Dfc(TAny* aPtr) |
|
951 // |
|
952 // Tranfers/encodes Row & CodeSeg buffers' content into Cook buffer (by Cook()), |
|
953 // and copies encoded data into user side descriptor (by DoDrainCooked()) |
|
954 // |
|
955 { |
|
956 DProfile& d=*(DProfile*)aPtr; |
|
957 TInt n=-1; |
|
958 while (n) |
|
959 { |
|
960 TInt r=d.DoDrainCooked(); // drain any cooked data if possible |
|
961 if (r<0 && r!=KErrUnderflow) |
|
962 { |
|
963 d.Complete(r); // error writing client buffer |
|
964 break; |
|
965 } |
|
966 n=d.Cook(); // cook the samples, return number cooked |
|
967 } |
|
968 } |
|
969 |
|
970 TUint8* DProfile::PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize) |
|
971 // |
|
972 // Put data into CodeSeg stream |
|
973 // This has to deal with wrap around at the end of the buffer |
|
974 // |
|
975 { |
|
976 for (TInt i = 0 ; i< aSize ; i++) |
|
977 { |
|
978 PUT(p,(TUint8)(*aSource),e,KCodeBufSize); |
|
979 aSource++; |
|
980 } |
|
981 return p; |
|
982 } |
|
983 |
|
984 TUint8* DProfile::GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize) |
|
985 // |
|
986 // Get 32 bits from CodeSeg stream. |
|
987 // This has to deal with wrap around at the end of the buffer |
|
988 // |
|
989 { |
|
990 for (TInt i = 0 ; i< aSize ; i++) |
|
991 GET_A_BYTE(p,aDest,e,KCodeBufSize); |
|
992 return p; |
|
993 } |
|
994 |
|
995 void DProfile::LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg) |
|
996 // |
|
997 // Records the event in CodeSeg buffer. |
|
998 // |
|
999 { |
|
1000 /// |
|
1001 /// |
|
1002 TUint8* localCSPut = iCSPut; |
|
1003 TInt available = KCodeBufSize + (TInt)iCSGet - (TInt)iCSPut; |
|
1004 if (available > KCodeBufSize) |
|
1005 available -= KCodeBufSize; |
|
1006 |
|
1007 switch (aEvent) |
|
1008 { |
|
1009 case EEventAddCodeSeg: |
|
1010 { |
|
1011 TInt textOffset = 0; |
|
1012 TInt textSize= pCodeSeg->iFileName->Length(); |
|
1013 //Restrict file name to max 255 sharacters. If bigger, record the last 255 bytes only |
|
1014 if (textSize > 255) |
|
1015 { |
|
1016 textOffset = textSize-255; |
|
1017 textSize = 255; |
|
1018 } |
|
1019 if ((available -= 13+textSize) < 0) //13 bytes needed for sample counter, run address, size and text size) |
|
1020 { |
|
1021 iReport.iCodeSegErrCounter++; |
|
1022 return; |
|
1023 } |
|
1024 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt)); |
|
1025 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iRunAddress), sizeof(TInt)); |
|
1026 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iSize), sizeof(TInt)); |
|
1027 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&textSize), sizeof(TInt8)); |
|
1028 localCSPut = PutStream( localCSPut, CODESEGBUFEND, pCodeSeg->iFileName->Ptr()+textOffset, textSize); |
|
1029 iCSPut = localCSPut; |
|
1030 |
|
1031 DEBUG_PROFILER |
|
1032 ( |
|
1033 TBuf<256> buf(textSize+1); |
|
1034 buf.Copy(pCodeSeg->iFileName->Ptr()+textOffset,textSize); buf.SetLength(textSize+1); buf[textSize]=0; |
|
1035 Kern::Printf("CREATE CS:%s, %x, %x,", buf.Ptr(), pCodeSeg->iRunAddress, pCodeSeg->iSize); |
|
1036 ) |
|
1037 break; |
|
1038 } |
|
1039 case EEventRemoveCodeSeg: |
|
1040 { |
|
1041 if ((available-=8) < 0) //8 bytes needed for sample counter and run address |
|
1042 { |
|
1043 iReport.iCodeSegErrCounter++; |
|
1044 return; |
|
1045 } |
|
1046 TInt runAddress = pCodeSeg->iRunAddress | 1; |
|
1047 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt)); |
|
1048 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&runAddress), sizeof(TInt)); |
|
1049 iCSPut = localCSPut; |
|
1050 |
|
1051 DEBUG_PROFILER(Kern::Printf("DELETE CS:%x", pCodeSeg->iRunAddress);) |
|
1052 break; |
|
1053 } |
|
1054 default: |
|
1055 return; |
|
1056 } |
|
1057 if (available < KCodeBufSize/2) //Start emptying CodeSeg (and Raw Buffer, as well) Buffer if more then a half full. |
|
1058 iDfc.Enque(); |
|
1059 } |
|
1060 |
|
1061 TUint DProfile::KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData) |
|
1062 // |
|
1063 // Logs non-XIP CodeSeg Create/Delete events. |
|
1064 // In GettingSegments mode, it logs only deletion of the marked segments. |
|
1065 // Runs in the content of the Kernel scheduler |
|
1066 // |
|
1067 { |
|
1068 if (aEvent==EEventAddCodeSeg || aEvent==EEventRemoveCodeSeg) |
|
1069 { |
|
1070 DProfile *p = (DProfile*)aPrivateData; |
|
1071 DEpocCodeSeg* pCodeSeg = (DEpocCodeSeg*)(DCodeSeg*)a1; |
|
1072 |
|
1073 Kern::AccessCode(); |
|
1074 if ((!pCodeSeg->iXIP) && (!p->iMarkedOnlySegments || pCodeSeg->iMark&DCodeSeg::EMarkProfilerTAG)) |
|
1075 p->LogCodeSegEvent(aEvent, pCodeSeg); |
|
1076 Kern::EndAccessCode(); |
|
1077 |
|
1078 } |
|
1079 return DKernelEventHandler::ERunNext; |
|
1080 } |
|
1081 |
|
1082 |
|
1083 void DProfile::Sample(TAny* aPtr) |
|
1084 { |
|
1085 DProfile& d=*(DProfile*)aPtr; |
|
1086 d.iTimer.Again(d.iPeriod); |
|
1087 TUint8 next_put=(TUint8)(d.iRPut+1); |
|
1088 if (next_put!=d.iRGet) // space in raw buffer |
|
1089 { |
|
1090 DThread* pT=Kern::NThreadToDThread(NKern::CurrentThread()); |
|
1091 if (pT!=NULL) |
|
1092 { |
|
1093 SRawSample* p=d.iRawBuf+d.iRPut; |
|
1094 d.iRPut=next_put; |
|
1095 p->iPC=((d.iIntStackTop)[-1]) & ~1; //clear LSB bit (in case of Jazelle code) |
|
1096 p->iSampleCounter=d.iNextSampleCounter++; |
|
1097 |
|
1098 if (IDFCRunning()) |
|
1099 { |
|
1100 p->iThreadId=KiDFCId; //indicates iDFC running |
|
1101 if (!d.iIDFCSeenBefore) |
|
1102 { |
|
1103 d.iIDFCSeenBefore = ETrue; |
|
1104 p->iPC|=1; // set bit 0 of PC to indicate new thread |
|
1105 } |
|
1106 else |
|
1107 { |
|
1108 TUint8 used=(TUint8)(d.iRPut-d.iRGet); |
|
1109 if (used<=KRawBufSize/2) |
|
1110 return; |
|
1111 } |
|
1112 } |
|
1113 else |
|
1114 { |
|
1115 |
|
1116 if (TAG(pT)!=d.iStartTime) // not seen this before |
|
1117 { |
|
1118 TAG(pT)=d.iStartTime; |
|
1119 p->iThreadId=(TUint)pT; |
|
1120 p->iPC|=1; // set bit 0 of PC to indicate new thread |
|
1121 } |
|
1122 else |
|
1123 { |
|
1124 p->iThreadId=pT->iId; |
|
1125 TUint8 used=(TUint8)(d.iRPut-d.iRGet); |
|
1126 if (used<=KRawBufSize/2) |
|
1127 return; |
|
1128 } |
|
1129 } |
|
1130 d.iDfc.Add(); // queue DFC if new thread seen or buffer more than half full |
|
1131 } |
|
1132 } |
|
1133 else |
|
1134 d.iReport.iRowBufferErrCounter++; |
|
1135 } |
|
1136 |