Blame view
tools/perf/builtin-kmem.c
17.2 KB
ba77c9e11 perf: Add 'perf k... |
1 2 3 4 5 6 7 8 |
#include "builtin.h" #include "perf.h" #include "util/util.h" #include "util/cache.h" #include "util/symbol.h" #include "util/thread.h" #include "util/header.h" |
94c744b6c perf tools: Intro... |
9 |
#include "util/session.h" |
45694aa77 perf tools: Renam... |
10 |
#include "util/tool.h" |
ba77c9e11 perf: Add 'perf k... |
11 12 13 14 15 |
#include "util/parse-options.h" #include "util/trace-event.h" #include "util/debug.h" |
ba77c9e11 perf: Add 'perf k... |
16 17 18 19 20 |
#include <linux/rbtree.h> struct alloc_stat; typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); |
efad14150 perf report: Acce... |
21 |
static const char *input_name; |
ba77c9e11 perf: Add 'perf k... |
22 |
|
ba77c9e11 perf: Add 'perf k... |
23 24 |
static int alloc_flag; static int caller_flag; |
ba77c9e11 perf: Add 'perf k... |
25 26 |
static int alloc_lines = -1; static int caller_lines = -1; |
7707b6b6f perf kmem: Add ne... |
27 |
static bool raw_ip; |
29b3e1528 perf kmem: Defaul... |
28 |
static char default_sort_order[] = "frag,hit,bytes"; |
7d0d39459 perf kmem: Collec... |
29 30 |
static int *cpunode_map; static int max_cpu_num; |
ba77c9e11 perf: Add 'perf k... |
31 |
struct alloc_stat { |
079d3f653 perf kmem: Measur... |
32 33 |
u64 call_site; u64 ptr; |
ba77c9e11 perf: Add 'perf k... |
34 35 36 |
u64 bytes_req; u64 bytes_alloc; u32 hit; |
079d3f653 perf kmem: Measur... |
37 38 39 |
u32 pingpong; short alloc_cpu; |
ba77c9e11 perf: Add 'perf k... |
40 41 42 43 44 45 46 47 48 49 |
struct rb_node node; }; static struct rb_root root_alloc_stat; static struct rb_root root_alloc_sorted; static struct rb_root root_caller_stat; static struct rb_root root_caller_sorted; static unsigned long total_requested, total_allocated; |
7d0d39459 perf kmem: Collec... |
50 |
static unsigned long nr_allocs, nr_cross_allocs; |
ba77c9e11 perf: Add 'perf k... |
51 |
|
7d0d39459 perf kmem: Collec... |
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
#define PATH_SYS_NODE "/sys/devices/system/node" static void init_cpunode_map(void) { FILE *fp; int i; fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); if (!fp) { max_cpu_num = 4096; return; } if (fscanf(fp, "%d", &max_cpu_num) < 1) die("Failed to read 'kernel_max' from sysfs"); max_cpu_num++; cpunode_map = calloc(max_cpu_num, sizeof(int)); if (!cpunode_map) die("calloc"); for (i = 0; i < max_cpu_num; i++) cpunode_map[i] = -1; fclose(fp); } static void setup_cpunode_map(void) { struct dirent *dent1, *dent2; DIR *dir1, *dir2; unsigned int cpu, mem; char buf[PATH_MAX]; init_cpunode_map(); dir1 = opendir(PATH_SYS_NODE); if (!dir1) return; |
659d8cfbb perf tools: Do a ... |
89 90 91 |
while ((dent1 = readdir(dir1)) != NULL) { if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) |
7d0d39459 perf kmem: Collec... |
92 93 94 95 96 97 |
continue; snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); dir2 = opendir(buf); if (!dir2) continue; |
659d8cfbb perf tools: Do a ... |
98 99 100 |
while ((dent2 = readdir(dir2)) != NULL) { if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) |
7d0d39459 perf kmem: Collec... |
101 102 103 |
continue; cpunode_map[cpu] = mem; } |
8442da1d9 perf kmem: Add mi... |
104 |
closedir(dir2); |
7d0d39459 perf kmem: Collec... |
105 |
} |
8442da1d9 perf kmem: Add mi... |
106 |
closedir(dir1); |
7d0d39459 perf kmem: Collec... |
107 |
} |
079d3f653 perf kmem: Measur... |
108 109 |
static void insert_alloc_stat(unsigned long call_site, unsigned long ptr, int bytes_req, int bytes_alloc, int cpu) |
ba77c9e11 perf: Add 'perf k... |
110 111 112 113 |
{ struct rb_node **node = &root_alloc_stat.rb_node; struct rb_node *parent = NULL; struct alloc_stat *data = NULL; |
ba77c9e11 perf: Add 'perf k... |
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
while (*node) { parent = *node; data = rb_entry(*node, struct alloc_stat, node); if (ptr > data->ptr) node = &(*node)->rb_right; else if (ptr < data->ptr) node = &(*node)->rb_left; else break; } if (data && data->ptr == ptr) { data->hit++; data->bytes_req += bytes_req; |
4efb5290a perf kmem: Fix st... |
129 |
data->bytes_alloc += bytes_alloc; |
ba77c9e11 perf: Add 'perf k... |
130 131 |
} else { data = malloc(sizeof(*data)); |
079d3f653 perf kmem: Measur... |
132 133 |
if (!data) die("malloc"); |
ba77c9e11 perf: Add 'perf k... |
134 |
data->ptr = ptr; |
079d3f653 perf kmem: Measur... |
135 |
data->pingpong = 0; |
ba77c9e11 perf: Add 'perf k... |
136 137 138 139 140 141 142 |
data->hit = 1; data->bytes_req = bytes_req; data->bytes_alloc = bytes_alloc; rb_link_node(&data->node, parent, node); rb_insert_color(&data->node, &root_alloc_stat); } |
079d3f653 perf kmem: Measur... |
143 144 |
data->call_site = call_site; data->alloc_cpu = cpu; |
ba77c9e11 perf: Add 'perf k... |
145 146 147 148 149 150 151 152 |
} static void insert_caller_stat(unsigned long call_site, int bytes_req, int bytes_alloc) { struct rb_node **node = &root_caller_stat.rb_node; struct rb_node *parent = NULL; struct alloc_stat *data = NULL; |
ba77c9e11 perf: Add 'perf k... |
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
while (*node) { parent = *node; data = rb_entry(*node, struct alloc_stat, node); if (call_site > data->call_site) node = &(*node)->rb_right; else if (call_site < data->call_site) node = &(*node)->rb_left; else break; } if (data && data->call_site == call_site) { data->hit++; data->bytes_req += bytes_req; |
4efb5290a perf kmem: Fix st... |
168 |
data->bytes_alloc += bytes_alloc; |
ba77c9e11 perf: Add 'perf k... |
169 170 |
} else { data = malloc(sizeof(*data)); |
079d3f653 perf kmem: Measur... |
171 172 |
if (!data) die("malloc"); |
ba77c9e11 perf: Add 'perf k... |
173 |
data->call_site = call_site; |
079d3f653 perf kmem: Measur... |
174 |
data->pingpong = 0; |
ba77c9e11 perf: Add 'perf k... |
175 176 177 178 179 180 181 182 |
data->hit = 1; data->bytes_req = bytes_req; data->bytes_alloc = bytes_alloc; rb_link_node(&data->node, parent, node); rb_insert_color(&data->node, &root_caller_stat); } } |
f48f669d4 perf_event: Elimi... |
183 |
static void process_alloc_event(void *data, |
ba77c9e11 perf: Add 'perf k... |
184 |
struct event *event, |
7d0d39459 perf kmem: Collec... |
185 |
int cpu, |
ba77c9e11 perf: Add 'perf k... |
186 187 |
u64 timestamp __used, struct thread *thread __used, |
7d0d39459 perf kmem: Collec... |
188 |
int node) |
ba77c9e11 perf: Add 'perf k... |
189 190 191 192 193 |
{ unsigned long call_site; unsigned long ptr; int bytes_req; int bytes_alloc; |
7d0d39459 perf kmem: Collec... |
194 |
int node1, node2; |
ba77c9e11 perf: Add 'perf k... |
195 |
|
f48f669d4 perf_event: Elimi... |
196 197 198 199 |
ptr = raw_field_value(event, "ptr", data); call_site = raw_field_value(event, "call_site", data); bytes_req = raw_field_value(event, "bytes_req", data); bytes_alloc = raw_field_value(event, "bytes_alloc", data); |
ba77c9e11 perf: Add 'perf k... |
200 |
|
079d3f653 perf kmem: Measur... |
201 |
insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu); |
ba77c9e11 perf: Add 'perf k... |
202 203 204 205 |
insert_caller_stat(call_site, bytes_req, bytes_alloc); total_requested += bytes_req; total_allocated += bytes_alloc; |
7d0d39459 perf kmem: Collec... |
206 207 208 |
if (node) { node1 = cpunode_map[cpu]; |
f48f669d4 perf_event: Elimi... |
209 |
node2 = raw_field_value(event, "node", data); |
7d0d39459 perf kmem: Collec... |
210 211 212 213 |
if (node1 != node2) nr_cross_allocs++; } nr_allocs++; |
ba77c9e11 perf: Add 'perf k... |
214 |
} |
079d3f653 perf kmem: Measur... |
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
static int ptr_cmp(struct alloc_stat *, struct alloc_stat *); static int callsite_cmp(struct alloc_stat *, struct alloc_stat *); static struct alloc_stat *search_alloc_stat(unsigned long ptr, unsigned long call_site, struct rb_root *root, sort_fn_t sort_fn) { struct rb_node *node = root->rb_node; struct alloc_stat key = { .ptr = ptr, .call_site = call_site }; while (node) { struct alloc_stat *data; int cmp; data = rb_entry(node, struct alloc_stat, node); cmp = sort_fn(&key, data); if (cmp < 0) node = node->rb_left; else if (cmp > 0) node = node->rb_right; else return data; } return NULL; } |
f48f669d4 perf_event: Elimi... |
242 |
static void process_free_event(void *data, |
079d3f653 perf kmem: Measur... |
243 244 |
struct event *event, int cpu, |
ba77c9e11 perf: Add 'perf k... |
245 246 247 |
u64 timestamp __used, struct thread *thread __used) { |
079d3f653 perf kmem: Measur... |
248 249 |
unsigned long ptr; struct alloc_stat *s_alloc, *s_caller; |
f48f669d4 perf_event: Elimi... |
250 |
ptr = raw_field_value(event, "ptr", data); |
079d3f653 perf kmem: Measur... |
251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); if (!s_alloc) return; if (cpu != s_alloc->alloc_cpu) { s_alloc->pingpong++; s_caller = search_alloc_stat(0, s_alloc->call_site, &root_caller_stat, callsite_cmp); assert(s_caller); s_caller->pingpong++; } s_alloc->alloc_cpu = -1; |
ba77c9e11 perf: Add 'perf k... |
265 |
} |
8115d60c3 perf tools: Kill ... |
266 267 |
static void process_raw_event(union perf_event *raw_event __used, void *data, int cpu, u64 timestamp, struct thread *thread) |
ba77c9e11 perf: Add 'perf k... |
268 |
{ |
ba77c9e11 perf: Add 'perf k... |
269 270 |
struct event *event; int type; |
f48f669d4 perf_event: Elimi... |
271 |
type = trace_parse_common_type(data); |
ba77c9e11 perf: Add 'perf k... |
272 273 274 275 |
event = trace_find_event(type); if (!strcmp(event->name, "kmalloc") || !strcmp(event->name, "kmem_cache_alloc")) { |
f48f669d4 perf_event: Elimi... |
276 |
process_alloc_event(data, event, cpu, timestamp, thread, 0); |
ba77c9e11 perf: Add 'perf k... |
277 278 279 280 281 |
return; } if (!strcmp(event->name, "kmalloc_node") || !strcmp(event->name, "kmem_cache_alloc_node")) { |
f48f669d4 perf_event: Elimi... |
282 |
process_alloc_event(data, event, cpu, timestamp, thread, 1); |
ba77c9e11 perf: Add 'perf k... |
283 284 285 286 287 |
return; } if (!strcmp(event->name, "kfree") || !strcmp(event->name, "kmem_cache_free")) { |
f48f669d4 perf_event: Elimi... |
288 |
process_free_event(data, event, cpu, timestamp, thread); |
ba77c9e11 perf: Add 'perf k... |
289 290 291 |
return; } } |
45694aa77 perf tools: Renam... |
292 |
static int process_sample_event(struct perf_tool *tool __used, |
d20deb64e perf tools: Pass ... |
293 |
union perf_event *event, |
8115d60c3 perf tools: Kill ... |
294 |
struct perf_sample *sample, |
9e69c2108 perf session: Pas... |
295 |
struct perf_evsel *evsel __used, |
743eb8686 perf tools: Resol... |
296 |
struct machine *machine) |
ba77c9e11 perf: Add 'perf k... |
297 |
{ |
743eb8686 perf tools: Resol... |
298 |
struct thread *thread = machine__findnew_thread(machine, event->ip.pid); |
ba77c9e11 perf: Add 'perf k... |
299 |
|
ba77c9e11 perf: Add 'perf k... |
300 301 302 303 304 305 306 307 308 |
if (thread == NULL) { pr_debug("problem processing %d event, skipping it. ", event->header.type); return -1; } dump_printf(" ... thread: %s:%d ", thread->comm, thread->pid); |
640c03ce8 perf session: Par... |
309 310 |
process_raw_event(event, sample->raw_data, sample->cpu, sample->time, thread); |
ba77c9e11 perf: Add 'perf k... |
311 312 313 |
return 0; } |
45694aa77 perf tools: Renam... |
314 |
static struct perf_tool perf_kmem = { |
587570d4c perf: Use generic... |
315 |
.sample = process_sample_event, |
8115d60c3 perf tools: Kill ... |
316 |
.comm = perf_event__process_comm, |
587570d4c perf: Use generic... |
317 |
.ordered_samples = true, |
ba77c9e11 perf: Add 'perf k... |
318 |
}; |
ba77c9e11 perf: Add 'perf k... |
319 320 321 322 323 324 325 |
static double fragmentation(unsigned long n_req, unsigned long n_alloc) { if (n_alloc == 0) return 0.0; else return 100.0 - (100.0 * n_req / n_alloc); } |
4aa656364 perf session: Mov... |
326 327 |
static void __print_result(struct rb_root *root, struct perf_session *session, int n_lines, int is_caller) |
ba77c9e11 perf: Add 'perf k... |
328 329 |
{ struct rb_node *next; |
23346f21b perf tools: Renam... |
330 |
struct machine *machine; |
ba77c9e11 perf: Add 'perf k... |
331 |
|
079d3f653 perf kmem: Measur... |
332 333 334 |
printf("%.102s ", graph_dotted_line); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); |
47103277f perf kmem: Increa... |
335 336 |
printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag "); |
079d3f653 perf kmem: Measur... |
337 338 |
printf("%.102s ", graph_dotted_line); |
ba77c9e11 perf: Add 'perf k... |
339 340 |
next = rb_first(root); |
23346f21b perf tools: Renam... |
341 342 |
machine = perf_session__find_host_machine(session); if (!machine) { |
a1645ce12 perf: 'perf kvm' ... |
343 344 345 346 |
pr_err("__print_result: couldn't find kernel information "); return; } |
ba77c9e11 perf: Add 'perf k... |
347 |
while (next && n_lines--) { |
1b145ae58 perf kmem: Resolv... |
348 349 350 |
struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; |
71cf8b8ff perf kmem: Fixup ... |
351 |
struct map *map; |
079d3f653 perf kmem: Measur... |
352 |
char buf[BUFSIZ]; |
1b145ae58 perf kmem: Resolv... |
353 354 355 356 |
u64 addr; if (is_caller) { addr = data->call_site; |
7707b6b6f perf kmem: Add ne... |
357 |
if (!raw_ip) |
5c0541d53 perf symbols: Add... |
358 |
sym = machine__find_kernel_function(machine, addr, &map, NULL); |
1b145ae58 perf kmem: Resolv... |
359 360 361 362 |
} else addr = data->ptr; if (sym != NULL) |
9486aa387 perf tools: Fix 6... |
363 |
snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name, |
71cf8b8ff perf kmem: Fixup ... |
364 |
addr - map->unmap_ip(map, sym->start)); |
1b145ae58 perf kmem: Resolv... |
365 |
else |
9486aa387 perf tools: Fix 6... |
366 |
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr); |
079d3f653 perf kmem: Measur... |
367 |
printf(" %-34s |", buf); |
ba77c9e11 perf: Add 'perf k... |
368 |
|
47103277f perf kmem: Increa... |
369 370 |
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%% ", |
079d3f653 perf kmem: Measur... |
371 |
(unsigned long long)data->bytes_alloc, |
ba77c9e11 perf: Add 'perf k... |
372 373 374 375 |
(unsigned long)data->bytes_alloc / data->hit, (unsigned long long)data->bytes_req, (unsigned long)data->bytes_req / data->hit, (unsigned long)data->hit, |
079d3f653 perf kmem: Measur... |
376 |
(unsigned long)data->pingpong, |
ba77c9e11 perf: Add 'perf k... |
377 378 379 380 381 382 |
fragmentation(data->bytes_req, data->bytes_alloc)); next = rb_next(next); } if (n_lines == -1) |
079d3f653 perf kmem: Measur... |
383 384 |
printf(" ... | ... | ... | ... | ... | ... "); |
ba77c9e11 perf: Add 'perf k... |
385 |
|
079d3f653 perf kmem: Measur... |
386 387 |
printf("%.102s ", graph_dotted_line); |
ba77c9e11 perf: Add 'perf k... |
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
} static void print_summary(void) { printf(" SUMMARY ======= "); printf("Total bytes requested: %lu ", total_requested); printf("Total bytes allocated: %lu ", total_allocated); printf("Total bytes wasted on internal fragmentation: %lu ", total_allocated - total_requested); printf("Internal fragmentation: %f%% ", fragmentation(total_requested, total_allocated)); |
7d0d39459 perf kmem: Collec... |
406 407 |
printf("Cross CPU allocations: %lu/%lu ", nr_cross_allocs, nr_allocs); |
ba77c9e11 perf: Add 'perf k... |
408 |
} |
4aa656364 perf session: Mov... |
409 |
static void print_result(struct perf_session *session) |
ba77c9e11 perf: Add 'perf k... |
410 411 |
{ if (caller_flag) |
4aa656364 perf session: Mov... |
412 |
__print_result(&root_caller_sorted, session, caller_lines, 1); |
ba77c9e11 perf: Add 'perf k... |
413 |
if (alloc_flag) |
4aa656364 perf session: Mov... |
414 |
__print_result(&root_alloc_sorted, session, alloc_lines, 0); |
ba77c9e11 perf: Add 'perf k... |
415 416 |
print_summary(); } |
29b3e1528 perf kmem: Defaul... |
417 418 419 420 421 422 423 424 |
struct sort_dimension { const char name[20]; sort_fn_t cmp; struct list_head list; }; static LIST_HEAD(caller_sort); static LIST_HEAD(alloc_sort); |
ba77c9e11 perf: Add 'perf k... |
425 |
static void sort_insert(struct rb_root *root, struct alloc_stat *data, |
29b3e1528 perf kmem: Defaul... |
426 |
struct list_head *sort_list) |
ba77c9e11 perf: Add 'perf k... |
427 428 429 |
{ struct rb_node **new = &(root->rb_node); struct rb_node *parent = NULL; |
29b3e1528 perf kmem: Defaul... |
430 |
struct sort_dimension *sort; |
ba77c9e11 perf: Add 'perf k... |
431 432 433 |
while (*new) { struct alloc_stat *this; |
29b3e1528 perf kmem: Defaul... |
434 |
int cmp = 0; |
ba77c9e11 perf: Add 'perf k... |
435 436 437 |
this = rb_entry(*new, struct alloc_stat, node); parent = *new; |
29b3e1528 perf kmem: Defaul... |
438 439 440 441 442 |
list_for_each_entry(sort, sort_list, list) { cmp = sort->cmp(data, this); if (cmp) break; } |
ba77c9e11 perf: Add 'perf k... |
443 444 445 446 447 448 449 450 451 452 453 454 |
if (cmp > 0) new = &((*new)->rb_left); else new = &((*new)->rb_right); } rb_link_node(&data->node, parent, new); rb_insert_color(&data->node, root); } static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, |
29b3e1528 perf kmem: Defaul... |
455 |
struct list_head *sort_list) |
ba77c9e11 perf: Add 'perf k... |
456 457 458 459 460 461 462 463 464 465 466 |
{ struct rb_node *node; struct alloc_stat *data; for (;;) { node = rb_first(root); if (!node) break; rb_erase(node, root); data = rb_entry(node, struct alloc_stat, node); |
29b3e1528 perf kmem: Defaul... |
467 |
sort_insert(root_sorted, data, sort_list); |
ba77c9e11 perf: Add 'perf k... |
468 469 470 471 472 |
} } static void sort_result(void) { |
29b3e1528 perf kmem: Defaul... |
473 474 |
__sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort); __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); |
ba77c9e11 perf: Add 'perf k... |
475 476 477 478 |
} static int __cmd_kmem(void) { |
d549c7690 perf session: Rem... |
479 |
int err = -EINVAL; |
21ef97f05 perf session: Fal... |
480 |
struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
45694aa77 perf tools: Renam... |
481 |
0, false, &perf_kmem); |
4aa656364 perf session: Mov... |
482 483 |
if (session == NULL) return -ENOMEM; |
e727ca73f perf kmem: Resolv... |
484 485 |
if (perf_session__create_kernel_maps(session) < 0) goto out_delete; |
d549c7690 perf session: Rem... |
486 487 |
if (!perf_session__has_traces(session, "kmem record")) goto out_delete; |
ba77c9e11 perf: Add 'perf k... |
488 |
setup_pager(); |
45694aa77 perf tools: Renam... |
489 |
err = perf_session__process_events(session, &perf_kmem); |
4aa656364 perf session: Mov... |
490 491 |
if (err != 0) goto out_delete; |
ba77c9e11 perf: Add 'perf k... |
492 |
sort_result(); |
4aa656364 perf session: Mov... |
493 494 495 496 |
print_result(session); out_delete: perf_session__delete(session); return err; |
ba77c9e11 perf: Add 'perf k... |
497 498 499 |
} static const char * const kmem_usage[] = { |
90b86a9f7 perf kmem: Show u... |
500 |
"perf kmem [<options>] {record|stat}", |
ba77c9e11 perf: Add 'perf k... |
501 502 |
NULL }; |
ba77c9e11 perf: Add 'perf k... |
503 504 505 506 507 508 509 510 |
static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->ptr < r->ptr) return -1; else if (l->ptr > r->ptr) return 1; return 0; } |
29b3e1528 perf kmem: Defaul... |
511 512 513 514 |
static struct sort_dimension ptr_sort_dimension = { .name = "ptr", .cmp = ptr_cmp, }; |
ba77c9e11 perf: Add 'perf k... |
515 516 517 518 519 520 521 522 |
static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->call_site < r->call_site) return -1; else if (l->call_site > r->call_site) return 1; return 0; } |
29b3e1528 perf kmem: Defaul... |
523 524 525 526 |
static struct sort_dimension callsite_sort_dimension = { .name = "callsite", .cmp = callsite_cmp, }; |
f3ced7cdb perf kmem: Add --... |
527 528 529 530 531 532 533 534 |
static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->hit < r->hit) return -1; else if (l->hit > r->hit) return 1; return 0; } |
29b3e1528 perf kmem: Defaul... |
535 536 537 538 |
static struct sort_dimension hit_sort_dimension = { .name = "hit", .cmp = hit_cmp, }; |
ba77c9e11 perf: Add 'perf k... |
539 540 541 542 543 544 545 546 |
static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->bytes_alloc < r->bytes_alloc) return -1; else if (l->bytes_alloc > r->bytes_alloc) return 1; return 0; } |
29b3e1528 perf kmem: Defaul... |
547 548 549 550 |
static struct sort_dimension bytes_sort_dimension = { .name = "bytes", .cmp = bytes_cmp, }; |
f3ced7cdb perf kmem: Add --... |
551 552 553 554 555 556 557 558 559 560 561 562 563 |
static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r) { double x, y; x = fragmentation(l->bytes_req, l->bytes_alloc); y = fragmentation(r->bytes_req, r->bytes_alloc); if (x < y) return -1; else if (x > y) return 1; return 0; } |
29b3e1528 perf kmem: Defaul... |
564 565 566 567 |
static struct sort_dimension frag_sort_dimension = { .name = "frag", .cmp = frag_cmp, }; |
079d3f653 perf kmem: Measur... |
568 569 570 571 572 573 574 575 576 577 578 579 580 |
static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->pingpong < r->pingpong) return -1; else if (l->pingpong > r->pingpong) return 1; return 0; } static struct sort_dimension pingpong_sort_dimension = { .name = "pingpong", .cmp = pingpong_cmp, }; |
29b3e1528 perf kmem: Defaul... |
581 582 583 584 585 586 |
static struct sort_dimension *avail_sorts[] = { &ptr_sort_dimension, &callsite_sort_dimension, &hit_sort_dimension, &bytes_sort_dimension, &frag_sort_dimension, |
079d3f653 perf kmem: Measur... |
587 |
&pingpong_sort_dimension, |
29b3e1528 perf kmem: Defaul... |
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 |
}; #define NUM_AVAIL_SORTS \ (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *)) static int sort_dimension__add(const char *tok, struct list_head *list) { struct sort_dimension *sort; int i; for (i = 0; i < NUM_AVAIL_SORTS; i++) { if (!strcmp(avail_sorts[i]->name, tok)) { sort = malloc(sizeof(*sort)); if (!sort) die("malloc"); memcpy(sort, avail_sorts[i], sizeof(*sort)); list_add_tail(&sort->list, list); return 0; } } return -1; } static int setup_sorting(struct list_head *sort_list, const char *arg) { char *tok; char *str = strdup(arg); if (!str) die("strdup"); while (true) { tok = strsep(&str, ","); if (!tok) break; if (sort_dimension__add(tok, sort_list) < 0) { error("Unknown --sort key: '%s'", tok); |
1b22859d4 perf kmem: Fix a ... |
626 |
free(str); |
29b3e1528 perf kmem: Defaul... |
627 628 629 630 631 632 633 |
return -1; } } free(str); return 0; } |
ba77c9e11 perf: Add 'perf k... |
634 635 636 |
static int parse_sort_opt(const struct option *opt __used, const char *arg, int unset __used) { |
ba77c9e11 perf: Add 'perf k... |
637 638 |
if (!arg) return -1; |
ba77c9e11 perf: Add 'perf k... |
639 |
if (caller_flag > alloc_flag) |
29b3e1528 perf kmem: Defaul... |
640 |
return setup_sorting(&caller_sort, arg); |
ba77c9e11 perf: Add 'perf k... |
641 |
else |
29b3e1528 perf kmem: Defaul... |
642 |
return setup_sorting(&alloc_sort, arg); |
ba77c9e11 perf: Add 'perf k... |
643 644 645 |
return 0; } |
90b86a9f7 perf kmem: Show u... |
646 |
static int parse_caller_opt(const struct option *opt __used, |
793124169 perf kmem: Fix un... |
647 |
const char *arg __used, int unset __used) |
ba77c9e11 perf: Add 'perf k... |
648 |
{ |
90b86a9f7 perf kmem: Show u... |
649 650 651 |
caller_flag = (alloc_flag + 1); return 0; } |
ba77c9e11 perf: Add 'perf k... |
652 |
|
90b86a9f7 perf kmem: Show u... |
653 |
static int parse_alloc_opt(const struct option *opt __used, |
793124169 perf kmem: Fix un... |
654 |
const char *arg __used, int unset __used) |
90b86a9f7 perf kmem: Show u... |
655 656 |
{ alloc_flag = (caller_flag + 1); |
ba77c9e11 perf: Add 'perf k... |
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
return 0; } static int parse_line_opt(const struct option *opt __used, const char *arg, int unset __used) { int lines; if (!arg) return -1; lines = strtoul(arg, NULL, 10); if (caller_flag > alloc_flag) caller_lines = lines; else alloc_lines = lines; return 0; } static const struct option kmem_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), |
90b86a9f7 perf kmem: Show u... |
681 682 683 684 685 686 |
OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, "show per-callsite statistics", parse_caller_opt), OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, "show per-allocation statistics", parse_alloc_opt), |
29b3e1528 perf kmem: Defaul... |
687 |
OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", |
079d3f653 perf kmem: Measur... |
688 |
"sort by keys: ptr, call_site, bytes, hit, pingpong, frag", |
ba77c9e11 perf: Add 'perf k... |
689 690 |
parse_sort_opt), OPT_CALLBACK('l', "line", NULL, "num", |
90b86a9f7 perf kmem: Show u... |
691 |
"show n lines", |
ba77c9e11 perf: Add 'perf k... |
692 |
parse_line_opt), |
7707b6b6f perf kmem: Add ne... |
693 |
OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), |
ba77c9e11 perf: Add 'perf k... |
694 695 696 697 698 699 700 |
OPT_END() }; static const char *record_args[] = { "record", "-a", "-R", |
ba77c9e11 perf: Add 'perf k... |
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
"-f", "-c", "1", "-e", "kmem:kmalloc", "-e", "kmem:kmalloc_node", "-e", "kmem:kfree", "-e", "kmem:kmem_cache_alloc", "-e", "kmem:kmem_cache_alloc_node", "-e", "kmem:kmem_cache_free", }; static int __cmd_record(int argc, const char **argv) { unsigned int rec_argc, i, j; const char **rec_argv; rec_argc = ARRAY_SIZE(record_args) + argc - 1; rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
ce47dc56a perf tools: Catch... |
718 719 |
if (rec_argv == NULL) return -ENOMEM; |
ba77c9e11 perf: Add 'perf k... |
720 721 722 723 724 725 726 727 728 729 730 |
for (i = 0; i < ARRAY_SIZE(record_args); i++) rec_argv[i] = strdup(record_args[i]); for (j = 1; j < (unsigned int)argc; j++, i++) rec_argv[i] = argv[j]; return cmd_record(i, rec_argv, NULL); } int cmd_kmem(int argc, const char **argv, const char *prefix __used) { |
ba77c9e11 perf: Add 'perf k... |
731 |
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); |
90b86a9f7 perf kmem: Show u... |
732 |
if (!argc) |
ba77c9e11 perf: Add 'perf k... |
733 |
usage_with_options(kmem_usage, kmem_options); |
655000e7c perf symbols: Ado... |
734 |
symbol__init(); |
90b86a9f7 perf kmem: Show u... |
735 736 737 738 739 740 741 742 743 |
if (!strncmp(argv[0], "rec", 3)) { return __cmd_record(argc, argv); } else if (!strcmp(argv[0], "stat")) { setup_cpunode_map(); if (list_empty(&caller_sort)) setup_sorting(&caller_sort, default_sort_order); if (list_empty(&alloc_sort)) setup_sorting(&alloc_sort, default_sort_order); |
ba77c9e11 perf: Add 'perf k... |
744 |
|
90b86a9f7 perf kmem: Show u... |
745 |
return __cmd_kmem(); |
b00eca8cd perf kmem: Print ... |
746 747 |
} else usage_with_options(kmem_usage, kmem_options); |
7d0d39459 perf kmem: Collec... |
748 |
|
90b86a9f7 perf kmem: Show u... |
749 |
return 0; |
ba77c9e11 perf: Add 'perf k... |
750 |
} |