Document:	Sampling Profiler For EPOC Devices	
Number:	.AAANNN.NNNN/NNNN.NN	Status:		
Owner:	Dennis May	Last Revised:	09/07/2007	
Function:		
Contributors:	Adam Stevenson, Djordje Kovacevic, Mark Tai	
Key Reviewers:	     Oleg Isakov	
Distribution	     	
Approved By:	Name Of Approval Authority	Approval Ref.:	Approval Ref	

Introduction
Purpose and scope
This document describes the use of the sampling and analysis tools that can be used to profile execution on EPOC devices.
References
Open Issues
The following issues need to be resolved before this document is completed.
Abbrevations
XIP image	eXecuteInPlace ROM image. The executables are executed from the place they are stored. Their run addresses are decided during rombuild process. NOR Flash is an example of XIP media.	
non-XIP image	The executables of the non-XIP image are copied into RAM before being run. Their run addresses are decided during run time. NAND Flash is an example non-XIP media.	

Document History
Date	Version	Status	Description	
27/09/2000	0.01	Draft		
31/05/2005	0.02	Draft	Added non-XIP support in profiler	
08/09/2006	0.03	Draft	Added description of –drive= option	
09/07/2007	0.04	Draft	Changed XIP to non-XIP for –rofs option. 	

The profiling tool
The profiler consists of a program running on the target device which periodically samples the PC (program counter) and current thread ID, saving the resulting trace data to a file. This program comprises of:
•	a kernel-dependant device driver, currently only ARM targets are supported
•	an executable which can be controlled via command line, programmatically by IPC, or through a console interface
•	an optional DLL which provides a hot-key UI to replace the default console
Currently the sample rate is fixed at 1000Hz, typically generating 3Kb of data per second of processor activity. It can run on a standard build of EPOC (no kernel modifications are required), and does not require changes to the code to be profiled.
When profiler runs, it gets all the data from the device driver and stores it in memory. This ultimately becomes a limitation and when the profiler runs out of heap memory, it stops and writes all the data to disk. With a default heap that can grow to 1MB and with 3Kb of data per second, the profiler will run for 5-6 minutes before running out of memory and stopping. You can increase the maximum heap size of the profiler in order for it to run longer.
The tool also supports profiling of non-XIP executables. It uses Kernel’s debug interface to collects the relevant information regarding non-XIP code segments. In order to do so, Kernel must be built with __DEBUGGER_SUPPORT__ option. (Assabet, Lubbock and H2 are all built by default in this way.)
After profiling the trace data file ([drive letter]:\PROFILER.DAT on the device) is transferred to a PC, where it can be processed using a Win32 tool: Analyse. Some of its feature include:
•	Providing various reports based on the trace data, including a basic trace listing, a thread/location profile distribution table and a thread/time profile
•	A rom symbol file can be used to translate code addresses into DLL or function names
•	The trace can be filtered in various ways before generating the report, e.g. restrict analysis to a single thread, process, DLL and/or time interval.
•	Output suitable for pasting directly into Excel can be generated to enable simple graphical analysis of the results

Using the profiler
Installing the profiler
To generate the profiling trace, the profile program must be installed on the EPOC device (e.g. by copying to a CF card). Three parts of the program need to be installed as follows:
File	Found in	Install to	
Profiler.exe	\epoc\release\<ABI>\urel	\sys\bin or other suitable location on the device	
Sampler.ldd	\epoc\release\<ABI>\urel	\sys\bin	
ProfilerKeys.dll	\epoc\release\<ABI>\urel	\sys\bin	
In this table, ABI refers to the flavour of ARM build required—e.g. ARM4. The final component—ProfilerKeys.dll—requires the full Window Server to function, thus it is optional and omitting from the installation will cause the profiler to provide a console UI instead of using hot keys.
Running the profiler
To load the profiler, the Profiler.exe program must be executed on the target device, it then remains loaded until asked to exit.
Profiler commands
The Profiler understands four commands:
start	Start sampling, if not already doing so.	
stop	Stop sampling. If restarted, the new samples are added to the current trace data.	
close	Stop sampling if necessary and then close the trace data file. If restarted, the current trace data is discarded and a new trace file started. This allows the file to be copied from the internal disk without having to unload the profiler.	
unload	Stop sampling if necessary and unload the profiler. This first ensures that all trace data is saved before the program exits.	
It also accepts the following options:
-noui	Starts profiler application without user interface. Particularly useful on eshell window server systems with no touch sensitive screen.
In such case, the profiler should be started from the ESHELL by:
start profiler start –noui
and stopped by:
profiler exit	
-xiponly	Suppresses any activity in the tool regarding non-XIP support. This is recommended option when running the profiler tool on XIP-only platforms.	
-drive=[drive letter]	Specify which drive to store the trace data file (PROFILER.DAT), where [drive letter] can be one of the 26 drive letters, ‘A’ to ‘Z’.  When this drive letter option is not specified the default drive C is used.  If the file cannot be created at the specified drive the profiler will not start.	

