1.	Overview
POSER’s emulator can be used to profile the code that it’s executing.  It does this by counting cycles while it simulates the opcodes, and watching for JSR/RTS and interrupt/RTE events.  It dumps the resulting data to  file that is compatible with the Metrowerks Profiler application for Macintosh. 

2.	Profiler Implementation
2.1	UAE Changes
This section describes the profiling changes to the UAE sources.

2.1.1	Build68K
UAE is built into POSER in three stages.  In the first stage, Build68K takes a text file called “table68K” containing information about the 68K opcodes and produces “cpudefs.c”, a C source file with a compressed version of UAE specific information for each opcode.  There are no profiling changes to this stage.  In the third stage, the POSER sources are combined with the sources produced by GenCPU to build the final executable.  The profiling changes for POSER are described in the next section.

2.1.2	GenCPU
In the second stage, GenCPU is built from sources and the cpudefs.c file from state one.  When run, GenCPU can produce functions to emulate each 68K Opcode.  GenCPU has been modified to produce a table of read cycle, write cycle, and extra CPU cycles for each opcode.  In addition, some of the opcode functions (e.g. Bcc) include specific code to tell the profiler to increment the clock.

As GenCPU is running, it adds comments to each opcode function to indicate what the profiling numbers are for that opcode.  (gen_opcode)  The numbers are specific to the MC68000 processor, to match the MC68EC000 used in the Palm devices.  The source for the data is the “Motorola MC68000 8-/16-/32-Microprocessors User’s Manual” section 8.  Read and write cycles are tracked seperately, as well as additional CPU-only cycles for each opcode.

gen_opcode calls genaread and genastore for each opcode to read in arguments and write out results.  These functions have been modified to accumulate read, write and extra CPU cycles for each operation. 

After calling gen_opcode, generate_func takes the accumulated read, write, and extra cycle counts for the opcode and produces data for a table that’s used by POSER.  It also writes a comment after each code block with the expected (per the Motorola manual) and actual (per accumulated numbers) cycle counts for the function.  This is handy because the resulting .c file with the opcode functions can be viewed to check the accuracy of the cycle counting.

2.1.3	Inaccuracies
Here are some known inaccuracies with the current cycle counts.

JMP, JSR, RTS, RTE, and Bcc fall short by one read each, for 6 or 8 cycles each depending on ROM or RAM.  I believe this is because the CPU does instruction prefetch, and so the times in Motorola’s tables actually include the time needed to fetch the next instruction, not the current instruction.  PC-changing instructions need to throw away the prefetched instruction and read in the real next instruction, so this takes an extra read cycle.  POSER does not prefetch, so there’s no good way to account for this.  (Since the next instruction may be in ROM or RAM, the actual CPU cycles taken may differ.)

ANDSR, ORSR, MV2SR and other "SR" instructions are 2 reads short, for 12 or 16 cycles.  I’m not sure why this is, some implementation detail with how the CPU references the status register?

MUL and DIV instructions are counted as worst case.  Both have unusual execution times which depend on the values being multiplied or divided.  The DIV instructions is supposedly less than a 10% difference from best to worst case, for a range of 144-158 cycles for signed and 126-140 cycles for unsigned values.  These are counted as 158 and 140 cycles (plus wait states and effective address calculation times.)  The MUL operations take 38+2n cycles, where n is the number of 1’s in the memory value for MULU, and the number of 10 or 01 patterns in the memory value catenated with 0 (as the LSB).  70 is indicated as the max value, and that is what is used.
2.2	POSER changes
Switches.h has a  HAS_PROFILING switch, which must be non-zero to include profiling in an Emulator build.

2.2.1	CEumlatorApp.cpp
CEmulatorApp .cpp contains a little bit of code that hooks the user interface up with the functions in Profiling.cpp.  Changes are made in CEmulatorApp::ObeyCommand to responds to menu and key events generated by the user, and in CEmulatorApp::FindCommandStatus to makes sure the menu state is correct.  The profiling menu is defined in Palm OS Emulator.ppob.

