xref: /plan9-contrib/sys/src/cmd/ptrace/st.c (revision af198995f3f1f1d87a386835b2bcd15ca90a6fed)
1 #include "all.h"
2 /*
3  * The main data structures are kept here.
4  *
5  * All events are converted into St (sched trace)
6  * structures and kept allocated at graph[]
7  * If input is from device, this array is sorted by time.
8  * Most other data structures rely on indexes on graph[].
9  *
10  * All events for a process are linked together using the
11  * St.pnext index.
12  *
13  * There is a Proc per process, with an array of indexes in graph[]
14  * for events that changed state. These are usually log of the
15  * size of the trace for the process.
16  *
17  * For browsing, each proc is noted with the row used and
18  * the first and last slots in Proc.state shown in the current view.
19  * This narrows the events to be considered for a plot.
20  *
21  * Starting with a Proc.state[i], all following events can
22  * be retrieved by following the St.pnext-linked list.
23  *
24  * As a further aid, the code plotting processes decorates
25  * St's with the x for its start point, so that we can scan
26  * a series of St's (or state[]s) and determine which one is
27  * at the mouse.
28  *
29  */
30 typedef struct Name Name;
31 struct Name
32 {
33 	int pid;
34 	char name[10];
35 };
36 
37 static int lno = 1;
38 static char *fname;
39 St **graph;
40 int ngraph;
41 
42 static Name *name;
43 static int nname;
44 
45 Proc *proc;
46 int nproc;
47 
48 static char *etname[Nevent] = {
49 	[SAdmit] =	"Admit",
50 	[SRelease] =	"Release",
51 	[SEdf] =	"Edf",
52 	[SRun] =	"Run",
53 	[SReady] =	"Ready",
54 	[SSleep] =	"Sleep",
55 	[SYield] =	"Yield",
56 	[SSlice] =	"Slice",
57 	[SDeadline] =	"Deadline",
58 	[SExpel] =	"Expel",
59 	[SDead] =	"Dead",
60 	[SInts] =	"Ints",
61 	[SInte] =	"Inte",
62 	[STrap] =	"Trap",
63 	[SUser] = 	"User",
64 	[SName]	=	"Name",
65 };
66 
67 /* Should be generated */
68 static char *ssnames[] =
69 {
70 	"Dead",
71 	"Moribund",
72 	"Ready",
73 	"Scheding",
74 	"Running",
75 	"Queueing",
76 	"QueueingR",
77 	"QueueingW",
78 	"Wakeme",
79 	"Broken",
80 	"Stopped",
81 	"Rendez",
82 	"Waitrelease",
83 };
84 
85 static char*
ssname(int i)86 ssname(int i)
87 {
88 	if(i >= 0 && i < nelem(ssnames))
89 		return ssnames[i];
90 	return "unknown";
91 }
92 
93 static void
error(char * msg,...)94 error(char* msg, ...)
95 {
96 	char	buf[500];
97 	va_list	arg;
98 
99 	va_start(arg, msg);
100 	vseprint(buf, buf+sizeof(buf), msg, arg);
101 	va_end(arg);
102 	Bflush(bout);
103 	fprint(2, "%s: %s:%d: %s\n", argv0, fname, lno, msg);
104 }
105 
106 int
Tfmt(Fmt * f)107 Tfmt(Fmt *f)
108 {
109 	vlong t, i;
110 	static char *u[] = {"s", "m", "µ", "n"};
111 	static vlong d[] = {S(1), MS(1), US(1), 1};
112 	static char spc[3+2+1];
113 
114 	t = va_arg(f->args, vlong);
115 	if((f->flags&FmtSharp) == 0)
116 		return fmtprint(f, "%011lld", t);
117 
118 	if(spc[0] == 0)
119 		memset(spc, ' ', sizeof spc - 1);
120 
121 	for(i = 0; i < nelem(u); i++){
122 		fmtprint(f, "%3lld%s ", t/d[i], u[i]);
123 		t %= d[i];
124 	}
125 	return 0;
126 }
127 
128 /*
129  * This tries to combine printing the state information for
130  * the text file format so it keeps all the information and
131  * is easy to parse, with a compact representation amenable
132  * for an editor.
133  * %#G is intended to print the state in the window.
134  * %G is intended to print the full state for the file.
135  * If the output of %G is changed, readgraph() must be updated
136  * to parse it properly.
137  * As of now it uses the (awk) fields 1, 2, 3, 4, NF-1, and NF.
138  * all other fields are ignored for reading.
139  */
140 int
Gfmt(Fmt * f)141 Gfmt(Fmt *f)
142 {
143 	St *g;
144 	vlong arg;
145 	u64int addr;
146 	extern char *scname[];
147 	extern int maxscval;
148 
149 	g = va_arg(f->args, St*);
150 	if(f->flags&FmtSharp)
151 		fmtprint(f, " %s", etname[g->state]);
152 	else{
153 		fmtprint(f, "%T %6d %02d", g->time, g->pid, g->machno);
154 		fmtprint(f, " %-4s", (g->name && g->name[0]) ? g->name : "_");
155 		fmtprint(f, " %-6s", etname[g->state]);
156 	}
157 	switch(g->etype){
158 	case SSleep:
159 		arg = g->arg&0xFF;
160 		fmtprint(f, " %s pc %#ullx", ssname(arg), g->arg>>8);
161 		if(f->flags&FmtSharp)
162 			return 0;
163 		break;
164 	case STrap:
165 		arg = g->arg;
166 		addr = g->arg & STrapMask;
167 		if(arg&STrapRPF)
168 			fmtprint(f, " %-10s r %#ullx", "pfault", addr);
169 		else if(arg&STrapWPF)
170 			fmtprint(f, " %-10s w %#ullx", "pfault", addr);
171 		else if(arg&STrapSC){
172 			arg &= ~STrapSC;
173 			if(arg <= maxscval)
174 				fmtprint(f, " %-10s", scname[arg]);
175 			else
176 				fmtprint(f, " sc%ulld", arg);
177 		}else
178 			fmtprint(f, " %-10s", etname[g->etype]);
179 		if(f->flags&FmtSharp)
180 			return 0;
181 		break;
182 	default:
183 		fmtprint(f, " %-10s", etname[g->etype]);
184 	}
185 
186 	if(f->flags&FmtSharp)
187 		return fmtprint(f, " %#ullx", g->arg);
188 	else
189 		return fmtprint(f, " %2d %#ullx", g->etype, g->arg);
190 }
191 
192 static void
addname(St * g)193 addname(St *g)
194 {
195 	int i;
196 
197 	for(i = 0; i < nname; i++)
198 		if(name[i].pid == 0 || name[i].pid == g->pid)
199 			break;
200 	if(i == nname){
201 		if((nname%Incr) == 0){
202 			name = realloc(name, (nname+Incr)*sizeof name[0]);
203 			if(name == nil)
204 				sysfatal("malloc: %r");
205 		}
206 		nname++;
207 	}
208 	name[i].pid = g->pid;
209 	assert(sizeof g->arg <= sizeof name[i].name);
210 	memmove(name[i].name, &g->arg, sizeof g->arg);
211 	name[i].name[sizeof g->arg] = 0;
212 }
213 
214 static char*
pidname(int pid)215 pidname(int pid)
216 {
217 	int i;
218 
219 	for(i = 0; i < nname; i++)
220 		if(name[i].pid == pid)
221 			return name[i].name;
222 	return "_";
223 }
224 
225 static St*
readdev(Biobuf * bin)226 readdev(Biobuf *bin)
227 {
228 	uchar buf[PTsize], *p;
229 	long nr;
230 	uvlong x;
231 	St *g;
232 
233 	nr = Bread(bin, buf, sizeof buf);
234 	if(nr == 0)
235 		return nil;
236 	if(nr < 0){
237 		error("read: %r");
238 		return nil;
239 	}
240 	if(nr != sizeof buf){
241 		error("wrong event size");
242 		return nil;
243 	}
244 	lno++;	/* count events instead of lines */
245 
246 	g = mallocz(sizeof *g, 1);
247 	if(g == nil)
248 		sysfatal("no mem");
249 	g->pid = le32get(buf, &p);
250 	g->etype = le32get(p, &p);
251 	g->state = g->etype;
252 	g->machno = le32get(p, &p);
253 	x = le64get(p, &p);
254 	g->time = x;
255 	g->arg = le64get(p, &p);
256 	if(g->etype == SName)
257 		addname(g);
258 	g->name = pidname(g->pid);
259 
260 	if(verb)
261 		fprint(2, "pid %ud etype %ud mach %ud time %lld arg %lld\n",
262 			g->pid, g->etype, g->machno, g->time, g->arg);
263 	return g;
264 }
265 
266 static St*
readfile(Biobuf * bin)267 readfile(Biobuf *bin)
268 {
269 	char *s;
270 	char *toks[18];
271 	int ntoks, et;
272 	St* g;
273 
274 	while((s = Brdstr(bin, '\n', 1)) != nil){
275 		lno++;
276 		if(s[0] == '#')
277 			goto next;
278 		ntoks = tokenize(s, toks, nelem(toks));
279 		if(ntoks == 0)
280 			goto next;
281 		if(ntoks < 8){
282 			error("wrong event at %s\n", toks[0]);
283 			goto next;
284 		}
285 		g = mallocz(sizeof *g, 1);
286 		if(g == nil)
287 			sysfatal("no memory");
288 		g->time = (vlong)strtoull(toks[0], nil, 10);
289 		g->pid = strtoul(toks[1], nil, 0);
290 		g->machno = strtoul(toks[2], nil, 0);
291 		g->name = strdup(toks[3]);
292 
293 		et = strtoul(toks[ntoks-2], 0, 0);
294 		if(et < 0 || et >= Nevent || etname[et] == nil){
295 			error("unknown state id %d\n", et);
296 			goto next;
297 		}
298 		g->etype = et;
299 		g->state = et;
300 		g->arg = strtoull(toks[ntoks-1], nil, 0);
301 		free(s);
302 		return g;
303 	next:
304 		free(s);
305 	}
306 	return nil;
307 }
308 
309 static void
addgraph(St * g)310 addgraph(St *g)
311 {
312 	if((ngraph%Incr) == 0){
313 		graph = realloc(graph, (ngraph+Incr)*sizeof graph[0]);
314 		if(graph == nil)
315 			sysfatal("malloc: %r");
316 	}
317 	graph[ngraph++] = g;
318 }
319 
320 static int
graphcmp(void * e1,void * e2)321 graphcmp(void *e1, void *e2)
322 {
323 	St **s1, **s2;
324 	vlong d;
325 
326 	s1 = e1;
327 	s2 = e2;
328 	d = (*s1)->time - (*s2)->time;
329 	if(d < 0)
330 		return -1;
331 	if(d > 0)
332 		return 1;
333 	return 0;
334 }
335 
336 void
readall(char * f,int isdev)337 readall(char *f, int isdev)
338 {
339 	Biobuf *bin;
340 	St *g;
341 	St *(*rd)(Biobuf*);
342 	vlong t0;
343 	int i;
344 
345 	fname = f;
346 	bin = Bopen(f, OREAD);
347 	if(bin == nil)
348 		sysfatal("%s: Bopen: %r", f);
349 
350 	if(isdev)
351 		rd = readdev;
352 	else
353 		rd = readfile;
354 
355 	while((g = rd(bin)) != nil)
356 		addgraph(g);
357 	Bterm(bin);
358 
359 	if(ngraph == 0)
360 		return;
361 
362 	if(isdev)
363 		qsort(graph, ngraph, sizeof graph[0], graphcmp);
364 
365 	t0 = graph[0]->time;
366 	for(i = 0; i < ngraph; i++)
367 		graph[i]->time -= t0;
368 }
369 
370 static Proc*
getproc(St * g)371 getproc(St *g)
372 {
373 	int i;
374 
375 	for(i = 0; i < nproc; i++)
376 		if(proc[i].pid == g->pid)
377 			break;
378 	if(i == nproc){
379 		if((nproc%Incr) == 0){
380 			proc = realloc(proc, (nproc+Incr)*sizeof proc[0]);
381 			if(proc == nil)
382 				sysfatal("malloc: %r");
383 		}
384 		memset(&proc[nproc], 0, sizeof proc[nproc]);
385 		nproc++;
386 	}
387 	proc[i].pid = g->pid;
388 	return &proc[i];
389 }
390 
391 St*
pgraph(Proc * p,int i)392 pgraph(Proc *p, int i)
393 {
394 	int id;
395 
396 	if(i < 0 || i >= p->nstate)
397 		return nil;
398 	id = p->state[i];
399 	if(id >= ngraph)
400 		return nil;
401 	return graph[id];
402 }
403 
404 static void
appstate(Proc * p,int i)405 appstate(Proc *p, int i)
406 {
407 		if((p->nstate%Incr) == 0){
408 			p->state = realloc(p->state, (p->nstate+Incr)*sizeof p->state[0]);
409 			if(p->state == nil)
410 				sysfatal("malloc: %r");
411 		}
412 		p->state[p->nstate++] = i;
413 }
414 
415 void
makeprocs(void)416 makeprocs(void)
417 {
418 	Proc *p, *lastp;
419 	St *g, *sg;
420 	int i, j;
421 
422 	lastp = nil;
423 	for(i = 0; i < ngraph; i++){
424 		g = graph[i];
425 		if(lastp != nil && lastp->pid == g->pid)
426 			p = lastp;
427 		else
428 			p = getproc(g);
429 
430 		if(p->pnextp != nil)
431 			*p->pnextp = i;
432 		p->pnextp = &g->pnext;
433 
434 		switch(g->etype){
435 		case SReady:
436 		case SRun:
437 		case SSleep:
438 		case SDead:
439 			appstate(p, i);
440 			break;
441 		default:
442 			if(p->nstate == 0){
443 				appstate(p, i);
444 				g->state= SRun;
445 			}else{
446 				sg = graph[p->state[p->nstate-1]];
447 				g->state = sg->state;
448 			}
449 		}
450 		lastp = p;
451 	}
452 
453 	if(0)
454 	for(i = 0; i < nproc; i++){
455 		p = &proc[i];
456 		Bprint(bout, "proc pid %d:\n", p->pid);
457 		for(j = 0; j < p->nstate; j++)
458 			Bprint(bout, "%G\n", graph[p->state[j]]);
459 		Bprint(bout, "\n\n");
460 	}
461 }
462 
463