Command line control
The Profiler can be controlled from the command line, by specifying the action as a parameter to the Profiler.exe program. If the profiler is already loaded, this will just pass on the control request to the running program, otherwise it will load the profiler ready for further commands. E.g. use the following command in ESHELL:
profiler start
Console control
The console UI is used if the ProfilerKeys.dll is not present on the device. When the console is in the foreground, it interprets the following keypresses to control the profiler:
Key	Action	
s	Start	
p	Stop	
c	Close	
x	unload	
?	Display the key mapping used to control the profiler	
Hot key control
If ProfilerKeys.dll is installed then the profiler hooks the following key combinations which can be used to control the profiler from any application:
Key	Action	
Fn+Ctrl+Shift+1	start	
Fn+Ctrl+Shift+2	stop	
Fn+Ctrl+Shift+3	close	
Fn+Ctrl+Shift+4	unload	
Fn+Ctrl+Shift+0	Display the current operation of the profiler in an infoprint	
Programmatic control
The profiler can also be controlled from within another program on the device by using the API defined in profiler.h. This defines the Profiler class, which has static functions corresponding to each profiler command. Note that Profiler::Start() will not load the profiler if it is not currently loaded, it will only cause an already loaded profiler to start sampling. It is possible to check for this possibility and use the command line to run the profiler using the following code:
if (Profiler::Start() == KErrNotFound)
	{
	_LIT(KProfiler,"profiler");
	_LIT(KStart,"start");
	RProcess p;
	if (p.Create(KProfiler,KStart) == KErrNone)
		{
		p.Resume();
		p.Close();
		}
	}

