• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2013 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4  *
5  *
6  * This code was inspired by Ezequiel Garcia's trace_analyze program:
7  *   git://github.com/ezequielgarcia/trace_analyze.git
8  *
9  * Unfortuntately, I hate working with Python, and I also had trouble
10  * getting it to work, as I had an old python on my Fedora 13, and it
11  * was written for the newer version. I decided to do some of it here
12  * in C.
13  */
14 #include <dirent.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <signal.h>
20 
21 #include "trace-local.h"
22 #include "trace-hash-local.h"
23 #include "list.h"
24 
25 static int kmalloc_type;
26 static int kmalloc_node_type;
27 static int kfree_type;
28 static int kmem_cache_alloc_type;
29 static int kmem_cache_alloc_node_type;
30 static int kmem_cache_free_type;
31 
32 static struct tep_format_field *common_type_mem;
33 
34 static struct tep_format_field *kmalloc_callsite_field;
35 static struct tep_format_field *kmalloc_bytes_req_field;
36 static struct tep_format_field *kmalloc_bytes_alloc_field;
37 static struct tep_format_field *kmalloc_ptr_field;
38 
39 static struct tep_format_field *kmalloc_node_callsite_field;
40 static struct tep_format_field *kmalloc_node_bytes_req_field;
41 static struct tep_format_field *kmalloc_node_bytes_alloc_field;
42 static struct tep_format_field *kmalloc_node_ptr_field;
43 
44 static struct tep_format_field *kfree_ptr_field;
45 
46 static struct tep_format_field *kmem_cache_callsite_field;
47 static struct tep_format_field *kmem_cache_bytes_req_field;
48 static struct tep_format_field *kmem_cache_bytes_alloc_field;
49 static struct tep_format_field *kmem_cache_ptr_field;
50 
51 static struct tep_format_field *kmem_cache_node_callsite_field;
52 static struct tep_format_field *kmem_cache_node_bytes_req_field;
53 static struct tep_format_field *kmem_cache_node_bytes_alloc_field;
54 static struct tep_format_field *kmem_cache_node_ptr_field;
55 
56 static struct tep_format_field *kmem_cache_free_ptr_field;
57 
zalloc(size_t size)58 static void *zalloc(size_t size)
59 {
60 	return calloc(1, size);
61 }
62 
63 static struct tep_event *
update_event(struct tep_handle * pevent,const char * sys,const char * name,int * id)64 update_event(struct tep_handle *pevent,
65 	     const char *sys, const char *name, int *id)
66 {
67 	struct tep_event *event;
68 
69 	event = tep_find_event_by_name(pevent, sys, name);
70 	if (!event)
71 		return NULL;
72 
73 	*id = event->id;
74 
75 	return event;
76 }
77 
update_kmalloc(struct tep_handle * pevent)78 static void update_kmalloc(struct tep_handle *pevent)
79 {
80 	struct tep_event *event;
81 
82 	event = update_event(pevent, "kmem", "kmalloc", &kmalloc_type);
83 	if (!event)
84 		return;
85 
86 	kmalloc_callsite_field = tep_find_field(event, "call_site");
87 	kmalloc_bytes_req_field = tep_find_field(event, "bytes_req");
88 	kmalloc_bytes_alloc_field = tep_find_field(event, "bytes_alloc");
89 	kmalloc_ptr_field = tep_find_field(event, "ptr");
90 }
91 
update_kmalloc_node(struct tep_handle * pevent)92 static void update_kmalloc_node(struct tep_handle *pevent)
93 {
94 	struct tep_event *event;
95 
96 	event = update_event(pevent, "kmem", "kmalloc_node", &kmalloc_node_type);
97 	if (!event)
98 		return;
99 
100 	kmalloc_node_callsite_field = tep_find_field(event, "call_site");
101 	kmalloc_node_bytes_req_field = tep_find_field(event, "bytes_req");
102 	kmalloc_node_bytes_alloc_field = tep_find_field(event, "bytes_alloc");
103 	kmalloc_node_ptr_field = tep_find_field(event, "ptr");
104 }
105 
update_kfree(struct tep_handle * pevent)106 static void update_kfree(struct tep_handle *pevent)
107 {
108 	struct tep_event *event;
109 
110 	event = update_event(pevent, "kmem", "kfree", &kfree_type);
111 	if (!event)
112 		return;
113 
114 	kfree_ptr_field = tep_find_field(event, "ptr");
115 }
116 
update_kmem_cache_alloc(struct tep_handle * pevent)117 static void update_kmem_cache_alloc(struct tep_handle *pevent)
118 {
119 	struct tep_event *event;
120 
121 	event = update_event(pevent, "kmem", "kmem_cache_alloc", &kmem_cache_alloc_type);
122 	if (!event)
123 		return;
124 
125 	kmem_cache_callsite_field = tep_find_field(event, "call_site");
126 	kmem_cache_bytes_req_field = tep_find_field(event, "bytes_req");
127 	kmem_cache_bytes_alloc_field = tep_find_field(event, "bytes_alloc");
128 	kmem_cache_ptr_field = tep_find_field(event, "ptr");
129 }
130 
update_kmem_cache_alloc_node(struct tep_handle * pevent)131 static void update_kmem_cache_alloc_node(struct tep_handle *pevent)
132 {
133 	struct tep_event *event;
134 
135 	event = update_event(pevent, "kmem", "kmem_cache_alloc_node",
136 			     &kmem_cache_alloc_node_type);
137 	if (!event)
138 		return;
139 
140 	kmem_cache_node_callsite_field = tep_find_field(event, "call_site");
141 	kmem_cache_node_bytes_req_field = tep_find_field(event, "bytes_req");
142 	kmem_cache_node_bytes_alloc_field = tep_find_field(event, "bytes_alloc");
143 	kmem_cache_node_ptr_field = tep_find_field(event, "ptr");
144 }
145 
update_kmem_cache_free(struct tep_handle * pevent)146 static void update_kmem_cache_free(struct tep_handle *pevent)
147 {
148 	struct tep_event *event;
149 
150 	event = update_event(pevent, "kmem", "kmem_cache_free", &kmem_cache_free_type);
151 	if (!event)
152 		return;
153 
154 	kmem_cache_free_ptr_field = tep_find_field(event, "ptr");
155 }
156 
157 struct func_descr {
158 	struct func_descr	*next;
159 	const char		*func;
160 	unsigned long		total_alloc;
161 	unsigned long		total_req;
162 	unsigned long		current_alloc;
163 	unsigned long		current_req;
164 	unsigned long		max_alloc;
165 	unsigned long		max_req;
166 	unsigned long		waste;
167 	unsigned long		max_waste;
168 };
169 
170 struct ptr_descr {
171 	struct ptr_descr	*next;
172 	struct func_descr	*func;
173 	unsigned long long	ptr;
174 	unsigned long		alloc;
175 	unsigned long		req;
176 };
177 
178 #define HASH_BITS	12
179 #define HASH_SIZE	(1 << HASH_BITS)
180 #define HASH_MASK	(HASH_SIZE - 1);
181 
182 static struct func_descr *func_hash[HASH_SIZE];
183 static struct ptr_descr *ptr_hash[HASH_SIZE];
184 static struct func_descr **func_list;
185 
186 static unsigned func_count;
187 
make_key(const void * ptr,int size)188 static int make_key(const void *ptr, int size)
189 {
190 	int key = 0;
191 	int i;
192 	char *kp = (char *)&key;
193 	const char *indx = ptr;
194 
195 	for (i = 0; i < size; i++)
196 		kp[i & 3] ^= indx[i];
197 
198 	return trace_hash(key);
199 }
200 
find_func(const char * func)201 static struct func_descr *find_func(const char *func)
202 {
203 	struct func_descr *funcd;
204 	int key = make_key(func, strlen(func)) & HASH_MASK;
205 
206 	for (funcd = func_hash[key]; funcd; funcd = funcd->next) {
207 		/*
208 		 * As func is always a constant to one pointer,
209 		 * we can use a direct compare instead of strcmp.
210 		 */
211 		if (funcd->func == func)
212 			return funcd;
213 	}
214 
215 	return NULL;
216 }
217 
create_func(const char * func)218 static struct func_descr *create_func(const char *func)
219 {
220 	struct func_descr *funcd;
221 	int key = make_key(func, strlen(func)) & HASH_MASK;
222 
223 	funcd = zalloc(sizeof(*funcd));
224 	if (!funcd)
225 		die("malloc");
226 
227 	funcd->func = func;
228 	funcd->next = func_hash[key];
229 	func_hash[key] = funcd;
230 
231 	func_count++;
232 
233 	return funcd;
234 }
235 
find_ptr(unsigned long long ptr)236 static struct ptr_descr *find_ptr(unsigned long long ptr)
237 {
238 	struct ptr_descr *ptrd;
239 	int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;
240 
241 	for (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) {
242 		if (ptrd->ptr == ptr)
243 			return ptrd;
244 	}
245 
246 	return NULL;
247 }
248 
create_ptr(unsigned long long ptr)249 static struct ptr_descr *create_ptr(unsigned long long ptr)
250 {
251 	struct ptr_descr *ptrd;
252 	int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;
253 
254 	ptrd = zalloc(sizeof(*ptrd));
255 	if (!ptrd)
256 		die("malloc");
257 
258 	ptrd->ptr = ptr;
259 	ptrd->next = ptr_hash[key];
260 	ptr_hash[key] = ptrd;
261 
262 	return ptrd;
263 }
264 
remove_ptr(unsigned long long ptr)265 static void remove_ptr(unsigned long long ptr)
266 {
267 	struct ptr_descr *ptrd, **last;
268 	int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK;
269 
270 	last = &ptr_hash[key];
271 	for (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) {
272 		if (ptrd->ptr == ptr)
273 			break;
274 		last = &ptrd->next;
275 	}
276 
277 	if (!ptrd)
278 		return;
279 
280 	*last = ptrd->next;
281 	free(ptrd);
282 }
283 
add_kmalloc(const char * func,unsigned long long ptr,unsigned int req,int alloc)284 static void add_kmalloc(const char *func, unsigned long long ptr,
285 			unsigned int req, int alloc)
286 {
287 	struct func_descr *funcd;
288 	struct ptr_descr *ptrd;
289 
290 	funcd = find_func(func);
291 	if (!funcd)
292 		funcd = create_func(func);
293 
294 	funcd->total_alloc += alloc;
295 	funcd->total_req += req;
296 	funcd->current_alloc += alloc;
297 	funcd->current_req += req;
298 	if (funcd->current_alloc > funcd->max_alloc)
299 		funcd->max_alloc = funcd->current_alloc;
300 	if (funcd->current_req > funcd->max_req)
301 		funcd->max_req = funcd->current_req;
302 
303 	ptrd = find_ptr(ptr);
304 	if (!ptrd)
305 		ptrd = create_ptr(ptr);
306 
307 	ptrd->alloc = alloc;
308 	ptrd->req = req;
309 	ptrd->func = funcd;
310 }
311 
remove_kmalloc(unsigned long long ptr)312 static void remove_kmalloc(unsigned long long ptr)
313 {
314 	struct func_descr *funcd;
315 	struct ptr_descr *ptrd;
316 
317 	ptrd = find_ptr(ptr);
318 	if (!ptrd)
319 		return;
320 
321 	funcd = ptrd->func;
322 	funcd->current_alloc -= ptrd->alloc;
323 	funcd->current_req -= ptrd->req;
324 
325 	remove_ptr(ptr);
326 }
327 
328 static void
process_kmalloc(struct tep_handle * pevent,struct tep_record * record,struct tep_format_field * callsite_field,struct tep_format_field * bytes_req_field,struct tep_format_field * bytes_alloc_field,struct tep_format_field * ptr_field)329 process_kmalloc(struct tep_handle *pevent, struct tep_record *record,
330 		struct tep_format_field *callsite_field,
331 		struct tep_format_field *bytes_req_field,
332 		struct tep_format_field *bytes_alloc_field,
333 		struct tep_format_field *ptr_field)
334 {
335 	unsigned long long callsite;
336 	unsigned long long val;
337 	unsigned long long ptr;
338 	unsigned int req;
339 	int alloc;
340 	const char *func;
341 
342 	tep_read_number_field(callsite_field, record->data, &callsite);
343 	tep_read_number_field(bytes_req_field, record->data, &val);
344 	req = val;
345 	tep_read_number_field(bytes_alloc_field, record->data, &val);
346 	alloc = val;
347 	tep_read_number_field(ptr_field, record->data, &ptr);
348 
349 	func = tep_find_function(pevent, callsite);
350 
351 	add_kmalloc(func, ptr, req, alloc);
352 }
353 
354 static void
process_kfree(struct tep_handle * pevent,struct tep_record * record,struct tep_format_field * ptr_field)355 process_kfree(struct tep_handle *pevent, struct tep_record *record,
356 	      struct tep_format_field *ptr_field)
357 {
358 	unsigned long long ptr;
359 
360 	tep_read_number_field(ptr_field, record->data, &ptr);
361 
362 	remove_kmalloc(ptr);
363 }
364 
365 static void
process_record(struct tep_handle * pevent,struct tep_record * record)366 process_record(struct tep_handle *pevent, struct tep_record *record)
367 {
368 	unsigned long long val;
369 	int type;
370 
371 	tep_read_number_field(common_type_mem, record->data, &val);
372 	type = val;
373 
374 	if (type == kmalloc_type)
375 		return process_kmalloc(pevent, record,
376 				       kmalloc_callsite_field,
377 				       kmalloc_bytes_req_field,
378 				       kmalloc_bytes_alloc_field,
379 				       kmalloc_ptr_field);
380 	if (type == kmalloc_node_type)
381 		return process_kmalloc(pevent, record,
382 				       kmalloc_node_callsite_field,
383 				       kmalloc_node_bytes_req_field,
384 				       kmalloc_node_bytes_alloc_field,
385 				       kmalloc_node_ptr_field);
386 	if (type == kfree_type)
387 		return process_kfree(pevent, record, kfree_ptr_field);
388 
389 	if (type == kmem_cache_alloc_type)
390 		return process_kmalloc(pevent, record,
391 				       kmem_cache_callsite_field,
392 				       kmem_cache_bytes_req_field,
393 				       kmem_cache_bytes_alloc_field,
394 				       kmem_cache_ptr_field);
395 	if (type == kmem_cache_alloc_node_type)
396 		return process_kmalloc(pevent, record,
397 				       kmem_cache_node_callsite_field,
398 				       kmem_cache_node_bytes_req_field,
399 				       kmem_cache_node_bytes_alloc_field,
400 				       kmem_cache_node_ptr_field);
401 	if (type == kmem_cache_free_type)
402 		return process_kfree(pevent, record, kmem_cache_free_ptr_field);
403 }
404 
func_cmp(const void * a,const void * b)405 static int func_cmp(const void *a, const void *b)
406 {
407 	const struct func_descr *fa = *(const struct func_descr **)a;
408 	const struct func_descr *fb = *(const struct func_descr **)b;
409 
410 	if (fa->waste > fb->waste)
411 		return -1;
412 	if (fa->waste < fb->waste)
413 		return 1;
414 	return 0;
415 }
416 
sort_list(void)417 static void sort_list(void)
418 {
419 	struct func_descr *funcd;
420 	int h;
421 	int i = 0;
422 
423 	func_list = zalloc(sizeof(*func_list) * func_count);
424 
425 	for (h = 0; h < HASH_SIZE; h++) {
426 		for (funcd = func_hash[h]; funcd; funcd = funcd->next) {
427 			funcd->waste = funcd->current_alloc - funcd->current_req;
428 			funcd->max_waste = funcd->max_alloc - funcd->max_req;
429 			if (i == func_count)
430 				die("more funcs than expected\n");
431 			func_list[i++] = funcd;
432 		}
433 	}
434 
435 	qsort(func_list, func_count, sizeof(*func_list), func_cmp);
436 }
437 
print_list(void)438 static void print_list(void)
439 {
440 	struct func_descr *funcd;
441 	int i;
442 
443 	printf("                Function            \t");
444 	printf("Waste\tAlloc\treq\t\tTotAlloc     TotReq\t\tMaxAlloc     MaxReq\t");
445 	printf("MaxWaste\n");
446 	printf("                --------            \t");
447 	printf("-----\t-----\t---\t\t--------     ------\t\t--------     ------\t");
448 	printf("--------\n");
449 
450 	for (i = 0; i < func_count; i++) {
451 		funcd = func_list[i];
452 
453 		printf("%32s\t%ld\t%ld\t%ld\t\t%8ld   %8ld\t\t%8ld   %8ld\t%ld\n",
454 		       funcd->func, funcd->waste,
455 		       funcd->current_alloc, funcd->current_req,
456 		       funcd->total_alloc, funcd->total_req,
457 		       funcd->max_alloc, funcd->max_req, funcd->max_waste);
458 	}
459 }
460 
do_trace_mem(struct tracecmd_input * handle)461 static void do_trace_mem(struct tracecmd_input *handle)
462 {
463 	struct tep_handle *pevent = tracecmd_get_tep(handle);
464 	struct tep_record *record;
465 	struct tep_event *event;
466 	int missed_events = 0;
467 	int cpus;
468 	int cpu;
469 	int ret;
470 
471 	ret = tracecmd_init_data(handle);
472 	if (ret < 0)
473 		die("failed to init data");
474 
475 	if (ret > 0)
476 		die("trace-cmd mem does not work with latency traces\n");
477 
478 	cpus = tracecmd_cpus(handle);
479 
480 	/* Need to get any event */
481 	for (cpu = 0; cpu < cpus; cpu++) {
482 		record = tracecmd_peek_data(handle, cpu);
483 		if (record)
484 			break;
485 	}
486 	if (!record)
487 		die("No records found in file");
488 
489 	ret = tep_data_type(pevent, record);
490 	event = tep_find_event(pevent, ret);
491 
492 	common_type_mem = tep_find_common_field(event, "common_type");
493 	if (!common_type_mem)
494 		die("Can't find a 'type' field?");
495 
496 	update_kmalloc(pevent);
497 	update_kmalloc_node(pevent);
498 	update_kfree(pevent);
499 	update_kmem_cache_alloc(pevent);
500 	update_kmem_cache_alloc_node(pevent);
501 	update_kmem_cache_free(pevent);
502 
503 	while ((record = tracecmd_read_next_data(handle, &cpu))) {
504 
505 		/* record missed event */
506 		if (!missed_events && record->missed_events)
507 			missed_events = 1;
508 
509 		process_record(pevent, record);
510 		tracecmd_free_record(record);
511 	}
512 
513 	sort_list();
514 	print_list();
515 }
516 
trace_mem(int argc,char ** argv)517 void trace_mem(int argc, char **argv)
518 {
519 	struct tracecmd_input *handle;
520 	const char *input_file = NULL;
521 	int ret;
522 
523 	for (;;) {
524 		int c;
525 
526 		c = getopt(argc-1, argv+1, "+hi:");
527 		if (c == -1)
528 			break;
529 		switch (c) {
530 		case 'h':
531 			usage(argv);
532 			break;
533 		case 'i':
534 			if (input_file)
535 				die("Only one input for mem");
536 			input_file = optarg;
537 			break;
538 		default:
539 			usage(argv);
540 		}
541 	}
542 
543 	if ((argc - optind) >= 2) {
544 		if (input_file)
545 			usage(argv);
546 		input_file = argv[optind + 1];
547 	}
548 
549 	if (!input_file)
550 		input_file = DEFAULT_INPUT_FILE;
551 
552 	handle = tracecmd_alloc(input_file, 0);
553 	if (!handle)
554 		die("can't open %s\n", input_file);
555 
556 	ret = tracecmd_read_headers(handle, 0);
557 	if (ret)
558 		return;
559 
560 	do_trace_mem(handle);
561 
562 	tracecmd_close(handle);
563 }
564