1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 #include "netif.h"
8
9 #include <ptrace.h>
10
11
12 enum {
13 Qdir,
14 Qctl,
15 Qdata,
16 };
17
18 enum {
19 CMsize,
20 CMtrace,
21 };
22
23 static Dirtab tracedir[]=
24 {
25 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
26 "ptracectl", {Qctl}, 0, 0664,
27 "ptrace", {Qdata}, 0, 0440,
28 };
29
30 static Lock tlk;
31 static int topens;
32 static int tproduced, tconsumed, ntevents, ntmask;
33 static uchar *tevents;
34
35 static Chan*
traceattach(char * spec)36 traceattach(char *spec)
37 {
38 return devattach(L'σ', spec);
39 }
40
41 static Walkqid*
tracewalk(Chan * c,Chan * nc,char ** name,int nname)42 tracewalk(Chan *c, Chan *nc, char **name, int nname)
43 {
44 return devwalk(c, nc, name, nname, tracedir, nelem(tracedir), devgen);
45 }
46
47 static long
tracestat(Chan * c,uchar * db,long n)48 tracestat(Chan *c, uchar *db, long n)
49 {
50 return devstat(c, db, n, tracedir, nelem(tracedir), devgen);
51 }
52
53 static void
_ptrace(Proc * p,int etype,vlong ts,vlong arg)54 _ptrace(Proc *p, int etype, vlong ts, vlong arg)
55 {
56 int i;
57 uchar *te;
58
59 if (p->trace == 0 || topens == 0)
60 return;
61
62 lock(&tlk);
63 if (p->trace == 0 || topens == 0 || tproduced - tconsumed >= ntevents){
64 unlock(&tlk);
65 return;
66 }
67 if(ts == 0)
68 ts = todget(nil);
69 i = (tproduced&ntmask) * PTsize;
70 te = &tevents[i];
71 PBIT32(te, (int)p->pid);
72 te += BIT32SZ;
73 PBIT32(te, etype);
74 te += BIT32SZ;
75 PBIT32(te, m->machno);
76 te += BIT32SZ;
77 PBIT64(te, ts);
78 te += BIT64SZ;
79 PBIT64(te, arg);
80 tproduced++;
81 if(etype == SDead)
82 p->trace = 0;
83 unlock(&tlk);
84 }
85
86
87 static Chan*
traceopen(Chan * c,int omode)88 traceopen(Chan *c, int omode)
89 {
90 int q;
91
92 q = c->qid.path;
93 switch(q){
94 case Qdata:
95 lock(&tlk);
96 if (waserror()){
97 unlock(&tlk);
98 nexterror();
99 }
100 if(topens > 0)
101 error(Einuse);
102 if(ntevents == 0)
103 error("must set trace size first");
104 if(up->trace != 0)
105 error("a traced process can't open the trace device");
106 tproduced = tconsumed = 0;
107 proctrace = _ptrace;
108 topens++;
109 poperror();
110 unlock(&tlk);
111 break;
112 }
113 c = devopen(c, omode, tracedir, nelem(tracedir), devgen);
114 return c;
115 }
116
117 static void
traceclose(Chan * c)118 traceclose(Chan *c)
119 {
120 int q;
121
122 q = c->qid.path;
123 switch(q){
124 case Qdata:
125 if(c->flag & COPEN){
126 lock(&tlk);
127 topens--;
128 tproduced = tconsumed = 0;
129 proctrace = nil;
130 unlock(&tlk);
131 }
132 }
133 }
134
135 static long
traceread(Chan * c,void * a,long n,vlong offset)136 traceread(Chan *c, void *a, long n, vlong offset)
137 {
138 int q, xopens, xevents, xprod, xcons, navail, ne, i;
139 char *s, *e;
140
141 q = c->qid.path;
142 switch(q){
143 case Qdir:
144 return devdirread(c, a, n, tracedir, nelem(tracedir), devgen);
145 break;
146 case Qctl:
147 e = up->genbuf + sizeof up->genbuf;
148 lock(&tlk);
149 xopens = topens;
150 xevents = ntevents;
151 xprod = tproduced;
152 xcons = tconsumed;
153 unlock(&tlk);
154 s = seprint(up->genbuf, e, "opens %d\n", xopens);
155 s = seprint(s, e, "size %d\n", xevents);
156 s = seprint(s, e, "produced %d\n", xprod);
157 seprint(s, e, "consumed %d\n", xcons);
158 return readstr(offset, a, n, up->genbuf);
159 break;
160 case Qdata:
161 /* no locks! */
162 navail = tproduced - tconsumed;
163 if(navail <= 0)
164 return 0;
165 if(navail > n / PTsize)
166 navail = n / PTsize;
167 s = a;
168 e = a;
169 while(navail > 0) {
170 if((tconsumed & ntmask) + navail > ntevents)
171 ne = ntevents - (tconsumed & ntmask);
172 else
173 ne = navail;
174 i = ne * PTsize;
175 memmove(e, &tevents[(tconsumed & ntmask)*PTsize], i);
176
177 tconsumed += ne;
178 e += i;
179 navail -= ne;
180 }
181 return e - s;
182 break;
183 default:
184 error(Egreg);
185 }
186 error("not yet implemented");
187 return -1;
188 }
189
190 static Cmdtab proccmd[] =
191 {
192 CMsize, "size", 2,
193 CMtrace, "trace", 0,
194 };
195
196 static long
tracewrite(Chan * c,void * a,long n,vlong)197 tracewrite(Chan *c, void *a, long n, vlong)
198 {
199 int i, q, sz, msk, pid;
200 Cmdbuf *cb;
201 Cmdtab *ct;
202 uchar *nt;
203 Proc *p;
204
205 q = c->qid.path;
206 switch(q){
207 case Qctl:
208 break;
209 default:
210 error(Egreg);
211 }
212 cb = parsecmd(a, n);
213 if(waserror()){
214 free(cb);
215 nexterror();
216 }
217 ct = lookupcmd(cb, proccmd, nelem(proccmd));
218
219 switch(ct->index){
220 case CMsize:
221 sz = atoi(cb->f[1]);
222 if(sz == 0){
223 lock(&tlk);
224 ntevents = 0;
225 ntmask = 0;
226 unlock(&tlk);
227 }
228 msk = sz-1;
229 if((sz&msk) != 0)
230 error("wrong size. use a power of two.");
231 if(sz > 512*1024)
232 error("size is too large");
233 lock(&tlk);
234 if(waserror()){
235 unlock(&tlk);
236 nexterror();
237 }
238 if(sz > ntevents){
239 nt = realloc(tevents, PTsize * sz);
240 if(nt == nil)
241 error("not enough memory");
242 tevents = nt;
243 ntevents = sz;
244 ntmask = msk;
245 }
246 tproduced = 0;
247 tconsumed = 0;
248 poperror();
249 unlock(&tlk);
250 break;
251 case CMtrace:
252 if(cb->nf != 2 && cb->nf != 3)
253 error(Ebadctl);
254 pid = atoi(cb->f[1]);
255 i = psindex(pid);
256 if(i < 0)
257 error(Eprocdied);
258 p = psincref(i);
259 if(p == nil)
260 error(Eprocdied);
261 qlock(&p->debug);
262 if(waserror()){
263 qunlock(&p->debug);
264 psdecref(p);
265 nexterror();
266 }
267 if(p->pid != pid)
268 error(Eprocdied);
269 if(cb->nf == 2)
270 p->trace ^= p->trace;
271 else
272 p->trace = (atoi(cb->f[2]) != 0);
273 poperror();
274 qunlock(&p->debug);
275 psdecref(p);
276 break;
277 }
278
279 poperror();
280 free(cb);
281 return n;
282 }
283
284 Dev ptracedevtab = {
285 L'σ',
286 "ptrace",
287 devreset,
288 devinit,
289 devshutdown,
290 traceattach,
291 tracewalk,
292 tracestat,
293 traceopen,
294 devcreate,
295 traceclose,
296 traceread,
297 devbread,
298 tracewrite,
299 devbwrite,
300 devremove,
301 devwstat,
302 };
303