mirror of https://github.com/golang/go.git
libmach:
* heuristic to go farther during stack traces. * significantly improved Linux thread handing. acid: * update to new libmach interface. prof: * use new libmach interface. * multiple thread support (derived from Rob's copy). * first steps toward pprof-like graphs: keep counters indexed by pc,callerpc pairs. R=r DELTA=909 (576 added, 123 deleted, 210 changed) OCL=24240 CL=24259
This commit is contained in:
parent
9aa28f9231
commit
736903c170
|
|
@ -415,7 +415,7 @@ int ctlproc(int pid, char *msg);
|
||||||
void detachproc(Map *m);
|
void detachproc(Map *m);
|
||||||
int procnotes(int pid, char ***pnotes);
|
int procnotes(int pid, char ***pnotes);
|
||||||
char* proctextfile(int pid);
|
char* proctextfile(int pid);
|
||||||
int procthreadpids(int pid, int **thread);
|
int procthreadpids(int pid, int *tid, int ntid);
|
||||||
char* procstatus(int);
|
char* procstatus(int);
|
||||||
|
|
||||||
Maprw fdrw;
|
Maprw fdrw;
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,16 @@
|
||||||
#include <ureg_amd64.h>
|
#include <ureg_amd64.h>
|
||||||
#include <mach_amd64.h>
|
#include <mach_amd64.h>
|
||||||
|
|
||||||
int pid;
|
|
||||||
char* file = "6.out";
|
char* file = "6.out";
|
||||||
static Fhdr fhdr;
|
static Fhdr fhdr;
|
||||||
int have_syms;
|
int have_syms;
|
||||||
int fd;
|
int fd;
|
||||||
Map *map;
|
|
||||||
Map *symmap;
|
Map *symmap;
|
||||||
struct Ureg ureg;
|
struct Ureg ureg;
|
||||||
int total_sec = 0;
|
int total_sec = 0;
|
||||||
int delta_msec = 100;
|
int delta_msec = 100;
|
||||||
int collapse = 1; // collapse histogram trace points in same function
|
int nsample;
|
||||||
|
int nsamplethread;
|
||||||
|
|
||||||
// output formats
|
// output formats
|
||||||
int functions; // print functions
|
int functions; // print functions
|
||||||
|
|
@ -30,6 +29,12 @@ int linenums; // print file and line numbers rather than function names
|
||||||
int registers; // print registers
|
int registers; // print registers
|
||||||
int stacks; // print stack traces
|
int stacks; // print stack traces
|
||||||
|
|
||||||
|
int pid; // main process pid
|
||||||
|
|
||||||
|
int nthread; // number of threads
|
||||||
|
int thread[32]; // thread pids
|
||||||
|
Map *map[32]; // thread maps
|
||||||
|
|
||||||
void
|
void
|
||||||
Usage(void)
|
Usage(void)
|
||||||
{
|
{
|
||||||
|
|
@ -40,12 +45,14 @@ Usage(void)
|
||||||
fprint(2, "\t\t-l: dynamic file and line numbers\n");
|
fprint(2, "\t\t-l: dynamic file and line numbers\n");
|
||||||
fprint(2, "\t\t-r: dynamic registers\n");
|
fprint(2, "\t\t-r: dynamic registers\n");
|
||||||
fprint(2, "\t\t-s: dynamic function stack traces\n");
|
fprint(2, "\t\t-s: dynamic function stack traces\n");
|
||||||
|
fprint(2, "\t\t-hs: include stack info in histograms\n");
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct PC PC;
|
typedef struct PC PC;
|
||||||
struct PC {
|
struct PC {
|
||||||
uvlong pc;
|
uvlong pc;
|
||||||
|
uvlong callerpc;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
PC* next;
|
PC* next;
|
||||||
};
|
};
|
||||||
|
|
@ -88,20 +95,105 @@ regprint(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
sample(void)
|
getthreads(void)
|
||||||
|
{
|
||||||
|
int i, j, curn, found;
|
||||||
|
Map *curmap[nelem(map)];
|
||||||
|
int curthread[nelem(map)];
|
||||||
|
static int complained = 0;
|
||||||
|
|
||||||
|
curn = procthreadpids(pid, curthread, nelem(curthread));
|
||||||
|
if(curn <= 0)
|
||||||
|
return curn;
|
||||||
|
|
||||||
|
if(curn > nelem(map)) {
|
||||||
|
if(complained == 0) {
|
||||||
|
fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
|
||||||
|
complained = 1;
|
||||||
|
}
|
||||||
|
curn = nelem(map);
|
||||||
|
}
|
||||||
|
if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
|
||||||
|
return curn; // no changes
|
||||||
|
|
||||||
|
// Number of threads has changed (might be the init case).
|
||||||
|
// A bit expensive but rare enough not to bother being clever.
|
||||||
|
for(i = 0; i < curn; i++) {
|
||||||
|
found = 0;
|
||||||
|
for(j = 0; j < nthread; j++) {
|
||||||
|
if(curthread[i] == thread[j]) {
|
||||||
|
found = 1;
|
||||||
|
curmap[i] = map[j];
|
||||||
|
map[j] = nil;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// map new thread
|
||||||
|
curmap[i] = attachproc(curthread[i], &fhdr);
|
||||||
|
if(curmap[i] == nil) {
|
||||||
|
fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(j = 0; j < nthread; j++)
|
||||||
|
if(map[j] != nil)
|
||||||
|
detachproc(map[j]);
|
||||||
|
|
||||||
|
nthread = curn;
|
||||||
|
memmove(thread, curthread, nthread*sizeof thread[0]);
|
||||||
|
memmove(map, curmap, sizeof map);
|
||||||
|
return nthread;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sample(Map *map)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
static int n;
|
static int n;
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
for(i = 0; i < sizeof ureg; i+=8) {
|
if(registers) {
|
||||||
if(get8(map, (uvlong)i, &((uvlong*)&ureg)[i/8]) < 0) {
|
for(i = 0; i < sizeof ureg; i+=8) {
|
||||||
if(n == 1)
|
if(get8(map, (uvlong)i, &((uvlong*)&ureg)[i/8]) < 0)
|
||||||
fprint(2, "prof: can't read registers at %d: %r\n", i);
|
goto bad;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// we need only two registers
|
||||||
|
if(get8(map, offsetof(struct Ureg, ip), (uvlong*)&ureg.ip) < 0)
|
||||||
|
goto bad;
|
||||||
|
if(get8(map, offsetof(struct Ureg, sp), (uvlong*)&ureg.sp) < 0)
|
||||||
|
goto bad;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
bad:
|
||||||
|
if(n == 1)
|
||||||
|
fprint(2, "prof: can't read registers: %r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
|
||||||
|
{
|
||||||
|
int h;
|
||||||
|
PC *x;
|
||||||
|
|
||||||
|
h = (pc + callerpc*101) % Ncounters;
|
||||||
|
for(x = counters[h]; x != NULL; x = x->next) {
|
||||||
|
if(x->pc == pc && x->callerpc == callerpc) {
|
||||||
|
x->count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = malloc(sizeof(PC));
|
||||||
|
x->pc = pc;
|
||||||
|
x->callerpc = callerpc;
|
||||||
|
x->count = 1;
|
||||||
|
x->next = counters[h];
|
||||||
|
counters[h] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
uvlong nextpc;
|
uvlong nextpc;
|
||||||
|
|
@ -114,53 +206,40 @@ xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
|
||||||
print("syms\n");
|
print("syms\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(nextpc == 0)
|
if(histograms)
|
||||||
nextpc = sym->value;
|
addtohistogram(nextpc, pc, sp);
|
||||||
print("%s(", sym->name);
|
if(!histograms || stacks > 1) {
|
||||||
print(")");
|
if(nextpc == 0)
|
||||||
if(nextpc != sym->value)
|
nextpc = sym->value;
|
||||||
print("+%#llux ", nextpc - sym->value);
|
print("%s(", sym->name);
|
||||||
if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
|
print(")");
|
||||||
print(" %s", buf);
|
if(nextpc != sym->value)
|
||||||
|
print("+%#llux ", nextpc - sym->value);
|
||||||
|
if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
|
||||||
|
print(" %s", buf);
|
||||||
|
}
|
||||||
|
print("\n");
|
||||||
}
|
}
|
||||||
print("\n");
|
|
||||||
nextpc = pc;
|
nextpc = pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
stacktracepcsp(uvlong pc, uvlong sp)
|
stacktracepcsp(Map *map, uvlong pc, uvlong sp)
|
||||||
{
|
{
|
||||||
nextpc = 0;
|
nextpc = pc;
|
||||||
if(machdata->ctrace==nil)
|
if(machdata->ctrace==nil)
|
||||||
fprint(2, "no machdata->ctrace\n");
|
fprint(2, "no machdata->ctrace\n");
|
||||||
else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
|
else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
|
||||||
fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
|
fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
|
||||||
else
|
else {
|
||||||
print("\n");
|
addtohistogram(nextpc, 0, sp);
|
||||||
}
|
if(!histograms || stacks > 1)
|
||||||
|
print("\n");
|
||||||
void
|
|
||||||
addtohistogram(uvlong pc, uvlong sp)
|
|
||||||
{
|
|
||||||
int h;
|
|
||||||
PC *x;
|
|
||||||
|
|
||||||
h = pc % Ncounters;
|
|
||||||
for(x = counters[h]; x != NULL; x = x->next) {
|
|
||||||
if(x->pc == pc) {
|
|
||||||
x->count++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
x = malloc(sizeof(PC));
|
|
||||||
x->pc = pc;
|
|
||||||
x->count = 1;
|
|
||||||
x->next = counters[h];
|
|
||||||
counters[h] = x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
printpc(uvlong pc, uvlong sp)
|
printpc(Map *map, uvlong pc, uvlong sp)
|
||||||
{
|
{
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
if(registers)
|
if(registers)
|
||||||
|
|
@ -172,117 +251,136 @@ printpc(uvlong pc, uvlong sp)
|
||||||
print("%s\n", buf);
|
print("%s\n", buf);
|
||||||
}
|
}
|
||||||
if(stacks){
|
if(stacks){
|
||||||
stacktracepcsp(pc, sp);
|
stacktracepcsp(map, pc, sp);
|
||||||
}
|
}
|
||||||
if(histograms){
|
else if(histograms){
|
||||||
addtohistogram(pc, sp);
|
addtohistogram(pc, 0, sp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
samples(void)
|
samples(void)
|
||||||
{
|
{
|
||||||
int msec;
|
int i, pid, msec;
|
||||||
struct timespec req;
|
struct timespec req;
|
||||||
|
|
||||||
req.tv_sec = delta_msec/1000;
|
req.tv_sec = delta_msec/1000;
|
||||||
req.tv_nsec = 1000000*(delta_msec % 1000);
|
req.tv_nsec = 1000000*(delta_msec % 1000);
|
||||||
for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
|
for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
|
||||||
ctlproc(pid, "stop");
|
nsample++;
|
||||||
if(!sample()) {
|
nsamplethread += nthread;
|
||||||
|
for(i = 0; i < nthread; i++) {
|
||||||
|
pid = thread[i];
|
||||||
|
if(ctlproc(pid, "stop") < 0)
|
||||||
|
return;
|
||||||
|
if(!sample(map[i])) {
|
||||||
|
ctlproc(pid, "start");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printpc(map[i], ureg.ip, ureg.sp);
|
||||||
ctlproc(pid, "start");
|
ctlproc(pid, "start");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
printpc(ureg.ip, ureg.sp);
|
|
||||||
ctlproc(pid, "start");
|
|
||||||
nanosleep(&req, NULL);
|
nanosleep(&req, NULL);
|
||||||
|
getthreads();
|
||||||
|
if(nthread == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
typedef struct Func Func;
|
||||||
comparepc(const void *va, const void *vb)
|
struct Func
|
||||||
{
|
{
|
||||||
const PC *const*a = va;
|
Func *next;
|
||||||
const PC *const*b = vb;
|
Symbol s;
|
||||||
return (*a)->pc - (*b)->pc;
|
uint onstack;
|
||||||
|
uint leaf;
|
||||||
|
};
|
||||||
|
|
||||||
|
Func *func[257];
|
||||||
|
int nfunc;
|
||||||
|
|
||||||
|
Func*
|
||||||
|
findfunc(uvlong pc)
|
||||||
|
{
|
||||||
|
Func *f;
|
||||||
|
uint h;
|
||||||
|
Symbol s;
|
||||||
|
|
||||||
|
if(pc == 0)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
if(!findsym(pc, CTEXT, &s))
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
h = s.value % nelem(func);
|
||||||
|
for(f = func[h]; f != NULL; f = f->next)
|
||||||
|
if(f->s.value == s.value)
|
||||||
|
return f;
|
||||||
|
|
||||||
|
f = mallocz(sizeof *f, 1);
|
||||||
|
f->s = s;
|
||||||
|
f->next = func[h];
|
||||||
|
func[h] = f;
|
||||||
|
nfunc++;
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
comparecount(const void *va, const void *vb)
|
compareleaf(const void *va, const void *vb)
|
||||||
{
|
{
|
||||||
const PC *const*a = va;
|
Func *a, *b;
|
||||||
const PC *const*b = vb;
|
|
||||||
return (*b)->count - (*a)->count; // sort downwards
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
a = *(Func**)va;
|
||||||
func(char *s, int n, uvlong pc)
|
b = *(Func**)vb;
|
||||||
{
|
if(a->leaf != b->leaf)
|
||||||
char *p;
|
return b->leaf - a->leaf;
|
||||||
|
if(a->onstack != b->onstack)
|
||||||
symoff(s, n, pc, CANY);
|
return b->onstack - a->onstack;
|
||||||
p = strchr(s, '+');
|
return strcmp(a->s.name, b->s.name);
|
||||||
if(p != NULL)
|
|
||||||
*p = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dumphistogram()
|
dumphistogram()
|
||||||
{
|
{
|
||||||
int h;
|
int i, h, n;
|
||||||
PC *x;
|
PC *x;
|
||||||
PC **pcs;
|
Func *f, **ff;
|
||||||
uint i;
|
|
||||||
uint j;
|
|
||||||
uint npc;
|
|
||||||
uint ncount;
|
|
||||||
char b1[100];
|
|
||||||
char b2[100];
|
|
||||||
|
|
||||||
if(!histograms)
|
if(!histograms)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// count samples
|
// assign counts to functions.
|
||||||
ncount = 0;
|
for(h = 0; h < Ncounters; h++) {
|
||||||
npc = 0;
|
|
||||||
for(h = 0; h < Ncounters; h++)
|
|
||||||
for(x = counters[h]; x != NULL; x = x->next) {
|
for(x = counters[h]; x != NULL; x = x->next) {
|
||||||
ncount += x->count;
|
f = findfunc(x->pc);
|
||||||
npc++;
|
if(f) {
|
||||||
}
|
f->onstack += x->count;
|
||||||
// build array
|
f->leaf += x->count;
|
||||||
pcs = malloc(npc*sizeof(PC*));
|
|
||||||
i = 0;
|
|
||||||
for(h = 0; h < Ncounters; h++)
|
|
||||||
for(x = counters[h]; x != NULL; x = x->next)
|
|
||||||
pcs[i++] = x;
|
|
||||||
if(collapse) {
|
|
||||||
// combine counts in same function
|
|
||||||
// sort by address
|
|
||||||
qsort(pcs, npc, sizeof(PC*), comparepc);
|
|
||||||
for(i = j = 0; i < npc; i++){
|
|
||||||
x = pcs[i];
|
|
||||||
func(b2, sizeof(b2), x->pc);
|
|
||||||
if(j > 0 && strcmp(b1, b2) == 0) {
|
|
||||||
pcs[j-1]->count += x->count;
|
|
||||||
} else {
|
|
||||||
strcpy(b1, b2);
|
|
||||||
pcs[j++] = x;
|
|
||||||
}
|
}
|
||||||
|
f = findfunc(x->callerpc);
|
||||||
|
if(f)
|
||||||
|
f->leaf -= x->count;
|
||||||
}
|
}
|
||||||
npc = j;
|
|
||||||
}
|
}
|
||||||
// sort by count
|
|
||||||
qsort(pcs, npc, sizeof(PC*), comparecount);
|
// build array
|
||||||
// print array
|
ff = malloc(nfunc*sizeof ff[0]);
|
||||||
for(i = 0; i < npc; i++){
|
n = 0;
|
||||||
x = pcs[i];
|
for(h = 0; h < nelem(func); h++)
|
||||||
print("%5.2f%%\t", 100.0*(double)x->count/(double)ncount);
|
for(f = func[h]; f != NULL; f = f->next)
|
||||||
if(collapse)
|
ff[n++] = f;
|
||||||
func(b2, sizeof b2, x->pc);
|
|
||||||
else
|
// sort by leaf counts
|
||||||
symoff(b2, sizeof(b2), x->pc, CANY);
|
qsort(ff, nfunc, sizeof ff[0], compareleaf);
|
||||||
print("%s\n", b2);
|
|
||||||
|
// print.
|
||||||
|
print("%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
|
||||||
|
for(i = 0; i < nfunc; i++) {
|
||||||
|
f = ff[i];
|
||||||
|
print("%6.2f%%\t", 100.0*(double)f->leaf/nsample);
|
||||||
|
if(stacks)
|
||||||
|
print("%6.2f%%\t", 100.0*(double)f->onstack/nsample);
|
||||||
|
print("%s\n", f->s.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,13 +411,21 @@ startprocess(char **argv)
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
detach(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < nthread; i++)
|
||||||
|
detachproc(map[i]);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
ARGBEGIN{
|
ARGBEGIN{
|
||||||
case 'c':
|
|
||||||
collapse = 0;
|
|
||||||
break;
|
|
||||||
case 'd':
|
case 'd':
|
||||||
delta_msec = atoi(EARGF(Usage()));
|
delta_msec = atoi(EARGF(Usage()));
|
||||||
break;
|
break;
|
||||||
|
|
@ -342,7 +448,7 @@ main(int argc, char *argv[])
|
||||||
registers = 1;
|
registers = 1;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
stacks = 1;
|
stacks++;
|
||||||
break;
|
break;
|
||||||
}ARGEND
|
}ARGEND
|
||||||
if(pid <= 0 && argc == 0)
|
if(pid <= 0 && argc == 0)
|
||||||
|
|
@ -355,18 +461,13 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
if(argc > 0)
|
if(argc > 0)
|
||||||
file = argv[0];
|
file = argv[0];
|
||||||
|
else if(pid)
|
||||||
|
file = proctextfile(pid);
|
||||||
fd = open(file, 0);
|
fd = open(file, 0);
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
fprint(2, "prof: can't open %s: %r\n", file);
|
fprint(2, "prof: can't open %s: %r\n", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if(pid <= 0)
|
|
||||||
pid = startprocess(argv);
|
|
||||||
map = attachproc(pid, &fhdr);
|
|
||||||
if(map == nil) {
|
|
||||||
fprint(2, "prof: can't attach to %d: %r\n", pid);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if(crackhdr(fd, &fhdr)) {
|
if(crackhdr(fd, &fhdr)) {
|
||||||
have_syms = syminit(fd, &fhdr);
|
have_syms = syminit(fd, &fhdr);
|
||||||
if(!have_syms) {
|
if(!have_syms) {
|
||||||
|
|
@ -376,9 +477,18 @@ main(int argc, char *argv[])
|
||||||
fprint(2, "prof: crack header for %s: %r\n", file);
|
fprint(2, "prof: crack header for %s: %r\n", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
ctlproc(pid, "start");
|
if(pid <= 0)
|
||||||
|
pid = startprocess(argv);
|
||||||
|
attachproc(pid, &fhdr); // initializes thread list
|
||||||
|
if(getthreads() <= 0) {
|
||||||
|
detach();
|
||||||
|
fprint(2, "prof: can't find threads for pid %d\n", pid);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for(i = 0; i < nthread; i++)
|
||||||
|
ctlproc(thread[i], "start");
|
||||||
samples();
|
samples();
|
||||||
detachproc(map);
|
detach();
|
||||||
dumphistogram();
|
dumphistogram();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ static int
|
||||||
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
|
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uvlong osp;
|
uvlong osp, pc1;
|
||||||
Symbol s, f, s1;
|
Symbol s, f, s1;
|
||||||
extern Mach mamd64;
|
extern Mach mamd64;
|
||||||
int isamd64;
|
int isamd64;
|
||||||
|
|
@ -189,19 +189,30 @@ i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
s1 = s;
|
s1 = s;
|
||||||
|
pc1 = 0;
|
||||||
if(pc != s.value) { /* not at first instruction */
|
if(pc != s.value) { /* not at first instruction */
|
||||||
if(findlocal(&s, FRAMENAME, &f) == 0)
|
if(findlocal(&s, FRAMENAME, &f) == 0)
|
||||||
break;
|
break;
|
||||||
|
geta(map, sp, &pc1);
|
||||||
sp += f.value-mach->szaddr;
|
sp += f.value-mach->szaddr;
|
||||||
}
|
}
|
||||||
if (geta(map, sp, &pc) < 0)
|
if(geta(map, sp, &pc) < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// If PC is not valid, assume we caught the function
|
||||||
|
// before it moved the stack pointer down or perhaps
|
||||||
|
// after it moved the stack pointer back up.
|
||||||
|
// Try the PC we'd have gotten without the stack
|
||||||
|
// pointer adjustment above (pc != s.value).
|
||||||
|
// This only matters for the first frame, and it is only
|
||||||
|
// a heuristic, but it does help.
|
||||||
|
if(!findsym(pc, CTEXT, &s) || strcmp(s.name, "etext") == 0)
|
||||||
|
pc = pc1;
|
||||||
|
|
||||||
if(pc == 0)
|
if(pc == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (pc != retfromnewstack)
|
if(pc != retfromnewstack)
|
||||||
(*trace)(map, pc, sp, &s1);
|
(*trace)(map, pc, sp, &s1);
|
||||||
sp += mach->szaddr;
|
sp += mach->szaddr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -341,11 +341,10 @@ attachproc(int id, Fhdr *fp)
|
||||||
|
|
||||||
// Return list of ids for threads in id.
|
// Return list of ids for threads in id.
|
||||||
int
|
int
|
||||||
procthreadpids(int id, int **thread)
|
procthreadpids(int id, int *out, int nout)
|
||||||
{
|
{
|
||||||
Thread *t;
|
Thread *t;
|
||||||
int i, n, pid;
|
int i, n, pid;
|
||||||
int *out;
|
|
||||||
|
|
||||||
t = idtotable(id);
|
t = idtotable(id);
|
||||||
if(t == nil)
|
if(t == nil)
|
||||||
|
|
@ -353,17 +352,13 @@ procthreadpids(int id, int **thread)
|
||||||
pid = t->pid;
|
pid = t->pid;
|
||||||
addpid(pid, 1); // force refresh of thread list
|
addpid(pid, 1); // force refresh of thread list
|
||||||
n = 0;
|
n = 0;
|
||||||
for(i=0; i<nthr; i++)
|
for(i=0; i<nthr; i++) {
|
||||||
if(thr[i].pid == pid)
|
if(thr[i].pid == pid) {
|
||||||
|
if(n < nout)
|
||||||
|
out[n] = -(i+1);
|
||||||
n++;
|
n++;
|
||||||
out = malloc(n*sizeof out[0]);
|
}
|
||||||
if(out == nil)
|
}
|
||||||
return -1;
|
|
||||||
n = 0;
|
|
||||||
for(i=0; i<nthr; i++)
|
|
||||||
if(thr[i].pid == pid)
|
|
||||||
out[n++] = -(i+1);
|
|
||||||
*thread = out;
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include <u.h>
|
#include <u.h>
|
||||||
#include <sys/syscall.h> /* for tkill */
|
#include <sys/syscall.h> /* for tkill */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/signal.h>
|
#include <sys/signal.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
@ -55,96 +56,364 @@ struct user_regs_struct {
|
||||||
unsigned long ds,es,fs,gs;
|
unsigned long ds,es,fs,gs;
|
||||||
};
|
};
|
||||||
|
|
||||||
// return pid's state letter or -1 on error.
|
// Linux gets very upset if a debugger forgets the reported state
|
||||||
// set *tpid to tracer pid
|
// of a debugged process, so we keep everything we know about
|
||||||
static int
|
// a debugged process in the LinuxThread structure.
|
||||||
procstate(int pid, int *tpid)
|
//
|
||||||
|
// We can poll for state changes by calling waitpid and interpreting
|
||||||
|
// the integer status code that comes back. Wait1 does this.
|
||||||
|
//
|
||||||
|
// If the process is already running, it is an error to PTRACE_CONT it.
|
||||||
|
//
|
||||||
|
// If the process is already stopped, it is an error to stop it again.
|
||||||
|
//
|
||||||
|
// If the process is stopped because of a signal, the debugger must
|
||||||
|
// relay the signal to the PTRACE_CONT call, or else the signal is
|
||||||
|
// dropped.
|
||||||
|
//
|
||||||
|
// If the process exits, the debugger should detach so that the real
|
||||||
|
// parent can reap the zombie.
|
||||||
|
//
|
||||||
|
// On first attach, the debugger should set a handful of flags in order
|
||||||
|
// to catch future events like fork, clone, exec, etc.
|
||||||
|
|
||||||
|
// One for every attached thread.
|
||||||
|
typedef struct LinuxThread LinuxThread;
|
||||||
|
struct LinuxThread
|
||||||
{
|
{
|
||||||
char buf[1024];
|
int pid;
|
||||||
int fd, n;
|
int tid;
|
||||||
char *p;
|
int state;
|
||||||
|
int signal;
|
||||||
|
int child;
|
||||||
|
int exitcode;
|
||||||
|
};
|
||||||
|
|
||||||
snprint(buf, sizeof buf, "/proc/%d/stat", pid);
|
static int trace = 0;
|
||||||
if((fd = open(buf, OREAD)) < 0)
|
|
||||||
return -1;
|
|
||||||
n = read(fd, buf, sizeof buf-1);
|
|
||||||
close(fd);
|
|
||||||
if(n <= 0)
|
|
||||||
return -1;
|
|
||||||
buf[n] = 0;
|
|
||||||
|
|
||||||
/* command name is in parens, no parens afterward */
|
static LinuxThread **thr;
|
||||||
p = strrchr(buf, ')');
|
static int nthr;
|
||||||
if(p == nil || *++p != ' ')
|
static int mthr;
|
||||||
return -1;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
/* p is now state letter. p+1 is tracer pid */
|
static int realpid(int pid);
|
||||||
if(tpid)
|
|
||||||
*tpid = atoi(p+1);
|
enum
|
||||||
return *p;
|
{
|
||||||
|
Unknown,
|
||||||
|
Detached,
|
||||||
|
Attached,
|
||||||
|
AttachStop,
|
||||||
|
Stopped,
|
||||||
|
Running,
|
||||||
|
Forking,
|
||||||
|
Vforking,
|
||||||
|
VforkDone,
|
||||||
|
Cloning,
|
||||||
|
Execing,
|
||||||
|
Exiting,
|
||||||
|
Exited,
|
||||||
|
Killed,
|
||||||
|
|
||||||
|
NSTATE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static char* statestr[NSTATE] = {
|
||||||
|
"Unknown",
|
||||||
|
"Detached",
|
||||||
|
"Attached",
|
||||||
|
"AttachStop",
|
||||||
|
"Stopped",
|
||||||
|
"Running",
|
||||||
|
"Forking",
|
||||||
|
"Vforking",
|
||||||
|
"VforkDone",
|
||||||
|
"Cloning",
|
||||||
|
"Execing",
|
||||||
|
"Exiting",
|
||||||
|
"Exited",
|
||||||
|
"Killed"
|
||||||
|
};
|
||||||
|
|
||||||
|
static LinuxThread*
|
||||||
|
attachthread(int pid, int tid, int *new, int newstate)
|
||||||
|
{
|
||||||
|
int i, n, status;
|
||||||
|
LinuxThread **p, *t;
|
||||||
|
uintptr flags;
|
||||||
|
|
||||||
|
if(new)
|
||||||
|
*new = 0;
|
||||||
|
|
||||||
|
for(i=0; i<nthr; i++)
|
||||||
|
if((pid == 0 || thr[i]->pid == pid) && thr[i]->tid == tid) {
|
||||||
|
t = thr[i];
|
||||||
|
goto fixup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!new)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
if(nthr >= mthr) {
|
||||||
|
n = mthr;
|
||||||
|
if(n == 0)
|
||||||
|
n = 64;
|
||||||
|
else
|
||||||
|
n *= 2;
|
||||||
|
p = realloc(thr, n*sizeof thr[0]);
|
||||||
|
if(p == nil)
|
||||||
|
return nil;
|
||||||
|
thr = p;
|
||||||
|
mthr = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = malloc(sizeof *t);
|
||||||
|
if(t == nil)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
thr[nthr++] = t;
|
||||||
|
t->pid = pid;
|
||||||
|
t->tid = tid;
|
||||||
|
t->state = newstate;
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "new thread %d %d\n", t->pid, t->tid);
|
||||||
|
if(new)
|
||||||
|
*new = 1;
|
||||||
|
|
||||||
|
fixup:
|
||||||
|
if(t->state == Detached) {
|
||||||
|
if(ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
|
||||||
|
fprint(2, "ptrace ATTACH %d: %r\n", tid);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
t->state = Attached;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t->state == Attached) {
|
||||||
|
// wait for stop, so we can set options
|
||||||
|
if(waitpid(tid, &status, __WALL|WUNTRACED|WSTOPPED) < 0)
|
||||||
|
return nil;
|
||||||
|
if(!WIFSTOPPED(status)) {
|
||||||
|
fprint(2, "waitpid %d: status=%#x not stopped\n", tid);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
t->state = AttachStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t->state == AttachStop) {
|
||||||
|
// set options so we'll find out about new threads
|
||||||
|
flags = PTRACE_O_TRACEFORK |
|
||||||
|
PTRACE_O_TRACEVFORK |
|
||||||
|
PTRACE_O_TRACECLONE |
|
||||||
|
PTRACE_O_TRACEEXEC |
|
||||||
|
PTRACE_O_TRACEVFORKDONE |
|
||||||
|
PTRACE_O_TRACEEXIT;
|
||||||
|
if(ptrace(PTRACE_SETOPTIONS, tid, 0, (void*)flags) < 0) {
|
||||||
|
fprint(2, "ptrace PTRACE_SETOPTIONS %d: %r\n", tid);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
t->state = Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static LinuxThread*
|
||||||
attached(int pid)
|
findthread(int tid)
|
||||||
{
|
{
|
||||||
int tpid;
|
return attachthread(0, tid, nil, 0);
|
||||||
|
|
||||||
return procstate(pid, &tpid) == 'T' && tpid == pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
waitstop(int pid)
|
procthreadpids(int pid, int *p, int np)
|
||||||
{
|
{
|
||||||
int p, status;
|
int i, n;
|
||||||
|
LinuxThread *t;
|
||||||
|
|
||||||
p = procstate(pid, nil);
|
n = 0;
|
||||||
if(p < 0)
|
for(i=0; i<nthr; i++) {
|
||||||
|
t = thr[i];
|
||||||
|
if(t->pid == pid) {
|
||||||
|
switch(t->state) {
|
||||||
|
case Exited:
|
||||||
|
case Detached:
|
||||||
|
case Killed:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(n < np)
|
||||||
|
p[n] = t->tid;
|
||||||
|
n++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a single wait and update the corresponding thread.
|
||||||
|
static int
|
||||||
|
wait1(int nohang)
|
||||||
|
{
|
||||||
|
int tid, new, status, event;
|
||||||
|
ulong data;
|
||||||
|
LinuxThread *t;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
NormalStop = 0x137f
|
||||||
|
};
|
||||||
|
|
||||||
|
if(nohang != 0)
|
||||||
|
nohang = WNOHANG;
|
||||||
|
|
||||||
|
tid = waitpid(-1, &status, __WALL|WUNTRACED|WSTOPPED|WCONTINUED|nohang);
|
||||||
|
if(tid < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if(p == 'T')
|
if(tid == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for(;;){
|
if(trace > 0 && status != NormalStop)
|
||||||
p = waitpid(pid, &status, WUNTRACED|__WALL);
|
fprint(2, "TID %d: %#x\n", tid, status);
|
||||||
if(p <= 0){
|
|
||||||
if(errno == ECHILD){
|
// If we've not heard of this tid, something is wrong.
|
||||||
if(procstate(pid, nil) == 'T')
|
t = findthread(tid);
|
||||||
return 0;
|
if(t == nil) {
|
||||||
}
|
fprint(2, "ptrace waitpid: unexpected new tid %d status %#x\n", tid, status);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
if(WIFEXITED(status) || WIFSTOPPED(status))
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(WIFSTOPPED(status)) {
|
||||||
|
t->state = Stopped;
|
||||||
|
t->signal = WSTOPSIG(status);
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: stopped %#x%s\n", tid, status,
|
||||||
|
status != NormalStop ? " ***" : "");
|
||||||
|
if(t->signal == SIGTRAP && (event = status>>16) != 0) { // ptrace event
|
||||||
|
switch(event) {
|
||||||
|
case PTRACE_EVENT_FORK:
|
||||||
|
t->state = Forking;
|
||||||
|
goto child;
|
||||||
|
|
||||||
|
case PTRACE_EVENT_VFORK:
|
||||||
|
t->state = Vforking;
|
||||||
|
goto child;
|
||||||
|
|
||||||
|
case PTRACE_EVENT_CLONE:
|
||||||
|
t->state = Cloning;
|
||||||
|
goto child;
|
||||||
|
|
||||||
|
child:
|
||||||
|
if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
|
||||||
|
fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t->child = data;
|
||||||
|
attachthread(t->pid, t->child, &new, Running);
|
||||||
|
if(!new)
|
||||||
|
fprint(2, "ptrace child: not new\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PTRACE_EVENT_EXEC:
|
||||||
|
t->state = Execing;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PTRACE_EVENT_VFORK_DONE:
|
||||||
|
t->state = VforkDone;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PTRACE_EVENT_EXIT:
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: exiting %#x\n", tid, status);
|
||||||
|
t->state = Exiting;
|
||||||
|
if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
|
||||||
|
fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t->exitcode = data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(WIFCONTINUED(status)) {
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: continued %#x\n", tid, status);
|
||||||
|
t->state = Running;
|
||||||
|
}
|
||||||
|
if(WIFEXITED(status)) {
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: exited %#x\n", tid, status);
|
||||||
|
t->state = Exited;
|
||||||
|
t->exitcode = WEXITSTATUS(status);
|
||||||
|
t->signal = -1;
|
||||||
|
ptrace(PTRACE_DETACH, t->tid, 0, 0);
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: detach exited\n", tid);
|
||||||
|
}
|
||||||
|
if(WIFSIGNALED(status)) {
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: signaled %#x\n", tid, status);
|
||||||
|
t->state = Exited;
|
||||||
|
t->signal = WTERMSIG(status);
|
||||||
|
t->exitcode = -1;
|
||||||
|
ptrace(PTRACE_DETACH, t->tid, 0, 0);
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: detach signaled\n", tid);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int attachedpids[1000];
|
|
||||||
static int nattached;
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ptraceattach(int pid)
|
waitstop(LinuxThread *t)
|
||||||
{
|
{
|
||||||
int i;
|
while(t->state == Running)
|
||||||
|
if(wait1(0) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
for(i=0; i<nattached; i++)
|
// Attach to and stop all threads in process pid.
|
||||||
if(attachedpids[i] == pid)
|
// Must stop everyone in order to make sure we set
|
||||||
return 0;
|
// the "tell me about new threads" option in every
|
||||||
if(nattached == nelem(attachedpids)){
|
// task.
|
||||||
werrstr("attached to too many processes");
|
int
|
||||||
|
attachallthreads(int pid)
|
||||||
|
{
|
||||||
|
int tid, foundnew, new;
|
||||||
|
char buf[100];
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *de;
|
||||||
|
LinuxThread *t;
|
||||||
|
|
||||||
|
if(pid == 0) {
|
||||||
|
fprint(2, "attachallthreads(0)\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!attached(pid) && ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
|
pid = realpid(pid);
|
||||||
werrstr("ptrace attach %d: %r", pid);
|
|
||||||
|
snprint(buf, sizeof buf, "/proc/%d/task", pid);
|
||||||
|
if((d = opendir(buf)) == nil) {
|
||||||
|
fprint(2, "opendir %s: %r\n", buf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(waitstop(pid) < 0){
|
// Loop in case new threads are being created right now.
|
||||||
fprint(2, "waitstop %d: %r", pid);
|
// We stop every thread as we find it, so eventually
|
||||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
// this has to stop (or the system runs out of procs).
|
||||||
return -1;
|
do {
|
||||||
}
|
foundnew = 0;
|
||||||
attachedpids[nattached++] = pid;
|
while((de = readdir(d)) != nil) {
|
||||||
|
tid = atoi(de->d_name);
|
||||||
|
if(tid == 0)
|
||||||
|
continue;
|
||||||
|
t = attachthread(pid, tid, &new, Detached);
|
||||||
|
foundnew |= new;
|
||||||
|
if(t)
|
||||||
|
waitstop(t);
|
||||||
|
}
|
||||||
|
rewinddir(d);
|
||||||
|
} while(foundnew);
|
||||||
|
closedir(d);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +422,12 @@ attachproc(int pid, Fhdr *fp)
|
||||||
{
|
{
|
||||||
Map *map;
|
Map *map;
|
||||||
|
|
||||||
if(ptraceattach(pid) < 0)
|
if(pid == 0) {
|
||||||
|
fprint(2, "attachproc(0)\n");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(findthread(pid) == nil && attachallthreads(pid) < 0)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
map = newmap(0, 4);
|
map = newmap(0, 4);
|
||||||
|
|
@ -172,8 +446,16 @@ attachproc(int pid, Fhdr *fp)
|
||||||
void
|
void
|
||||||
detachproc(Map *m)
|
detachproc(Map *m)
|
||||||
{
|
{
|
||||||
if(m->pid > 0)
|
LinuxThread *t;
|
||||||
ptrace(PTRACE_DETACH, m->pid, 0, 0);
|
|
||||||
|
t = findthread(m->pid);
|
||||||
|
if(t != nil) {
|
||||||
|
ptrace(PTRACE_DETACH, t->tid, 0, 0);
|
||||||
|
t->state = Detached;
|
||||||
|
if(trace)
|
||||||
|
fprint(2, "tid %d: detachproc\n", t->tid);
|
||||||
|
// TODO(rsc): Reclaim thread structs somehow?
|
||||||
|
}
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,22 +501,18 @@ detachproc(Map *m)
|
||||||
36. processor
|
36. processor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
static int
|
||||||
procnotes(int pid, char ***pnotes)
|
readstat(int pid, char *buf, int nbuf, char **f, int nf)
|
||||||
{
|
{
|
||||||
char buf[1024], *f[40];
|
int fd, n;
|
||||||
int fd, i, n, nf;
|
char *p;
|
||||||
char *p, *s, **notes;
|
|
||||||
ulong sigs;
|
|
||||||
extern char *_p9sigstr(int, char*);
|
|
||||||
|
|
||||||
*pnotes = nil;
|
snprint(buf, nbuf, "/proc/%d/stat", pid);
|
||||||
snprint(buf, sizeof buf, "/proc/%d/stat", pid);
|
|
||||||
if((fd = open(buf, OREAD)) < 0){
|
if((fd = open(buf, OREAD)) < 0){
|
||||||
fprint(2, "open %s: %r\n", buf);
|
fprint(2, "open %s: %r\n", buf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
n = read(fd, buf, sizeof buf-1);
|
n = read(fd, buf, nbuf-1);
|
||||||
close(fd);
|
close(fd);
|
||||||
if(n <= 0){
|
if(n <= 0){
|
||||||
fprint(2, "read %s: %r\n", buf);
|
fprint(2, "read %s: %r\n", buf);
|
||||||
|
|
@ -250,10 +528,49 @@ procnotes(int pid, char ***pnotes)
|
||||||
}
|
}
|
||||||
++p;
|
++p;
|
||||||
|
|
||||||
nf = tokenize(p, f, nelem(f));
|
nf = tokenize(p, f, nf);
|
||||||
if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
|
if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
|
||||||
strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
|
strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
|
||||||
strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
|
strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
|
||||||
|
|
||||||
|
return nf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
readstatus(int pid, char *buf, int nbuf, char *key)
|
||||||
|
{
|
||||||
|
int fd, n;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
snprint(buf, nbuf, "/proc/%d/status", pid);
|
||||||
|
if((fd = open(buf, OREAD)) < 0){
|
||||||
|
fprint(2, "open %s: %r\n", buf);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
n = read(fd, buf, nbuf-1);
|
||||||
|
close(fd);
|
||||||
|
if(n <= 0){
|
||||||
|
fprint(2, "read %s: %r\n", buf);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
buf[n] = 0;
|
||||||
|
p = strstr(buf, key);
|
||||||
|
if(p)
|
||||||
|
return p+strlen(key);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
procnotes(int pid, char ***pnotes)
|
||||||
|
{
|
||||||
|
char buf[1024], *f[40];
|
||||||
|
int i, n, nf;
|
||||||
|
char *s, **notes;
|
||||||
|
ulong sigs;
|
||||||
|
extern char *_p9sigstr(int, char*);
|
||||||
|
|
||||||
|
*pnotes = nil;
|
||||||
|
nf = readstat(pid, buf, sizeof buf, f, nelem(f));
|
||||||
if(nf <= 28)
|
if(nf <= 28)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
@ -278,20 +595,31 @@ procnotes(int pid, char ***pnotes)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
realpid(int pid)
|
||||||
|
{
|
||||||
|
char buf[1024], *p;
|
||||||
|
|
||||||
|
p = readstatus(pid, buf, sizeof buf, "\nTgid:");
|
||||||
|
if(p == nil)
|
||||||
|
return pid;
|
||||||
|
return atoi(p);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ctlproc(int pid, char *msg)
|
ctlproc(int pid, char *msg)
|
||||||
{
|
{
|
||||||
int i;
|
int new;
|
||||||
|
LinuxThread *t;
|
||||||
|
uintptr data;
|
||||||
|
|
||||||
|
while(wait1(1) > 0)
|
||||||
|
;
|
||||||
|
|
||||||
if(strcmp(msg, "attached") == 0){
|
if(strcmp(msg, "attached") == 0){
|
||||||
for(i=0; i<nattached; i++)
|
t = attachthread(pid, pid, &new, Attached);
|
||||||
if(attachedpids[i]==pid)
|
if(t == nil)
|
||||||
return 0;
|
|
||||||
if(nattached == nelem(attachedpids)){
|
|
||||||
werrstr("attached to too many processes");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
attachedpids[nattached++] = pid;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,32 +629,72 @@ ctlproc(int pid, char *msg)
|
||||||
werrstr("can only hang self");
|
werrstr("can only hang self");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(strcmp(msg, "kill") == 0)
|
|
||||||
return ptrace(PTRACE_KILL, pid, 0, 0);
|
t = findthread(pid);
|
||||||
if(strcmp(msg, "startstop") == 0){
|
if(t == nil) {
|
||||||
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
|
werrstr("not attached to pid %d", pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(t->state == Exited) {
|
||||||
|
werrstr("pid %d has exited", pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(t->state == Killed) {
|
||||||
|
werrstr("pid %d has been killed", pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(msg, "kill") == 0) {
|
||||||
|
if(ptrace(PTRACE_KILL, pid, 0, 0) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return waitstop(pid);
|
t->state = Killed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcmp(msg, "startstop") == 0){
|
||||||
|
if(ctlproc(pid, "start") < 0)
|
||||||
|
return -1;
|
||||||
|
return waitstop(t);
|
||||||
}
|
}
|
||||||
if(strcmp(msg, "sysstop") == 0){
|
if(strcmp(msg, "sysstop") == 0){
|
||||||
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
|
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return waitstop(pid);
|
t->state = Running;
|
||||||
|
return waitstop(t);
|
||||||
}
|
}
|
||||||
if(strcmp(msg, "stop") == 0){
|
if(strcmp(msg, "stop") == 0){
|
||||||
|
if(trace > 1)
|
||||||
|
fprint(2, "tid %d: tkill stop\n", pid);
|
||||||
|
if(t->state == Stopped)
|
||||||
|
return 0;
|
||||||
if(syscall(__NR_tkill, pid, SIGSTOP) < 0)
|
if(syscall(__NR_tkill, pid, SIGSTOP) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return waitstop(pid);
|
return waitstop(t);
|
||||||
}
|
}
|
||||||
if(strcmp(msg, "step") == 0){
|
if(strcmp(msg, "step") == 0){
|
||||||
|
if(t->state == Running) {
|
||||||
|
werrstr("cannot single-step unstopped %d", pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
|
if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return waitstop(pid);
|
return waitstop(t);
|
||||||
|
}
|
||||||
|
if(strcmp(msg, "start") == 0) {
|
||||||
|
if(t->state == Running)
|
||||||
|
return 0;
|
||||||
|
data = 0;
|
||||||
|
if(t->state == Stopped && t->signal != SIGSTOP)
|
||||||
|
data = t->signal;
|
||||||
|
if(trace && data)
|
||||||
|
fprint(2, "tid %d: continue %lud\n", pid, (ulong)data);
|
||||||
|
if(ptrace(PTRACE_CONT, pid, 0, (void*)data) < 0)
|
||||||
|
return -1;
|
||||||
|
t->state = Running;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcmp(msg, "waitstop") == 0) {
|
||||||
|
return waitstop(t);
|
||||||
}
|
}
|
||||||
if(strcmp(msg, "waitstop") == 0)
|
|
||||||
return waitstop(pid);
|
|
||||||
if(strcmp(msg, "start") == 0)
|
|
||||||
return ptrace(PTRACE_CONT, pid, 0, 0);
|
|
||||||
werrstr("unknown control message '%s'", msg);
|
werrstr("unknown control message '%s'", msg);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -344,32 +712,6 @@ proctextfile(int pid)
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
procthreadpids(int pid, int **thread)
|
|
||||||
{
|
|
||||||
int i, fd, nd, *t, nt;
|
|
||||||
char buf[100];
|
|
||||||
Dir *d;
|
|
||||||
|
|
||||||
snprint(buf, sizeof buf, "/proc/%d/task", pid);
|
|
||||||
if((fd = open(buf, OREAD)) < 0)
|
|
||||||
return -1;
|
|
||||||
nd = dirreadall(fd, &d);
|
|
||||||
close(fd);
|
|
||||||
if(nd < 0)
|
|
||||||
return -1;
|
|
||||||
nt = 0;
|
|
||||||
for(i=0; i<nd; i++)
|
|
||||||
if(d[i].mode&DMDIR)
|
|
||||||
nt++;
|
|
||||||
t = malloc(nt*sizeof t[0]);
|
|
||||||
nt = 0;
|
|
||||||
for(i=0; i<nd; i++)
|
|
||||||
if(d[i].mode&DMDIR)
|
|
||||||
t[nt++] = atoi(d[i].name);
|
|
||||||
*thread = t;
|
|
||||||
return nt;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n)
|
ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n)
|
||||||
|
|
@ -544,19 +886,11 @@ ptraceerr:
|
||||||
char*
|
char*
|
||||||
procstatus(int pid)
|
procstatus(int pid)
|
||||||
{
|
{
|
||||||
int c;
|
LinuxThread *t;
|
||||||
|
|
||||||
c = procstate(pid, nil);
|
t = findthread(pid);
|
||||||
if(c < 0)
|
if(t == nil)
|
||||||
return "Dead";
|
return "???";
|
||||||
switch(c) {
|
|
||||||
case 'T':
|
return statestr[t->state];
|
||||||
return "Stopped";
|
|
||||||
case 'Z':
|
|
||||||
return "Zombie";
|
|
||||||
case 'R':
|
|
||||||
return "Running";
|
|
||||||
// TODO: translate more characters here
|
|
||||||
}
|
|
||||||
return "Running";
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue