xref: /inferno-os/utils/kprof/kprof.c (revision 2b69dba5038ffd0b59cf30a4c44bce549e5097f8)
1 #include <lib9.h>
2 #include <bio.h>
3 #include <mach.h>
4 
5 enum {
6 	SpecialTotalTicks,
7 	SpecialOutsideTicks,
8 	SpecialMicroSecondsPerTick,
9 	SpecialSamples,
10 	SpecialSampleSize,
11 	SpecialSampleLogBucketSize,
12 	SpecialMax
13 };
14 
15 int pcres = 8;
16 ulong uspertick;
17 
18 struct COUNTER
19 {
20 	char 	*name;		/* function name */
21 	ulong	time;		/* ticks spent there */
22 };
23 
24 void
25 error(int perr, char *s)
26 {
27 	fprint(2, "kprof: %s", s);
28 	if(perr)
29 		fprint(2, ": %r\n");
30 	else
31 		fprint(2, "\n");
32 	exits(s);
33 }
34 
35 int
36 compar(void *va, void *vb)
37 {
38 	struct COUNTER *a, *b;
39 
40 	a = (struct COUNTER *)va;
41 	b = (struct COUNTER *)vb;
42 	if(a->time < b->time)
43 		return -1;
44 	if(a->time == b->time)
45 		return 0;
46 	return 1;
47 }
48 
49 ulong
50 tickstoms(ulong ticks)
51 {
52 	return ((vlong)ticks * uspertick) / 1000;
53 }
54 
55 void
56 main(int argc, char *argv[])
57 {
58 	int fd;
59 	long i, j, k, n;
60 	Dir *d;
61 	char *name;
62 	ulong *data;
63 	ulong tbase, sum;
64 	long delta;
65 	Symbol s;
66 	Biobuf outbuf;
67 	Fhdr f;
68 	struct COUNTER *cp;
69 
70 	if(argc != 3)
71 		error(0, "usage: kprof text data");
72 	/*
73 	 * Read symbol table
74 	 */
75 	fd = open(argv[1], OREAD);
76 	if(fd < 0)
77 		error(1, argv[1]);
78 	if (!crackhdr(fd, &f))
79 		error(1, "read text header");
80 	if (f.type == FNONE)
81 		error(0, "text file not an a.out");
82 	if (syminit(fd, &f) < 0)
83 		error(1, "syminit");
84 	close(fd);
85 	/*
86 	 * Read timing data
87 	 */
88 	fd = open(argv[2], OREAD);
89 	if(fd < 0)
90 		error(1, argv[2]);
91 	if((d = dirfstat(fd)) == nil)
92 		error(1, "stat");
93 	n = d->length/sizeof(data[0]);
94 	if(n < 2)
95 		error(0, "data file too short");
96 	data = malloc(d->length);
97 	if(data == 0)
98 		error(1, "malloc");
99 	if(read(fd, data, d->length) < 0)
100 		error(1, "text read");
101 	close(fd);
102 	free(d);
103 	for(i=0; i<n; i++)
104 		data[i] = beswal(data[i]);
105 	pcres = 1 << data[SpecialSampleLogBucketSize];
106 	uspertick = data[SpecialMicroSecondsPerTick];
107 	if (data[SpecialSampleSize] != sizeof(data[0]))
108 		error(0, "only sample size 4 supported\n");
109 	delta = data[SpecialTotalTicks] - data[SpecialOutsideTicks];
110 	print("total: %lud	in kernel text: %lud	outside kernel text: %lud\n",
111 		data[0], delta, data[1]);
112 	if(data[0] == 0)
113 		exits(0);
114 	if (!textsym(&s, 0))
115 		error(0, "no text symbols");
116 	tbase = s.value & ~(mach->pgsize-1);	/* align down to page */
117 	print("KTZERO %.8lux\n", tbase);
118 	/*
119 	 * Accumulate counts for each function
120 	 */
121 	cp = 0;
122 	k = 0;
123 	for (i = 0, j = (s.value-tbase)/pcres+SpecialMax; j < n; i++) {
124 		name = s.name;		/* save name */
125 		if (!textsym(&s, i))	/* get next symbol */
126 			break;
127 		sum = 0;
128 		while (j < n && j*pcres < s.value-tbase)
129 			sum += data[j++];
130 		if (sum) {
131 			cp = realloc(cp, (k+1)*sizeof(struct COUNTER));
132 			if (cp == 0)
133 				error(1, "realloc");
134 			cp[k].name = name;
135 			cp[k].time = sum;
136 			k++;
137 		}
138 	}
139 	if (!k)
140 		error(0, "no counts");
141 	cp[k].time = 0;			/* "etext" can take no time */
142 	/*
143 	 * Sort by time and print
144 	 */
145 	qsort(cp, k, sizeof(struct COUNTER), compar);
146 	Binit(&outbuf, 1, OWRITE);
147 	Bprint(&outbuf, "ms	  %%	sym\n");
148 	while(--k>=0)
149 		Bprint(&outbuf, "%lud\t%3lud.%ld\t%s\n",
150 				tickstoms(cp[k].time),
151 				100*cp[k].time/delta,
152 				(1000*cp[k].time/delta)%10,
153 				cp[k].name);
154 	exits(0);
155 }
156