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