2.2.2	CPU_REG.cpp
CPU_REG.cpp defines and loads the table of read, write, and extra cycles for each opcode.

Emulator::Execute has been modified to count cycles as the opcode executes.  Most of the cycle counting is done by the functions that read and write memory, but the extra CPU-only cycles are counted in Emulator::Execute. Emulator::Execute also sets and clears a flag to tell the profiler to pay attention to cycles.  This is necessary so that the emulator’s internal use of the memory read/write functions (to draw the screen, call ROM traps, etc) are not charged against the running application.

CPU_REG.cpp also has some code controlled by the HAS_PROFILING_DEBUG flag to help debug the profiler.  This code compares the actual cycles counted as the opcodes are executed to the ones specified as expected in the table.  That is also done in Emulator::Execute.

Hardware::Initialize loads the table of cycle counts.

Software::ProcessException is hooked in to the emulator opcodes to special case exceptions.  It allows POSER to patch out the exception handlers and do its own thing.  The profiler counts time spent in exceptions seperately from time spent in regular functions, so Software::ProcessException calls ProfleInterruptEnter to do this.  If the emulator patched out the exception, the ProfileInterruptExit is also called here.  If not, then the interrupt is not finished until the RTE opcode is executed, and Software::ProcessRTE handles this.

Software::ProcessJSR is called by the CPU emulator function for JSR opcodes.  It calls ProfileFnEnter to start profiling for the new function.  Similarly, Software::ProcessRTS calls ProfileFnExit to stop profiling for the function and return to the enclosing function.

Software::ProcessTrap14 has been taken over to allow executing code to contol aspects of the emulator.  Controlling the profiler is part of this, and there are “E-traps” to init, start, stop, dump, and cleanup profiling.

Software::ProcessTrap15 creates a “fake” function call by calling ProfileFnEnter and ProfileFnExit.  By using the trap number for the address, this allows the profiler to count “F-traps” by trap number, which is handy for finding out which system traps are used most often. Software::ProcessTrap15 also normally skips the emulated trap dispatcher, preferring instead to get the trap address and do the jump itself.  This greatly improves performance when emulating normal code, since about 10% of the time is spent in the trap dispatcher for common operations.  When profiling, native dispatch is skipped, because we actually want to know what overhead the trap dispatcher is requiring.

2.2.3	RAM_ROM.cpp
RAM_ROM.cpp implements reading and writing to memory for various memory banks.  Because the Palm devices require wait states when accessing memory, it’s not enough to simply count reads and writes as 4 cycles each (which is the CPU overhead for them.)  Palm devices use different wait states for different memory, and quite possibly devices will have different wait state characteristics in the future.  RAM_ROM.cpp counts this.

