xref: /minix3/minix/commands/sprofdiff/sprofdiff.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc #include <assert.h>
2*433d6423SLionel Sambuc #include <errno.h>
3*433d6423SLionel Sambuc #include <math.h>
4*433d6423SLionel Sambuc #include <stdio.h>
5*433d6423SLionel Sambuc #include <stdlib.h>
6*433d6423SLionel Sambuc #include <string.h>
7*433d6423SLionel Sambuc #include <unistd.h>
8*433d6423SLionel Sambuc #include <minix/type.h>
9*433d6423SLionel Sambuc 
10*433d6423SLionel Sambuc #include "tdist.h"
11*433d6423SLionel Sambuc 
12*433d6423SLionel Sambuc /* user-configurable settings */
13*433d6423SLionel Sambuc #define DEBUG 0
14*433d6423SLionel Sambuc 
15*433d6423SLionel Sambuc #define PROC_NAME_WIDTH 10
16*433d6423SLionel Sambuc 
17*433d6423SLionel Sambuc #define SYMBOL_NAME_WIDTH 24
18*433d6423SLionel Sambuc 
19*433d6423SLionel Sambuc /* types */
20*433d6423SLionel Sambuc #define SYMBOL_HASHTAB_SIZE 1024
21*433d6423SLionel Sambuc 
22*433d6423SLionel Sambuc #define SYMBOL_NAME_SIZE 52
23*433d6423SLionel Sambuc 
24*433d6423SLionel Sambuc struct symbol_count {
25*433d6423SLionel Sambuc 	unsigned long sum;
26*433d6423SLionel Sambuc 	unsigned long long sum2;
27*433d6423SLionel Sambuc 	unsigned long min;
28*433d6423SLionel Sambuc 	unsigned long max;
29*433d6423SLionel Sambuc };
30*433d6423SLionel Sambuc 
31*433d6423SLionel Sambuc enum symbol_class {
32*433d6423SLionel Sambuc 	sc_total,
33*433d6423SLionel Sambuc 	sc_idle,
34*433d6423SLionel Sambuc 	sc_system,
35*433d6423SLionel Sambuc 	sc_user,
36*433d6423SLionel Sambuc 	sc_process,
37*433d6423SLionel Sambuc 	sc_symbol
38*433d6423SLionel Sambuc };
39*433d6423SLionel Sambuc 
40*433d6423SLionel Sambuc struct symbol_info {
41*433d6423SLionel Sambuc 	struct symbol_info *next;
42*433d6423SLionel Sambuc 	struct symbol_info *hashtab_next;
43*433d6423SLionel Sambuc 	char binary[PROC_NAME_LEN];
44*433d6423SLionel Sambuc 	char name[SYMBOL_NAME_SIZE];
45*433d6423SLionel Sambuc 	struct symbol_count count[2];
46*433d6423SLionel Sambuc 	long diff;
47*433d6423SLionel Sambuc 	enum symbol_class class;
48*433d6423SLionel Sambuc };
49*433d6423SLionel Sambuc 
50*433d6423SLionel Sambuc /* global variables */
51*433d6423SLionel Sambuc static unsigned n1, n2;
52*433d6423SLionel Sambuc static struct symbol_info *symbols;
53*433d6423SLionel Sambuc static struct symbol_info *symbol_hashtab[SYMBOL_HASHTAB_SIZE];
54*433d6423SLionel Sambuc 
55*433d6423SLionel Sambuc /* prototypes */
56*433d6423SLionel Sambuc static double compute_sig(double avg1, double var1, double avg2, double var2);
57*433d6423SLionel Sambuc static void compute_stats(const struct symbol_count *count, unsigned n,
58*433d6423SLionel Sambuc 	double *avg, double *var);
59*433d6423SLionel Sambuc static void load_file(const char *path, int count_index);
60*433d6423SLionel Sambuc static void *malloc_checked(size_t size);
61*433d6423SLionel Sambuc static void print_report(void);
62*433d6423SLionel Sambuc static void print_report_line(const struct symbol_info *symbol);
63*433d6423SLionel Sambuc static int read_line(FILE *file, const char *path, int line, char *binary,
64*433d6423SLionel Sambuc 	char *name, unsigned long *samples);
65*433d6423SLionel Sambuc static enum symbol_class symbol_classify(const char *binary, const char *name);
66*433d6423SLionel Sambuc static unsigned string_hash(const char *s, size_t size);
67*433d6423SLionel Sambuc static struct symbol_info *symbol_find_or_add(const char *binary,
68*433d6423SLionel Sambuc 	const char *name);
69*433d6423SLionel Sambuc static unsigned symbol_hash(const char *binary, const char *name);
70*433d6423SLionel Sambuc static int symbol_qsort_compare(const void *p1, const void *p2);
71*433d6423SLionel Sambuc static void symbol_tally(const char *binary, const char *name,
72*433d6423SLionel Sambuc 	unsigned long samples, int count_index);
73*433d6423SLionel Sambuc static unsigned symbols_count(void);
74*433d6423SLionel Sambuc static void usage(const char *argv0);
75*433d6423SLionel Sambuc 
76*433d6423SLionel Sambuc #define MALLOC_CHECKED(type, count) \
77*433d6423SLionel Sambuc 	((type *) malloc_checked(sizeof(type) * (count)))
78*433d6423SLionel Sambuc 
79*433d6423SLionel Sambuc #if DEBUG
80*433d6423SLionel Sambuc #define dprintf(...) do { 						\
81*433d6423SLionel Sambuc 	fprintf(stderr, "debug(%s:%d): ", __FUNCTION__, __LINE__); 	\
82*433d6423SLionel Sambuc 	fprintf(stderr, __VA_ARGS__); 					\
83*433d6423SLionel Sambuc } while(0)
84*433d6423SLionel Sambuc #else
85*433d6423SLionel Sambuc #define dprintf(...)
86*433d6423SLionel Sambuc #endif
87*433d6423SLionel Sambuc 
main(int argc,char ** argv)88*433d6423SLionel Sambuc int main(int argc, char **argv) {
89*433d6423SLionel Sambuc 	int i;
90*433d6423SLionel Sambuc 
91*433d6423SLionel Sambuc #ifdef DEBUG
92*433d6423SLionel Sambuc 	/* disable buffering so the output mixes correctly */
93*433d6423SLionel Sambuc 	setvbuf(stdout, NULL, _IONBF, 0);
94*433d6423SLionel Sambuc 	setvbuf(stderr, NULL, _IONBF, 0);
95*433d6423SLionel Sambuc #endif
96*433d6423SLionel Sambuc 
97*433d6423SLionel Sambuc 	if (argc < 3) usage(argv[0]);
98*433d6423SLionel Sambuc 
99*433d6423SLionel Sambuc 	/* load left-hand files */
100*433d6423SLionel Sambuc 	for (i = 1; i < argc; i++) {
101*433d6423SLionel Sambuc 		if (strcmp(argv[i], "-r") == 0) {
102*433d6423SLionel Sambuc 			i++;
103*433d6423SLionel Sambuc 			break;
104*433d6423SLionel Sambuc 		}
105*433d6423SLionel Sambuc 		if (argc == 3 && i == 2) break;
106*433d6423SLionel Sambuc 		load_file(argv[i], 0);
107*433d6423SLionel Sambuc 		n1++;
108*433d6423SLionel Sambuc 	}
109*433d6423SLionel Sambuc 
110*433d6423SLionel Sambuc 	/* load right-hand files */
111*433d6423SLionel Sambuc 	for (; i < argc; i++) {
112*433d6423SLionel Sambuc 		load_file(argv[i], 1);
113*433d6423SLionel Sambuc 		n2++;
114*433d6423SLionel Sambuc 	}
115*433d6423SLionel Sambuc 
116*433d6423SLionel Sambuc 	if (n1 < 1 || n2 < 1) usage(argv[0]);
117*433d6423SLionel Sambuc 
118*433d6423SLionel Sambuc 	/* report analysis results */
119*433d6423SLionel Sambuc 	print_report();
120*433d6423SLionel Sambuc 	return 0;
121*433d6423SLionel Sambuc }
122*433d6423SLionel Sambuc 
compute_sig(double avg1,double var1,double avg2,double var2)123*433d6423SLionel Sambuc static double compute_sig(double avg1, double var1, double avg2, double var2) {
124*433d6423SLionel Sambuc 	double df, t, var;
125*433d6423SLionel Sambuc 
126*433d6423SLionel Sambuc 	/* prevent division by zero with lack of variance */
127*433d6423SLionel Sambuc 	var = var1 / n1 + var2 / n2;
128*433d6423SLionel Sambuc 	if (var <= 0 || n1 <= 1 || n2 <= 1) return -1;
129*433d6423SLionel Sambuc 
130*433d6423SLionel Sambuc 	/* do we have enough degrees of freedom? */
131*433d6423SLionel Sambuc 	df = var * var / (
132*433d6423SLionel Sambuc 		var1 * var1 / (n1 * n1 * (n1 - 1)) +
133*433d6423SLionel Sambuc 		var2 * var2 / (n2 * n2 * (n2 - 1)));
134*433d6423SLionel Sambuc 	if (df < 1) return -1;
135*433d6423SLionel Sambuc 
136*433d6423SLionel Sambuc 	/* perform t-test */
137*433d6423SLionel Sambuc 	t = (avg1 - avg2) / sqrt(var);
138*433d6423SLionel Sambuc 	return student_t_p_2tail(t, df);
139*433d6423SLionel Sambuc }
140*433d6423SLionel Sambuc 
compute_stats(const struct symbol_count * count,unsigned n,double * avg,double * var)141*433d6423SLionel Sambuc static void compute_stats(const struct symbol_count *count, unsigned n,
142*433d6423SLionel Sambuc 	double *avg, double *var) {
143*433d6423SLionel Sambuc 	double sum;
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc 	assert(count);
146*433d6423SLionel Sambuc 	assert(avg);
147*433d6423SLionel Sambuc 	assert(var);
148*433d6423SLionel Sambuc 
149*433d6423SLionel Sambuc 	sum = count->sum;
150*433d6423SLionel Sambuc 	if (n < 1) {
151*433d6423SLionel Sambuc 		*avg = 0;
152*433d6423SLionel Sambuc 	} else {
153*433d6423SLionel Sambuc 		*avg = sum / n;
154*433d6423SLionel Sambuc 	}
155*433d6423SLionel Sambuc 
156*433d6423SLionel Sambuc 	if (n < 2) {
157*433d6423SLionel Sambuc 		*var = 0;
158*433d6423SLionel Sambuc 	} else {
159*433d6423SLionel Sambuc 		*var = (count->sum2 - sum * sum / n) / (n - 1);
160*433d6423SLionel Sambuc 	}
161*433d6423SLionel Sambuc }
162*433d6423SLionel Sambuc 
load_file(const char * path,int count_index)163*433d6423SLionel Sambuc static void load_file(const char *path, int count_index) {
164*433d6423SLionel Sambuc 	char binary[PROC_NAME_LEN];
165*433d6423SLionel Sambuc 	FILE *file;
166*433d6423SLionel Sambuc 	int line;
167*433d6423SLionel Sambuc 	char name[SYMBOL_NAME_SIZE];
168*433d6423SLionel Sambuc 	unsigned long samples;
169*433d6423SLionel Sambuc 
170*433d6423SLionel Sambuc 	assert(path);
171*433d6423SLionel Sambuc 	assert(count_index == 0 || count_index == 1);
172*433d6423SLionel Sambuc 
173*433d6423SLionel Sambuc 	file = fopen(path, "r");
174*433d6423SLionel Sambuc 	if (!file) {
175*433d6423SLionel Sambuc 		fprintf(stderr, "error: cannot open \"%s\": %s\n",
176*433d6423SLionel Sambuc 			path, strerror(errno));
177*433d6423SLionel Sambuc 		exit(1);
178*433d6423SLionel Sambuc 	}
179*433d6423SLionel Sambuc 
180*433d6423SLionel Sambuc 	line = 1;
181*433d6423SLionel Sambuc 	while (read_line(file, path, line++, binary, name, &samples)) {
182*433d6423SLionel Sambuc 		symbol_tally(binary, name, samples, count_index);
183*433d6423SLionel Sambuc 	}
184*433d6423SLionel Sambuc 
185*433d6423SLionel Sambuc 	fclose(file);
186*433d6423SLionel Sambuc }
187*433d6423SLionel Sambuc 
malloc_checked(size_t size)188*433d6423SLionel Sambuc static void *malloc_checked(size_t size) {
189*433d6423SLionel Sambuc 	void *p;
190*433d6423SLionel Sambuc 	if (!size) return NULL;
191*433d6423SLionel Sambuc 	p = malloc(size);
192*433d6423SLionel Sambuc 	if (!p) {
193*433d6423SLionel Sambuc 		fprintf(stderr, "error: malloc cannot allocate %lu bytes: %s\n",
194*433d6423SLionel Sambuc 			(unsigned long) size, strerror(errno));
195*433d6423SLionel Sambuc 		exit(-1);
196*433d6423SLionel Sambuc 	}
197*433d6423SLionel Sambuc 	return p;
198*433d6423SLionel Sambuc }
199*433d6423SLionel Sambuc 
print_report(void)200*433d6423SLionel Sambuc static void print_report(void) {
201*433d6423SLionel Sambuc 	unsigned i, index, symbol_count;
202*433d6423SLionel Sambuc 	struct symbol_info *symbol, **symbol_list;
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc 	/* list the symbols in an array for sorting */
205*433d6423SLionel Sambuc 	symbol_count = symbols_count();
206*433d6423SLionel Sambuc 	symbol_list = MALLOC_CHECKED(struct symbol_info *, symbol_count);
207*433d6423SLionel Sambuc 	index = 0;
208*433d6423SLionel Sambuc 	for (symbol = symbols; symbol; symbol = symbol->next) {
209*433d6423SLionel Sambuc 		symbol_list[index++] = symbol;
210*433d6423SLionel Sambuc 
211*433d6423SLionel Sambuc 		/* sort by difference in average, multiply both sides by
212*433d6423SLionel Sambuc 		 * n1 * n2 to avoid division
213*433d6423SLionel Sambuc 		 */
214*433d6423SLionel Sambuc 		symbol->diff = (long) (symbol->count[1].sum * n1) -
215*433d6423SLionel Sambuc 			(long) (symbol->count[0].sum * n2);
216*433d6423SLionel Sambuc 	}
217*433d6423SLionel Sambuc 	assert(index == symbol_count);
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc 	/* sort symbols  */
220*433d6423SLionel Sambuc 	qsort(symbol_list, symbol_count, sizeof(struct symbol_info *),
221*433d6423SLionel Sambuc 		symbol_qsort_compare);
222*433d6423SLionel Sambuc 
223*433d6423SLionel Sambuc 	printf("%-*s %-*s ------avg------ ----stdev----    diff sig\n",
224*433d6423SLionel Sambuc 		PROC_NAME_WIDTH, "binary", SYMBOL_NAME_WIDTH, "symbol");
225*433d6423SLionel Sambuc 	printf("%-*s    left   right   left  right\n",
226*433d6423SLionel Sambuc 		PROC_NAME_WIDTH + SYMBOL_NAME_WIDTH + 1, "");
227*433d6423SLionel Sambuc 	printf("\n");
228*433d6423SLionel Sambuc 	for (i = 0; i < symbol_count; i++) {
229*433d6423SLionel Sambuc 		if (i > 0 && symbol_list[i]->class >= sc_process &&
230*433d6423SLionel Sambuc 			symbol_list[i]->class != symbol_list[i - 1]->class) {
231*433d6423SLionel Sambuc 			printf("\n");
232*433d6423SLionel Sambuc 		}
233*433d6423SLionel Sambuc 		print_report_line(symbol_list[i]);
234*433d6423SLionel Sambuc 	}
235*433d6423SLionel Sambuc 	printf("\n");
236*433d6423SLionel Sambuc 	printf("significance levels (two-tailed):\n");
237*433d6423SLionel Sambuc 	printf("  *    p < 0.05\n");
238*433d6423SLionel Sambuc 	printf("  **   p < 0.01\n");
239*433d6423SLionel Sambuc 	printf("  ***  p < 0.001\n");
240*433d6423SLionel Sambuc 	free(symbol_list);
241*433d6423SLionel Sambuc }
242*433d6423SLionel Sambuc 
print_report_line(const struct symbol_info * symbol)243*433d6423SLionel Sambuc static void print_report_line(const struct symbol_info *symbol) {
244*433d6423SLionel Sambuc 	double avg1, avg2, p, var1, var2;
245*433d6423SLionel Sambuc 
246*433d6423SLionel Sambuc 	/* compute statistics; t is Welch's t, which is a t-test that allows
247*433d6423SLionel Sambuc 	 * for unpaired samples with unequal variance; df is the degrees of
248*433d6423SLionel Sambuc 	 * freedom as given by the Welch-Satterthwaite equation
249*433d6423SLionel Sambuc 	 */
250*433d6423SLionel Sambuc 	compute_stats(&symbol->count[0], n1, &avg1, &var1);
251*433d6423SLionel Sambuc 	compute_stats(&symbol->count[1], n2, &avg2, &var2);
252*433d6423SLionel Sambuc 	p = compute_sig(avg1, var1, avg2, var2);
253*433d6423SLionel Sambuc 
254*433d6423SLionel Sambuc 	/* list applicable values */
255*433d6423SLionel Sambuc 	assert(PROC_NAME_WIDTH <= PROC_NAME_LEN);
256*433d6423SLionel Sambuc 	assert(SYMBOL_NAME_WIDTH <= SYMBOL_NAME_SIZE);
257*433d6423SLionel Sambuc 	printf("%-*.*s %-*.*s",
258*433d6423SLionel Sambuc 		PROC_NAME_WIDTH, PROC_NAME_WIDTH, symbol->binary,
259*433d6423SLionel Sambuc 		SYMBOL_NAME_WIDTH, SYMBOL_NAME_WIDTH, symbol->name);
260*433d6423SLionel Sambuc 	if (symbol->count[0].sum > 0) {
261*433d6423SLionel Sambuc 		printf("%8.0f", avg1);
262*433d6423SLionel Sambuc 	} else {
263*433d6423SLionel Sambuc 		printf("        ");
264*433d6423SLionel Sambuc 	}
265*433d6423SLionel Sambuc 	if (symbol->count[1].sum > 0) {
266*433d6423SLionel Sambuc 		printf("%8.0f", avg2);
267*433d6423SLionel Sambuc 	} else {
268*433d6423SLionel Sambuc 		printf("        ");
269*433d6423SLionel Sambuc 	}
270*433d6423SLionel Sambuc 	if (symbol->count[0].sum > 0 && n1 >= 2) {
271*433d6423SLionel Sambuc 		printf("%7.0f", sqrt(var1));
272*433d6423SLionel Sambuc 	} else {
273*433d6423SLionel Sambuc 		printf("       ");
274*433d6423SLionel Sambuc 	}
275*433d6423SLionel Sambuc 	if (symbol->count[1].sum > 0 && n2 >= 2) {
276*433d6423SLionel Sambuc 		printf("%7.0f", sqrt(var2));
277*433d6423SLionel Sambuc 	} else {
278*433d6423SLionel Sambuc 		printf("       ");
279*433d6423SLionel Sambuc 	}
280*433d6423SLionel Sambuc 	printf("%8.0f ", avg2 - avg1);
281*433d6423SLionel Sambuc 	if (p >= 0) {
282*433d6423SLionel Sambuc 		if (p <= 0.05) printf("*");
283*433d6423SLionel Sambuc 		if (p <= 0.01) printf("*");
284*433d6423SLionel Sambuc 		if (p <= 0.001) printf("*");
285*433d6423SLionel Sambuc 	}
286*433d6423SLionel Sambuc 	printf("\n");
287*433d6423SLionel Sambuc }
288*433d6423SLionel Sambuc 
read_line(FILE * file,const char * path,int line,char * binary,char * name,unsigned long * samples)289*433d6423SLionel Sambuc static int read_line(FILE *file, const char *path, int line, char *binary,
290*433d6423SLionel Sambuc 	char *name, unsigned long *samples) {
291*433d6423SLionel Sambuc 	int c, index;
292*433d6423SLionel Sambuc 
293*433d6423SLionel Sambuc 	assert(file);
294*433d6423SLionel Sambuc 	assert(binary);
295*433d6423SLionel Sambuc 	assert(name);
296*433d6423SLionel Sambuc 	assert(samples);
297*433d6423SLionel Sambuc 
298*433d6423SLionel Sambuc 	c = fgetc(file);
299*433d6423SLionel Sambuc 	if (c == EOF) return 0;
300*433d6423SLionel Sambuc 
301*433d6423SLionel Sambuc 	/* read binary name, truncating if necessary */
302*433d6423SLionel Sambuc 	index = 0;
303*433d6423SLionel Sambuc 	while (c != '\t' && c != '\n') {
304*433d6423SLionel Sambuc 		if (index < PROC_NAME_LEN) binary[index++] = c;
305*433d6423SLionel Sambuc 		c = fgetc(file);
306*433d6423SLionel Sambuc 	}
307*433d6423SLionel Sambuc 	if (index < PROC_NAME_LEN) binary[index] = 0;
308*433d6423SLionel Sambuc 
309*433d6423SLionel Sambuc 	/* read tab */
310*433d6423SLionel Sambuc 	if (c != '\t') {
311*433d6423SLionel Sambuc 		fprintf(stderr, "error: garbage %d after binary name "
312*433d6423SLionel Sambuc 			"(\"%s\", line %d)\n", c, path, line);
313*433d6423SLionel Sambuc 		exit(1);
314*433d6423SLionel Sambuc 	}
315*433d6423SLionel Sambuc 	c = fgetc(file);
316*433d6423SLionel Sambuc 
317*433d6423SLionel Sambuc 	/* read symbol name, truncating if necessary */
318*433d6423SLionel Sambuc 	index = 0;
319*433d6423SLionel Sambuc 	while (c != '\t' && c != '\n') {
320*433d6423SLionel Sambuc 		if (index < SYMBOL_NAME_SIZE) name[index++] = c;
321*433d6423SLionel Sambuc 		c = fgetc(file);
322*433d6423SLionel Sambuc 	}
323*433d6423SLionel Sambuc 	if (index < SYMBOL_NAME_SIZE) name[index] = 0;
324*433d6423SLionel Sambuc 
325*433d6423SLionel Sambuc 	/* read tab */
326*433d6423SLionel Sambuc 	if (c != '\t') {
327*433d6423SLionel Sambuc 		fprintf(stderr, "error: garbage %d after symbol name "
328*433d6423SLionel Sambuc 			"(\"%s\", line %d)\n", c, path, line);
329*433d6423SLionel Sambuc 		exit(1);
330*433d6423SLionel Sambuc 	}
331*433d6423SLionel Sambuc 	c = fgetc(file);
332*433d6423SLionel Sambuc 
333*433d6423SLionel Sambuc 	/* read number of samples */
334*433d6423SLionel Sambuc 	*samples = 0;
335*433d6423SLionel Sambuc 	while (c >= '0' && c <= '9') {
336*433d6423SLionel Sambuc 		*samples = *samples * 10 + (c - '0');
337*433d6423SLionel Sambuc 		c = fgetc(file);
338*433d6423SLionel Sambuc 	}
339*433d6423SLionel Sambuc 
340*433d6423SLionel Sambuc 	/* read newline */
341*433d6423SLionel Sambuc 	if (c != '\n') {
342*433d6423SLionel Sambuc 		fprintf(stderr, "error: garbage %d after sample count "
343*433d6423SLionel Sambuc 			"(\"%s\", line %d)\n", c, path, line);
344*433d6423SLionel Sambuc 		exit(1);
345*433d6423SLionel Sambuc 	}
346*433d6423SLionel Sambuc 	return 1;
347*433d6423SLionel Sambuc }
348*433d6423SLionel Sambuc 
string_hash(const char * s,size_t size)349*433d6423SLionel Sambuc static unsigned string_hash(const char *s, size_t size) {
350*433d6423SLionel Sambuc 	unsigned result = 0;
351*433d6423SLionel Sambuc 
352*433d6423SLionel Sambuc 	assert(s);
353*433d6423SLionel Sambuc 
354*433d6423SLionel Sambuc 	while (*s && size-- > 0) {
355*433d6423SLionel Sambuc 		result = result * 31 + *(s++);
356*433d6423SLionel Sambuc 	}
357*433d6423SLionel Sambuc 	return result;
358*433d6423SLionel Sambuc }
359*433d6423SLionel Sambuc 
symbol_classify(const char * binary,const char * name)360*433d6423SLionel Sambuc static enum symbol_class symbol_classify(const char *binary, const char *name) {
361*433d6423SLionel Sambuc 	if (strncmp(binary, "(total)", PROC_NAME_LEN) == 0) return sc_total;
362*433d6423SLionel Sambuc 	if (strncmp(binary, "(idle)", PROC_NAME_LEN) == 0) return sc_idle;
363*433d6423SLionel Sambuc 	if (strncmp(binary, "(system)", PROC_NAME_LEN) == 0) return sc_system;
364*433d6423SLionel Sambuc 	if (strncmp(binary, "(user)", PROC_NAME_LEN) == 0) return sc_user;
365*433d6423SLionel Sambuc 	if (strncmp(name, "(total)", SYMBOL_NAME_SIZE) == 0) return sc_process;
366*433d6423SLionel Sambuc 	return sc_symbol;
367*433d6423SLionel Sambuc }
368*433d6423SLionel Sambuc 
symbol_find_or_add(const char * binary,const char * name)369*433d6423SLionel Sambuc static struct symbol_info *symbol_find_or_add(const char *binary,
370*433d6423SLionel Sambuc 	const char *name) {
371*433d6423SLionel Sambuc 	struct symbol_info **ptr, *symbol;
372*433d6423SLionel Sambuc 
373*433d6423SLionel Sambuc 	assert(binary);
374*433d6423SLionel Sambuc 	assert(name);
375*433d6423SLionel Sambuc 
376*433d6423SLionel Sambuc 	/* look up symbol in hash table */
377*433d6423SLionel Sambuc 	ptr = &symbol_hashtab[symbol_hash(binary, name) % SYMBOL_HASHTAB_SIZE];
378*433d6423SLionel Sambuc 	while ((symbol = *ptr)) {
379*433d6423SLionel Sambuc 		if (strncmp(symbol->binary, binary, PROC_NAME_LEN) == 0 &&
380*433d6423SLionel Sambuc 			strncmp(symbol->name, name, SYMBOL_NAME_SIZE) == 0) {
381*433d6423SLionel Sambuc 			return symbol;
382*433d6423SLionel Sambuc 		}
383*433d6423SLionel Sambuc 		ptr = &symbol->hashtab_next;
384*433d6423SLionel Sambuc 	}
385*433d6423SLionel Sambuc 
386*433d6423SLionel Sambuc 	/* unknown symbol, add it */
387*433d6423SLionel Sambuc 	*ptr = symbol = MALLOC_CHECKED(struct symbol_info, 1);
388*433d6423SLionel Sambuc 	memset(symbol, 0, sizeof(struct symbol_info));
389*433d6423SLionel Sambuc 	strncpy(symbol->binary, binary, PROC_NAME_LEN);
390*433d6423SLionel Sambuc 	strncpy(symbol->name, name, SYMBOL_NAME_SIZE);
391*433d6423SLionel Sambuc 	symbol->count[0].min = ~0UL;
392*433d6423SLionel Sambuc 	symbol->count[1].min = ~0UL;
393*433d6423SLionel Sambuc 	symbol->class = symbol_classify(binary, name);
394*433d6423SLionel Sambuc 
395*433d6423SLionel Sambuc 	/* also add to linked list */
396*433d6423SLionel Sambuc 	symbol->next = symbols;
397*433d6423SLionel Sambuc 	symbols = symbol;
398*433d6423SLionel Sambuc 	return symbol;
399*433d6423SLionel Sambuc }
400*433d6423SLionel Sambuc 
symbol_hash(const char * binary,const char * name)401*433d6423SLionel Sambuc static unsigned symbol_hash(const char *binary, const char *name) {
402*433d6423SLionel Sambuc 	return string_hash(binary, PROC_NAME_LEN) +
403*433d6423SLionel Sambuc 		string_hash(name, SYMBOL_NAME_SIZE);
404*433d6423SLionel Sambuc }
405*433d6423SLionel Sambuc 
symbol_qsort_compare(const void * p1,const void * p2)406*433d6423SLionel Sambuc static int symbol_qsort_compare(const void *p1, const void *p2) {
407*433d6423SLionel Sambuc 	int r;
408*433d6423SLionel Sambuc 	const struct symbol_info *s1, *s2;
409*433d6423SLionel Sambuc 
410*433d6423SLionel Sambuc 	assert(p1);
411*433d6423SLionel Sambuc 	assert(p2);
412*433d6423SLionel Sambuc 	s1 = *(const struct symbol_info **) p1;
413*433d6423SLionel Sambuc 	s2 = *(const struct symbol_info **) p2;
414*433d6423SLionel Sambuc 	assert(s1);
415*433d6423SLionel Sambuc 	assert(s2);
416*433d6423SLionel Sambuc 
417*433d6423SLionel Sambuc 	/* totals come first */
418*433d6423SLionel Sambuc 	if (s1->class < s2->class) return -1;
419*433d6423SLionel Sambuc 	if (s1->class > s2->class) return 1;
420*433d6423SLionel Sambuc 
421*433d6423SLionel Sambuc 	/* sort by difference in average */
422*433d6423SLionel Sambuc 	if (s1->diff < s2->diff) return -1;
423*433d6423SLionel Sambuc 	if (s1->diff > s2->diff) return 1;
424*433d6423SLionel Sambuc 
425*433d6423SLionel Sambuc 	/* otherwise, by name */
426*433d6423SLionel Sambuc 	r = strncmp(s1->binary, s2->binary, PROC_NAME_LEN);
427*433d6423SLionel Sambuc 	if (r) return r;
428*433d6423SLionel Sambuc 
429*433d6423SLionel Sambuc 	return strncmp(s1->name, s2->name, SYMBOL_NAME_SIZE);
430*433d6423SLionel Sambuc }
431*433d6423SLionel Sambuc 
symbol_tally(const char * binary,const char * name,unsigned long samples,int count_index)432*433d6423SLionel Sambuc static void symbol_tally(const char *binary, const char *name,
433*433d6423SLionel Sambuc 	unsigned long samples, int count_index) {
434*433d6423SLionel Sambuc 	struct symbol_count *count;
435*433d6423SLionel Sambuc 	struct symbol_info *symbol;
436*433d6423SLionel Sambuc 
437*433d6423SLionel Sambuc 	/* look up or add symbol */
438*433d6423SLionel Sambuc 	symbol = symbol_find_or_add(binary, name);
439*433d6423SLionel Sambuc 
440*433d6423SLionel Sambuc 	/* update count */
441*433d6423SLionel Sambuc 	count = &symbol->count[count_index];
442*433d6423SLionel Sambuc 	count->sum += samples;
443*433d6423SLionel Sambuc 	count->sum2 += (unsigned long long) samples * samples;
444*433d6423SLionel Sambuc 	if (count->min > samples) count->min = samples;
445*433d6423SLionel Sambuc 	if (count->max < samples) count->max = samples;
446*433d6423SLionel Sambuc }
447*433d6423SLionel Sambuc 
symbols_count(void)448*433d6423SLionel Sambuc static unsigned symbols_count(void) {
449*433d6423SLionel Sambuc 	int count = 0;
450*433d6423SLionel Sambuc 	const struct symbol_info *symbol;
451*433d6423SLionel Sambuc 
452*433d6423SLionel Sambuc 	for (symbol = symbols; symbol; symbol = symbol->next) {
453*433d6423SLionel Sambuc 		count++;
454*433d6423SLionel Sambuc 	}
455*433d6423SLionel Sambuc 	return count;
456*433d6423SLionel Sambuc }
457*433d6423SLionel Sambuc 
usage(const char * argv0)458*433d6423SLionel Sambuc static void usage(const char *argv0) {
459*433d6423SLionel Sambuc 	printf("usage:\n");
460*433d6423SLionel Sambuc 	printf("  %s leftfile rightfile\n", argv0);
461*433d6423SLionel Sambuc 	printf("  %s leftfile... -r rightfile...\n", argv0);
462*433d6423SLionel Sambuc 	printf("\n");
463*433d6423SLionel Sambuc 	printf("sprofdiff compares the sprofile information from multiple\n");
464*433d6423SLionel Sambuc 	printf("output files of sprofalyze -d.\n");
465*433d6423SLionel Sambuc 	exit(1);
466*433d6423SLionel Sambuc }
467