ref: 90032afb5f48daa2f05f572b645da92db945284f
dir: /sys/src/ape/lib/ap/plan9/profile.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