plan9fox/sys/src/ape/lib/ap/plan9/profile.c
cinap_lenrek 2c5c784255 prof: properly save and restore RARG for amd64
amd64 passes first argument in RARG (BP) register
which has the be preserved duing _profin() and
_profout() calls. to handle this we introduce
_saveret() and _savearg(). _saveret() returns
AX, _savearg() returns RARG (BP). for archs other
and amd64, _saveret() and _savearg() are the
same function, doing nothing.

restoing works with dummy function:

uintptr
_restore(uintptr, uintptr ret)
{
	return ret;
}

...

ret = _saveret();
arg = _savearg();
...
return _restore(arg, ret);

as we pass arg as the first argument, RARG (BP) is
restored.
2014-02-17 13:25:24 +01:00

334 lines
6.6 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include "sys9.h"
enum {
Profoff, /* No profiling */
Profuser, /* Measure user time only (default) */
Profkernel, /* Measure user + kernel time */
Proftime, /* Measure total time */
Profsample, /* Use clock interrupt to sample (default when there is no cycle counter) */
}; /* what */
typedef long long vlong;
typedef unsigned long ulong;
typedef unsigned long long uvlong;
#include "/sys/include/tos.h"
extern void* sbrk(ulong);
extern long _callpc(void**);
extern void* _savearg(void);
extern void* _saveret(void);
extern void _cycles(uvlong*); /* 64-bit value of the cycle counter if there is one, 0 if there isn't */
static ulong khz;
static ulong perr;
static int havecycles;
typedef struct Plink Plink;
struct Plink
{
Plink *old;
Plink *down;
Plink *link;
long pc;
long count;
vlong time;
};
#pragma profile off
static void*
_restore(void*, void *ret)
{
return ret;
}
void*
_profin(void)
{
void *dummy;
long pc;
Plink *pp, *p;
void *ret, *arg;
vlong t;
ret = _saveret();
arg = _savearg();
pc = _callpc(&dummy);
pp = _tos->prof.pp;
if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
return _restore(arg, ret);
for(p=pp->down; p; p=p->link)
if(p->pc == pc)
goto out;
p = _tos->prof.next + 1;
if(p >= _tos->prof.last){
_tos->prof.pp = 0;
perr++;
return _restore(arg, ret);
}
_tos->prof.next = p;
p->link = pp->down;
pp->down = p;
p->pc = pc;
p->old = pp;
p->down = 0;
p->count = 0;
p->time = 0LL;
out:
_tos->prof.pp = p;
p->count++;
switch(_tos->prof.what){
case Profkernel:
p->time = p->time - _tos->pcycles;
goto proftime;
case Profuser:
/* Add kernel cycles on proc entry */
p->time = p->time + _tos->kcycles;
/* fall through */
case Proftime:
proftime: /* Subtract cycle counter on proc entry */
_cycles((uvlong*)&t);
p->time = p->time - t;
break;
case Profsample:
p->time = p->time - _tos->clock;
break;
}
return _restore(arg, ret);
}
void*
_profout(void)
{
Plink *p;
void *ret, *arg;
vlong t;
ret = _saveret();
arg = _savearg();
p = _tos->prof.pp;
if (p == NULL || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
return arg; /* Not our process */
switch(_tos->prof.what){
case Profkernel: /* Add proc cycles on proc entry */
p->time = p->time + _tos->pcycles;
goto proftime;
case Profuser: /* Subtract kernel cycles on proc entry */
p->time = p->time - _tos->kcycles;
/* fall through */
case Proftime:
proftime: /* Add cycle counter on proc entry */
_cycles((uvlong*)&t);
p->time = p->time + t;
break;
case Profsample:
p->time = p->time + _tos->clock;
break;
}
_tos->prof.pp = p->old;
return _restore(arg, ret);
}
/* stdio may not be ready for us yet */
static void
err(char *fmt, ...)
{
int fd;
va_list arg;
char buf[128];
if((fd = open("/dev/cons", OWRITE)) == -1)
return;
va_start(arg, fmt);
/*
* C99 now requires *snprintf to return the number of characters
* that *would* have been emitted, had there been room for them,
* or a negative value on an `encoding error'. Arrgh!
*/
vsnprintf(buf, sizeof buf, fmt, arg);
va_end(arg);
write(fd, buf, strlen(buf));
close(fd);
}
void
_profdump(void)
{
int f;
long n;
Plink *p;
char *vp;
char filename[64];
if (_tos->prof.what == 0)
return; /* No profiling */
if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
return; /* Not our process */
if(perr)
err("%lud Prof errors\n", perr);
_tos->prof.pp = NULL;
if (_tos->prof.pid)
snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
else
snprintf(filename, sizeof filename - 1, "prof.out");
f = creat(filename, 0666);
if(f < 0) {
err("%s: cannot create - %s\n", filename, strerror(errno));
return;
}
_tos->prof.pid = ~0; /* make sure data gets dumped once */
switch(_tos->prof.what){
case Profkernel:
_cycles((uvlong*)&_tos->prof.first->time);
_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
break;
case Profuser:
_cycles((uvlong*)&_tos->prof.first->time);
_tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
break;
case Proftime:
_cycles((uvlong*)&_tos->prof.first->time);
break;
case Profsample:
_tos->prof.first->time = _tos->clock;
break;
}
vp = (char*)_tos->prof.first;
for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
/*
* short down
*/
n = 0xffff;
if(p->down)
n = p->down - _tos->prof.first;
vp[0] = n>>8;
vp[1] = n;
/*
* short right
*/
n = 0xffff;
if(p->link)
n = p->link - _tos->prof.first;
vp[2] = n>>8;
vp[3] = n;
vp += 4;
/*
* long pc
*/
n = p->pc;
vp[0] = n>>24;
vp[1] = n>>16;
vp[2] = n>>8;
vp[3] = n;
vp += 4;
/*
* long count
*/
n = p->count;
vp[0] = n>>24;
vp[1] = n>>16;
vp[2] = n>>8;
vp[3] = n;
vp += 4;
/*
* vlong time
*/
if (havecycles){
n = (vlong)(p->time / (vlong)khz);
}else
n = p->time;
vp[0] = n>>24;
vp[1] = n>>16;
vp[2] = n>>8;
vp[3] = n;
vp += 4;
}
write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
close(f);
}
void
_profinit(int entries, int what)
{
if (_tos->prof.what == 0)
return; /* Profiling not linked in */
_tos->prof.pp = NULL;
_tos->prof.first = calloc(entries*sizeof(Plink),1);
_tos->prof.last = _tos->prof.first + entries;
_tos->prof.next = _tos->prof.first;
_tos->prof.pid = _tos->pid;
_tos->prof.what = what;
_tos->clock = 1;
}
void
_profmain(void)
{
char ename[50];
int n, f;
n = 2000;
if (_tos->cyclefreq != 0LL){
khz = _tos->cyclefreq / 1000; /* Report times in milliseconds */
havecycles = 1;
}
f = open("/env/profsize", OREAD);
if(f >= 0) {
memset(ename, 0, sizeof(ename));
read(f, ename, sizeof(ename)-1);
close(f);
n = atol(ename);
}
_tos->prof.what = Profuser;
f = open("/env/proftype", OREAD);
if(f >= 0) {
memset(ename, 0, sizeof(ename));
read(f, ename, sizeof(ename)-1);
close(f);
if (strcmp(ename, "user") == 0)
_tos->prof.what = Profuser;
else if (strcmp(ename, "kernel") == 0)
_tos->prof.what = Profkernel;
else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
_tos->prof.what = Proftime;
else if (strcmp(ename, "sample") == 0)
_tos->prof.what = Profsample;
}
_tos->prof.first = sbrk(n*sizeof(Plink));
_tos->prof.last = sbrk(0);
_tos->prof.next = _tos->prof.first;
_tos->prof.pp = NULL;
_tos->prof.pid = _tos->pid;
atexit(_profdump);
_tos->clock = 1;
}
void prof(void (*fn)(void*), void *arg, int entries, int what)
{
_profinit(entries, what);
_tos->prof.pp = _tos->prof.next;
fn(arg);
_profdump();
}
#pragma profile on