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