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