summaryrefslogtreecommitdiff
path: root/sys/src/cmd/snap
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/snap
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/snap')
-rwxr-xr-xsys/src/cmd/snap/mkfile22
-rwxr-xr-xsys/src/cmd/snap/read.c246
-rwxr-xr-xsys/src/cmd/snap/snap.c65
-rwxr-xr-xsys/src/cmd/snap/snap.h66
-rwxr-xr-xsys/src/cmd/snap/snapfs.c191
-rwxr-xr-xsys/src/cmd/snap/take.c295
-rwxr-xr-xsys/src/cmd/snap/util.c39
-rwxr-xr-xsys/src/cmd/snap/write.c82
8 files changed, 1006 insertions, 0 deletions
diff --git a/sys/src/cmd/snap/mkfile b/sys/src/cmd/snap/mkfile
new file mode 100755
index 000000000..78835b215
--- /dev/null
+++ b/sys/src/cmd/snap/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+BIN=/$objtype/bin
+
+TARG=snap snapfs
+OFILES=\
+ read.$O\
+ take.$O\
+ util.$O\
+ write.$O\
+
+HFILES=snap.h
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+ ${TARG:%=%.c}\
+ ${TARG:%=/386/bin/%}\
+
+LIB=/$objtype/lib/lib9p.a
+
+</sys/src/cmd/mkmany
diff --git a/sys/src/cmd/snap/read.c b/sys/src/cmd/snap/read.c
new file mode 100755
index 000000000..961781807
--- /dev/null
+++ b/sys/src/cmd/snap/read.c
@@ -0,0 +1,246 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "snap.h"
+
+void
+panic(char *s)
+{
+ fprint(2, "%s\n", s);
+ abort();
+ exits(s);
+}
+
+static Proc*
+findpid(Proc *plist, long pid)
+{
+ while(plist) {
+ if(plist->pid == pid)
+ break;
+ plist = plist->link;
+ }
+ return plist;
+}
+
+Page*
+findpage(Proc *plist, long pid, int type, uvlong off)
+{
+ Seg *s;
+ int i;
+
+ plist = findpid(plist, pid);
+ if(plist == nil)
+ panic("can't find referenced pid");
+
+ if(type == 't') {
+ if(off%Pagesize)
+ panic("bad text offset alignment");
+ s = plist->text;
+ if(off >= s->len)
+ return nil;
+ return s->pg[off/Pagesize];
+ }
+
+ s = nil;
+ for(i=0; i<plist->nseg; i++) {
+ s = plist->seg[i];
+ if(s && s->offset <= off && off < s->offset+s->len)
+ break;
+ s = nil;
+ }
+ if(s == nil)
+ return nil;
+
+ off -= s->offset;
+ if(off%Pagesize)
+ panic("bad mem offset alignment");
+
+ return s->pg[off/Pagesize];
+}
+
+static int
+Breadnumber(Biobuf *b, char *buf)
+{
+ int i;
+ int c;
+ int havedigits;
+
+ havedigits = 0;
+ for(i=0; i<22; i++){
+ if((c = Bgetc(b)) == Beof)
+ return -1;
+ if('0' <= c && c <= '9'){
+ *buf++ = c;
+ havedigits = 1;
+ }else if(c == ' '){
+ if(havedigits){
+ while((c = Bgetc(b)) == ' ')
+ ;
+ if(c != Beof)
+ Bungetc(b);
+ break;
+ }
+ }else{
+ werrstr("bad character %.2ux", c);
+ return -1;
+ }
+ }
+ *buf = 0;
+ return 0;
+}
+
+static int
+Breadulong(Biobuf *b, ulong *x)
+{
+ char buf[32];
+
+ if(Breadnumber(b, buf) < 0)
+ return -1;
+ *x = strtoul(buf, 0, 0);
+ return 0;
+}
+
+static int
+Breaduvlong(Biobuf *b, uvlong *x)
+{
+ char buf[32];
+
+ if(Breadnumber(b, buf) < 0)
+ return -1;
+ *x = strtoull(buf, 0, 0);
+ return 0;
+}
+
+static Data*
+readdata(Biobuf *b)
+{
+ Data *d;
+ char str[32];
+ long len;
+
+ if(Bread(b, str, 12) != 12)
+ panic("can't read data hdr\n");
+
+ len = atoi(str);
+ d = emalloc(sizeof(*d) + len);
+ if(Bread(b, d->data, len) != len)
+ panic("can't read data body\n");
+ d->len = len;
+ return d;
+}
+
+static Seg*
+readseg(Seg **ps, Biobuf *b, Proc *plist)
+{
+ Seg *s;
+ Page **pp;
+ int i, npg;
+ int t;
+ int n, len;
+ ulong pid;
+ uvlong off;
+ char buf[Pagesize];
+ static char zero[Pagesize];
+
+ s = emalloc(sizeof *s);
+ if(Breaduvlong(b, &s->offset) < 0
+ || Breaduvlong(b, &s->len) < 0)
+ panic("error reading segment");
+
+ npg = (s->len + Pagesize-1)/Pagesize;
+ s->npg = npg;
+
+ if(s->npg == 0)
+ return s;
+
+ pp = emalloc(sizeof(*pp)*npg);
+ s->pg = pp;
+ *ps = s;
+
+ len = Pagesize;
+ for(i=0; i<npg; i++) {
+ if(i == npg-1)
+ len = s->len - i*Pagesize;
+
+ switch(t = Bgetc(b)) {
+ case 'z':
+ pp[i] = datapage(zero, len);
+ if(debug)
+ fprint(2, "0x%.8llux all zeros\n", s->offset+i*Pagesize);
+ break;
+ case 'm':
+ case 't':
+ if(Breadulong(b, &pid) < 0
+ || Breaduvlong(b, &off) < 0)
+ panic("error reading segment x");
+ pp[i] = findpage(plist, pid, t, off);
+ if(pp[i] == nil)
+ panic("bad page reference in snapshot");
+ if(debug)
+ fprint(2, "0x%.8llux same as %s pid %lud 0x%.8llux\n", s->offset+i*Pagesize, t=='m'?"mem":"text", pid, off);
+ break;
+ case 'r':
+ if((n=Bread(b, buf, len)) != len)
+ sysfatal("short read of segment %d/%d at %llx: %r", n, len, Boffset(b));
+ pp[i] = datapage(buf, len);
+ if(debug)
+ fprint(2, "0x%.8llux is raw data\n", s->offset+i*Pagesize);
+ break;
+ default:
+ fprint(2, "bad type char %#.2ux\n", t);
+ panic("error reading segment");
+ }
+ }
+ return s;
+}
+
+Proc*
+readsnap(Biobuf *b)
+{
+ char *q;
+ char buf[12];
+ long pid;
+ Proc *p, *plist;
+ int i, n;
+
+ if((q = Brdline(b, '\n')) == nil)
+ panic("error reading snapshot file");
+ if(strncmp(q, "process snapshot", strlen("process snapshot")) != 0)
+ panic("bad snapshot file format");
+
+ plist = nil;
+ while(q = Brdline(b, '\n')) {
+ q[Blinelen(b)-1] = 0;
+ pid = atol(q);
+ q += 12;
+ p = findpid(plist, pid);
+ if(p == nil) {
+ p = emalloc(sizeof(*p));
+ p->link = plist;
+ p->pid = pid;
+ plist = p;
+ }
+
+ for(i=0; i<Npfile; i++) {
+ if(strcmp(pfile[i], q) == 0) {
+ p->d[i] = readdata(b);
+ break;
+ }
+ }
+ if(i != Npfile)
+ continue;
+ if(strcmp(q, "mem") == 0) {
+ if(Bread(b, buf, 12) != 12)
+ panic("can't read memory section");
+ n = atoi(buf);
+ p->nseg = n;
+ p->seg = emalloc(n*sizeof(*p->seg));
+ for(i=0; i<n; i++)
+ readseg(&p->seg[i], b, plist);
+ } else if(strcmp(q, "text") == 0)
+ readseg(&p->text, b, plist);
+ else
+ panic("unknown section");
+ }
+ return plist;
+}
diff --git a/sys/src/cmd/snap/snap.c b/sys/src/cmd/snap/snap.c
new file mode 100755
index 000000000..16912be34
--- /dev/null
+++ b/sys/src/cmd/snap/snap.c
@@ -0,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "snap.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-o snapfile] pid...\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *user, *sys, *arch, *term, *ofile;
+ int i;
+ long pid, me;
+ Biobuf *b;
+ Dir *d;
+ Proc *p;
+
+ ofile = "/fd/1";
+ ARGBEGIN{
+ case 'o':
+ ofile = ARGF();
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc < 1)
+ usage();
+
+ /* get kernel compilation time */
+ if((d = dirstat("#/")) == nil) {
+ fprint(2, "cannot stat #/ ???\n");
+ exits("stat");
+ }
+
+ if((b = Bopen(ofile, OWRITE)) == nil) {
+ fprint(2, "cannot write to \"%s\"\n", ofile);
+ exits("Bopen");
+ }
+
+ if((user = getuser()) == nil)
+ user = "gre";
+ if((sys = sysname()) == nil)
+ sys = "gnot";
+ if((arch = getenv("cputype")) == nil)
+ arch = "unknown";
+ if((term = getenv("terminal")) == nil)
+ term = "unknown terminal type";
+
+ Bprint(b, "process snapshot %ld %s@%s %s %ld \"%s\"\n",
+ time(0), user, sys, arch, d->mtime, term);
+ me = getpid();
+ for(i=0; i<argc; i++) {
+ if((pid = atol(argv[i])) == me)
+ fprint(2, "warning: will not snapshot self\n");
+ else if(p = snap(pid, 1))
+ writesnap(b, p);
+ }
+ exits(0);
+}
diff --git a/sys/src/cmd/snap/snap.h b/sys/src/cmd/snap/snap.h
new file mode 100755
index 000000000..cc50b8a38
--- /dev/null
+++ b/sys/src/cmd/snap/snap.h
@@ -0,0 +1,66 @@
+typedef struct Data Data;
+typedef struct Page Page;
+typedef struct Proc Proc;
+typedef struct Seg Seg;
+
+enum {
+ Psegment = 0,
+ Pfd,
+ Pfpregs,
+ Pkregs,
+ Pnoteid,
+ Pns,
+ Pproc,
+ Pregs,
+ Pstatus,
+ Npfile,
+
+ Pagesize = 1024, /* need not relate to kernel */
+};
+
+struct Data {
+ ulong len;
+ char data[1];
+};
+
+struct Seg {
+ char* name;
+ uvlong offset;
+ uvlong len;
+ Page** pg;
+ int npg;
+};
+
+struct Page {
+ Page* link;
+ ulong len;
+ char* data;
+
+ /* when page is written, these hold the ptr to it */
+ int written;
+ int type;
+ ulong pid;
+ uvlong offset;
+};
+
+struct Proc {
+ Proc *link;
+ long pid;
+ Data* d[Npfile];
+ Seg** seg; /* memory segments */
+ int nseg;
+ Seg* text; /* text file */
+};
+
+extern char *pfile[Npfile];
+
+Proc* snap(long pid, int usetext);
+void* emalloc(ulong);
+void* erealloc(void*, ulong);
+char* estrdup(char*);
+void writesnap(Biobuf*, Proc*);
+Page* datapage(char *p, long len);
+Proc* readsnap(Biobuf *b);
+Page* findpage(Proc *plist, long pid, int type, uvlong off);
+
+int debug;
diff --git a/sys/src/cmd/snap/snapfs.c b/sys/src/cmd/snap/snapfs.c
new file mode 100755
index 000000000..d27305ae5
--- /dev/null
+++ b/sys/src/cmd/snap/snapfs.c
@@ -0,0 +1,191 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "snap.h"
+
+typedef struct PD PD;
+struct PD {
+ int isproc;
+ union {
+ Proc *p;
+ Data *d;
+ };
+};
+
+PD*
+PDProc(Proc *p)
+{
+ PD *pd;
+
+ pd = emalloc(sizeof(*pd));
+ pd->isproc = 1;
+ pd->p = p;
+ return pd;
+}
+
+PD*
+PDData(Data *d)
+{
+ PD *pd;
+
+ pd = emalloc(sizeof(*pd));
+ pd->isproc = 0;
+ pd->d = d;
+ return pd;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: snapfs [-a] [-m mtpt] file\n");
+ exits("usage");
+}
+
+char*
+memread(Proc *p, File *f, void *buf, long *count, vlong offset)
+{
+ Page *pg;
+ int po;
+
+ po = offset%Pagesize;
+ if(!(pg = findpage(p, p->pid, f->name[0], offset-po)))
+ return "address not mapped";
+
+ if(*count > Pagesize-po)
+ *count = Pagesize-po;
+
+ memmove(buf, pg->data+po, *count);
+ return nil;
+}
+
+char*
+dataread(Data *d, void *buf, long *count, vlong offset)
+{
+ assert(d != nil);
+
+ if(offset >= d->len) {
+ *count = 0;
+ return nil;
+ }
+
+ if(offset+*count >= d->len)
+ *count = d->len - offset;
+
+ memmove(buf, d->data+offset, *count);
+ return nil;
+}
+
+void
+fsread(Req *r)
+{
+ char *e;
+ PD *pd;
+ Fid *fid;
+ void *data;
+ vlong offset;
+ long count;
+
+ fid = r->fid;
+ data = r->ofcall.data;
+ offset = r->ifcall.offset;
+ count = r->ifcall.count;
+ pd = fid->file->aux;
+
+ if(pd->isproc)
+ e = memread(pd->p, fid->file, data, &count, offset);
+ else
+ e = dataread(pd->d, data, &count, offset);
+
+ if(e == nil)
+ r->ofcall.count = count;
+ respond(r, e);
+}
+
+Srv fs = {
+ .read = fsread,
+};
+
+File*
+ecreatefile(File *a, char *b, char *c, ulong d, void *e)
+{
+ File *f;
+
+ f = createfile(a, b, c, d, e);
+ if(f == nil)
+ sysfatal("error creating snap tree: %r");
+ return f;
+}
+
+void
+main(int argc, char **argv)
+{
+ Biobuf *b;
+ Data *d;
+ File *fdir, *f;
+ Proc *p, *plist;
+ Tree *tree;
+ char *mtpt, buf[32];
+ int i, mflag;
+
+ mtpt = "/proc";
+ mflag = MBEFORE;
+
+ ARGBEGIN{
+ case 'D':
+ chatty9p++;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'a':
+ mflag = MAFTER;
+ break;
+ case 'm':
+ mtpt = ARGF();
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+
+ b = Bopen(argv[0], OREAD);
+ if(b == nil) {
+ fprint(2, "cannot open \"%s\": %r\n", argv[0]);
+ exits("Bopen");
+ }
+
+ if((plist = readsnap(b)) == nil) {
+ fprint(2, "readsnap fails\n");
+ exits("readsnap");
+ }
+
+ tree = alloctree(nil, nil, DMDIR|0555, nil);
+ fs.tree = tree;
+
+ for(p=plist; p; p=p->link) {
+ print("process %ld %.*s\n", p->pid, 28, p->d[Pstatus] ? p->d[Pstatus]->data : "");
+
+ snprint(buf, sizeof buf, "%ld", p->pid);
+ fdir = ecreatefile(tree->root, buf, nil, DMDIR|0555, nil);
+ ecreatefile(fdir, "ctl", nil, 0777, nil);
+ if(p->text)
+ ecreatefile(fdir, "text", nil, 0777, PDProc(p));
+
+ ecreatefile(fdir, "mem", nil, 0666, PDProc(p));
+ for(i=0; i<Npfile; i++) {
+ if(d = p->d[i]) {
+ f = ecreatefile(fdir, pfile[i], nil, 0666, PDData(d));
+ f->length = d->len;
+ }
+ }
+ }
+
+ postmountsrv(&fs, nil, mtpt, mflag);
+ exits(0);
+}
diff --git a/sys/src/cmd/snap/take.c b/sys/src/cmd/snap/take.c
new file mode 100755
index 000000000..bea32bf2a
--- /dev/null
+++ b/sys/src/cmd/snap/take.c
@@ -0,0 +1,295 @@
+#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) & (nelem(pgtab)-1);
+ if(sum == 0) {
+ iszero = 1;
+ for(q=p, ep=p+len; q<ep; q++)
+ if(*q != 0) {
+ iszero = 0;
+ break;
+ }
+ } else
+ iszero = 0;
+
+ 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;
+}
diff --git a/sys/src/cmd/snap/util.c b/sys/src/cmd/snap/util.c
new file mode 100755
index 000000000..bfca991ed
--- /dev/null
+++ b/sys/src/cmd/snap/util.c
@@ -0,0 +1,39 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "snap.h"
+
+void*
+emalloc(ulong n)
+{
+ void *v;
+ v = malloc(n);
+ if(v == nil){
+ fprint(2, "out of memory\n");
+ exits("memory");
+ }
+ memset(v, 0, n);
+ return v;
+}
+
+void*
+erealloc(void *v, ulong n)
+{
+ v = realloc(v, n);
+ if(v == nil) {
+ fprint(2, "out of memory\n");
+ exits("memory");
+ }
+ return v;
+}
+
+char*
+estrdup(char *s)
+{
+ s = strdup(s);
+ if(s == nil) {
+ fprint(2, "out of memory\n");
+ exits("memory");
+ }
+ return s;
+}
diff --git a/sys/src/cmd/snap/write.c b/sys/src/cmd/snap/write.c
new file mode 100755
index 000000000..2cbb1e5c0
--- /dev/null
+++ b/sys/src/cmd/snap/write.c
@@ -0,0 +1,82 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "snap.h"
+
+char *pfile[Npfile] = {
+ [Psegment] "segment",
+ [Pfd] "fd",
+ [Pfpregs] "fpregs",
+ [Pnoteid] "noteid",
+ [Pkregs] "kregs",
+ [Pns] "ns",
+ [Pproc] "proc",
+ [Pregs] "regs",
+ [Pstatus] "status",
+};
+
+static void
+writeseg(Biobuf *b, Proc *proc, Seg *s)
+{
+ int i, npg;
+ Page **pp, *p;
+ int type;
+
+ if(s == nil){
+ Bprint(b, "%-11ud %-11ud ", 0, 0);
+ return;
+ }
+
+ type = proc->text == s ? 't' : 'm';
+ npg = (s->len+Pagesize-1)/Pagesize;
+ if(npg != s->npg)
+ abort();
+
+ Bprint(b, "%-11llud %-11llud ", s->offset, s->len);
+ if(s->len == 0)
+ return;
+
+ for(i=0, pp=s->pg, p=*pp; i<npg; i++, pp++, p=*pp) {
+ if(p->written) {
+ if(p->type == 'z') {
+ Bprint(b, "z");
+ continue;
+ }
+ Bprint(b, "%c%-11ld %-11llud ", p->type, p->pid, p->offset);
+ } else {
+ Bprint(b, "r");
+ Bwrite(b, p->data, p->len);
+ if(p->len != Pagesize && i != npg-1)
+ abort();
+ p->written = 1;
+ p->type = type;
+ p->offset = s->offset + i*Pagesize;
+ p->pid = proc->pid;
+ }
+ }
+}
+
+
+void
+writesnap(Biobuf *b, Proc *p)
+{
+ int i, n;
+ Data *d;
+
+ for(i=0; i<Npfile; i++)
+ if(d = p->d[i]) {
+ Bprint(b, "%-11ld %s\n%-11lud ", p->pid, pfile[i], d->len);
+ Bwrite(b, d->data, d->len);
+ }
+
+ if(p->text) {
+ Bprint(b, "%-11ld text\n", p->pid);
+ writeseg(b, p, p->text);
+ }
+
+ if(n = p->nseg) {
+ Bprint(b, "%-11ld mem\n%-11d ", p->pid, n);
+ for(i=0; i<n; i++)
+ writeseg(b, p, p->seg[i]);
+ }
+}