Update contrib.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32utils\profiler\profiler.cpp
23 // The name of the output file use to save the sample data
24 _LIT(KFileName,"?:\\PROFILER.DAT");
25 const TInt KFileNameLen=15;
27 // The name of the DLL used as an alternative UI controller
28 _LIT(KProfilerKeysDll,"ProfilerKeys");
30 // The size of the buffers used for reading sample data and writing to file
31 const TInt KBufferSize = 0x800;
33 // The sample rate used by the profiler
34 const TInt KSampleRate = 1000;
36 const TInt KCommandMask = 0x00ff;
37 const TInt KCommandNone = 0x0010;
38 const TInt KCommandNoUi = 0x0100;
39 const TInt KCommandXIPOnly = 0x0200;
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
46 static MProfilerController* NewL(TInt aPriority, MProfilerEngine& aEngine);
48 CPServer(TInt aPriority, MProfilerEngine& aEngine);
50 CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
53 // The session class used by the server controller
54 class CPSession : public CSession2
57 inline const CPServer& Server() const;
58 void ServiceL(const RMessage2& aMessage);
62 // The default UI controller class which uses a Console
63 class CConsole : public CActive, private MProfilerController
66 static MProfilerController* NewL(TInt aPriority, MProfilerEngine& aEngine);
68 CConsole(TInt aPriority, MProfilerEngine& aEngine);
79 CConsoleBase* iConsole;
83 // The buffers used for transferring data from the device driver to the file
87 TBuf8<KBufferSize> iBuf;
92 // The active object responsible for reading data from the device
93 class CReader : public CActive
96 CReader(TInt aPriority, CProfiler& aProfiler);
100 void Queue(TBuffer* aBuf);
105 CProfiler& iProfiler;
111 // The active object responsible for writing data out (to file)
112 class CWriter : public CActive
115 CWriter(TInt aPriority, CProfiler& aProfiler);
118 TInt Open(const TDesC& aFile);
120 void Queue(TBuffer* aBuf);
125 CProfiler& iProfiler;
132 // The profiler engine itself.
133 class CProfiler : public CBase, private MProfilerEngine
135 enum {EControlPriority = 10, EReaderPriority = 0, EWriterPriority = -10};
137 /** Specifies the state of the engine*/
141 Initial state. The file is closed. Driver is inactive
145 Engine enters this state on client's Start request (if -xiponly is not specified).
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.
154 Sends async. read request to the driver. Once completed, it immediately sends another while
155 writing the collected records into the file.
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.
165 No active calls to the driver. On the client's Start request, will go back into ERunning mode.
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..
176 static CProfiler* NewLC(TInt aCmd, TDesC* aDrive);
178 TInt Control(Profiler::TState aCommand);
180 void ReadComplete(TBuffer* aBuf);
181 void WriteComplete(TBuffer* aBuf);
185 void ConstructL(TInt aCmd, TDesC* aDrive);
186 MProfilerController* CreateUiL();
194 Profiler::TState State() const;
198 MProfilerController* iServer;
200 MProfilerController* iUi;
203 Profiler::TState iLastCommand;
205 // The FIFO queue of data that has to be written
209 // The LIFO list of free buffers
215 CProfiler* CProfiler::NewLC(TInt aCmd, TDesC* aDrive)
217 CProfiler* self = new(ELeave) CProfiler;
218 CleanupStack::PushL(self);
219 self->ConstructL(aCmd, aDrive);
223 CProfiler::CProfiler()
226 CProfiler::~CProfiler()
236 // discard the buffers in the free list
240 TBuffer* n = b->iNext;
245 // discard any buffers in the holding queue
249 TBuffer* n = b->iNext;
255 void CProfiler::ConstructL(TInt aCmd, TDesC* aDrive)
257 // Build the profiler engine
260 // Set drive letter of where to store profiler data
263 // Run the engine at maximum priority to try and ensure that the sampler device
264 // does not get choked and start dropping samples
266 me.SetPriority(EPriorityRealTime);
267 User::LeaveIfError(User::RenameThread(KProfilerName));
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))
278 // Start off with two buffers in the free list for sample data
279 TBuffer* buf = new(ELeave) TBuffer;
282 buf = new(ELeave) TBuffer;
286 // idenify the running mode
287 iXIPOnly = aCmd & KCommandXIPOnly;
289 // start profiling if requested
290 if ((aCmd & KCommandMask) == Profiler::EStart)
291 User::LeaveIfError(Control(Profiler::EStart));
295 MProfilerController* CProfiler::CreateUiL()
297 // deal with the UI acquisition part of construction
298 // If ProfilerKeys.Dll is available, use it; otherwise create a text console
301 _LIT(KWindowServerName,"*WindowServer");
302 TFindServer find(KWindowServerName);
304 if (find.Next(n) == KErrNotFound)
306 // No UI on this device [yet]. Run without one.
310 if (iUiCode.Load(KProfilerKeysDll,TUidType(KNullUid, KUidProfilerKeys)) == KErrNone)
312 TProfilerControllerFactoryL factoryL = TProfilerControllerFactoryL(iUiCode.Lookup(1));
313 MProfilerController* ui = NULL;
314 TRAPD(error, ui = factoryL(EControlPriority, *this));
315 if (error == KErrNone)
318 // Couldn't create alternative UI, so use the console
321 return CConsole::NewL(EControlPriority, *this);
324 TInt CProfiler::Control(Profiler::TState aCommand)
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.
333 DEBUG_PROFILER(RDebug::Printf("*CTRL %d",iState);)
336 Profiler::TState oldCommand = iLastCommand;
338 //Record the command. In most cases, it is WriteComplete method
339 //to perform state transition (based on this value)
340 iLastCommand = aCommand;
344 case Profiler::EStart:
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);
356 if (KErrNone != r) // Re-open the file
358 iReader->iSampler.Reset(iXIPOnly); // Reset the sampler
362 iReader->iSampler.Start(KSampleRate); // Start sampler
363 if (!iReader->IsActive())
364 Read(); // Start reading
368 iState = EGettingSegments;
369 iReader->iSampler.ResetSegments(); // Reset segments
370 GetSegments(); // Start getting segments
375 iReader->iSampler.Start(KSampleRate); // Start sampler
376 if (!iReader->IsActive())
377 Read(); //Start reading
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;
385 break; //end of case Profiler::EStart
387 case Profiler::EStop:
392 iLastCommand = oldCommand;
393 return KErrGeneral; //The command makes no sense in this state
395 iReader->iSampler.Stop(); //Stop sampling.
397 case EGettingSegments: //Will do GettingSegments->Running->Stopped transitions
398 case EDraining: //Stopping already in progress
399 case EStopped: //Already stopped.
402 break; //end of case Profiler::EStop
404 case Profiler::EClose:
408 iState = EGettingErrors;
412 iReader->iSampler.Stop();
414 case EClosed: //Already closed.
415 case EGettingErrors: //Closing in progress
416 case EGettingSegments:
420 break; //end of case Profiler::EStop
422 case Profiler::EUnload:
426 CActiveScheduler::Stop(); // Terminate application.
429 iState = EGettingErrors;
433 iReader->iSampler.Stop();
437 case EGettingSegments:
440 break;//end of case Profiler::Unload
443 DEBUG_PROFILER(RDebug::Printf("*CTRL end %d",iState);)
447 Profiler::TState CProfiler::State() const
449 // Report the current state of the engine
456 return Profiler::EStop;
458 return Profiler::EClose;
460 return Profiler::EStart;
464 void CProfiler::Read()
466 // Pass a free buffer to the reader, allocating one if necessary
469 TBuffer* buf = iFree;
477 RDebug::Print(_L("PROFILER: No more memory ... stopping"));
478 CProfiler::Control(Profiler::EStop);
485 TBool CProfiler::GetSegments()
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.
491 TBuffer* buf = iFree;
496 RDebug::Printf("PROFILER: No available buffer for GetSegments");
500 iReader->iSampler.GetSegments(buf->iBuf);
501 if (!buf->iBuf.Length())
503 buf->iNext = iFree;//Return empty buffer to the free list
508 iWriter->Queue(buf);//Pass the buffer to the write engine.
512 TBool CProfiler::Drain()
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.
518 TBuffer* buf = iFree;
523 RDebug::Printf("PROFILER: No available buffer for Drain");
527 iReader->iSampler.Drain(buf->iBuf);
529 if (!buf->iBuf.Length())
531 buf->iNext = iFree;//Return empty buffer to the free list
535 iWriter->Queue(buf); //Pass the buffer to the write engine.
540 void CProfiler::GetErrors()
542 // Gets error report from the device and pass the buffer to the write engine
545 TBuffer* buf = iFree;
550 RDebug::Printf("PROFILER: No available buffer for GetErrors");
553 iReader->iSampler.GetErrors(buf->iBuf);
557 void CProfiler::Write()
559 // Pass a queued buffer to the writer
562 TBuffer* buf = iHead;
569 void CProfiler::ReadComplete(TBuffer* aBuf)
571 // Handle a completed read buffer
574 DEBUG_PROFILER(RDebug::Printf("*RC %d",iState);)
576 //Add the buffer to the queue
584 if (!iWriter->IsActive())
587 if (iLastCommand == Profiler::EStart)
588 Read(); //Request another read
590 DEBUG_PROFILER(RDebug::Printf("*RC end %d",iState);)
593 void CProfiler::WriteComplete(TBuffer* aBuf)
595 // Handle a flushed write buffer.
598 DEBUG_PROFILER(RDebug::Printf("*WC %d",iState);)
600 aBuf->iNext = iFree;//Return empty buffer to the free list
605 case EGettingSegments:
607 break;//More code segments to be completed
609 //Always go to the running state after the segments are collected....
611 iReader->iSampler.Start(KSampleRate);
614 //...but stop sampler immediately if we got another user command
615 if (iLastCommand != Profiler::EStart)
617 iReader->iSampler.Stop();
619 break; //the end of EGettingSegments case
624 Write(); // There are more buffers to go to the file.
627 if (iLastCommand != Profiler::EStart)
628 {//The user has stopped the profiler.
631 break;//More data to drain.
633 //Drain returned empty. May progress further with the engine state
634 if (iLastCommand == Profiler::EStop)
638 iState = EGettingErrors;
642 break;//the end of ERunning case
646 break; //still draining;
649 switch (iLastCommand)
651 case Profiler::EStart:
652 //While draining, we received another Start command
654 iReader->iSampler.Start(KSampleRate);
657 case Profiler::EStop:
661 iState = EGettingErrors;
664 break; //the end of EDraining case
669 switch (iLastCommand)
671 case Profiler::EUnload:
672 CActiveScheduler::Stop(); //Terminate application.
674 case Profiler::EStart:
675 Control(Profiler::EStart);
680 break; //the end of EGettingErrors case
683 RDebug::Printf("PROFILER: WriteComplete in %d state", iState);
689 DEBUG_PROFILER(RDebug::Printf("*WC end %d",iState);)
694 CReader::CReader(TInt aPriority, CProfiler& aProfiler)
695 :CActive(aPriority), iProfiler(aProfiler)
697 CActiveScheduler::Add(this);
705 User::FreeLogicalDevice(KSamplerName);
708 void CReader::ConstructL()
710 TInt r=User::LoadLogicalDevice(KSamplerName);
711 if (r!=KErrNone && r!=KErrAlreadyExists)
713 User::LeaveIfError(iSampler.Open());
718 // Pass the full buffer to the engine
723 iProfiler.ReadComplete(data);
726 void CReader::DoCancel()
728 iSampler.ReadCancel();
731 void CReader::Queue(TBuffer* aBuf)
733 // Queue a request to read data into the empty buffer
737 iSampler.Read(aBuf->iBuf, iStatus);
741 CWriter::CWriter(TInt aPriority, CProfiler& aProfiler)
742 :CActive(aPriority), iProfiler(aProfiler)
744 CActiveScheduler::Add(this);
755 void CWriter::ConstructL()
757 User::LeaveIfError(iFs.Connect());
760 TInt CWriter::Open(const TDesC& aFile)
762 // Open the file for saving the sample data
765 return iFile.Replace(iFs,aFile,EFileWrite);
768 void CWriter::Close()
776 void CWriter::Queue(TBuffer* aBuf)
778 // Queue a request to write the full buffer into the file
782 iFile.Write(aBuf->iBuf, iStatus);
788 // Return the empty buffer back to the engine
793 iProfiler.WriteComplete(data);
796 void CWriter::DoCancel()
798 // RFile does not provide a WriteCancel() function
805 inline const CPServer& CPSession::Server() const
806 {return *static_cast<const CPServer*>(CSession2::Server());}
808 void CPSession::ServiceL(const RMessage2& aMessage)
810 // Handle a IPC request to control the profiler
813 aMessage.Complete(Server().Control(Profiler::TState(aMessage.Function())));
816 MProfilerController* CPServer::NewL(TInt aPriority, MProfilerEngine& aEngine)
818 // Create and start the server to provide the Profiler interface
821 CPServer* self = new(ELeave) CPServer(aPriority, aEngine);
822 CleanupStack::PushL(self);
823 self->StartL(KProfilerName);
828 CPServer::CPServer(TInt aPriority, MProfilerEngine& aEngine)
829 :CServer2(aPriority), MProfilerController(aEngine)
832 void CPServer::Release()
837 CSession2* CPServer::NewSessionL(const TVersion&,const RMessage2&) const
839 return new(ELeave) CPSession();
843 // Console Controller
845 MProfilerController* CConsole::NewL(TInt aPriority, MProfilerEngine& aEngine)
847 // Create and start the console UI for the profiler
850 CConsole* self = new(ELeave) CConsole(aPriority, aEngine);
851 CleanupStack::PushL(self);
857 CConsole::CConsole(TInt aPriority, MProfilerEngine& aEngine)
858 :CActive(aPriority), MProfilerController(aEngine)
860 CActiveScheduler::Add(this);
863 void CConsole::ConstructL()
865 iConsole = Console::NewL(KProfilerName, TSize(KConsFullScreen,KConsFullScreen));
870 CConsole::~CConsole()
876 void CConsole::Release()
881 void CConsole::Help()
883 // Display the instructions on the console
886 _LIT(KInstructions,"[S]tart, Sto[p], [C]lose or E[x]it\r\n");
887 iConsole->Write(KInstructions);
890 void CConsole::Queue()
892 // Request a key press from the console
895 iConsole->Read(iStatus);
899 void CConsole::RunL()
901 // Handle a key press from the console
904 TInt key = iConsole->KeyCode();
906 Profiler::TState command;
910 command = Profiler::EStart;
913 command = Profiler::EStop;
916 command = Profiler::EClose;
919 command = Profiler::EUnload;
921 case '?': case 'h': case 'H':
930 void CConsole::DoCancel()
932 iConsole->ReadCancel();
936 void MainL(TInt aCmd, TDesC* aDrive)
938 // Construct and run the profile engine
941 CProfiler* p = CProfiler::NewLC(aCmd, aDrive);
942 CActiveScheduler::Start();
943 CleanupStack::PopAndDestroy(p);
946 TInt GetCommand(TDes &aDrive)
948 // Decode the command line arguments into a profiler control request
949 // aDrive is the drive number to store the profiler data on
952 _LIT(KStart,"start");
954 _LIT(KClose,"close");
955 _LIT(KUnload,"unload");
958 _LIT(KXIPOnly,"-xiponly");
959 _LIT(KDrive,"-drive=");
960 const TInt KDriveOffset=7;
962 User::CommandLine(c);
964 if (c.FindF(KNoUi) >= 0)
966 if (c.FindF(KXIPOnly) >= 0)
967 cmd |= KCommandXIPOnly;
969 // get the drive letter if any
970 TInt pos = c.FindF(KDrive);
975 driveLet.SetLength(1);
976 driveLet[0] = c[pos];
977 driveLet.UpperCase();
978 if (driveLet[0] >= 'A' && driveLet[0] <= 'Z')
980 aDrive[0] = driveLet[0];
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;
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
1006 TInt command = GetCommand(drive);
1007 if ((command & KCommandMask) != KCommandNone)
1009 TInt r = Profiler::Control(Profiler::TState(command & KCommandMask));
1010 if (r != KErrNotFound || (command & KCommandMask) != Profiler::EStart)
1013 CTrapCleanup::New();
1014 TRAPD(r,MainL(command, &drive));
1016 RDebug::Print(_L("PROFILER: Error starting profiler"));