|
1 // Copyright (c) 1998-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\profiler.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include <e32cons.h> |
|
19 #include <f32file.h> |
|
20 #include "profiler.h" |
|
21 #include "sampler.h" |
|
22 |
|
23 // The name of the output file use to save the sample data |
|
24 _LIT(KFileName,"?:\\PROFILER.DAT"); |
|
25 const TInt KFileNameLen=15; |
|
26 |
|
27 // The name of the DLL used as an alternative UI controller |
|
28 _LIT(KProfilerKeysDll,"ProfilerKeys"); |
|
29 |
|
30 // The size of the buffers used for reading sample data and writing to file |
|
31 const TInt KBufferSize = 0x800; |
|
32 |
|
33 // The sample rate used by the profiler |
|
34 const TInt KSampleRate = 1000; |
|
35 |
|
36 const TInt KCommandMask = 0x00ff; |
|
37 const TInt KCommandNone = 0x0010; |
|
38 const TInt KCommandNoUi = 0x0100; |
|
39 const TInt KCommandXIPOnly = 0x0200; |
|
40 |
|
41 // The controller class used to provide the Profiler functions. |
|
42 // This runs as a server in the engine thread |
|
43 class CPServer : public CServer2, public MProfilerController |
|
44 { |
|
45 public: |
|
46 static MProfilerController* NewL(TInt aPriority, MProfilerEngine& aEngine); |
|
47 private: |
|
48 CPServer(TInt aPriority, MProfilerEngine& aEngine); |
|
49 void Release(); |
|
50 CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const; |
|
51 }; |
|
52 |
|
53 // The session class used by the server controller |
|
54 class CPSession : public CSession2 |
|
55 { |
|
56 private: |
|
57 inline const CPServer& Server() const; |
|
58 void ServiceL(const RMessage2& aMessage); |
|
59 }; |
|
60 |
|
61 |
|
62 // The default UI controller class which uses a Console |
|
63 class CConsole : public CActive, private MProfilerController |
|
64 { |
|
65 public: |
|
66 static MProfilerController* NewL(TInt aPriority, MProfilerEngine& aEngine); |
|
67 private: |
|
68 CConsole(TInt aPriority, MProfilerEngine& aEngine); |
|
69 void ConstructL(); |
|
70 ~CConsole(); |
|
71 void Release(); |
|
72 // |
|
73 void Help(); |
|
74 void Queue(); |
|
75 // |
|
76 void RunL(); |
|
77 void DoCancel(); |
|
78 private: |
|
79 CConsoleBase* iConsole; |
|
80 }; |
|
81 |
|
82 |
|
83 // The buffers used for transferring data from the device driver to the file |
|
84 struct TBuffer |
|
85 { |
|
86 TBuffer* iNext; |
|
87 TBuf8<KBufferSize> iBuf; |
|
88 }; |
|
89 |
|
90 class CProfiler; |
|
91 |
|
92 // The active object responsible for reading data from the device |
|
93 class CReader : public CActive |
|
94 { |
|
95 public: |
|
96 CReader(TInt aPriority, CProfiler& aProfiler); |
|
97 ~CReader(); |
|
98 // |
|
99 void ConstructL(); |
|
100 void Queue(TBuffer* aBuf); |
|
101 private: |
|
102 void RunL(); |
|
103 void DoCancel(); |
|
104 private: |
|
105 CProfiler& iProfiler; |
|
106 TBuffer* iBuf; |
|
107 public: |
|
108 RSampler iSampler; |
|
109 }; |
|
110 |
|
111 // The active object responsible for writing data out (to file) |
|
112 class CWriter : public CActive |
|
113 { |
|
114 public: |
|
115 CWriter(TInt aPriority, CProfiler& aProfiler); |
|
116 ~CWriter(); |
|
117 void ConstructL(); |
|
118 TInt Open(const TDesC& aFile); |
|
119 void Close(); |
|
120 void Queue(TBuffer* aBuf); |
|
121 private: |
|
122 void RunL(); |
|
123 void DoCancel(); |
|
124 private: |
|
125 CProfiler& iProfiler; |
|
126 TBuffer* iBuf; |
|
127 RFile iFile; |
|
128 RFs iFs; |
|
129 }; |
|
130 |
|
131 |
|
132 // The profiler engine itself. |
|
133 class CProfiler : public CBase, private MProfilerEngine |
|
134 { |
|
135 enum {EControlPriority = 10, EReaderPriority = 0, EWriterPriority = -10}; |
|
136 |
|
137 /** Specifies the state of the engine*/ |
|
138 enum TState |
|
139 { |
|
140 /** |
|
141 Initial state. The file is closed. Driver is inactive |
|
142 */ |
|
143 EClosed, |
|
144 /** |
|
145 Engine enters this state on client's Start request (if -xiponly is not specified). |
|
146 Opens the file. |
|
147 Resets the driver and nonXIP code segments. |
|
148 Sends GetSegments calls to the driver until driver returns zero length reply. |
|
149 Leaves this state (goes into ERunning) when the last data (obtained by GetSegment) is |
|
150 written into the file. |
|
151 */ |
|
152 EGettingSegments, |
|
153 /** |
|
154 Sends async. read request to the driver. Once completed, it immediately sends another while |
|
155 writing the collected records into the file. |
|
156 */ |
|
157 ERunning, |
|
158 /** |
|
159 Get into this state from ERunning on the client's Stop, Close or Exit request. |
|
160 Sends Drain calls to the driver until driver returns zero length reply. |
|
161 Leaves this state when all records are written into the file. |
|
162 */ |
|
163 EDraining, |
|
164 /** |
|
165 No active calls to the driver. On the client's Start request, will go back into ERunning mode. |
|
166 */ |
|
167 EStopped, |
|
168 /** |
|
169 Get into this state on client's Close or Exit request. |
|
170 Sends a single GetErrorReport request to the driver. After data has been recorded into the file, |
|
171 it closes the file and goes into EClosed state or terminates application.. |
|
172 */ |
|
173 EGettingErrors |
|
174 }; |
|
175 public: |
|
176 static CProfiler* NewLC(TInt aCmd, TDesC* aDrive); |
|
177 // |
|
178 TInt Control(Profiler::TState aCommand); |
|
179 // |
|
180 void ReadComplete(TBuffer* aBuf); |
|
181 void WriteComplete(TBuffer* aBuf); |
|
182 private: |
|
183 CProfiler(); |
|
184 ~CProfiler(); |
|
185 void ConstructL(TInt aCmd, TDesC* aDrive); |
|
186 MProfilerController* CreateUiL(); |
|
187 // |
|
188 void Read(); |
|
189 void Write(); |
|
190 TBool GetSegments(); |
|
191 TBool Drain(); |
|
192 void GetErrors(); |
|
193 // |
|
194 Profiler::TState State() const; |
|
195 private: |
|
196 CReader* iReader; |
|
197 CWriter* iWriter; |
|
198 MProfilerController* iServer; |
|
199 RLibrary iUiCode; |
|
200 MProfilerController* iUi; |
|
201 TState iState; |
|
202 TBool iXIPOnly; |
|
203 Profiler::TState iLastCommand; |
|
204 // |
|
205 // The FIFO queue of data that has to be written |
|
206 TBuffer* iHead; |
|
207 TBuffer* iTail; |
|
208 // |
|
209 // The LIFO list of free buffers |
|
210 TBuffer* iFree; |
|
211 TDesC* iDrive; |
|
212 }; |
|
213 |
|
214 |
|
215 CProfiler* CProfiler::NewLC(TInt aCmd, TDesC* aDrive) |
|
216 { |
|
217 CProfiler* self = new(ELeave) CProfiler; |
|
218 CleanupStack::PushL(self); |
|
219 self->ConstructL(aCmd, aDrive); |
|
220 return self; |
|
221 } |
|
222 |
|
223 CProfiler::CProfiler() |
|
224 {} |
|
225 |
|
226 CProfiler::~CProfiler() |
|
227 { |
|
228 delete iReader; |
|
229 delete iWriter; |
|
230 if (iServer) |
|
231 iServer->Release(); |
|
232 if (iUi) |
|
233 iUi->Release(); |
|
234 iUiCode.Close(); |
|
235 |
|
236 // discard the buffers in the free list |
|
237 TBuffer* b=iFree; |
|
238 while (b) |
|
239 { |
|
240 TBuffer* n = b->iNext; |
|
241 delete b; |
|
242 b = n; |
|
243 } |
|
244 |
|
245 // discard any buffers in the holding queue |
|
246 b=iHead; |
|
247 while (b) |
|
248 { |
|
249 TBuffer* n = b->iNext; |
|
250 delete b; |
|
251 b = n; |
|
252 } |
|
253 } |
|
254 |
|
255 void CProfiler::ConstructL(TInt aCmd, TDesC* aDrive) |
|
256 // |
|
257 // Build the profiler engine |
|
258 // |
|
259 { |
|
260 // Set drive letter of where to store profiler data |
|
261 iDrive = aDrive; |
|
262 |
|
263 // Run the engine at maximum priority to try and ensure that the sampler device |
|
264 // does not get choked and start dropping samples |
|
265 RThread me; |
|
266 me.SetPriority(EPriorityRealTime); |
|
267 User::LeaveIfError(User::RenameThread(KProfilerName)); |
|
268 |
|
269 CActiveScheduler::Install(new(ELeave) CActiveScheduler); |
|
270 iReader = new(ELeave) CReader(EReaderPriority,*this); |
|
271 iReader->ConstructL(); |
|
272 iWriter = new(ELeave) CWriter(EWriterPriority,*this); |
|
273 iWriter->ConstructL(); |
|
274 iServer = CPServer::NewL(EControlPriority,*this); |
|
275 if (!(aCmd & KCommandNoUi)) |
|
276 iUi = CreateUiL(); |
|
277 |
|
278 // Start off with two buffers in the free list for sample data |
|
279 TBuffer* buf = new(ELeave) TBuffer; |
|
280 buf->iNext = 0; |
|
281 iFree = buf; |
|
282 buf = new(ELeave) TBuffer; |
|
283 buf->iNext = iFree; |
|
284 iFree = buf; |
|
285 |
|
286 // idenify the running mode |
|
287 iXIPOnly = aCmd & KCommandXIPOnly; |
|
288 |
|
289 // start profiling if requested |
|
290 if ((aCmd & KCommandMask) == Profiler::EStart) |
|
291 User::LeaveIfError(Control(Profiler::EStart)); |
|
292 |
|
293 } |
|
294 |
|
295 MProfilerController* CProfiler::CreateUiL() |
|
296 // |
|
297 // deal with the UI acquisition part of construction |
|
298 // If ProfilerKeys.Dll is available, use it; otherwise create a text console |
|
299 // |
|
300 { |
|
301 _LIT(KWindowServerName,"*WindowServer"); |
|
302 TFindServer find(KWindowServerName); |
|
303 TFullName n; |
|
304 if (find.Next(n) == KErrNotFound) |
|
305 { |
|
306 // No UI on this device [yet]. Run without one. |
|
307 return 0; |
|
308 } |
|
309 |
|
310 if (iUiCode.Load(KProfilerKeysDll,TUidType(KNullUid, KUidProfilerKeys)) == KErrNone) |
|
311 { |
|
312 TProfilerControllerFactoryL factoryL = TProfilerControllerFactoryL(iUiCode.Lookup(1)); |
|
313 MProfilerController* ui = NULL; |
|
314 TRAPD(error, ui = factoryL(EControlPriority, *this)); |
|
315 if (error == KErrNone) |
|
316 return ui; |
|
317 |
|
318 // Couldn't create alternative UI, so use the console |
|
319 iUiCode.Close(); |
|
320 } |
|
321 return CConsole::NewL(EControlPriority, *this); |
|
322 } |
|
323 |
|
324 TInt CProfiler::Control(Profiler::TState aCommand) |
|
325 // |
|
326 // Handle a command from one of the controllers. |
|
327 // This method specifies the flow of the engine state (iState attr). |
|
328 // The most of transtions is not performed immediately but after all |
|
329 // current data are recorded into the file - see WriteComplete method. |
|
330 // |
|
331 { |
|
332 |
|
333 DEBUG_PROFILER(RDebug::Printf("*CTRL %d",iState);) |
|
334 |
|
335 TInt r = KErrNone; |
|
336 Profiler::TState oldCommand = iLastCommand; |
|
337 |
|
338 //Record the command. In most cases, it is WriteComplete method |
|
339 //to perform state transition (based on this value) |
|
340 iLastCommand = aCommand; |
|
341 |
|
342 switch (aCommand) |
|
343 { |
|
344 case Profiler::EStart: |
|
345 switch (iState) |
|
346 { |
|
347 case EClosed: |
|
348 { |
|
349 // Set the path of the output file to include the drive letter |
|
350 // specified at the command line or the default |
|
351 TBuf<KFileNameLen> path; |
|
352 path.Copy(KFileName); |
|
353 path[0] = (*iDrive)[0]; |
|
354 r = iWriter->Open(path); |
|
355 } |
|
356 if (KErrNone != r) // Re-open the file |
|
357 return r; |
|
358 iReader->iSampler.Reset(iXIPOnly); // Reset the sampler |
|
359 if(iXIPOnly) |
|
360 { |
|
361 iState = ERunning; |
|
362 iReader->iSampler.Start(KSampleRate); // Start sampler |
|
363 if (!iReader->IsActive()) |
|
364 Read(); // Start reading |
|
365 } |
|
366 else |
|
367 { |
|
368 iState = EGettingSegments; |
|
369 iReader->iSampler.ResetSegments(); // Reset segments |
|
370 GetSegments(); // Start getting segments |
|
371 } |
|
372 break; |
|
373 case EStopped: |
|
374 iState = ERunning; |
|
375 iReader->iSampler.Start(KSampleRate); // Start sampler |
|
376 if (!iReader->IsActive()) |
|
377 Read(); //Start reading |
|
378 break; |
|
379 case ERunning: //Already started. No action required. |
|
380 case EGettingSegments: //Already started. No action required. |
|
381 case EDraining: //Will restart after draining is completed. |
|
382 case EGettingErrors: //Will restart after getting errors is completed; |
|
383 break; |
|
384 } |
|
385 break; //end of case Profiler::EStart |
|
386 |
|
387 case Profiler::EStop: |
|
388 switch (iState) |
|
389 { |
|
390 case EClosed: |
|
391 case EGettingErrors: |
|
392 iLastCommand = oldCommand; |
|
393 return KErrGeneral; //The command makes no sense in this state |
|
394 case ERunning: |
|
395 iReader->iSampler.Stop(); //Stop sampling. |
|
396 break; |
|
397 case EGettingSegments: //Will do GettingSegments->Running->Stopped transitions |
|
398 case EDraining: //Stopping already in progress |
|
399 case EStopped: //Already stopped. |
|
400 break; |
|
401 } |
|
402 break; //end of case Profiler::EStop |
|
403 |
|
404 case Profiler::EClose: |
|
405 switch (iState) |
|
406 { |
|
407 case EStopped: |
|
408 iState = EGettingErrors; |
|
409 GetErrors(); |
|
410 break; |
|
411 case ERunning: |
|
412 iReader->iSampler.Stop(); |
|
413 break; |
|
414 case EClosed: //Already closed. |
|
415 case EGettingErrors: //Closing in progress |
|
416 case EGettingSegments: |
|
417 case EDraining: |
|
418 break; |
|
419 } |
|
420 break; //end of case Profiler::EStop |
|
421 |
|
422 case Profiler::EUnload: |
|
423 switch (iState) |
|
424 { |
|
425 case EClosed: |
|
426 CActiveScheduler::Stop(); // Terminate application. |
|
427 break; |
|
428 case EStopped: |
|
429 iState = EGettingErrors; |
|
430 GetErrors(); |
|
431 break; |
|
432 case ERunning: |
|
433 iReader->iSampler.Stop(); |
|
434 break; |
|
435 case EDraining: |
|
436 case EGettingErrors: |
|
437 case EGettingSegments: |
|
438 break; |
|
439 } |
|
440 break;//end of case Profiler::Unload |
|
441 } |
|
442 |
|
443 DEBUG_PROFILER(RDebug::Printf("*CTRL end %d",iState);) |
|
444 return KErrNone; |
|
445 } |
|
446 |
|
447 Profiler::TState CProfiler::State() const |
|
448 // |
|
449 // Report the current state of the engine |
|
450 // |
|
451 { |
|
452 switch (iState) |
|
453 { |
|
454 case EGettingErrors: |
|
455 case EStopped: |
|
456 return Profiler::EStop; |
|
457 case EClosed: |
|
458 return Profiler::EClose; |
|
459 default: |
|
460 return Profiler::EStart; |
|
461 } |
|
462 } |
|
463 |
|
464 void CProfiler::Read() |
|
465 // |
|
466 // Pass a free buffer to the reader, allocating one if necessary |
|
467 // |
|
468 { |
|
469 TBuffer* buf = iFree; |
|
470 if (buf) |
|
471 iFree = buf->iNext; |
|
472 else |
|
473 { |
|
474 buf = new TBuffer; |
|
475 if(!buf) |
|
476 { |
|
477 RDebug::Print(_L("PROFILER: No more memory ... stopping")); |
|
478 CProfiler::Control(Profiler::EStop); |
|
479 return; |
|
480 } |
|
481 } |
|
482 iReader->Queue(buf); |
|
483 } |
|
484 |
|
485 TBool CProfiler::GetSegments() |
|
486 // |
|
487 // Gets the list of the current non-XIP segments from device. |
|
488 // Returns true if zero-length desc is returned, otherwise ... |
|
489 // ...passes the buffer to write engine and returns false. |
|
490 { |
|
491 TBuffer* buf = iFree; |
|
492 if (buf) |
|
493 iFree = buf->iNext; |
|
494 else |
|
495 { |
|
496 RDebug::Printf("PROFILER: No available buffer for GetSegments"); |
|
497 User::Invariant(); |
|
498 } |
|
499 |
|
500 iReader->iSampler.GetSegments(buf->iBuf); |
|
501 if (!buf->iBuf.Length()) |
|
502 { |
|
503 buf->iNext = iFree;//Return empty buffer to the free list |
|
504 iFree = buf; |
|
505 return ETrue; |
|
506 } |
|
507 |
|
508 iWriter->Queue(buf);//Pass the buffer to the write engine. |
|
509 return EFalse; |
|
510 } |
|
511 |
|
512 TBool CProfiler::Drain() |
|
513 // |
|
514 // Drains all remaining records from the device |
|
515 // Returns true if zero-length desc is returned, otherwise ... |
|
516 // ...passes the buffer to the write engine and returns false. |
|
517 { |
|
518 TBuffer* buf = iFree; |
|
519 if (buf) |
|
520 iFree = buf->iNext; |
|
521 else |
|
522 { |
|
523 RDebug::Printf("PROFILER: No available buffer for Drain"); |
|
524 User::Invariant(); |
|
525 } |
|
526 |
|
527 iReader->iSampler.Drain(buf->iBuf); |
|
528 |
|
529 if (!buf->iBuf.Length()) |
|
530 { |
|
531 buf->iNext = iFree;//Return empty buffer to the free list |
|
532 iFree = buf; |
|
533 return ETrue; |
|
534 } |
|
535 iWriter->Queue(buf); //Pass the buffer to the write engine. |
|
536 return EFalse; |
|
537 } |
|
538 |
|
539 |
|
540 void CProfiler::GetErrors() |
|
541 // |
|
542 // Gets error report from the device and pass the buffer to the write engine |
|
543 // |
|
544 { |
|
545 TBuffer* buf = iFree; |
|
546 if (buf) |
|
547 iFree = buf->iNext; |
|
548 else |
|
549 { |
|
550 RDebug::Printf("PROFILER: No available buffer for GetErrors"); |
|
551 User::Invariant(); |
|
552 } |
|
553 iReader->iSampler.GetErrors(buf->iBuf); |
|
554 iWriter->Queue(buf); |
|
555 } |
|
556 |
|
557 void CProfiler::Write() |
|
558 // |
|
559 // Pass a queued buffer to the writer |
|
560 // |
|
561 { |
|
562 TBuffer* buf = iHead; |
|
563 iHead = buf->iNext; |
|
564 if (iHead == 0) |
|
565 iTail = 0; |
|
566 iWriter->Queue(buf); |
|
567 } |
|
568 |
|
569 void CProfiler::ReadComplete(TBuffer* aBuf) |
|
570 // |
|
571 // Handle a completed read buffer |
|
572 // |
|
573 { |
|
574 DEBUG_PROFILER(RDebug::Printf("*RC %d",iState);) |
|
575 |
|
576 //Add the buffer to the queue |
|
577 aBuf->iNext = 0; |
|
578 if (iTail) |
|
579 iTail->iNext = aBuf; |
|
580 else |
|
581 iHead = aBuf; |
|
582 iTail = aBuf; |
|
583 |
|
584 if (!iWriter->IsActive()) |
|
585 Write(); |
|
586 |
|
587 if (iLastCommand == Profiler::EStart) |
|
588 Read(); //Request another read |
|
589 |
|
590 DEBUG_PROFILER(RDebug::Printf("*RC end %d",iState);) |
|
591 } |
|
592 |
|
593 void CProfiler::WriteComplete(TBuffer* aBuf) |
|
594 // |
|
595 // Handle a flushed write buffer. |
|
596 // |
|
597 { |
|
598 DEBUG_PROFILER(RDebug::Printf("*WC %d",iState);) |
|
599 |
|
600 aBuf->iNext = iFree;//Return empty buffer to the free list |
|
601 iFree = aBuf; |
|
602 |
|
603 switch (iState) |
|
604 { |
|
605 case EGettingSegments: |
|
606 if (!GetSegments()) |
|
607 break;//More code segments to be completed |
|
608 |
|
609 //Always go to the running state after the segments are collected.... |
|
610 iState = ERunning; |
|
611 iReader->iSampler.Start(KSampleRate); |
|
612 Read(); |
|
613 |
|
614 //...but stop sampler immediately if we got another user command |
|
615 if (iLastCommand != Profiler::EStart) |
|
616 { |
|
617 iReader->iSampler.Stop(); |
|
618 } |
|
619 break; //the end of EGettingSegments case |
|
620 |
|
621 case ERunning: |
|
622 if (iHead) |
|
623 { |
|
624 Write(); // There are more buffers to go to the file. |
|
625 break; |
|
626 } |
|
627 if (iLastCommand != Profiler::EStart) |
|
628 {//The user has stopped the profiler. |
|
629 iState = EDraining; |
|
630 if (!Drain()) |
|
631 break;//More data to drain. |
|
632 |
|
633 //Drain returned empty. May progress further with the engine state |
|
634 if (iLastCommand == Profiler::EStop) |
|
635 iState = EStopped; |
|
636 else |
|
637 { |
|
638 iState = EGettingErrors; |
|
639 GetErrors(); |
|
640 } |
|
641 } |
|
642 break;//the end of ERunning case |
|
643 |
|
644 case EDraining: |
|
645 if (!Drain()) |
|
646 break; //still draining; |
|
647 |
|
648 //Drain is completed |
|
649 switch (iLastCommand) |
|
650 { |
|
651 case Profiler::EStart: |
|
652 //While draining, we received another Start command |
|
653 iState = ERunning; |
|
654 iReader->iSampler.Start(KSampleRate); |
|
655 Read(); |
|
656 break; |
|
657 case Profiler::EStop: |
|
658 iState = EStopped; |
|
659 break; |
|
660 default: |
|
661 iState = EGettingErrors; |
|
662 GetErrors(); |
|
663 } |
|
664 break; //the end of EDraining case |
|
665 |
|
666 case EGettingErrors: |
|
667 iWriter->Close(); |
|
668 iState = EClosed; |
|
669 switch (iLastCommand) |
|
670 { |
|
671 case Profiler::EUnload: |
|
672 CActiveScheduler::Stop(); //Terminate application. |
|
673 break; |
|
674 case Profiler::EStart: |
|
675 Control(Profiler::EStart); |
|
676 break; |
|
677 default: |
|
678 break; |
|
679 } |
|
680 break; //the end of EGettingErrors case |
|
681 |
|
682 default: |
|
683 RDebug::Printf("PROFILER: WriteComplete in %d state", iState); |
|
684 User::Invariant(); |
|
685 break; |
|
686 |
|
687 } |
|
688 |
|
689 DEBUG_PROFILER(RDebug::Printf("*WC end %d",iState);) |
|
690 } |
|
691 |
|
692 |
|
693 |
|
694 CReader::CReader(TInt aPriority, CProfiler& aProfiler) |
|
695 :CActive(aPriority), iProfiler(aProfiler) |
|
696 { |
|
697 CActiveScheduler::Add(this); |
|
698 } |
|
699 |
|
700 CReader::~CReader() |
|
701 { |
|
702 Cancel(); |
|
703 delete iBuf; |
|
704 iSampler.Close(); |
|
705 User::FreeLogicalDevice(KSamplerName); |
|
706 } |
|
707 |
|
708 void CReader::ConstructL() |
|
709 { |
|
710 TInt r=User::LoadLogicalDevice(KSamplerName); |
|
711 if (r!=KErrNone && r!=KErrAlreadyExists) |
|
712 User::Leave(r); |
|
713 User::LeaveIfError(iSampler.Open()); |
|
714 } |
|
715 |
|
716 void CReader::RunL() |
|
717 // |
|
718 // Pass the full buffer to the engine |
|
719 // |
|
720 { |
|
721 TBuffer* data=iBuf; |
|
722 iBuf = 0; |
|
723 iProfiler.ReadComplete(data); |
|
724 } |
|
725 |
|
726 void CReader::DoCancel() |
|
727 { |
|
728 iSampler.ReadCancel(); |
|
729 } |
|
730 |
|
731 void CReader::Queue(TBuffer* aBuf) |
|
732 // |
|
733 // Queue a request to read data into the empty buffer |
|
734 // |
|
735 { |
|
736 iBuf = aBuf; |
|
737 iSampler.Read(aBuf->iBuf, iStatus); |
|
738 SetActive(); |
|
739 } |
|
740 |
|
741 CWriter::CWriter(TInt aPriority, CProfiler& aProfiler) |
|
742 :CActive(aPriority), iProfiler(aProfiler) |
|
743 { |
|
744 CActiveScheduler::Add(this); |
|
745 } |
|
746 |
|
747 CWriter::~CWriter() |
|
748 { |
|
749 Cancel(); |
|
750 delete iBuf; |
|
751 iFile.Close(); |
|
752 iFs.Close(); |
|
753 } |
|
754 |
|
755 void CWriter::ConstructL() |
|
756 { |
|
757 User::LeaveIfError(iFs.Connect()); |
|
758 } |
|
759 |
|
760 TInt CWriter::Open(const TDesC& aFile) |
|
761 // |
|
762 // Open the file for saving the sample data |
|
763 // |
|
764 { |
|
765 return iFile.Replace(iFs,aFile,EFileWrite); |
|
766 } |
|
767 |
|
768 void CWriter::Close() |
|
769 // |
|
770 // Release the file |
|
771 // |
|
772 { |
|
773 iFile.Close(); |
|
774 } |
|
775 |
|
776 void CWriter::Queue(TBuffer* aBuf) |
|
777 // |
|
778 // Queue a request to write the full buffer into the file |
|
779 // |
|
780 { |
|
781 iBuf = aBuf; |
|
782 iFile.Write(aBuf->iBuf, iStatus); |
|
783 SetActive(); |
|
784 } |
|
785 |
|
786 void CWriter::RunL() |
|
787 // |
|
788 // Return the empty buffer back to the engine |
|
789 // |
|
790 { |
|
791 TBuffer* data=iBuf; |
|
792 iBuf = 0; |
|
793 iProfiler.WriteComplete(data); |
|
794 } |
|
795 |
|
796 void CWriter::DoCancel() |
|
797 // |
|
798 // RFile does not provide a WriteCancel() function |
|
799 // |
|
800 {} |
|
801 |
|
802 |
|
803 // Server controller |
|
804 |
|
805 inline const CPServer& CPSession::Server() const |
|
806 {return *static_cast<const CPServer*>(CSession2::Server());} |
|
807 |
|
808 void CPSession::ServiceL(const RMessage2& aMessage) |
|
809 // |
|
810 // Handle a IPC request to control the profiler |
|
811 // |
|
812 { |
|
813 aMessage.Complete(Server().Control(Profiler::TState(aMessage.Function()))); |
|
814 } |
|
815 |
|
816 MProfilerController* CPServer::NewL(TInt aPriority, MProfilerEngine& aEngine) |
|
817 // |
|
818 // Create and start the server to provide the Profiler interface |
|
819 // |
|
820 { |
|
821 CPServer* self = new(ELeave) CPServer(aPriority, aEngine); |
|
822 CleanupStack::PushL(self); |
|
823 self->StartL(KProfilerName); |
|
824 CleanupStack::Pop(); |
|
825 return self; |
|
826 } |
|
827 |
|
828 CPServer::CPServer(TInt aPriority, MProfilerEngine& aEngine) |
|
829 :CServer2(aPriority), MProfilerController(aEngine) |
|
830 {} |
|
831 |
|
832 void CPServer::Release() |
|
833 { |
|
834 delete this; |
|
835 } |
|
836 |
|
837 CSession2* CPServer::NewSessionL(const TVersion&,const RMessage2&) const |
|
838 { |
|
839 return new(ELeave) CPSession(); |
|
840 } |
|
841 |
|
842 |
|
843 // Console Controller |
|
844 |
|
845 MProfilerController* CConsole::NewL(TInt aPriority, MProfilerEngine& aEngine) |
|
846 // |
|
847 // Create and start the console UI for the profiler |
|
848 // |
|
849 { |
|
850 CConsole* self = new(ELeave) CConsole(aPriority, aEngine); |
|
851 CleanupStack::PushL(self); |
|
852 self->ConstructL(); |
|
853 CleanupStack::Pop(); |
|
854 return self; |
|
855 } |
|
856 |
|
857 CConsole::CConsole(TInt aPriority, MProfilerEngine& aEngine) |
|
858 :CActive(aPriority), MProfilerController(aEngine) |
|
859 { |
|
860 CActiveScheduler::Add(this); |
|
861 } |
|
862 |
|
863 void CConsole::ConstructL() |
|
864 { |
|
865 iConsole = Console::NewL(KProfilerName, TSize(KConsFullScreen,KConsFullScreen)); |
|
866 Help(); |
|
867 Queue(); |
|
868 } |
|
869 |
|
870 CConsole::~CConsole() |
|
871 { |
|
872 Cancel(); |
|
873 delete iConsole; |
|
874 } |
|
875 |
|
876 void CConsole::Release() |
|
877 { |
|
878 delete this; |
|
879 } |
|
880 |
|
881 void CConsole::Help() |
|
882 // |
|
883 // Display the instructions on the console |
|
884 // |
|
885 { |
|
886 _LIT(KInstructions,"[S]tart, Sto[p], [C]lose or E[x]it\r\n"); |
|
887 iConsole->Write(KInstructions); |
|
888 } |
|
889 |
|
890 void CConsole::Queue() |
|
891 // |
|
892 // Request a key press from the console |
|
893 // |
|
894 { |
|
895 iConsole->Read(iStatus); |
|
896 SetActive(); |
|
897 } |
|
898 |
|
899 void CConsole::RunL() |
|
900 // |
|
901 // Handle a key press from the console |
|
902 // |
|
903 { |
|
904 TInt key = iConsole->KeyCode(); |
|
905 Queue(); |
|
906 Profiler::TState command; |
|
907 switch (key) |
|
908 { |
|
909 case 's': case 'S': |
|
910 command = Profiler::EStart; |
|
911 break; |
|
912 case 'p': case 'P': |
|
913 command = Profiler::EStop; |
|
914 break; |
|
915 case 'c': case 'C': |
|
916 command = Profiler::EClose; |
|
917 break; |
|
918 case 'x': case 'X': |
|
919 command = Profiler::EUnload; |
|
920 break; |
|
921 case '?': case 'h': case 'H': |
|
922 Help(); |
|
923 return; |
|
924 default: |
|
925 return; |
|
926 } |
|
927 Control(command); |
|
928 } |
|
929 |
|
930 void CConsole::DoCancel() |
|
931 { |
|
932 iConsole->ReadCancel(); |
|
933 } |
|
934 |
|
935 |
|
936 void MainL(TInt aCmd, TDesC* aDrive) |
|
937 // |
|
938 // Construct and run the profile engine |
|
939 // |
|
940 { |
|
941 CProfiler* p = CProfiler::NewLC(aCmd, aDrive); |
|
942 CActiveScheduler::Start(); |
|
943 CleanupStack::PopAndDestroy(p); |
|
944 } |
|
945 |
|
946 TInt GetCommand(TDes &aDrive) |
|
947 // |
|
948 // Decode the command line arguments into a profiler control request |
|
949 // aDrive is the drive number to store the profiler data on |
|
950 // |
|
951 { |
|
952 _LIT(KStart,"start"); |
|
953 _LIT(KStop,"stop"); |
|
954 _LIT(KClose,"close"); |
|
955 _LIT(KUnload,"unload"); |
|
956 _LIT(KExit,"exit"); |
|
957 _LIT(KNoUi,"-noui"); |
|
958 _LIT(KXIPOnly,"-xiponly"); |
|
959 _LIT(KDrive,"-drive="); |
|
960 const TInt KDriveOffset=7; |
|
961 TBuf<64> c; |
|
962 User::CommandLine(c); |
|
963 TInt cmd = 0; |
|
964 if (c.FindF(KNoUi) >= 0) |
|
965 cmd |= KCommandNoUi; |
|
966 if (c.FindF(KXIPOnly) >= 0) |
|
967 cmd |= KCommandXIPOnly; |
|
968 |
|
969 // get the drive letter if any |
|
970 TInt pos = c.FindF(KDrive); |
|
971 if(pos >= 0) |
|
972 { |
|
973 pos += KDriveOffset; |
|
974 TBuf<1> driveLet; |
|
975 driveLet.SetLength(1); |
|
976 driveLet[0] = c[pos]; |
|
977 driveLet.UpperCase(); |
|
978 if (driveLet[0] >= 'A' && driveLet[0] <= 'Z') |
|
979 { |
|
980 aDrive[0] = driveLet[0]; |
|
981 } |
|
982 } |
|
983 if (c.FindF(KStart) >= 0) |
|
984 return cmd | Profiler::EStart; |
|
985 if (c.FindF(KStop) >= 0) |
|
986 return cmd | Profiler::EStop; |
|
987 if (c.FindF(KClose) >= 0) |
|
988 return cmd | Profiler::EClose; |
|
989 if (c.FindF(KUnload) >= 0) |
|
990 return cmd | Profiler::EUnload; |
|
991 if (c.FindF(KExit) >= 0) |
|
992 return cmd | Profiler::EUnload; |
|
993 return cmd | KCommandNone; |
|
994 } |
|
995 |
|
996 TInt E32Main() |
|
997 // |
|
998 // Profiler.exe entry point |
|
999 // Decode any command-line argument - which can be used to control a running profile engine |
|
1000 // Otherwise start the engine in this process |
|
1001 // |
|
1002 { |
|
1003 TBuf<1> drive; |
|
1004 drive.SetLength(1); |
|
1005 drive[0] = 'C'; |
|
1006 TInt command = GetCommand(drive); |
|
1007 if ((command & KCommandMask) != KCommandNone) |
|
1008 { |
|
1009 TInt r = Profiler::Control(Profiler::TState(command & KCommandMask)); |
|
1010 if (r != KErrNotFound || (command & KCommandMask) != Profiler::EStart) |
|
1011 return r; |
|
1012 } |
|
1013 CTrapCleanup::New(); |
|
1014 TRAPD(r,MainL(command, &drive)); |
|
1015 if (r != KErrNone) |
|
1016 RDebug::Print(_L("PROFILER: Error starting profiler")); |
|
1017 return r; |
|
1018 } |