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