1ba77c9e1SLi Zefan #include "builtin.h" 2ba77c9e1SLi Zefan #include "perf.h" 3ba77c9e1SLi Zefan 40f7d2f1bSArnaldo Carvalho de Melo #include "util/evlist.h" 5fcf65bf1SArnaldo Carvalho de Melo #include "util/evsel.h" 6ba77c9e1SLi Zefan #include "util/util.h" 7ba77c9e1SLi Zefan #include "util/cache.h" 8ba77c9e1SLi Zefan #include "util/symbol.h" 9ba77c9e1SLi Zefan #include "util/thread.h" 10ba77c9e1SLi Zefan #include "util/header.h" 1194c744b6SArnaldo Carvalho de Melo #include "util/session.h" 1245694aa7SArnaldo Carvalho de Melo #include "util/tool.h" 13ba77c9e1SLi Zefan 14ba77c9e1SLi Zefan #include "util/parse-options.h" 15ba77c9e1SLi Zefan #include "util/trace-event.h" 16f5fc1412SJiri Olsa #include "util/data.h" 174b627957SDon Zickus #include "util/cpumap.h" 18ba77c9e1SLi Zefan 19ba77c9e1SLi Zefan #include "util/debug.h" 20ba77c9e1SLi Zefan 21ba77c9e1SLi Zefan #include <linux/rbtree.h> 228d9233f2SArnaldo Carvalho de Melo #include <linux/string.h> 23*77cfe388SNamhyung Kim #include <locale.h> 24ba77c9e1SLi Zefan 25ba77c9e1SLi Zefan struct alloc_stat; 26ba77c9e1SLi Zefan typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); 27ba77c9e1SLi Zefan 28ba77c9e1SLi Zefan static int alloc_flag; 29ba77c9e1SLi Zefan static int caller_flag; 30ba77c9e1SLi Zefan 31ba77c9e1SLi Zefan static int alloc_lines = -1; 32ba77c9e1SLi Zefan static int caller_lines = -1; 33ba77c9e1SLi Zefan 347707b6b6SLi Zefan static bool raw_ip; 357707b6b6SLi Zefan 36ba77c9e1SLi Zefan struct alloc_stat { 37ba77c9e1SLi Zefan u64 call_site; 38ba77c9e1SLi Zefan u64 ptr; 39ba77c9e1SLi Zefan u64 bytes_req; 40ba77c9e1SLi Zefan u64 bytes_alloc; 41ba77c9e1SLi Zefan u32 hit; 42079d3f65SLi Zefan u32 pingpong; 43079d3f65SLi Zefan 44079d3f65SLi Zefan short alloc_cpu; 45ba77c9e1SLi Zefan 46ba77c9e1SLi Zefan struct rb_node node; 47ba77c9e1SLi Zefan }; 48ba77c9e1SLi Zefan 49ba77c9e1SLi Zefan static struct rb_root root_alloc_stat; 50ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted; 51ba77c9e1SLi Zefan static struct rb_root root_caller_stat; 52ba77c9e1SLi Zefan static struct rb_root root_caller_sorted; 53ba77c9e1SLi Zefan 54ba77c9e1SLi Zefan static unsigned long total_requested, total_allocated; 557d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs; 56ba77c9e1SLi Zefan 572814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, 58079d3f65SLi Zefan int bytes_req, int bytes_alloc, int cpu) 59ba77c9e1SLi Zefan { 60ba77c9e1SLi Zefan struct rb_node **node = &root_alloc_stat.rb_node; 61ba77c9e1SLi Zefan struct rb_node *parent = NULL; 62ba77c9e1SLi Zefan struct alloc_stat *data = NULL; 63ba77c9e1SLi Zefan 64ba77c9e1SLi Zefan while (*node) { 65ba77c9e1SLi Zefan parent = *node; 66ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node); 67ba77c9e1SLi Zefan 68ba77c9e1SLi Zefan if (ptr > data->ptr) 69ba77c9e1SLi Zefan node = &(*node)->rb_right; 70ba77c9e1SLi Zefan else if (ptr < data->ptr) 71ba77c9e1SLi Zefan node = &(*node)->rb_left; 72ba77c9e1SLi Zefan else 73ba77c9e1SLi Zefan break; 74ba77c9e1SLi Zefan } 75ba77c9e1SLi Zefan 76ba77c9e1SLi Zefan if (data && data->ptr == ptr) { 77ba77c9e1SLi Zefan data->hit++; 78ba77c9e1SLi Zefan data->bytes_req += bytes_req; 794efb5290SWenji Huang data->bytes_alloc += bytes_alloc; 80ba77c9e1SLi Zefan } else { 81ba77c9e1SLi Zefan data = malloc(sizeof(*data)); 822814eb05SArnaldo Carvalho de Melo if (!data) { 832814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__); 842814eb05SArnaldo Carvalho de Melo return -1; 852814eb05SArnaldo Carvalho de Melo } 86ba77c9e1SLi Zefan data->ptr = ptr; 87079d3f65SLi Zefan data->pingpong = 0; 88ba77c9e1SLi Zefan data->hit = 1; 89ba77c9e1SLi Zefan data->bytes_req = bytes_req; 90ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc; 91ba77c9e1SLi Zefan 92ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node); 93ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_alloc_stat); 94ba77c9e1SLi Zefan } 95079d3f65SLi Zefan data->call_site = call_site; 96079d3f65SLi Zefan data->alloc_cpu = cpu; 972814eb05SArnaldo Carvalho de Melo return 0; 98ba77c9e1SLi Zefan } 99ba77c9e1SLi Zefan 1002814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site, 101ba77c9e1SLi Zefan int bytes_req, int bytes_alloc) 102ba77c9e1SLi Zefan { 103ba77c9e1SLi Zefan struct rb_node **node = &root_caller_stat.rb_node; 104ba77c9e1SLi Zefan struct rb_node *parent = NULL; 105ba77c9e1SLi Zefan struct alloc_stat *data = NULL; 106ba77c9e1SLi Zefan 107ba77c9e1SLi Zefan while (*node) { 108ba77c9e1SLi Zefan parent = *node; 109ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node); 110ba77c9e1SLi Zefan 111ba77c9e1SLi Zefan if (call_site > data->call_site) 112ba77c9e1SLi Zefan node = &(*node)->rb_right; 113ba77c9e1SLi Zefan else if (call_site < data->call_site) 114ba77c9e1SLi Zefan node = &(*node)->rb_left; 115ba77c9e1SLi Zefan else 116ba77c9e1SLi Zefan break; 117ba77c9e1SLi Zefan } 118ba77c9e1SLi Zefan 119ba77c9e1SLi Zefan if (data && data->call_site == call_site) { 120ba77c9e1SLi Zefan data->hit++; 121ba77c9e1SLi Zefan data->bytes_req += bytes_req; 1224efb5290SWenji Huang data->bytes_alloc += bytes_alloc; 123ba77c9e1SLi Zefan } else { 124ba77c9e1SLi Zefan data = malloc(sizeof(*data)); 1252814eb05SArnaldo Carvalho de Melo if (!data) { 1262814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__); 1272814eb05SArnaldo Carvalho de Melo return -1; 1282814eb05SArnaldo Carvalho de Melo } 129ba77c9e1SLi Zefan data->call_site = call_site; 130079d3f65SLi Zefan data->pingpong = 0; 131ba77c9e1SLi Zefan data->hit = 1; 132ba77c9e1SLi Zefan data->bytes_req = bytes_req; 133ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc; 134ba77c9e1SLi Zefan 135ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node); 136ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_caller_stat); 137ba77c9e1SLi Zefan } 1382814eb05SArnaldo Carvalho de Melo 1392814eb05SArnaldo Carvalho de Melo return 0; 140ba77c9e1SLi Zefan } 141ba77c9e1SLi Zefan 1422814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_alloc_event(struct perf_evsel *evsel, 1430f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample) 144ba77c9e1SLi Zefan { 1450f7d2f1bSArnaldo Carvalho de Melo unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"), 1460f7d2f1bSArnaldo Carvalho de Melo call_site = perf_evsel__intval(evsel, sample, "call_site"); 1470f7d2f1bSArnaldo Carvalho de Melo int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"), 1480f7d2f1bSArnaldo Carvalho de Melo bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc"); 149ba77c9e1SLi Zefan 1500f7d2f1bSArnaldo Carvalho de Melo if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) || 1512814eb05SArnaldo Carvalho de Melo insert_caller_stat(call_site, bytes_req, bytes_alloc)) 1522814eb05SArnaldo Carvalho de Melo return -1; 153ba77c9e1SLi Zefan 154ba77c9e1SLi Zefan total_requested += bytes_req; 155ba77c9e1SLi Zefan total_allocated += bytes_alloc; 1567d0d3945SLi Zefan 1570f7d2f1bSArnaldo Carvalho de Melo nr_allocs++; 1580f7d2f1bSArnaldo Carvalho de Melo return 0; 1590f7d2f1bSArnaldo Carvalho de Melo } 1600f7d2f1bSArnaldo Carvalho de Melo 1610f7d2f1bSArnaldo Carvalho de Melo static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel, 1620f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample) 1630f7d2f1bSArnaldo Carvalho de Melo { 1640f7d2f1bSArnaldo Carvalho de Melo int ret = perf_evsel__process_alloc_event(evsel, sample); 1650f7d2f1bSArnaldo Carvalho de Melo 1660f7d2f1bSArnaldo Carvalho de Melo if (!ret) { 1674b627957SDon Zickus int node1 = cpu__get_node(sample->cpu), 1680f7d2f1bSArnaldo Carvalho de Melo node2 = perf_evsel__intval(evsel, sample, "node"); 1690f7d2f1bSArnaldo Carvalho de Melo 1707d0d3945SLi Zefan if (node1 != node2) 1717d0d3945SLi Zefan nr_cross_allocs++; 1727d0d3945SLi Zefan } 1730f7d2f1bSArnaldo Carvalho de Melo 1740f7d2f1bSArnaldo Carvalho de Melo return ret; 175ba77c9e1SLi Zefan } 176ba77c9e1SLi Zefan 177079d3f65SLi Zefan static int ptr_cmp(struct alloc_stat *, struct alloc_stat *); 178079d3f65SLi Zefan static int callsite_cmp(struct alloc_stat *, struct alloc_stat *); 179079d3f65SLi Zefan 180079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr, 181079d3f65SLi Zefan unsigned long call_site, 182079d3f65SLi Zefan struct rb_root *root, 183079d3f65SLi Zefan sort_fn_t sort_fn) 184079d3f65SLi Zefan { 185079d3f65SLi Zefan struct rb_node *node = root->rb_node; 186079d3f65SLi Zefan struct alloc_stat key = { .ptr = ptr, .call_site = call_site }; 187079d3f65SLi Zefan 188079d3f65SLi Zefan while (node) { 189079d3f65SLi Zefan struct alloc_stat *data; 190079d3f65SLi Zefan int cmp; 191079d3f65SLi Zefan 192079d3f65SLi Zefan data = rb_entry(node, struct alloc_stat, node); 193079d3f65SLi Zefan 194079d3f65SLi Zefan cmp = sort_fn(&key, data); 195079d3f65SLi Zefan if (cmp < 0) 196079d3f65SLi Zefan node = node->rb_left; 197079d3f65SLi Zefan else if (cmp > 0) 198079d3f65SLi Zefan node = node->rb_right; 199079d3f65SLi Zefan else 200079d3f65SLi Zefan return data; 201079d3f65SLi Zefan } 202079d3f65SLi Zefan return NULL; 203079d3f65SLi Zefan } 204079d3f65SLi Zefan 2052814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_free_event(struct perf_evsel *evsel, 20622ad798cSArnaldo Carvalho de Melo struct perf_sample *sample) 207ba77c9e1SLi Zefan { 2080f7d2f1bSArnaldo Carvalho de Melo unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"); 209079d3f65SLi Zefan struct alloc_stat *s_alloc, *s_caller; 210079d3f65SLi Zefan 211079d3f65SLi Zefan s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); 212079d3f65SLi Zefan if (!s_alloc) 2132814eb05SArnaldo Carvalho de Melo return 0; 214079d3f65SLi Zefan 21522ad798cSArnaldo Carvalho de Melo if ((short)sample->cpu != s_alloc->alloc_cpu) { 216079d3f65SLi Zefan s_alloc->pingpong++; 217079d3f65SLi Zefan 218079d3f65SLi Zefan s_caller = search_alloc_stat(0, s_alloc->call_site, 219079d3f65SLi Zefan &root_caller_stat, callsite_cmp); 2202814eb05SArnaldo Carvalho de Melo if (!s_caller) 2212814eb05SArnaldo Carvalho de Melo return -1; 222079d3f65SLi Zefan s_caller->pingpong++; 223079d3f65SLi Zefan } 224079d3f65SLi Zefan s_alloc->alloc_cpu = -1; 2252814eb05SArnaldo Carvalho de Melo 2262814eb05SArnaldo Carvalho de Melo return 0; 227ba77c9e1SLi Zefan } 228ba77c9e1SLi Zefan 2290f7d2f1bSArnaldo Carvalho de Melo typedef int (*tracepoint_handler)(struct perf_evsel *evsel, 2300f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample); 231ba77c9e1SLi Zefan 2321d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused, 233d20deb64SArnaldo Carvalho de Melo union perf_event *event, 2348115d60cSArnaldo Carvalho de Melo struct perf_sample *sample, 235fcf65bf1SArnaldo Carvalho de Melo struct perf_evsel *evsel, 236743eb868SArnaldo Carvalho de Melo struct machine *machine) 237ba77c9e1SLi Zefan { 238ef89325fSAdrian Hunter struct thread *thread = machine__findnew_thread(machine, sample->pid, 23913ce34dfSNamhyung Kim sample->tid); 240ba77c9e1SLi Zefan 241ba77c9e1SLi Zefan if (thread == NULL) { 242ba77c9e1SLi Zefan pr_debug("problem processing %d event, skipping it.\n", 243ba77c9e1SLi Zefan event->header.type); 244ba77c9e1SLi Zefan return -1; 245ba77c9e1SLi Zefan } 246ba77c9e1SLi Zefan 247b9c5143aSFrederic Weisbecker dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); 248ba77c9e1SLi Zefan 249744a9719SArnaldo Carvalho de Melo if (evsel->handler != NULL) { 250744a9719SArnaldo Carvalho de Melo tracepoint_handler f = evsel->handler; 2510f7d2f1bSArnaldo Carvalho de Melo return f(evsel, sample); 2520f7d2f1bSArnaldo Carvalho de Melo } 2530f7d2f1bSArnaldo Carvalho de Melo 2540f7d2f1bSArnaldo Carvalho de Melo return 0; 255ba77c9e1SLi Zefan } 256ba77c9e1SLi Zefan 257fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = { 25855aa640fSArnaldo Carvalho de Melo .sample = process_sample_event, 2598115d60cSArnaldo Carvalho de Melo .comm = perf_event__process_comm, 26064c40908SNamhyung Kim .mmap = perf_event__process_mmap, 26164c40908SNamhyung Kim .mmap2 = perf_event__process_mmap2, 2620a8cb85cSJiri Olsa .ordered_events = true, 263ba77c9e1SLi Zefan }; 264ba77c9e1SLi Zefan 265ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc) 266ba77c9e1SLi Zefan { 267ba77c9e1SLi Zefan if (n_alloc == 0) 268ba77c9e1SLi Zefan return 0.0; 269ba77c9e1SLi Zefan else 270ba77c9e1SLi Zefan return 100.0 - (100.0 * n_req / n_alloc); 271ba77c9e1SLi Zefan } 272ba77c9e1SLi Zefan 2734aa65636SArnaldo Carvalho de Melo static void __print_result(struct rb_root *root, struct perf_session *session, 2744aa65636SArnaldo Carvalho de Melo int n_lines, int is_caller) 275ba77c9e1SLi Zefan { 276ba77c9e1SLi Zefan struct rb_node *next; 27734ba5122SArnaldo Carvalho de Melo struct machine *machine = &session->machines.host; 278ba77c9e1SLi Zefan 27965f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line); 280079d3f65SLi Zefan printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); 281079d3f65SLi Zefan printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n"); 28265f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line); 283ba77c9e1SLi Zefan 284ba77c9e1SLi Zefan next = rb_first(root); 285ba77c9e1SLi Zefan 286ba77c9e1SLi Zefan while (next && n_lines--) { 2871b145ae5SArnaldo Carvalho de Melo struct alloc_stat *data = rb_entry(next, struct alloc_stat, 2881b145ae5SArnaldo Carvalho de Melo node); 2891b145ae5SArnaldo Carvalho de Melo struct symbol *sym = NULL; 29071cf8b8fSArnaldo Carvalho de Melo struct map *map; 291079d3f65SLi Zefan char buf[BUFSIZ]; 2921b145ae5SArnaldo Carvalho de Melo u64 addr; 293ba77c9e1SLi Zefan 2941b145ae5SArnaldo Carvalho de Melo if (is_caller) { 2951b145ae5SArnaldo Carvalho de Melo addr = data->call_site; 2967707b6b6SLi Zefan if (!raw_ip) 2975c0541d5SArnaldo Carvalho de Melo sym = machine__find_kernel_function(machine, addr, &map, NULL); 2981b145ae5SArnaldo Carvalho de Melo } else 2991b145ae5SArnaldo Carvalho de Melo addr = data->ptr; 300ba77c9e1SLi Zefan 3011b145ae5SArnaldo Carvalho de Melo if (sym != NULL) 3029486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name, 30371cf8b8fSArnaldo Carvalho de Melo addr - map->unmap_ip(map, sym->start)); 3041b145ae5SArnaldo Carvalho de Melo else 3059486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr); 306079d3f65SLi Zefan printf(" %-34s |", buf); 3071b145ae5SArnaldo Carvalho de Melo 30865f46e02SNamhyung Kim printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n", 309079d3f65SLi Zefan (unsigned long long)data->bytes_alloc, 310ba77c9e1SLi Zefan (unsigned long)data->bytes_alloc / data->hit, 311ba77c9e1SLi Zefan (unsigned long long)data->bytes_req, 312ba77c9e1SLi Zefan (unsigned long)data->bytes_req / data->hit, 313ba77c9e1SLi Zefan (unsigned long)data->hit, 314079d3f65SLi Zefan (unsigned long)data->pingpong, 315ba77c9e1SLi Zefan fragmentation(data->bytes_req, data->bytes_alloc)); 316ba77c9e1SLi Zefan 317ba77c9e1SLi Zefan next = rb_next(next); 318ba77c9e1SLi Zefan } 319ba77c9e1SLi Zefan 320ba77c9e1SLi Zefan if (n_lines == -1) 321079d3f65SLi Zefan printf(" ... | ... | ... | ... | ... | ... \n"); 322ba77c9e1SLi Zefan 32365f46e02SNamhyung Kim printf("%.105s\n", graph_dotted_line); 324ba77c9e1SLi Zefan } 325ba77c9e1SLi Zefan 326ba77c9e1SLi Zefan static void print_summary(void) 327ba77c9e1SLi Zefan { 328ba77c9e1SLi Zefan printf("\nSUMMARY\n=======\n"); 329*77cfe388SNamhyung Kim printf("Total bytes requested: %'lu\n", total_requested); 330*77cfe388SNamhyung Kim printf("Total bytes allocated: %'lu\n", total_allocated); 331*77cfe388SNamhyung Kim printf("Total bytes wasted on internal fragmentation: %'lu\n", 332ba77c9e1SLi Zefan total_allocated - total_requested); 333ba77c9e1SLi Zefan printf("Internal fragmentation: %f%%\n", 334ba77c9e1SLi Zefan fragmentation(total_requested, total_allocated)); 335*77cfe388SNamhyung Kim printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs); 336ba77c9e1SLi Zefan } 337ba77c9e1SLi Zefan 3384aa65636SArnaldo Carvalho de Melo static void print_result(struct perf_session *session) 339ba77c9e1SLi Zefan { 340ba77c9e1SLi Zefan if (caller_flag) 3414aa65636SArnaldo Carvalho de Melo __print_result(&root_caller_sorted, session, caller_lines, 1); 342ba77c9e1SLi Zefan if (alloc_flag) 3434aa65636SArnaldo Carvalho de Melo __print_result(&root_alloc_sorted, session, alloc_lines, 0); 344ba77c9e1SLi Zefan print_summary(); 345ba77c9e1SLi Zefan } 346ba77c9e1SLi Zefan 34729b3e152SLi Zefan struct sort_dimension { 34829b3e152SLi Zefan const char name[20]; 34929b3e152SLi Zefan sort_fn_t cmp; 35029b3e152SLi Zefan struct list_head list; 35129b3e152SLi Zefan }; 35229b3e152SLi Zefan 35329b3e152SLi Zefan static LIST_HEAD(caller_sort); 35429b3e152SLi Zefan static LIST_HEAD(alloc_sort); 35529b3e152SLi Zefan 356ba77c9e1SLi Zefan static void sort_insert(struct rb_root *root, struct alloc_stat *data, 35729b3e152SLi Zefan struct list_head *sort_list) 358ba77c9e1SLi Zefan { 359ba77c9e1SLi Zefan struct rb_node **new = &(root->rb_node); 360ba77c9e1SLi Zefan struct rb_node *parent = NULL; 36129b3e152SLi Zefan struct sort_dimension *sort; 362ba77c9e1SLi Zefan 363ba77c9e1SLi Zefan while (*new) { 364ba77c9e1SLi Zefan struct alloc_stat *this; 36529b3e152SLi Zefan int cmp = 0; 366ba77c9e1SLi Zefan 367ba77c9e1SLi Zefan this = rb_entry(*new, struct alloc_stat, node); 368ba77c9e1SLi Zefan parent = *new; 369ba77c9e1SLi Zefan 37029b3e152SLi Zefan list_for_each_entry(sort, sort_list, list) { 37129b3e152SLi Zefan cmp = sort->cmp(data, this); 37229b3e152SLi Zefan if (cmp) 37329b3e152SLi Zefan break; 37429b3e152SLi Zefan } 375ba77c9e1SLi Zefan 376ba77c9e1SLi Zefan if (cmp > 0) 377ba77c9e1SLi Zefan new = &((*new)->rb_left); 378ba77c9e1SLi Zefan else 379ba77c9e1SLi Zefan new = &((*new)->rb_right); 380ba77c9e1SLi Zefan } 381ba77c9e1SLi Zefan 382ba77c9e1SLi Zefan rb_link_node(&data->node, parent, new); 383ba77c9e1SLi Zefan rb_insert_color(&data->node, root); 384ba77c9e1SLi Zefan } 385ba77c9e1SLi Zefan 386ba77c9e1SLi Zefan static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, 38729b3e152SLi Zefan struct list_head *sort_list) 388ba77c9e1SLi Zefan { 389ba77c9e1SLi Zefan struct rb_node *node; 390ba77c9e1SLi Zefan struct alloc_stat *data; 391ba77c9e1SLi Zefan 392ba77c9e1SLi Zefan for (;;) { 393ba77c9e1SLi Zefan node = rb_first(root); 394ba77c9e1SLi Zefan if (!node) 395ba77c9e1SLi Zefan break; 396ba77c9e1SLi Zefan 397ba77c9e1SLi Zefan rb_erase(node, root); 398ba77c9e1SLi Zefan data = rb_entry(node, struct alloc_stat, node); 39929b3e152SLi Zefan sort_insert(root_sorted, data, sort_list); 400ba77c9e1SLi Zefan } 401ba77c9e1SLi Zefan } 402ba77c9e1SLi Zefan 403ba77c9e1SLi Zefan static void sort_result(void) 404ba77c9e1SLi Zefan { 40529b3e152SLi Zefan __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort); 40629b3e152SLi Zefan __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); 407ba77c9e1SLi Zefan } 408ba77c9e1SLi Zefan 4092b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session) 410ba77c9e1SLi Zefan { 411d549c769SArnaldo Carvalho de Melo int err = -EINVAL; 4120f7d2f1bSArnaldo Carvalho de Melo const struct perf_evsel_str_handler kmem_tracepoints[] = { 4130f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmalloc", perf_evsel__process_alloc_event, }, 4140f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, 4150f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmalloc_node", perf_evsel__process_alloc_node_event, }, 4160f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, }, 4170f7d2f1bSArnaldo Carvalho de Melo { "kmem:kfree", perf_evsel__process_free_event, }, 4180f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, 4190f7d2f1bSArnaldo Carvalho de Melo }; 420ba77c9e1SLi Zefan 421d549c769SArnaldo Carvalho de Melo if (!perf_session__has_traces(session, "kmem record")) 4222b2b2c68SNamhyung Kim goto out; 423d549c769SArnaldo Carvalho de Melo 4240f7d2f1bSArnaldo Carvalho de Melo if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { 4250f7d2f1bSArnaldo Carvalho de Melo pr_err("Initializing perf session tracepoint handlers failed\n"); 4262b2b2c68SNamhyung Kim goto out; 4270f7d2f1bSArnaldo Carvalho de Melo } 4280f7d2f1bSArnaldo Carvalho de Melo 4294aa65636SArnaldo Carvalho de Melo setup_pager(); 430b7b61cbeSArnaldo Carvalho de Melo err = perf_session__process_events(session); 4314aa65636SArnaldo Carvalho de Melo if (err != 0) 4322b2b2c68SNamhyung Kim goto out; 4334aa65636SArnaldo Carvalho de Melo sort_result(); 4344aa65636SArnaldo Carvalho de Melo print_result(session); 4352b2b2c68SNamhyung Kim out: 4364aa65636SArnaldo Carvalho de Melo return err; 437ba77c9e1SLi Zefan } 438ba77c9e1SLi Zefan 439ba77c9e1SLi Zefan static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) 440ba77c9e1SLi Zefan { 441ba77c9e1SLi Zefan if (l->ptr < r->ptr) 442ba77c9e1SLi Zefan return -1; 443ba77c9e1SLi Zefan else if (l->ptr > r->ptr) 444ba77c9e1SLi Zefan return 1; 445ba77c9e1SLi Zefan return 0; 446ba77c9e1SLi Zefan } 447ba77c9e1SLi Zefan 44829b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = { 44929b3e152SLi Zefan .name = "ptr", 45029b3e152SLi Zefan .cmp = ptr_cmp, 45129b3e152SLi Zefan }; 45229b3e152SLi Zefan 453ba77c9e1SLi Zefan static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) 454ba77c9e1SLi Zefan { 455ba77c9e1SLi Zefan if (l->call_site < r->call_site) 456ba77c9e1SLi Zefan return -1; 457ba77c9e1SLi Zefan else if (l->call_site > r->call_site) 458ba77c9e1SLi Zefan return 1; 459ba77c9e1SLi Zefan return 0; 460ba77c9e1SLi Zefan } 461ba77c9e1SLi Zefan 46229b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = { 46329b3e152SLi Zefan .name = "callsite", 46429b3e152SLi Zefan .cmp = callsite_cmp, 46529b3e152SLi Zefan }; 46629b3e152SLi Zefan 467f3ced7cdSPekka Enberg static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r) 468f3ced7cdSPekka Enberg { 469f3ced7cdSPekka Enberg if (l->hit < r->hit) 470f3ced7cdSPekka Enberg return -1; 471f3ced7cdSPekka Enberg else if (l->hit > r->hit) 472f3ced7cdSPekka Enberg return 1; 473f3ced7cdSPekka Enberg return 0; 474f3ced7cdSPekka Enberg } 475f3ced7cdSPekka Enberg 47629b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = { 47729b3e152SLi Zefan .name = "hit", 47829b3e152SLi Zefan .cmp = hit_cmp, 47929b3e152SLi Zefan }; 48029b3e152SLi Zefan 481ba77c9e1SLi Zefan static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) 482ba77c9e1SLi Zefan { 483ba77c9e1SLi Zefan if (l->bytes_alloc < r->bytes_alloc) 484ba77c9e1SLi Zefan return -1; 485ba77c9e1SLi Zefan else if (l->bytes_alloc > r->bytes_alloc) 486ba77c9e1SLi Zefan return 1; 487ba77c9e1SLi Zefan return 0; 488ba77c9e1SLi Zefan } 489ba77c9e1SLi Zefan 49029b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = { 49129b3e152SLi Zefan .name = "bytes", 49229b3e152SLi Zefan .cmp = bytes_cmp, 49329b3e152SLi Zefan }; 49429b3e152SLi Zefan 495f3ced7cdSPekka Enberg static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r) 496f3ced7cdSPekka Enberg { 497f3ced7cdSPekka Enberg double x, y; 498f3ced7cdSPekka Enberg 499f3ced7cdSPekka Enberg x = fragmentation(l->bytes_req, l->bytes_alloc); 500f3ced7cdSPekka Enberg y = fragmentation(r->bytes_req, r->bytes_alloc); 501f3ced7cdSPekka Enberg 502f3ced7cdSPekka Enberg if (x < y) 503f3ced7cdSPekka Enberg return -1; 504f3ced7cdSPekka Enberg else if (x > y) 505f3ced7cdSPekka Enberg return 1; 506f3ced7cdSPekka Enberg return 0; 507f3ced7cdSPekka Enberg } 508f3ced7cdSPekka Enberg 50929b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = { 51029b3e152SLi Zefan .name = "frag", 51129b3e152SLi Zefan .cmp = frag_cmp, 51229b3e152SLi Zefan }; 51329b3e152SLi Zefan 514079d3f65SLi Zefan static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r) 515079d3f65SLi Zefan { 516079d3f65SLi Zefan if (l->pingpong < r->pingpong) 517079d3f65SLi Zefan return -1; 518079d3f65SLi Zefan else if (l->pingpong > r->pingpong) 519079d3f65SLi Zefan return 1; 520079d3f65SLi Zefan return 0; 521079d3f65SLi Zefan } 522079d3f65SLi Zefan 523079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = { 524079d3f65SLi Zefan .name = "pingpong", 525079d3f65SLi Zefan .cmp = pingpong_cmp, 526079d3f65SLi Zefan }; 527079d3f65SLi Zefan 52829b3e152SLi Zefan static struct sort_dimension *avail_sorts[] = { 52929b3e152SLi Zefan &ptr_sort_dimension, 53029b3e152SLi Zefan &callsite_sort_dimension, 53129b3e152SLi Zefan &hit_sort_dimension, 53229b3e152SLi Zefan &bytes_sort_dimension, 53329b3e152SLi Zefan &frag_sort_dimension, 534079d3f65SLi Zefan &pingpong_sort_dimension, 53529b3e152SLi Zefan }; 53629b3e152SLi Zefan 53749e4ba54SSasha Levin #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts)) 53829b3e152SLi Zefan 53929b3e152SLi Zefan static int sort_dimension__add(const char *tok, struct list_head *list) 54029b3e152SLi Zefan { 54129b3e152SLi Zefan struct sort_dimension *sort; 54229b3e152SLi Zefan int i; 54329b3e152SLi Zefan 54429b3e152SLi Zefan for (i = 0; i < NUM_AVAIL_SORTS; i++) { 54529b3e152SLi Zefan if (!strcmp(avail_sorts[i]->name, tok)) { 5468d9233f2SArnaldo Carvalho de Melo sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i])); 5472814eb05SArnaldo Carvalho de Melo if (!sort) { 5488d9233f2SArnaldo Carvalho de Melo pr_err("%s: memdup failed\n", __func__); 5492814eb05SArnaldo Carvalho de Melo return -1; 5502814eb05SArnaldo Carvalho de Melo } 55129b3e152SLi Zefan list_add_tail(&sort->list, list); 55229b3e152SLi Zefan return 0; 55329b3e152SLi Zefan } 55429b3e152SLi Zefan } 55529b3e152SLi Zefan 55629b3e152SLi Zefan return -1; 55729b3e152SLi Zefan } 55829b3e152SLi Zefan 55929b3e152SLi Zefan static int setup_sorting(struct list_head *sort_list, const char *arg) 56029b3e152SLi Zefan { 56129b3e152SLi Zefan char *tok; 56229b3e152SLi Zefan char *str = strdup(arg); 563405f8755SNamhyung Kim char *pos = str; 56429b3e152SLi Zefan 5652814eb05SArnaldo Carvalho de Melo if (!str) { 5662814eb05SArnaldo Carvalho de Melo pr_err("%s: strdup failed\n", __func__); 5672814eb05SArnaldo Carvalho de Melo return -1; 5682814eb05SArnaldo Carvalho de Melo } 56929b3e152SLi Zefan 57029b3e152SLi Zefan while (true) { 571405f8755SNamhyung Kim tok = strsep(&pos, ","); 57229b3e152SLi Zefan if (!tok) 57329b3e152SLi Zefan break; 57429b3e152SLi Zefan if (sort_dimension__add(tok, sort_list) < 0) { 57529b3e152SLi Zefan error("Unknown --sort key: '%s'", tok); 5761b22859dSNamhyung Kim free(str); 57729b3e152SLi Zefan return -1; 57829b3e152SLi Zefan } 57929b3e152SLi Zefan } 58029b3e152SLi Zefan 58129b3e152SLi Zefan free(str); 58229b3e152SLi Zefan return 0; 58329b3e152SLi Zefan } 58429b3e152SLi Zefan 5851d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused, 5861d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused) 587ba77c9e1SLi Zefan { 588ba77c9e1SLi Zefan if (!arg) 589ba77c9e1SLi Zefan return -1; 590ba77c9e1SLi Zefan 591ba77c9e1SLi Zefan if (caller_flag > alloc_flag) 59229b3e152SLi Zefan return setup_sorting(&caller_sort, arg); 593ba77c9e1SLi Zefan else 59429b3e152SLi Zefan return setup_sorting(&alloc_sort, arg); 595ba77c9e1SLi Zefan 596ba77c9e1SLi Zefan return 0; 597ba77c9e1SLi Zefan } 598ba77c9e1SLi Zefan 5991d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused, 6001d037ca1SIrina Tirdea const char *arg __maybe_unused, 6011d037ca1SIrina Tirdea int unset __maybe_unused) 602ba77c9e1SLi Zefan { 603ba77c9e1SLi Zefan caller_flag = (alloc_flag + 1); 60490b86a9fSLi Zefan return 0; 60590b86a9fSLi Zefan } 60690b86a9fSLi Zefan 6071d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused, 6081d037ca1SIrina Tirdea const char *arg __maybe_unused, 6091d037ca1SIrina Tirdea int unset __maybe_unused) 61090b86a9fSLi Zefan { 61190b86a9fSLi Zefan alloc_flag = (caller_flag + 1); 612ba77c9e1SLi Zefan return 0; 613ba77c9e1SLi Zefan } 614ba77c9e1SLi Zefan 6151d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused, 6161d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused) 617ba77c9e1SLi Zefan { 618ba77c9e1SLi Zefan int lines; 619ba77c9e1SLi Zefan 620ba77c9e1SLi Zefan if (!arg) 621ba77c9e1SLi Zefan return -1; 622ba77c9e1SLi Zefan 623ba77c9e1SLi Zefan lines = strtoul(arg, NULL, 10); 624ba77c9e1SLi Zefan 625ba77c9e1SLi Zefan if (caller_flag > alloc_flag) 626ba77c9e1SLi Zefan caller_lines = lines; 627ba77c9e1SLi Zefan else 628ba77c9e1SLi Zefan alloc_lines = lines; 629ba77c9e1SLi Zefan 630ba77c9e1SLi Zefan return 0; 631ba77c9e1SLi Zefan } 632ba77c9e1SLi Zefan 6330433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv) 6340433ffbeSArnaldo Carvalho de Melo { 6350433ffbeSArnaldo Carvalho de Melo const char * const record_args[] = { 6364a4d371aSJiri Olsa "record", "-a", "-R", "-c", "1", 637ba77c9e1SLi Zefan "-e", "kmem:kmalloc", 638ba77c9e1SLi Zefan "-e", "kmem:kmalloc_node", 639ba77c9e1SLi Zefan "-e", "kmem:kfree", 640ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_alloc", 641ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_alloc_node", 642ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_free", 643ba77c9e1SLi Zefan }; 644ba77c9e1SLi Zefan unsigned int rec_argc, i, j; 645ba77c9e1SLi Zefan const char **rec_argv; 646ba77c9e1SLi Zefan 647ba77c9e1SLi Zefan rec_argc = ARRAY_SIZE(record_args) + argc - 1; 648ba77c9e1SLi Zefan rec_argv = calloc(rec_argc + 1, sizeof(char *)); 649ba77c9e1SLi Zefan 650ce47dc56SChris Samuel if (rec_argv == NULL) 651ce47dc56SChris Samuel return -ENOMEM; 652ce47dc56SChris Samuel 653ba77c9e1SLi Zefan for (i = 0; i < ARRAY_SIZE(record_args); i++) 654ba77c9e1SLi Zefan rec_argv[i] = strdup(record_args[i]); 655ba77c9e1SLi Zefan 656ba77c9e1SLi Zefan for (j = 1; j < (unsigned int)argc; j++, i++) 657ba77c9e1SLi Zefan rec_argv[i] = argv[j]; 658ba77c9e1SLi Zefan 659ba77c9e1SLi Zefan return cmd_record(i, rec_argv, NULL); 660ba77c9e1SLi Zefan } 661ba77c9e1SLi Zefan 6621d037ca1SIrina Tirdea int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) 663ba77c9e1SLi Zefan { 6640433ffbeSArnaldo Carvalho de Melo const char * const default_sort_order = "frag,hit,bytes"; 6650433ffbeSArnaldo Carvalho de Melo const struct option kmem_options[] = { 6660433ffbeSArnaldo Carvalho de Melo OPT_STRING('i', "input", &input_name, "file", "input file name"), 667bd72a33eSNamhyung Kim OPT_INCR('v', "verbose", &verbose, 668bd72a33eSNamhyung Kim "be more verbose (show symbol address, etc)"), 6690433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, 6700433ffbeSArnaldo Carvalho de Melo "show per-callsite statistics", parse_caller_opt), 6710433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, 6720433ffbeSArnaldo Carvalho de Melo "show per-allocation statistics", parse_alloc_opt), 6730433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", 6740433ffbeSArnaldo Carvalho de Melo "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", 6750433ffbeSArnaldo Carvalho de Melo parse_sort_opt), 6760433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), 6770433ffbeSArnaldo Carvalho de Melo OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), 6780433ffbeSArnaldo Carvalho de Melo OPT_END() 6790433ffbeSArnaldo Carvalho de Melo }; 6803bca2354SRamkumar Ramachandra const char *const kmem_subcommands[] = { "record", "stat", NULL }; 6813bca2354SRamkumar Ramachandra const char *kmem_usage[] = { 6823bca2354SRamkumar Ramachandra NULL, 6830433ffbeSArnaldo Carvalho de Melo NULL 6840433ffbeSArnaldo Carvalho de Melo }; 6852b2b2c68SNamhyung Kim struct perf_session *session; 6862b2b2c68SNamhyung Kim struct perf_data_file file = { 6872b2b2c68SNamhyung Kim .path = input_name, 6882b2b2c68SNamhyung Kim .mode = PERF_DATA_MODE_READ, 6892b2b2c68SNamhyung Kim }; 6902b2b2c68SNamhyung Kim int ret = -1; 6912b2b2c68SNamhyung Kim 6923bca2354SRamkumar Ramachandra argc = parse_options_subcommand(argc, argv, kmem_options, 6933bca2354SRamkumar Ramachandra kmem_subcommands, kmem_usage, 0); 694ba77c9e1SLi Zefan 69590b86a9fSLi Zefan if (!argc) 696ba77c9e1SLi Zefan usage_with_options(kmem_usage, kmem_options); 697ba77c9e1SLi Zefan 6982b2b2c68SNamhyung Kim if (!strncmp(argv[0], "rec", 3)) { 6990a7e6d1bSNamhyung Kim symbol__init(NULL); 7002b2b2c68SNamhyung Kim return __cmd_record(argc, argv); 7012b2b2c68SNamhyung Kim } 7022b2b2c68SNamhyung Kim 7032b2b2c68SNamhyung Kim session = perf_session__new(&file, false, &perf_kmem); 7042b2b2c68SNamhyung Kim if (session == NULL) 70552e02834STaeung Song return -1; 7062b2b2c68SNamhyung Kim 7070a7e6d1bSNamhyung Kim symbol__init(&session->header.env); 708655000e7SArnaldo Carvalho de Melo 7092b2b2c68SNamhyung Kim if (!strcmp(argv[0], "stat")) { 710*77cfe388SNamhyung Kim setlocale(LC_ALL, ""); 711*77cfe388SNamhyung Kim 7124b627957SDon Zickus if (cpu__setup_cpunode_map()) 7132b2b2c68SNamhyung Kim goto out_delete; 71490b86a9fSLi Zefan 71529b3e152SLi Zefan if (list_empty(&caller_sort)) 71629b3e152SLi Zefan setup_sorting(&caller_sort, default_sort_order); 71729b3e152SLi Zefan if (list_empty(&alloc_sort)) 71829b3e152SLi Zefan setup_sorting(&alloc_sort, default_sort_order); 719ba77c9e1SLi Zefan 7202b2b2c68SNamhyung Kim ret = __cmd_kmem(session); 721b00eca8cSPekka Enberg } else 722b00eca8cSPekka Enberg usage_with_options(kmem_usage, kmem_options); 723ba77c9e1SLi Zefan 7242b2b2c68SNamhyung Kim out_delete: 7252b2b2c68SNamhyung Kim perf_session__delete(session); 7262b2b2c68SNamhyung Kim 7272b2b2c68SNamhyung Kim return ret; 72890b86a9fSLi Zefan } 72990b86a9fSLi Zefan 730