xref: /plan9/sys/src/cmd/snap/take.c (revision 50e5f38d649a06ef8aef42696e09b6c4c5964957)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <mach.h>
5 #include "snap.h"
6 
7 /* research 16-bit crc.  good enough. */
8 static ulong
sumr(ulong sum,void * buf,int n)9 sumr(ulong sum, void *buf, int n)
10 {
11 	uchar *s, *send;
12 
13 	if(buf == 0)
14 		return sum;
15 	for(s=buf, send=s+n; s<send; s++)
16 		if(sum & 1)
17 			sum = 0xffff & ((sum>>1)+*s+0x8000);
18 		else
19 			sum = 0xffff & ((sum>>1)+*s);
20 	return sum;
21 }
22 
23 static int npage;
24 static Page *pgtab[1<<10];
25 
26 Page*
datapage(char * p,long len)27 datapage(char *p, long len)
28 {
29 	Page *pg;
30 	char *q, *ep;
31 	long	sum;
32 	int iszero;
33 
34 	if(len > Pagesize) {
35 		fprint(2, "datapage cannot handle pages > 1024\n");
36 		exits("datapage");
37 	}
38 
39 	sum = sumr(0, p, len) & (nelem(pgtab)-1);
40 	if(sum == 0) {
41 		iszero = 1;
42 		for(q=p, ep=p+len; q<ep; q++)
43 			if(*q != 0) {
44 				iszero = 0;
45 				break;
46 			}
47 	} else
48 		iszero = 0;
49 
50 	for(pg = pgtab[sum]; pg; pg=pg->link)
51 		if(pg->len == len && memcmp(pg->data, p, len) == 0)
52 			break;
53 	if(pg)
54 		return pg;
55 
56 	pg = emalloc(sizeof(*pg)+len);
57 	pg->data = (char*)&pg[1];
58 	pg->type = 0;
59 	pg->len = len;
60 	memmove(pg->data, p, len);
61 	pg->link = pgtab[sum];
62 	pgtab[sum] = pg;
63 	if(iszero) {
64 		pg->type = 'z';
65 		pg->written = 1;
66 	}
67 
68 	++npage;
69 	return pg;
70 }
71 
72 static Data*
readsection(long pid,char * sec)73 readsection(long pid, char *sec)
74 {
75 	char buf[8192];
76 	int n, fd;
77 	int hdr, tot;
78 	Data *d = nil;
79 
80 	snprint(buf, sizeof buf, "/proc/%ld/%s", pid, sec);
81 	if((fd = open(buf, OREAD)) < 0)
82 		return nil;
83 
84 	tot = 0;
85 	hdr = (int)((Data*)0)->data;
86 	while((n = read(fd, buf, sizeof buf)) > 0) {
87 		d = erealloc(d, tot+n+hdr);
88 		memmove(d->data+tot, buf, n);
89 		tot += n;
90 	}
91 	close(fd);
92 	if(d == nil)
93 		return nil;
94 	d->len = tot;
95 	return d;
96 }
97 
98 static Seg*
readseg(int fd,vlong off,ulong len,char * name)99 readseg(int fd, vlong off, ulong len, char *name)
100 {
101 	char buf[Pagesize];
102 	Page **pg;
103 	int npg;
104 	Seg *s;
105 	ulong i;
106 	int n;
107 
108 	s = emalloc(sizeof(*s));
109 	s->name = estrdup(name);
110 
111 	if(seek(fd, off, 0) < 0) {
112 		fprint(2, "seek fails\n");
113 		goto Die;
114 	}
115 
116 	pg = nil;
117 	npg = 0;
118 	for(i=0; i<len; ) {
119 		n = Pagesize;
120 		if(n > len-i)
121 			n = len-i;
122 		if((n = readn(fd, buf, n)) <= 0)
123 			break;
124 		pg = erealloc(pg, sizeof(*pg)*(npg+1));
125 		pg[npg++] = datapage(buf, n);
126 		i += n;
127 		if(n != Pagesize)	/* any short read, planned or otherwise */
128 			break;
129 	}
130 
131 	if(i==0 && len!=0)
132 		goto Die;
133 
134 	s->offset = off;
135 	s->len = i;
136 	s->pg = pg;
137 	s->npg = npg;
138 	return s;
139 
140 Die:
141 	free(s->name);
142 	free(s);
143 	return nil;
144 }
145 
146 /* discover the stack pointer of the given process */
147 ulong
stackptr(Proc * proc,int fd)148 stackptr(Proc *proc, int fd)
149 {
150 	char *q;
151 	Fhdr f;
152 	Reglist *r;
153 	long textoff;
154 	int i;
155 	Data *dreg;
156 
157 	textoff = -1;
158 	for(i=0; i<proc->nseg; i++)
159 		if(proc->seg[i] && strcmp(proc->seg[i]->name, "Text") == 0)
160 			textoff = proc->seg[i]->offset;
161 
162 	if(textoff == -1)
163 		return 0;
164 
165 	seek(fd, textoff, 0);
166 	if(crackhdr(fd, &f) == 0)
167 		return 0;
168 
169 	machbytype(f.type);
170 	for(r=mach->reglist; r->rname; r++)
171 		if(strcmp(r->rname, mach->sp) == 0)
172 			break;
173 	if(r == nil) {
174 		fprint(2, "couldn't find stack pointer register?\n");
175 		return 0;
176 	}
177 
178 	if((dreg = proc->d[Pregs]) == nil)
179 		return 0;
180 
181 	if(r->roffs+mach->szreg > dreg->len) {
182 		fprint(2, "SP register too far into registers?\n");
183 		return 0;
184 	}
185 
186 	q = dreg->data+r->roffs;
187 	switch(mach->szreg) {
188 	case 2:	return machdata->swab(*(ushort*)q);
189 	case 4:	return machdata->swal(*(ulong*)q);
190 	case 8:	return machdata->swav(*(uvlong*)q);
191 	default:
192 		fprint(2, "register size is %d bytes?\n", mach->szreg);
193 		return 0;
194 	}
195 }
196 
197 Proc*
snap(long pid,int usetext)198 snap(long pid, int usetext)
199 {
200 	Data *d;
201 	Proc *proc;
202 	Seg **s;
203 	char *name, *segdat, *q, *f[128+1], buf[128];
204 	int fd, i, stacki, nf, np;
205 	uvlong off, len, stackoff, stacklen;
206 	uvlong sp;
207 
208 	proc = emalloc(sizeof(*proc));
209 	proc->pid = pid;
210 
211 	np = 0;
212 	for(i=0; i<Npfile; i++) {
213 		if(proc->d[i] = readsection(pid, pfile[i]))
214 			np++;
215 		else
216 			fprint(2, "warning: can't include /proc/%ld/%s\n", pid, pfile[i]);
217 	}
218 	if(np == 0)
219 		return nil;
220 
221 	if(usetext) {
222 		snprint(buf, sizeof buf, "/proc/%ld/text", pid);
223 		if((fd = open(buf, OREAD)) >= 0) {
224 			werrstr("");
225 			if((proc->text = readseg(fd, 0, 1<<31, "textfile")) == nil)
226 				fprint(2, "warning: can't include %s: %r\n", buf);
227 			close(fd);
228 		} else
229 			fprint(2, "warning: can't include /proc/%ld/text\n", pid);
230 	}
231 
232 	if((d=proc->d[Psegment]) == nil) {
233 		fprint(2, "warning: no segment table, no memory image\n");
234 		return proc;
235 	}
236 
237 	segdat = emalloc(d->len+1);
238 	memmove(segdat, d->data, d->len);
239 	segdat[d->len] = 0;
240 
241 	nf = getfields(segdat, f, nelem(f), 1, "\n");
242 	if(nf == nelem(f)) {
243 		nf--;
244 		fprint(2, "process %ld has >%d segments; only using first %d\n",
245 			pid, nf, nf);
246 	}
247 	if(nf <= 0) {
248 		fprint(2, "warning: couldn't understand segment table, no memory image\n");
249 		free(segdat);
250 		return proc;
251 	}
252 
253 	snprint(buf, sizeof buf, "/proc/%ld/mem", pid);
254 	if((fd = open(buf, OREAD)) < 0) {
255 		fprint(2, "warning: can't include /proc/%ld/mem\n", pid);
256 		return proc;
257 	}
258 
259 	s = emalloc(nf*sizeof(*s));
260 	stacklen = 0;
261 	stackoff = 0;
262 	stacki = 0;
263 	for(i=0; i<nf; i++) {
264 		if(q = strchr(f[i], ' '))
265 			*q = 0;
266 		name = f[i];
267 		off = strtoull(name+10, &q, 16);
268 		len = strtoull(q, &q, 16) - off;
269 		if(strcmp(name, "Stack") == 0) {
270 			stackoff = off;
271 			stacklen = len;
272 			stacki = i;
273 		} else
274 			s[i] = readseg(fd, off, len, name);
275 	}
276 	proc->nseg = nf;
277 	proc->seg = s;
278 
279 	/* stack hack: figure sp so don't need to page in the whole segment */
280 	if(stacklen) {
281 		sp = stackptr(proc, fd);
282 		if(stackoff <= sp && sp < stackoff+stacklen) {
283 			off = (sp - Pagesize) & ~(Pagesize - 1);
284 			if(off < stackoff)
285 				off = stackoff;
286 			len = stacklen - (off - stackoff);
287 		} else {	/* stack pointer not in segment.  thread library? */
288 			off = stackoff + stacklen - 16*1024;
289 			len = 16*1024;
290 		}
291 		s[stacki] = readseg(fd, off, len, "Stack");
292 	}
293 
294 	return proc;
295 }
296