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