Analysing the trace data
Once a trace data file has been obtained using the profiler, it needs to be transferred to a PC for analysis using the Analyse program, which should be present in the EPOC tools directory. Here are some example of standard uses of the tool, followed by a complete command reference describing all of the options.
Generating a full listing of the trace
The full trace recorded in the trace file can be output in text form using the listing -listing (-l) option:
analyse profiler.dat -listing
This outputs one line for each sample, recording the sample number, thread ID and a PC value. A table mapping these thread IDs to thread names is provided at the end of the listing. By default, samples of the NULL thread are not shown—processor inactivity can be detected by samples that are missing in the trace—if necessary the NULL thread can be reported using the -null (-n) option.
A more interesting trace can be generated if a ROM symbol file matching the device ROM is available—PC values are then displayed relative to symbols in the ROM. This is done using the -rom (-r) command option:
analyse profiler.dat -rom \epoc32\rom\myrom.symbol -listing
Finding out where threads spend most of their time
A distribution of thread against location can be generated using the -profile (-p) command option—this is the default if -l and -v are omitted. The address space can be divided up by address, by DLL or by function, the latter two requiring a symbol file. The following command will analyse the trace and report the amount of time spent by each thread in each function in the ROM:
analyse profiler.dat -rom \epoc32\rom\myrom.symbol -byfunction
You may need a very wide command prompt to read the tablulated output sensibly. The output begins with a summary of the sample analysed: the total number of samples, the number of active samples (i.e. ignoring the NULL thread), and the number of samples analysed in the following report. Then there is a thread ID to name mapping, followed by a table with the threads across the top and the locations down the right side. The first row contains the proportion of time spent overall in that thread, the last column is the proportion of time spent by all threads in that function. Note that both the threads and locations are sorted into descending order.
Usually, the last few threads and locations are of no interest for analysis, and eliminating them makes the output easier to follow. A ‘cut off’ value can be supplied which eliminates the output of results for threads or locations which contain fewer samples than the provided value. The -cutoff (-c) option takes a number which is specified in percent, e.g.
analyse profiler.dat -r \epoc32\rom\myrom.symbol -bf -cutuff0.5
This produces the same results as last time, but does not report on any threads or locations with less than 0.5% of the overall samples.
When using the analyse tool with a symbol file, there is a limitation you should be aware of. C static functions (LOCAL_C functions) are not listed in the symbol file by the tools (they do not appear in the .map file). As a consequence of this, hits in these functions will be wrongly attributed to the functions right before of them in ROM.
Finding out when threads are executing
A distribution of thread activity against time can be generated using the -activity (-v) command option. The following command will analyse the trace and report the amount of time spent by each thread in each block of 1000 samples (1 second):
analyse profiler.dat -activity -bysize1000
If the -bysize parameter is omitted, a default value of 100 samples is used. The cutoff option can be used to eliminate threads that contribute little to the overall execution time.
Restricting the analysis to certain samples
Filters can be provided to restrict the analysis to specific samples in the trace. These options generally work for all types of output.
Filtering by thread name
The samples for a specific thread can be analysed using the -thread (-t) option, this is followed by a basic wildcard pattern for the thread name to analyse. (The * character can represent zero or more of any character). E.g. to analyse just the threads in the file server the following commands could be used:
analyse profiler.dat -r \epoc32\rom\myrom.symbol -bf -thread efile*
Filtering by dll name
Analysis for the thread/location distribution can be restricted to DLLs matching a provided pattern using the -dll (-d) option. E.g. to restrict analysis to BitGdi.dll the following command could be used:
analyse profiler.dat -r \epoc32\rom\myrom.symbol -bf -dll bitgdi.dll
Filtering by function name
Analysis can be even more specific by providing a DLL and function pattern to match. In this case, the first matching function in the first matching DLL is analysed by address, defaulting to instruction-level granularity. This can provide code-path analysis for execution hotspots. E.g. to analyse execution in the ExecuteJava() function the following command could be used:
analyse profiler.dat -r \epoc32\rom\myrom.symbol -d pjavai.dll -function executejava
Note that the ‘function name’ usually includes the signature of the parameters, so it is often effective to add a * to the end of the function name for analysis.
Filtering by sample number
The -samples (-s) option allows a specific range of samples to be analysed, rather than the entire file. This can be useful if the trace contains activity which should not be analysed, or for generating high-resolution activity traces for specific parts of the trace. E.g. to analyse 1000 samples starting at sample number 2500, the following command could be used:
analyse profiler.dat -v -bs50 -samples2500+1000
Generating graphs and charts from the output
By default, spaces are used when formatting the output resulting in tables that are easily read by eye. However, very often it helps to be able to generate charts from the tabulated data for visual analysis. The -formatexcel (-mx) option instructs Analyse to format the output so that it is suitable for cut-and-paste into Microsoft Excel: using tabs rather than spaces, rearranging the tables for chart generation, and using numerical representation of sample data (rather than %). If the output from Analyse is directed to a file, this can then be opened by a text editor and the table can be pasted directly into Excel.
Profiling Jazelle code
Profiler tool clears the LSB bit in every sample. This is because it is used for some other markings that help keep log file short. ARM and Thumb instructions always appear on even program addresses. However, Jazelle instructions are one byte long and can appear on any program address. As the consequence, sampling Jazelle code does not give the exact result. If a Jazelle instruction on odd program address is sampled, it will be reported as the Jazelle instruction on the preceding even program address.

Analyse command reference
Command line
analyse [ report-type ] [ options ] tracefile
Reports
Report type	Abbrev.	Description	
-listing	-l	Generate a listing of the trace	
-profile	-p	Generate a thread vs. location profile of the data. This is the default type of report.	
-activity	-v	Generate a thread activity profile of the data	
Options
Option	Abbrev.	Description	
-help	-h
-?	Display a brief summary of the command line options	
-help config	-h config	Display an example of the config file	
-rom symbolfile	-r …	Supply a ROM symbol file of the XIP image for interpretation of PC values	
-samplesrange	-s…	Restrict the analysis to the sample range specified. The range can either be specified as begin-end or as begin+count, the sample numbers should be specified in decimal. So the following are equivalent:
-samples2500-3500
-samples2500+1000	
-null	-n	Include the EPOC Kernel NULL thread in the analysis. This thread is ignored by default.	
-thread name	-t …	Restrict analysis to threads whose full name (processname::threadname) matches the pattern provided. The pattern matching has a single wildcard character ‘*’, which will match zero or more of any character.	
-dll name	-d …	Restrict analysis to Dlls whose name matches the pattern provided.
This option is only effective for a Profile report, and requires a ROM symbol file.	
-function name	-f …	Restrict analysis to functions whose name matches the pattern provided. If -dll is also specified, then only functions matching the pattern within a matching Dll are analysed.
This option is only effective for a Profile report, and requires a ROM symbol file.	
-addressrange	-a…	Restrict analysis to the address range specified. This may be useful if no symbol file is available when generating a Profile report. The range can either be specified as begin-end or as begin+size, the addresses should be specified in hexadecimal. So the following would both analyse the first 6Mb of the ROM:
-address50000000-50600000
-address50000000+600000	
-by…	-b…	Determine the way in which the space is partitioned in a Profile or Activity report. Only one of the following four options should be specified:	
-bydll	-bd	Split the address space by Dll in a Profile report.
This requires a ROM symbol file. It is the default partitioning if neither -dll or ‑function are specified.	
-byfunction	-bf	Split the address space by function in a Profile report.
This requires a ROM symbol file. It is the default partitioning if -dll is specified without -function.	
-bysizesize	-bs…	In a Profile report, this will split the address space into blocks of approximately size bytes. This assumes that either the -address option has been used, or analysis is focussed on a single function using the -function option. It is the default partitioning if -function is specified, providing instruction level analysis.
In an Activity report, this is the number of samples grouped in each line of the report. The default value of size in this case is 100 samples.	
-bynumbercount	-bn…	Split the address space into count ‘buckets’ in a Profile report.
It is the default partitioning if -address is specified, providing 100 buckets.	
-cutoffpercent	-c…	Discard threads and locations from the output that account for less than percent samples in the analysis. Percent is a floating point percentage value, i.e. the following specifies a cutoff at 0.5%:
-cutoff.5	
-format…	-m…	Modify the format of the reports. Any number of the following options can be concatenated into the option string, e.g.
-formatexceltotalother	
-formatpercent	-mp	Report samples as a percentage of the total analysed. This is the default.	
-formatsamples	-ms	Report the number of samples for each entry.	
-formatexcel	-mx	Generate an excel compatible report for cut-and-paste.	
-formatzero	-mz	Report zero values explicitly. The default is to leave blanks.	
-formattotal	-mt	Just report the ‘total’ column in a Profile report, and discard the thread breakdown.	
-formatother	-mo	Discard the ‘other’ location from a Profile report. This is where all samples that do not fit in another identified location are accumulated.	
-oby	-o	Supply oby file of the non-XIP image	
-rofs	-z	Supply symbol file of the non-XIP image	
-config	-x	Supply configuration file. The file can contains all input argument and options. A template of the file is given in Appendix A.	
Options used in each type of report
Option	Listing	Profile	Activity	
-rom				
-samples				
-null				
-thread				
-dll				
-function				
-address				
-bydll				
-byfunction				
-bysize				
-bynumber				
-cutoff				
-formatpercent				
-formatsamples				
-formatexcel				
-formatzero				
-formattotal				
-formatother				
- oby				
- rofs				
- config				

Appendix A: non-XIP support and configuration file
Non-XIP executables are copied into RAM memory before being executed. Their run addresses are not known during ROM image build up. Therefore, corresponding symbol file does not contain run addresses of each particular function in the image, as it is the case with XIP images. Instead, it contains the offsets of the functions within their executables.
In order to calculate actual run addresses of non-XIP functions, the profiler tool also collects the events of non-XIP code segments creation/deletion. It uses Kernel debug interface and Kernel has to be built with __DEBUGGER_SUPPORT__ option.
During start-up of the profiler tool, the current list of all active non-XIP code segments are collected and stored into the c:\profiler.dat. Once the profiling is started, the driver is still continuously monitoring for such events and logs them into the file, alongside with sample data.

For each particular non-XIP image, both oby and symbol file have to be provided to the analyse tool (XIP image requires only symbol file). As there could be more non-XIP images in the system, a more convenient way for passing input arguments to analyse tool would be by means of configuration file.
Below is an example of the configuration file to profile NAND image of H2 Techview.
It is used as:
analyse –x analyse.cfg

;***************************
;Analase.cfg file
[Common]
TraceFile=PROFILER.DAT
;Mode=listing|profile|activity
Mode=profile
SymbolFile=om_001.techview.nand.symbol
;Range=100-200 | 100+100
;IncludeNullThread=0|1

[Profile]
Thread=
Dll=
Function=
;Range=1f1a+20 | 1f1a-1f3a
Range=

[Partition]
;Mode=dll|function
BucketSize=
NumberOfBuckets=

[Format]
;Mode=percentages|samples|excel
Mode=
;ZeroValues=0|1
ZeroValues=
;NoOthers=0|1
NoOthers=
;TotalOnly=0|1
TotalOnly=

