mirror of
https://github.com/aaronsgiles/uuUndo.git
synced 2024-11-24 14:31:25 +00:00
1 line
10 KiB
C
1 line
10 KiB
C
//
|
|
// When __PROFILE is called, stack frame looks like this:
|
|
//
|
|
// sp+08 Caller's return address
|
|
// sp+04 Address of function name string
|
|
// sp Return address to start of function
|
|
//
|
|
// After we save registers and such, stack frame looks like this:
|
|
//
|
|
// sp+28 Caller's return address
|
|
// sp+24 Address of function name string
|
|
// sp+20 Return address to start of function
|
|
// sp+16 saved A0
|
|
// sp+12 saved A1
|
|
// sp+08 saved D0
|
|
// sp+04 saved D1
|
|
// sp saved D2
|
|
//
|
|
// Upon returning from __PROFILE, stack frame should look like this:
|
|
//
|
|
// sp Address of __PROFILEEND
|
|
//
|
|
//
|
|
|
|
#include <Profiler.h>
|
|
#include <Threads.h>
|
|
#include <Timer.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#pragma profile off
|
|
|
|
#if defined(powerc) || defined(__powerc)
|
|
extern void Profile_GetTime(UnsignedWide *time);
|
|
#else
|
|
#define Profile_GetTime(time) Microseconds(time)
|
|
#endif
|
|
|
|
#define THREAD_OFFSET 100
|
|
|
|
#define NAME_LENGTH 23
|
|
#define MAX_FUNCS 1000
|
|
#define MAX_THREADS 50
|
|
#define STACK_DEPTH 100
|
|
|
|
typedef struct {
|
|
char name[NAME_LENGTH+1];
|
|
int calls;
|
|
long address;
|
|
UnsignedWide totalTime;
|
|
} FunctionInfo;
|
|
|
|
typedef struct {
|
|
FunctionInfo *callerFunction;
|
|
long returnAddress;
|
|
} StackFrame;
|
|
|
|
typedef struct {
|
|
StackFrame stack[STACK_DEPTH];
|
|
StackFrame *stackPtr;
|
|
FunctionInfo *currentFunction;
|
|
UnsignedWide startTime;
|
|
} ThreadInfo;
|
|
|
|
static ThreadInfo gThread[MAX_THREADS];
|
|
static FunctionInfo gFunction[MAX_FUNCS];
|
|
static long gProfilerOn = false;
|
|
static ThreadInfo *gCurrentThread;
|
|
static FunctionInfo *gCallerFunction;
|
|
static long gSaveArea[5];
|
|
|
|
static FunctionInfo *GetIndex(char *functionName:__A0, long address:__D0):__A0;
|
|
static void StartTime(FunctionInfo *function:__A0, int call:__D0);
|
|
static void StopTime(int index:__D0);
|
|
static void GetThreadInfo(void);
|
|
static void PushFrame(long returnAddress:__D0);
|
|
static long PopFrame(void):__D0;
|
|
static asm pascal void __PROFILEEND(void);
|
|
|
|
#pragma parameter AddToWide(__A0, __A1)
|
|
void AddToWide(UnsignedWide *a, UnsignedWide *b) = {
|
|
0x2011, // move.l 0(a1),d0 ;d0 = hi
|
|
0x2229, 0x0004, // move.l 4(a1),d1 ;d1 = lo
|
|
0x2410, // move.l 0(a0),d2 ;d2 = hi
|
|
0xD2A8, 0x0004, // add.l 4(a0),d1 ;add lo parts
|
|
0xD580, // addx.l d0,d2 ;add hi parts with carry
|
|
0x2082, // move.l d2,0(a0) ;store high result
|
|
0x2141, 0x0004 // move.l d1,4(a0) ;store low result
|
|
};
|
|
|
|
#pragma parameter SubFromWide(__A1, __A0)
|
|
void SubFromWide(UnsignedWide *a, UnsignedWide *b) = {
|
|
0x2011, // move.l 0(a1),d0 ;d0 = hi
|
|
0x2229, 0x0004, // move.l 4(a1),d1 ;d1 = lo
|
|
0x2410, // move.l 0(a0),d2 ;d2 = hi
|
|
0x92A8, 0x0004, // sub.l 4(a0),d1 ;add lo parts
|
|
0x9580, // subx.l d0,d2 ;add hi parts with carry
|
|
0x2082, // move.l d2,0(a0) ;store high result
|
|
0x2141, 0x0004 // move.l d1,4(a0) ;store low result
|
|
};
|
|
|
|
|
|
pascal OSErr ProfilerInit(ProfilerCollectionMethod method, ProfilerTimeBase timeBase, short numFunctions, short stackDepth)
|
|
{
|
|
int i;
|
|
|
|
// initialize the function table
|
|
memset(gFunction, 0, sizeof(gFunction));
|
|
strcpy(gFunction[0].name, "Root_Function");
|
|
gFunction[0].address = -1;
|
|
gFunction[0].calls = 1;
|
|
|
|
// initialize the thread table
|
|
memset(gThread, 0, sizeof(gThread));
|
|
for (i = 0; i < MAX_THREADS; i++) gThread[i].stackPtr = gThread[i].stack;
|
|
Profile_GetTime(&gThread[0].startTime);
|
|
gThread[0].currentFunction = gFunction;
|
|
|
|
// make sure we're turned off
|
|
gProfilerOn = false;
|
|
return noErr;
|
|
}
|
|
|
|
pascal void ProfilerSetStatus(short on)
|
|
{
|
|
gProfilerOn = (on) ? true : false;
|
|
}
|
|
|
|
pascal OSErr ProfilerDump(StringPtr filename)
|
|
{
|
|
/*
|
|
static char cfile[256];
|
|
FunctionInfo *function;
|
|
double temp;
|
|
FILE *f;
|
|
int i;
|
|
|
|
BlockMoveData(filename + 1, cfile, *filename);
|
|
cfile[*filename] = 0;
|
|
f = fopen(cfile, "w");
|
|
if (f) {
|
|
for (i = 0, function = gFunction; i < MAX_FUNCS; i++, function++)
|
|
if (function->address) {
|
|
temp = ((double)function->totalTime.hi * 65536.0 * 65536.0) + (double)function->totalTime.lo;
|
|
fprintf(f, "%-40s %15.0lf ticks, %15.2lf ticks/call, %7d calls\n", function->name,
|
|
temp, temp/function->calls, function->calls);
|
|
}
|
|
fclose(f);
|
|
}
|
|
return noErr;
|
|
*/
|
|
}
|
|
|
|
pascal void ProfilerTerm(void)
|
|
{
|
|
gProfilerOn = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void StartTime(FunctionInfo *function:__A0, int call:__D0)
|
|
{
|
|
UnsignedWide thisTime, diff;
|
|
FunctionInfo *last;
|
|
|
|
// get the current time
|
|
Profile_GetTime(&thisTime);
|
|
|
|
// if there was another function being timed, update that timer
|
|
last = gCurrentThread->currentFunction;
|
|
if (last) {
|
|
diff = gCurrentThread->startTime;
|
|
SubFromWide(&thisTime, &diff);
|
|
AddToWide(&last->totalTime, &diff);
|
|
}
|
|
|
|
// if we have a new function to track, reset the start time and increment the calling count
|
|
if (function) {
|
|
if (call) function->calls++;
|
|
gCurrentThread->startTime = thisTime;
|
|
}
|
|
|
|
// update the current thread's function pointer
|
|
gCurrentThread->currentFunction = function;
|
|
}
|
|
|
|
static FunctionInfo *GetIndex(char *functionName:__A0, long address:__D0):__A0
|
|
{
|
|
FunctionInfo *function;
|
|
register int i;
|
|
|
|
// loop over all functions, looking for an address match or an empty space
|
|
for (i = 0, function = gFunction; i < MAX_FUNCS; i++, function++) {
|
|
long a = function->address;
|
|
if (!a) break;
|
|
else if (a == address) return function;
|
|
}
|
|
|
|
// if we have space for a new function, create an entry
|
|
if (i < MAX_FUNCS) {
|
|
strncpy(function->name, functionName, NAME_LENGTH);
|
|
function->address = address;
|
|
return function;
|
|
}
|
|
|
|
// return nil if nothing could be found
|
|
return nil;
|
|
}
|
|
|
|
void GetThreadInfo(void)
|
|
{
|
|
// get a pointer to the current thread's info
|
|
ThreadID thread;
|
|
GetCurrentThread(&thread);
|
|
gCurrentThread = gThread + thread - THREAD_OFFSET;
|
|
}
|
|
|
|
void PushFrame(long returnAddress:__D0)
|
|
{
|
|
// get a pointer to the next empty stack frame, and increment the stack pointer
|
|
StackFrame *sp = gCurrentThread->stackPtr++;
|
|
|
|
// save the current function and return address
|
|
sp->callerFunction = gCurrentThread->currentFunction;
|
|
sp->returnAddress = returnAddress;
|
|
}
|
|
|
|
long PopFrame(void):__D0
|
|
{
|
|
// get a pointer to the next empty stack frame, and increment the stack pointer
|
|
StackFrame *sp = --gCurrentThread->stackPtr;
|
|
|
|
// save the current function and return address
|
|
gCallerFunction = sp->callerFunction;
|
|
return sp->returnAddress;
|
|
}
|
|
|
|
|
|
|
|
|
|
// When __PROFILE is called, stack frame looks like this:
|
|
//
|
|
// sp+0c Return space for Pascal function
|
|
// sp+08 Caller's return address
|
|
// sp+04 Address of function name string
|
|
// sp Return address to start of function
|
|
//
|
|
// After we save registers and such, stack frame looks like this:
|
|
//
|
|
// sp+32 Return space for Pascal function
|
|
// sp+28 Caller's return address
|
|
// sp+24 Address of function name string
|
|
// sp+20 Return address to start of function
|
|
// sp+16 saved A0
|
|
// sp+12 saved A1
|
|
// sp+08 saved D0
|
|
// sp+04 saved D1
|
|
// sp saved D2
|
|
//
|
|
// Upon returning from __PROFILE, stack frame should look like this:
|
|
//
|
|
// sp+04 Return space for Pascal function
|
|
// sp Address of __PROFILEEND
|
|
|
|
asm pascal void __PROFILE(char *functionName)
|
|
{
|
|
tst.l gProfilerOn // is the profiler on?
|
|
bne.s @doProfile // if so, go do the work
|
|
move.l (sp),4(sp) // otherwise, copy return address over parameter
|
|
addq.l #4,sp // adjust the stack pointer accordingly
|
|
rts // return
|
|
@doProfile:
|
|
_Debugger
|
|
clr.l gProfilerOn // clear the profiler flag -- we are not reentrant
|
|
movem.l a0/a1/d0-d2,-(sp) // save registers
|
|
jsr GetThreadInfo // get the current thread info into a global
|
|
move.l 28(sp),d0 // get the return address in d0
|
|
jsr PushFrame // and push it onto the stack
|
|
move.l 24(sp),a0 // now get the function name
|
|
move.l 20(sp),d0 // and the function address
|
|
move.l d0,24(sp) // (copy the address onto the stack for later)
|
|
jsr GetIndex // and return an index into our function table
|
|
moveq.l #1,d0 // set the call flag
|
|
jsr StartTime // start the timer for this baby (and stop the last one)
|
|
lea __PROFILEEND,a0 // point a0 to our profile end routine
|
|
move.l a0,28(sp) // store that in place of the real return address
|
|
movem.l (sp)+,a0/a1/d0-d2 // restore the registers
|
|
addq.l #4,sp // skip over stack space for the functionName parameter
|
|
move.l #1,gProfilerOn // turn the profiler flag back on
|
|
rts // and return
|
|
}
|
|
|
|
// sequence of events:
|
|
//
|
|
// call to __PROFILE
|
|
// clears profilerOn flag to prevent re-entrancy
|
|
// gets a pointer to the current thread info in gCurrentThread
|
|
// saves the currentFunction pointer and the real return address as a stack frame
|
|
// calculates a pointer to the new currentFunction
|
|
// stops the timer for the previous currentFunction and starts it for the new one
|
|
// replaces the real return address with a return into __PROFILEEND
|
|
// resets profilerOn flag
|
|
// returns to the beginning of the function
|
|
//
|
|
// return to __PROFILEEND
|
|
// clears profilerOn flag to prevent re-entrancy
|
|
// gets a pointer to the current thread info in gCurrentThread
|
|
// pops the stack frame, returning real return address and pointer to caller function
|
|
// saves real return address on the stack for returning
|
|
// stops the timer for the previous function and starts it back up for the caller function
|
|
// resets profilerOn flag
|
|
// returns to the calling function
|
|
//
|
|
|
|
// When __PROFILEEND is called, stack frame looks like this:
|
|
//
|
|
// sp Return space for Pascal function
|
|
//
|
|
// Upon returning from __PROFILE, stack frame should look like this:
|
|
//
|
|
// sp Address of __PROFILEEND
|
|
|
|
asm pascal void __PROFILEEND(void)
|
|
{
|
|
_Debugger
|
|
clr.l gProfilerOn // clear the profiler flag -- we are not reentrant
|
|
movem.l a0/d0,gSaveArea // save a0/d0
|
|
jsr GetThreadInfo // get the current thread info into a global
|
|
jsr PopFrame // pop the frame, returning the real return address
|
|
move.l d0,gSaveArea+8 // save return address in a safe place
|
|
move.l gCallerFunction,a0 // get pointer to new current function
|
|
clr.l d0 // clear the call flag
|
|
jsr StartTime // now start the timer
|
|
movem.l gSaveArea,a0/d0 // restore a0/d0
|
|
move.l gSaveArea+8,a1 // get return address in a1
|
|
move.l #1,gProfilerOn // turn the profiler flag back on
|
|
jmp (a1) // return to the caller
|
|
}
|