#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include "snap.h"
/* research 16-bit crc. good enough. */
static ulong
sumr(ulong sum, void *buf, int n)
{
uchar *s, *send;
if(buf == 0)
return sum;
for(s=buf, send=s+n; s<send; s++)
if(sum & 1)
sum = 0xffff & ((sum>>1)+*s+0x8000);
else
sum = 0xffff & ((sum>>1)+*s);
return sum;
}
static int npage;
static Page *pgtab[1<<10];
Page*
datapage(char *p, long len)
{
Page *pg;
char *q, *ep;
long sum;
int iszero;
if(len > Pagesize) {
fprint(2, "datapage cannot handle pages > 1024\n");
exits("datapage");
}
sum = sumr(0, p, len);
if(sum == 0) {
iszero = 1;
for(q=p, ep=p+len; q<ep; q++)
if(*q != 0) {
iszero = 0;
break;
}
} else
iszero = 0;
sum &= nelem(pgtab)-1;
for(pg = pgtab[sum]; pg; pg=pg->link)
if(pg->len == len && memcmp(pg->data, p, len) == 0)
break;
if(pg)
return pg;
pg = emalloc(sizeof(*pg)+len);
pg->data = (char*)&pg[1];
pg->type = 0;
pg->len = len;
memmove(pg->data, p, len);
pg->link = pgtab[sum];
pgtab[sum] = pg;
if(iszero) {
pg->type = 'z';
pg->written = 1;
}
++npage;
return pg;
}
static Data*
readsection(long pid, char *sec)
{
char buf[8192];
int n, fd;
int hdr, tot;
Data *d = nil;
snprint(buf, sizeof buf, "/proc/%ld/%s", pid, sec);
if((fd = open(buf, OREAD)) < 0)
return nil;
tot = 0;
hdr = (int)((Data*)0)->data;
while((n = read(fd, buf, sizeof buf)) > 0) {
d = erealloc(d, tot+n+hdr);
memmove(d->data+tot, buf, n);
tot += n;
}
close(fd);
if(d == nil)
return nil;
d->len = tot;
return d;
}
static Seg*
readseg(int fd, vlong off, ulong len, char *name)
{
char buf[Pagesize];
Page **pg;
int npg;
Seg *s;
ulong i;
int n;
s = emalloc(sizeof(*s));
s->name = estrdup(name);
if(seek(fd, off, 0) < 0) {
fprint(2, "seek fails\n");
goto Die;
}
pg = nil;
npg = 0;
for(i=0; i<len; ) {
n = Pagesize;
if(n > len-i)
n = len-i;
if((n = readn(fd, buf, n)) <= 0)
break;
pg = erealloc(pg, sizeof(*pg)*(npg+1));
pg[npg++] = datapage(buf, n);
i += n;
if(n != Pagesize) /* any short read, planned or otherwise */
break;
}
if(i==0 && len!=0)
goto Die;
s->offset = off;
s->len = i;
s->pg = pg;
s->npg = npg;
return s;
Die:
free(s->name);
free(s);
return nil;
}
/* discover the stack pointer of the given process */
ulong
stackptr(Proc *proc, int fd)
{
char *q;
Fhdr f;
Reglist *r;
long textoff;
int i;
Data *dreg;
textoff = -1;
for(i=0; i<proc->nseg; i++)
if(proc->seg[i] && strcmp(proc->seg[i]->name, "Text") == 0)
textoff = proc->seg[i]->offset;
if(textoff == -1)
return 0;
seek(fd, textoff, 0);
if(crackhdr(fd, &f) == 0)
return 0;
machbytype(f.type);
for(r=mach->reglist; r->rname; r++)
if(strcmp(r->rname, mach->sp) == 0)
break;
if(r == nil) {
fprint(2, "couldn't find stack pointer register?\n");
return 0;
}
if((dreg = proc->d[Pregs]) == nil)
return 0;
if(r->roffs+mach->szreg > dreg->len) {
fprint(2, "SP register too far into registers?\n");
return 0;
}
q = dreg->data+r->roffs;
switch(mach->szreg) {
case 2: return machdata->swab(*(ushort*)q);
case 4: return machdata->swal(*(ulong*)q);
case 8: return machdata->swav(*(uvlong*)q);
default:
fprint(2, "register size is %d bytes?\n", mach->szreg);
return 0;
}
}
Proc*
snap(long pid, int usetext)
{
Data *d;
Proc *proc;
Seg **s;
char *name, *segdat, *q, *f[128+1], buf[128];
int fd, i, stacki, nf, np;
uvlong off, len, stackoff, stacklen;
uvlong sp;
proc = emalloc(sizeof(*proc));
proc->pid = pid;
np = 0;
for(i=0; i<Npfile; i++) {
if(proc->d[i] = readsection(pid, pfile[i]))
np++;
else
fprint(2, "warning: can't include /proc/%ld/%s\n", pid, pfile[i]);
}
if(np == 0)
return nil;
if(usetext) {
snprint(buf, sizeof buf, "/proc/%ld/text", pid);
if((fd = open(buf, OREAD)) >= 0) {
werrstr("");
if((proc->text = readseg(fd, 0, 1<<31, "textfile")) == nil)
fprint(2, "warning: can't include %s: %r\n", buf);
close(fd);
} else
fprint(2, "warning: can't include /proc/%ld/text\n", pid);
}
if((d=proc->d[Psegment]) == nil) {
fprint(2, "warning: no segment table, no memory image\n");
return proc;
}
segdat = emalloc(d->len+1);
memmove(segdat, d->data, d->len);
segdat[d->len] = 0;
nf = getfields(segdat, f, nelem(f), 1, "\n");
if(nf == nelem(f)) {
nf--;
fprint(2, "process %ld has >%d segments; only using first %d\n",
pid, nf, nf);
}
if(nf <= 0) {
fprint(2, "warning: couldn't understand segment table, no memory image\n");
free(segdat);
return proc;
}
snprint(buf, sizeof buf, "/proc/%ld/mem", pid);
if((fd = open(buf, OREAD)) < 0) {
fprint(2, "warning: can't include /proc/%ld/mem\n", pid);
return proc;
}
s = emalloc(nf*sizeof(*s));
stacklen = 0;
stackoff = 0;
stacki = 0;
for(i=0; i<nf; i++) {
if(q = strchr(f[i], ' '))
*q = 0;
name = f[i];
off = strtoull(name+10, &q, 16);
len = strtoull(q, &q, 16) - off;
if(strcmp(name, "Stack") == 0) {
stackoff = off;
stacklen = len;
stacki = i;
} else
s[i] = readseg(fd, off, len, name);
}
proc->nseg = nf;
proc->seg = s;
/* stack hack: figure sp so don't need to page in the whole segment */
if(stacklen) {
sp = stackptr(proc, fd);
if(stackoff <= sp && sp < stackoff+stacklen) {
off = (sp - Pagesize) & ~(Pagesize - 1);
if(off < stackoff)
off = stackoff;
len = stacklen - (off - stackoff);
} else { /* stack pointer not in segment. thread library? */
off = stackoff + stacklen - 16*1024;
len = 16*1024;
}
s[stacki] = readseg(fd, off, len, "Stack");
}
return proc;
}
|