[NonXIP]
ObyFile1=om_001.techview.nand.rofs.oby
RofsSymbolFile1=om_001.techview.nand.rofs.symbol
;***************************
Any option provided in the configuration file can be overwritten by the same option in the command line.

Appendix B: The format of the profiler.dat file
Basic Elements
Profiler.dat is built up from the following basic elements:
-	UInt (32-bits unsigned integer);
-	Int (32-bit signed integer);
-	Text (up to 255 bytes long non-unicode text).

They are encoded as:
UInt
7 bits occupies a byte (starting from the least significant bits) with MSB=1 in the last byte. To decode, value should be extended to 32-bit length by 0s.
UInt (32 bits divided into groups of 7 bits)	Encoded as:	
1001|1000110|1000011|1101100|0001101	00001101 01101100 01000011 01000110 10001001	
0000|0000000|0000000|0001100|0001101	00001101 10001100	
0000|0000000|0000000|0000000|0001101	10001101	
Int
7 bits occupies a byte (starting from the least significant bits) with MSB=1 in the last byte. To decode, value should be extended to 32-bit length by the most significant bit.
Int (32 bits divided into groups of 7 bits)	Encoded as:	
1001|1000110|1000011|1101100|0001101	00001101 01101100 01000011 01000110 11111001	
1111|1111111|1111111|1111111|1111111	11111111	
0111|1111111|1111111|1111111|1111111	01111111 01111111 01111111 01111111 10000111	
1111|1111111|1111111|1111000|1111111	01111111 11111000	
0000|0000000|0000000|0000000|0001101	10001101	
Text
Supports up to 255 long non Unicode text:
Text	Encoded as:	
“Text”	4, ‘T’, ‘e’, ‘x’, ‘t’	
‘x’ times 255	255, ‘x’ times 255	
“”	0	
Derived Elements
The following derived elements are defined as well:
Tag
This is the header of the file.
Tag = Text(“Profiler”) + UInt(MajorVersionNumber)
Thread
Contains processID and thread name. Also can contain process name if the process is seen for the first time:
Thread(DThread*) = UInt(processID) [+ Text(processName) ]+ Text(threadName]
Repeat
Encode repeated sequence of the same sample:
Repeat(x) = Int(0) + UInt(x):
File build up
-	File starts with Tag element.
-	It is followed by samples that are recorded as:
Sample description	Sample record	
The first sample record.	Int(PC | 1)
UInt(threadID)
UInt(processID)   ]
Text(processName) ] Thread element
Text(threadName)  ]	
A sample record with a new thread and a new process seen	Int((PC - PC of the previous sample) | 1)
UInt(threadID)
UInt(processID)   ]
Text(processName) ] Thread element
Text(threadName)  ]	
A sample record with a new thread seen (with already known process)	Int((PC - PC of the previous sample) | 1)
UInt(threadID)
UInt(processID)   ]
Text(threadName)  ] Thread element	
A sample record with already known thread	Int((PC - PC of the previous sample) | 1)
UInt(threadID)	
A sample with the same thread ID as the previous sample	Int(PC - PC of the previous sample)	
A samples with the same PC and threadID that repeat N consecutive times	<Any sample record described above>
Repeat(N-1)	
Non-XIP code support
The record of non-XIP DCodeSeg creation/deletion can be inserted between any two consecutive sample records. It can also appear immediately after Tag element or just before Repeat element.
All records (samples and DCodeSeg) are placed in chronological order.

Description	Recorded as	
The record of nonXIP code segment creation. CodeSegFullName contains full path of the file name. If it is longer then 255 characters, the last 255 characters will be recorded.	Int(0)
UInt(0)
UInt(CodeSegRunAddress)
UInt(CodeSegSize)
Text(CodeSegFullName)	
The record of nonXIP code segment deletion	Int(0)
UInt(0)
UInt(CodeSegRunAddress | 1)	
Footer. Resides at the bottom of the file. 
RowBufferErrCounter is the number of the dropped sample records due to full buffer condition in the driver.
CookBufferErrCounter is the number of the dropped code segment events due to full buffer condition in the driver.
	Int(0)
UInt(0)
UInt(0)
UInt(RowBufferErrCounter)
UInt(CookBufferErrCounter)
UInt(ReportBitMask) where:
  bit 0 set in NonXIPMode
  bit 1 set if NonXIPMode but Kernel
            hasn’t got debugger support