Each of the GetLong, GetWord, GetByte, SetLong, SetWord, and SetByte functions for each memory type calls either ProfileIncrementRead or ProfileIncrementWrite, passing in the number of read or write cycles and the number of wait states for that memory type.  (The wait states are #defined in Profiling.h)  ProfileIncrementRead and ProfileIncrementWrite are inlined to improve performance in the emulator.

2.2.4	TrapPatches.cpp
TrapPatches.cpp turns off a couple of trap patches if the profiler is built into the emulator.  Normally, these are patched out to improve performance by avoiding emulating the ROM routines.  Some of the others should probably be turned off with the HAS_PROFILING switch as well, but they are required by the emulator itself.

2.3	Profiling Code
Most of the profiling code is in Profiling.h and Profiling.cpp.  This is the chunk that gathers and dumps the profiling data.

2.3.1	Accumulators and Flags
gReadCycles, gWriteCycles, and gClockCycles contain the total number of read, write, and clocks that have passed since profiling was started.  These globals are accessed from other modules through the inline functions in Profiling.h.

gProfilingEnabled, gProfilingOn, and gProfilingCounted are flags that indicate the current state of profiling affairs.  Enabled indicates profiling buffers have been allocated and profiling is happening or could start or stop at any time.  On indicates profiling data is currently being collected.  Counted indicates that the current instructions as well as memory reads and writes should be added to the global read/write/cycle counts.

2.3.2	Data Structures
The profiler uses two main data structures to accumulate data:  a tree of function call records and a stack of currently in progress functions or interrupts.

The tree is stored in memory using the same format as the Metrowerks profiler file, so dumping the output file is a simple matter of writing a memory block to disk.  See Appendix B for the record format.  Global variables contain the index of:  the root of the tree, the record used for overflow tracking, the record used as the root for interrupt tracking, and the index of the next available record.

The stack is an array of the following records.  Each element tracks a function or interrupt that is currently executing.
struct FnStackRecord {
	int call;								// fn data block for fn being called
	uaecptr returnAddress;			// return address aka (SP) to calling fn
	UInt64 cyclesAtEntry;			// cycle count when fn was called
	UInt64 cyclesAtInterrupt;		// cycle count when fn was interrupted
	UInt64 cyclesInKids;				// number of cycles spent in subroutines
	UInt64 cyclesInInterrupts;		// number of cycles spent in interrupts
};

A global variable holds the current top of the stack.

To handle interrupts, a separate stack is used that keeps track of the call stack record that matches the interrupt.  A stack is necessary for this because interrupts can be interrupted.  We allow up to 8 of these, but I’ve never seen more than 2 used.

2.3.3	GetRoutineName
GetRoutineName turns an address (or interrupt number or trap number) into a string representation for that function.  It attempts to use the ROM.map data to find the function name, and if no match is found it steps through the instructions found at the address indicated until it finds the end of the function, then looks for the debug symbol.  If the address was to a function in a code resource and the resource was moved, then the name will be incorrect.  GetRoutineName is completely responsible for turning an address into a name, and it will always return some string, even if passed an invalid address.
2.3.4	Generating Function Names
The functiosn InitStringTable, CleanupStringTable, and FindOrAddString do the work of creating the string table (as described in Appendix B) and then adding strings to it.  RecursiveAddressToStrings traverses the tree of functions and changes the address stored with each function record into an offset into the string table, which it is generating.  That’s a one-time operation, if you tried to do it twice you’d get garbage.
2.3.5	PopCallStackFn
PopCallStackFn is used when a function or interrupt is being exited from.  It updates the nodes in the tree to properly track call counts and time spent in interrupts and in children.  It is called both during normal function or interrupt exit and by cleanup code that’s trying to keep the profiler’s call stack in sync with the CPU’s call stack.
2.3.6	FindOrAddCall
FindOrAddCall is used when a function or interrupt is being entered.  It looks to see if the function has prevously been called from the current function or interrupt, and if so returns the existing record.  If not, a new record is allocated, initialized, and plugged into the tree.
2.3.7	ProfileInit
ProfileInit allocates the stack and tree with the passed sizes, and initializes a bunch of other data structures.
2.3.8	ProfileCleanup
ProfileCleanup frees the data structures allocated in ProfileInit
2.3.9	ProfileStart
ProfileStart turns on the profiling flags.  Currently it assumes that it is not called from within an interrupt, often is not the case.  When invoked from the POSER UI, the CPU is often currently processing an interrupt.
2.3.10	ProfileStop
ProfileStop turns off collection of profiling data.  If there are functions currently on the profiling stack, they are popped.
2.3.11	ProfileDump
ProfileDump reads in the ROM.map file, turns all the addresses in the call tree into real function names, generating the string table in the process, then dumps out the header, tree, and stringtable to disk.  This operation is destructive to the function tree, and cannot be done more than once.  (It should probably call ProfileCleanup to make sure this doesn’t happen.)
2.3.12	ProfileFnEnter
ProfileFnEnter is called by the emulator when a JSR is executed, and at a couple of other places.  It creates a record for the function (or reuses an existing record), and updates the profiler’s call stack.

ProfileFnEnter also supports a special profiling mode.  If the profiler is built with PROFILE_ONE_FN on, then it’s assumed that whenever profiling has been initialized, you always want to start profiling when a specified function is entered, and stop profiling when that function is exited.  That way you get profiling information for a particular function no matter who it’s called from.  In this case, ProfileFnEnter may be called before ProfileStart has been called, and if it detects that the target function has been entered it will start profiling at that point.
2.3.13	ProfileFnExit
ProfileFnExit is called by the emulator when an RTS is executed  The Palm OS does some funny things such that there is not always an RTS for every JSR.  To handle this, ProfileFnExit has to be smart, so it keeps the return address when a function is called and compares it to the destination address for the RTS.  Most of the time they match, and everything is fine, but when they don’t one of two things may be happening:  either a function is being called not by using JSR, but rather by pushing the callee’s address on the stack then executing an RTS, or a RTS has been skipped and the stack has been cleaned up by the executing code (the kernel does this.)

If the return address is not what we expected based on the profiling stack, then the first thing we do is walk up the stack looking to see if the address matches some higher function.  If it does, the stack is popped up to the function we expect, and the intervening functions are flagged as improperly exited.  If no match was found, we assume it’s a long jump disguised as an RTS, and call ProfileFnEnter instead.

The profiler’s call stack can (commonly) be popped to empty.  This happens when the function that was executing when ProfileStart was called exits.  If profiling is started from the POSER UI, this happens very often, so we do the best we can: create a new fake “root” for the call tree, representing the function we’re returning to, and continue on.  If profiling was started by the PROFILE_ONE_FN trick, then a pop to empty is considered exiting the target function, and profiling is turned off.
2.3.14	ProfileInterruptEnter
ProfileInterruptEnter does almost the same thing as ProfileFnEnter.  The difference is that the current call is added to the “interrupt” root rather than the currently executing function, and the profile’s call stack pointer is pushed onto the profiler’s interrupt stack.
2.3.15	ProfileInterruptExit
ProfileInterruptExit works much like ProfileFnExit, the calls stack’s record for the interrupt “function” is popped.  Unlike with RTS, RTE is reliably matched with an interrupt exit, so it’s not necessary to do the funny call stack searching.  However, when the RTE executes it’s possible that there may still be some unmatched calls (JSRs) that were executed as part of processing the interrupt.  (The kernel does this regularly.)  So we make sure to pop all the functions off the profiler’s call stack until we get to the one indicated by the profiler’s interrupt stack.

The Palm OS trap handler performs trap calls by pushing stuff on the CPU stack so that the RTE will jump to the real function (and then later be able to do an RTS from that fn to get back to the code that executed the TRAP interrupt.)  To handle this, the RTE destination is compared to the PC that was saved when the interrupt happened, and if they addresses don’t match then ProfileFnEnter is called to track the ”fake” function entry.

3.	Using the Profiler
3.1	POSER UI
You actually don't have to do anything at all to set up your app for profiling.  That's the beauty of having the emulator handle profiling--all the work can happen "between cycles"   It’s a good idea to set the compiler switch so that the debug symbols get embedded in your code, otherwise you won't be able to identify your functions in the output.  For the ROM, debug symbols are not necessary if a ROM.map file is available.  The ROM.map file will be used first, and if the function is not present then the debug symbols will be used.

To do a profiling run:
•	Launch the Mac version of the emulator.
•	Load the app or otherwise set up the situation to be profiled
•	Select "Start" from the profiling menu or press cmd-[
•	Do the profiled thing
•	Select "Stop" from the profiling menu, or press cmd-]
•	Select "Dump" from the profiling menu, or press cmd-\

You can start and stop more than once before dumping the results.  The profile data gathered in each start/stop pair will be added into that gathered in previous pairs.

When you dump the result, you'll find a file in the emulator directory called "POSER Profile Results".  At the moment, you have to change the type and creator to 'proF' so the Metrowerks profile will open it.  Once that’s done, just double-click it, or otherwise convince the "MW Profiler" app to open it.  See Reading the Results below.

When you profile this way, the profiler buffers are predefined in Profiling.h.  Currently they’re set to 20000 functions with a max stack of 200 functions.  That's enough to profile booting a ROM, and plenty for repetetive operations, but it quickly gets used up if you do gremlins.  (A few hundred gremlins will generate 20000 unique function calls.)

3.2	From Your App
You can Init, Start, Stop, Dump, and Cleanup the POSER profiler from application code, much like you can for the Metrowerks profiler for Mac.  To do this, #include “EmuTraps.h” and #include “EmuProfiling.h” in the module that will control the profiler.  (You might want to put EmuProfilingNOPs.c in the project for simulator builds only, so that the profiling functions will be stubbed out there.)  The APIs are described below.

3.2.1	ProfileInit
void ProfileInit(int maxCalls, int maxDepth)
	maxCalls - the number of function call buffers to allocate
	maxDepth - the number of stack frames to allocate
This allocates the profile buffers within the POSER memory partition.  It must be done prior to calling any other profiling function.
3.2.2	ProfileStart
void ProfileStart()
Begin profiling at that point in the code.  The instructions executed within the function that calls ProfileStart will appear in the result in a function body under the label “partial”, as will any code executed by calling functions if ProfileStop is not called when the function that called ProfileStart exits.
3.2.3	ProfileStop
void ProfileStop()
Stop profiling at that point in the code.  In general, it’s a good idea to start and stop profiling at the same level, but that is not required.  If you stop while nested deeper than where you started, all the functions still on the call stack will be popped as if they had exited at the point ProfileStop was called.
3.2.4	ProfileDump
void ProfileDump()
Dumps the accumulated results to a file called “POSER Profile Results”.  (Future versions should allow you to pass the file name as an argument to ProfileDump.)
3.2.5	ProfilePrint
void ProfilePrint()
A debugging function.  Prints the profile results as text to POSER’s Log file.  Can be very slow.
3.2.6	ProfileCleanup
void ProfileCleanup()
Free up the data structures allocated by ProfileInit.

4.	Reading the Results
The profiler dumps the results in a format compatible with Metrowerks profiler.  This format was reverse engineered, and is incompletely described in Appendix B.

Times are all theoretically in millisecond for a 16.580608 MHz clock.  The times are written to the output file as 64-bit integers of clocks, and a multiplier is applied to scale the result to milliseconds based on the clock speed.  5661.518 is a bit over 5 1/2 seconds, and 1.255 is a bit over 1 1/4 milliseconds.  (In theory at least, I'm still validating the data, if you see anything that strikes you as inaccurate, please tell me about it!)

The header in the output file can be ignored.  The Overhead field actually contains the number of cycles spent in interrupts, which should be identical to the +Children column for the interrupts block.

The first column contains the function name, with these special cases:
functions	a top-level cover that includes all regular function calls
interrupts	a top-level node that includes all interrupts (except the ones POSEr has patched out)
partial	means profiling was started in the middle of the function, so we don't have the address of the fn and consequently don't have a name.
overflow	a lump of all functions called when we're out of space to track more unique calls, where unique means called from the same path to the "root" of the call tree.
unknown	a function for which no name could be found.  Many functions in .prc files show up as unknown.

The rest of the names all take the form "Name $address.x" where:
   Name- is the name of the function or trap.
   address-For regular functions, the 4 byte address.
 	   -For traps, the 2 byte trap number.
	   -For interrupts, the 1 byte interrupt number.
   x- debugging info, indicating where the name information came from:
	't' = trap names table built in POSER,
	'm'= the ROM.Map file found in the same directory as POSER
	'd'=the debug symbol appended to the function’s instructions by the compiler
	'i'=invalid address flag (usually due to POSERs implementation internals)

The other columns are defined as follows.  Note MINIMUM and STACK SPACE are NOT what you expect:

count - the number of times the functions was called.

only - time spent in the function, not counting child fns or interrupts.

% (by only) - percentage of total profiling time spent in this fn/call.

+Children - time spent in the function including child fns, but not including interrupts

% (by +Children) - percentage of total profiling time spent in the fn and its kids

Average - "Only" divided by "count"

Maximum - the maximum time (in msec) spent in any 1 call to the fn.

Minimum - NOT WHAT YOU EXPECT.  This is actually the time spent handling interrupts for calls to that particlular instance of that fn. Due to the way the "Summary" is calculated, this number won't be correct in summary views.

Stack space - NOT WHAT YOU EXPECT.  More of a trap/interrupt counter plus some debug info.  The number in that field for a particular fn entry is incremented by 1 every time the fn is interrupted, by 10000 if the fn call is made by a faked up RTS instead of a JSR, and 1000 if the function was executing when an RTS occurred that didn't return to it's called but instead returned to some fn farther up the call chain.  Again, this will only be useful in the detail view, since the summary does some computation on it.
5.	Appendix A - Profiling Includes
5.1	EmuTraps.h
/*********************************************************************
 *
 * Copyright (c) 1998
 * 3Com/Palm Computing Division.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *
 *		This product includes software developed by 3Com and its
 *		contributors.
 *
 * 4. Neither 3Com nor the names of its contributors may be used to
 *    endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE 3COM AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 3COM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 *******************************************************************/
#pragma once

#define	emuTrapBase			0x0000

typedef enum {
	emuTrapProfileInit = emuTrapBase,
	emuTrapProfileStart,
	emuTrapProfileStop,
	emuTrapProfileDump,
	emuTrapProfileCleanup,

	// WARNING!! LEAVE THIS ONE AT THE END AND ALWAYS ADD NEW TRAPS TO
	//  THE END OF THE TRAP TABLE BUT RIGHT BEFORE THIS TRAP!!!!!!!!!
	emuTrapEmuReserved1,
	emuTrapEmuReserved2,
	emuTrapEmuReserved3,
	emuTrapEmuReserved4,
	
	emuTrapLastTrapNumber
	} EmuTrapNumber;

#define emuTrapNum				0x0E		// Emulator trap

#define EMU_TRAP(trapNum) \
	TWOWORD_INLINE(m68kTrapInstr+emuTrapNum,trapNum)

#define ASM_EMU_TRAP(trapNum)	\
			DC.W	m68kTrapInstr+emuTrapNum; \
			DC.W	trapNum

5.2	EmuProfiling.h
/*********************************************************************
 *
 * Copyright (c) 1998
 * 3Com/Palm Computing Division.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *
 *		This product includes software developed by 3Com and its
 *		contributors.
 *
 * 4. Neither 3Com nor the names of its contributors may be used to
 *    endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE 3COM AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 3COM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 *******************************************************************/
#pragma once

#include "EmuTraps.h"

void ProfileInit(int maxCalls, int maxDepth)
			EMU_TRAP(emuTrapProfileInit);

void ProfileStart()
			EMU_TRAP(emuTrapProfileStart);

void ProfileStop()
			EMU_TRAP(emuTrapProfileStop);

void ProfileDump()
			EMU_TRAP(emuTrapProfileDump);

void ProfileCleanup()
			EMU_TRAP(emuTrapProfileCleanup);

5.3	EmuProfilingNOPs.c
// stub out the functions for simulator builds only

void ProfileInit(int maxCalls, int maxDepth) {return;}
void ProfileStart() {return;}
void ProfileStop() {return;}
void ProfileDump() {return;}
void ProfilePrint() {return;}
void ProfileCleanup() {return;}
6.	Appendix B - Metrowerks Profiler File Format
The profile output file is composed of three sections.  A header section of 0x200 bytes, then a section of FnCallRecord structures containing an array of function data, followed by a string table containing the names of each function.  The functions are represented in a tree, where each node represents a given function when called from a particular path to the root of the tree.  That is, if A calls C and B calls C, then there will be at least 2 records in the tree representing calls to C, once when called from A and another when called from B.  I say at least 2 because A and B themselves may be called from multiple places, and each unique version of A and B will also have a unique child node representing C.
6.1.1	Header
The header section contains things like the number of functions in the function data array, and offset to the start of the string table, the size of the string table, and the record number (array index) of the root node in the function tree.

struct ProfFileHeader {
	UInt32	proF;					// 'proF'
	UInt32	version;				// 0x00040002
	UInt32	fnCount;				// number of unique fns (records) in log
	UInt32	four;					// 0x00000004

	UInt32	zeros1;				// 0x00000000
	UInt32	zeros2;				// 0x00000000
	UInt32	unknown;				// 0xB141A3A9	- maybe timebase data
	UInt32	recordsSize;		// size of header plus size of data (or offset to string table)
	
	UInt32	stringTableSize;	// size of string table in bytes
	UInt64	overhead;			// count for overhead
	Int32	rootRec;					// record number of root of tree
	
	UInt32	sixtyfour1;			// 0x00000064
	UInt32	sixtyfour2;			// 0x00000064
	UInt32	countsPerTime;  	// translation between counts at nodes and integers in column
											// 0x00FD0000 = 16.580608 MHz with display in seconds
											// 0x000040C4 = 16.580608 MHz with display in milliseconds
	UInt32	oddstuff0;			// seems like it can be 0, set by profiler tool itself
	
	UInt32	oddstuff1;			// seems like it can be 0, set by profiler tool itself
	UInt32	oddstuff2;			// seems like it can be 0, set by profiler tool itself
	UInt32	oddstuff3;			// seems like it can be 0, set by profiler tool itself
	UInt32	oddstuff4;			// seems like it can be 0, set by profiler tool itself

	Byte	unused[0x200 - 0x50];	// for 0x200 bytes
};

6.1.2	Function Array
Following the header (starting at offset 0x200 in the file) is an array of header.fnCount structs as defined below.  Each struct represents a unique node in the tree.  Most of these values are straightfoward, and appear in the results view representing what you expect.  The sib and kid records determine the relationship to other functions in the tree.  Children of a given node are found by looking at the node’s kid field to find the first child, then look at the child’s sib field to find the adjacent nodes which are all considered children of the first kid’s parent.  If the sib or kid contain -1 then there are no sibling or children nodes.

struct FnCallRecord {
	UInt32	name;				// also offset from start of name table to this fn's name
	UInt32	entries;			// times function was called
	UInt64	cyclesSelf;		// profiling data for this fn alone 
	UInt64	cyclesPlusKids;	// profiling data for this fn with kids
	UInt64	cyclesMin;		// profiling data for this fn alone, min
	UInt64	cyclesMax;		// profiling data for this fn alone, max
	Int32		sib;				// record number of sib, -1 for no sibs
	Int32		kid; 				// record number of kid, -1 for no kids
	UInt32	stackUsed;		// bytes of stack used by fn, we use it to count unmatched returns
};

6.1.3	String Table
After the array of function records is a string table.  This table contains the names of all the functions in the profiling output.  Each function name appears only once, and all the nodes that represent that function will refer to the same string.  Order of strings within the string table is not important.

The table is stores as adjacent C strings.  That is, the first byte of the table is the first byte of the first string, and subsequent bytes contain the rest of the first string until a NULL (0) byte appears.  Immediately after the terminator is the first byte of the next string.