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> 23ba77c9e1SLi Zefan 24ba77c9e1SLi Zefan struct alloc_stat; 25ba77c9e1SLi Zefan typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); 26ba77c9e1SLi Zefan 27ba77c9e1SLi Zefan static int alloc_flag; 28ba77c9e1SLi Zefan static int caller_flag; 29ba77c9e1SLi Zefan 30ba77c9e1SLi Zefan static int alloc_lines = -1; 31ba77c9e1SLi Zefan static int caller_lines = -1; 32ba77c9e1SLi Zefan 337707b6b6SLi Zefan static bool raw_ip; 347707b6b6SLi Zefan 35ba77c9e1SLi Zefan struct alloc_stat { 36ba77c9e1SLi Zefan u64 call_site; 37ba77c9e1SLi Zefan u64 ptr; 38ba77c9e1SLi Zefan u64 bytes_req; 39ba77c9e1SLi Zefan u64 bytes_alloc; 40ba77c9e1SLi Zefan u32 hit; 41079d3f65SLi Zefan u32 pingpong; 42079d3f65SLi Zefan 43079d3f65SLi Zefan short alloc_cpu; 44ba77c9e1SLi Zefan 45ba77c9e1SLi Zefan struct rb_node node; 46ba77c9e1SLi Zefan }; 47ba77c9e1SLi Zefan 48ba77c9e1SLi Zefan static struct rb_root root_alloc_stat; 49ba77c9e1SLi Zefan static struct rb_root root_alloc_sorted; 50ba77c9e1SLi Zefan static struct rb_root root_caller_stat; 51ba77c9e1SLi Zefan static struct rb_root root_caller_sorted; 52ba77c9e1SLi Zefan 53ba77c9e1SLi Zefan static unsigned long total_requested, total_allocated; 547d0d3945SLi Zefan static unsigned long nr_allocs, nr_cross_allocs; 55ba77c9e1SLi Zefan 562814eb05SArnaldo Carvalho de Melo static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, 57079d3f65SLi Zefan int bytes_req, int bytes_alloc, int cpu) 58ba77c9e1SLi Zefan { 59ba77c9e1SLi Zefan struct rb_node **node = &root_alloc_stat.rb_node; 60ba77c9e1SLi Zefan struct rb_node *parent = NULL; 61ba77c9e1SLi Zefan struct alloc_stat *data = NULL; 62ba77c9e1SLi Zefan 63ba77c9e1SLi Zefan while (*node) { 64ba77c9e1SLi Zefan parent = *node; 65ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node); 66ba77c9e1SLi Zefan 67ba77c9e1SLi Zefan if (ptr > data->ptr) 68ba77c9e1SLi Zefan node = &(*node)->rb_right; 69ba77c9e1SLi Zefan else if (ptr < data->ptr) 70ba77c9e1SLi Zefan node = &(*node)->rb_left; 71ba77c9e1SLi Zefan else 72ba77c9e1SLi Zefan break; 73ba77c9e1SLi Zefan } 74ba77c9e1SLi Zefan 75ba77c9e1SLi Zefan if (data && data->ptr == ptr) { 76ba77c9e1SLi Zefan data->hit++; 77ba77c9e1SLi Zefan data->bytes_req += bytes_req; 784efb5290SWenji Huang data->bytes_alloc += bytes_alloc; 79ba77c9e1SLi Zefan } else { 80ba77c9e1SLi Zefan data = malloc(sizeof(*data)); 812814eb05SArnaldo Carvalho de Melo if (!data) { 822814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__); 832814eb05SArnaldo Carvalho de Melo return -1; 842814eb05SArnaldo Carvalho de Melo } 85ba77c9e1SLi Zefan data->ptr = ptr; 86079d3f65SLi Zefan data->pingpong = 0; 87ba77c9e1SLi Zefan data->hit = 1; 88ba77c9e1SLi Zefan data->bytes_req = bytes_req; 89ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc; 90ba77c9e1SLi Zefan 91ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node); 92ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_alloc_stat); 93ba77c9e1SLi Zefan } 94079d3f65SLi Zefan data->call_site = call_site; 95079d3f65SLi Zefan data->alloc_cpu = cpu; 962814eb05SArnaldo Carvalho de Melo return 0; 97ba77c9e1SLi Zefan } 98ba77c9e1SLi Zefan 992814eb05SArnaldo Carvalho de Melo static int insert_caller_stat(unsigned long call_site, 100ba77c9e1SLi Zefan int bytes_req, int bytes_alloc) 101ba77c9e1SLi Zefan { 102ba77c9e1SLi Zefan struct rb_node **node = &root_caller_stat.rb_node; 103ba77c9e1SLi Zefan struct rb_node *parent = NULL; 104ba77c9e1SLi Zefan struct alloc_stat *data = NULL; 105ba77c9e1SLi Zefan 106ba77c9e1SLi Zefan while (*node) { 107ba77c9e1SLi Zefan parent = *node; 108ba77c9e1SLi Zefan data = rb_entry(*node, struct alloc_stat, node); 109ba77c9e1SLi Zefan 110ba77c9e1SLi Zefan if (call_site > data->call_site) 111ba77c9e1SLi Zefan node = &(*node)->rb_right; 112ba77c9e1SLi Zefan else if (call_site < data->call_site) 113ba77c9e1SLi Zefan node = &(*node)->rb_left; 114ba77c9e1SLi Zefan else 115ba77c9e1SLi Zefan break; 116ba77c9e1SLi Zefan } 117ba77c9e1SLi Zefan 118ba77c9e1SLi Zefan if (data && data->call_site == call_site) { 119ba77c9e1SLi Zefan data->hit++; 120ba77c9e1SLi Zefan data->bytes_req += bytes_req; 1214efb5290SWenji Huang data->bytes_alloc += bytes_alloc; 122ba77c9e1SLi Zefan } else { 123ba77c9e1SLi Zefan data = malloc(sizeof(*data)); 1242814eb05SArnaldo Carvalho de Melo if (!data) { 1252814eb05SArnaldo Carvalho de Melo pr_err("%s: malloc failed\n", __func__); 1262814eb05SArnaldo Carvalho de Melo return -1; 1272814eb05SArnaldo Carvalho de Melo } 128ba77c9e1SLi Zefan data->call_site = call_site; 129079d3f65SLi Zefan data->pingpong = 0; 130ba77c9e1SLi Zefan data->hit = 1; 131ba77c9e1SLi Zefan data->bytes_req = bytes_req; 132ba77c9e1SLi Zefan data->bytes_alloc = bytes_alloc; 133ba77c9e1SLi Zefan 134ba77c9e1SLi Zefan rb_link_node(&data->node, parent, node); 135ba77c9e1SLi Zefan rb_insert_color(&data->node, &root_caller_stat); 136ba77c9e1SLi Zefan } 1372814eb05SArnaldo Carvalho de Melo 1382814eb05SArnaldo Carvalho de Melo return 0; 139ba77c9e1SLi Zefan } 140ba77c9e1SLi Zefan 1412814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_alloc_event(struct perf_evsel *evsel, 1420f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample) 143ba77c9e1SLi Zefan { 1440f7d2f1bSArnaldo Carvalho de Melo unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"), 1450f7d2f1bSArnaldo Carvalho de Melo call_site = perf_evsel__intval(evsel, sample, "call_site"); 1460f7d2f1bSArnaldo Carvalho de Melo int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"), 1470f7d2f1bSArnaldo Carvalho de Melo bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc"); 148ba77c9e1SLi Zefan 1490f7d2f1bSArnaldo Carvalho de Melo if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) || 1502814eb05SArnaldo Carvalho de Melo insert_caller_stat(call_site, bytes_req, bytes_alloc)) 1512814eb05SArnaldo Carvalho de Melo return -1; 152ba77c9e1SLi Zefan 153ba77c9e1SLi Zefan total_requested += bytes_req; 154ba77c9e1SLi Zefan total_allocated += bytes_alloc; 1557d0d3945SLi Zefan 1560f7d2f1bSArnaldo Carvalho de Melo nr_allocs++; 1570f7d2f1bSArnaldo Carvalho de Melo return 0; 1580f7d2f1bSArnaldo Carvalho de Melo } 1590f7d2f1bSArnaldo Carvalho de Melo 1600f7d2f1bSArnaldo Carvalho de Melo static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel, 1610f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample) 1620f7d2f1bSArnaldo Carvalho de Melo { 1630f7d2f1bSArnaldo Carvalho de Melo int ret = perf_evsel__process_alloc_event(evsel, sample); 1640f7d2f1bSArnaldo Carvalho de Melo 1650f7d2f1bSArnaldo Carvalho de Melo if (!ret) { 1664b627957SDon Zickus int node1 = cpu__get_node(sample->cpu), 1670f7d2f1bSArnaldo Carvalho de Melo node2 = perf_evsel__intval(evsel, sample, "node"); 1680f7d2f1bSArnaldo Carvalho de Melo 1697d0d3945SLi Zefan if (node1 != node2) 1707d0d3945SLi Zefan nr_cross_allocs++; 1717d0d3945SLi Zefan } 1720f7d2f1bSArnaldo Carvalho de Melo 1730f7d2f1bSArnaldo Carvalho de Melo return ret; 174ba77c9e1SLi Zefan } 175ba77c9e1SLi Zefan 176079d3f65SLi Zefan static int ptr_cmp(struct alloc_stat *, struct alloc_stat *); 177079d3f65SLi Zefan static int callsite_cmp(struct alloc_stat *, struct alloc_stat *); 178079d3f65SLi Zefan 179079d3f65SLi Zefan static struct alloc_stat *search_alloc_stat(unsigned long ptr, 180079d3f65SLi Zefan unsigned long call_site, 181079d3f65SLi Zefan struct rb_root *root, 182079d3f65SLi Zefan sort_fn_t sort_fn) 183079d3f65SLi Zefan { 184079d3f65SLi Zefan struct rb_node *node = root->rb_node; 185079d3f65SLi Zefan struct alloc_stat key = { .ptr = ptr, .call_site = call_site }; 186079d3f65SLi Zefan 187079d3f65SLi Zefan while (node) { 188079d3f65SLi Zefan struct alloc_stat *data; 189079d3f65SLi Zefan int cmp; 190079d3f65SLi Zefan 191079d3f65SLi Zefan data = rb_entry(node, struct alloc_stat, node); 192079d3f65SLi Zefan 193079d3f65SLi Zefan cmp = sort_fn(&key, data); 194079d3f65SLi Zefan if (cmp < 0) 195079d3f65SLi Zefan node = node->rb_left; 196079d3f65SLi Zefan else if (cmp > 0) 197079d3f65SLi Zefan node = node->rb_right; 198079d3f65SLi Zefan else 199079d3f65SLi Zefan return data; 200079d3f65SLi Zefan } 201079d3f65SLi Zefan return NULL; 202079d3f65SLi Zefan } 203079d3f65SLi Zefan 2042814eb05SArnaldo Carvalho de Melo static int perf_evsel__process_free_event(struct perf_evsel *evsel, 20522ad798cSArnaldo Carvalho de Melo struct perf_sample *sample) 206ba77c9e1SLi Zefan { 2070f7d2f1bSArnaldo Carvalho de Melo unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"); 208079d3f65SLi Zefan struct alloc_stat *s_alloc, *s_caller; 209079d3f65SLi Zefan 210079d3f65SLi Zefan s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); 211079d3f65SLi Zefan if (!s_alloc) 2122814eb05SArnaldo Carvalho de Melo return 0; 213079d3f65SLi Zefan 21422ad798cSArnaldo Carvalho de Melo if ((short)sample->cpu != s_alloc->alloc_cpu) { 215079d3f65SLi Zefan s_alloc->pingpong++; 216079d3f65SLi Zefan 217079d3f65SLi Zefan s_caller = search_alloc_stat(0, s_alloc->call_site, 218079d3f65SLi Zefan &root_caller_stat, callsite_cmp); 2192814eb05SArnaldo Carvalho de Melo if (!s_caller) 2202814eb05SArnaldo Carvalho de Melo return -1; 221079d3f65SLi Zefan s_caller->pingpong++; 222079d3f65SLi Zefan } 223079d3f65SLi Zefan s_alloc->alloc_cpu = -1; 2242814eb05SArnaldo Carvalho de Melo 2252814eb05SArnaldo Carvalho de Melo return 0; 226ba77c9e1SLi Zefan } 227ba77c9e1SLi Zefan 2280f7d2f1bSArnaldo Carvalho de Melo typedef int (*tracepoint_handler)(struct perf_evsel *evsel, 2290f7d2f1bSArnaldo Carvalho de Melo struct perf_sample *sample); 230ba77c9e1SLi Zefan 2311d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused, 232d20deb64SArnaldo Carvalho de Melo union perf_event *event, 2338115d60cSArnaldo Carvalho de Melo struct perf_sample *sample, 234fcf65bf1SArnaldo Carvalho de Melo struct perf_evsel *evsel, 235743eb868SArnaldo Carvalho de Melo struct machine *machine) 236ba77c9e1SLi Zefan { 237ef89325fSAdrian Hunter struct thread *thread = machine__findnew_thread(machine, sample->pid, 23813ce34dfSNamhyung Kim sample->tid); 239ba77c9e1SLi Zefan 240ba77c9e1SLi Zefan if (thread == NULL) { 241ba77c9e1SLi Zefan pr_debug("problem processing %d event, skipping it.\n", 242ba77c9e1SLi Zefan event->header.type); 243ba77c9e1SLi Zefan return -1; 244ba77c9e1SLi Zefan } 245ba77c9e1SLi Zefan 246b9c5143aSFrederic Weisbecker dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); 247ba77c9e1SLi Zefan 248744a9719SArnaldo Carvalho de Melo if (evsel->handler != NULL) { 249744a9719SArnaldo Carvalho de Melo tracepoint_handler f = evsel->handler; 2500f7d2f1bSArnaldo Carvalho de Melo return f(evsel, sample); 2510f7d2f1bSArnaldo Carvalho de Melo } 2520f7d2f1bSArnaldo Carvalho de Melo 2530f7d2f1bSArnaldo Carvalho de Melo return 0; 254ba77c9e1SLi Zefan } 255ba77c9e1SLi Zefan 256fcf65bf1SArnaldo Carvalho de Melo static struct perf_tool perf_kmem = { 25755aa640fSArnaldo Carvalho de Melo .sample = process_sample_event, 2588115d60cSArnaldo Carvalho de Melo .comm = perf_event__process_comm, 25964c40908SNamhyung Kim .mmap = perf_event__process_mmap, 26064c40908SNamhyung Kim .mmap2 = perf_event__process_mmap2, 2610a8cb85cSJiri Olsa .ordered_events = true, 262ba77c9e1SLi Zefan }; 263ba77c9e1SLi Zefan 264ba77c9e1SLi Zefan static double fragmentation(unsigned long n_req, unsigned long n_alloc) 265ba77c9e1SLi Zefan { 266ba77c9e1SLi Zefan if (n_alloc == 0) 267ba77c9e1SLi Zefan return 0.0; 268ba77c9e1SLi Zefan else 269ba77c9e1SLi Zefan return 100.0 - (100.0 * n_req / n_alloc); 270ba77c9e1SLi Zefan } 271ba77c9e1SLi Zefan 2724aa65636SArnaldo Carvalho de Melo static void __print_result(struct rb_root *root, struct perf_session *session, 2734aa65636SArnaldo Carvalho de Melo int n_lines, int is_caller) 274ba77c9e1SLi Zefan { 275ba77c9e1SLi Zefan struct rb_node *next; 27634ba5122SArnaldo Carvalho de Melo struct machine *machine = &session->machines.host; 277ba77c9e1SLi Zefan 278079d3f65SLi Zefan printf("%.102s\n", graph_dotted_line); 279079d3f65SLi Zefan printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); 280079d3f65SLi Zefan printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n"); 281079d3f65SLi Zefan printf("%.102s\n", graph_dotted_line); 282ba77c9e1SLi Zefan 283ba77c9e1SLi Zefan next = rb_first(root); 284ba77c9e1SLi Zefan 285ba77c9e1SLi Zefan while (next && n_lines--) { 2861b145ae5SArnaldo Carvalho de Melo struct alloc_stat *data = rb_entry(next, struct alloc_stat, 2871b145ae5SArnaldo Carvalho de Melo node); 2881b145ae5SArnaldo Carvalho de Melo struct symbol *sym = NULL; 28971cf8b8fSArnaldo Carvalho de Melo struct map *map; 290079d3f65SLi Zefan char buf[BUFSIZ]; 2911b145ae5SArnaldo Carvalho de Melo u64 addr; 292ba77c9e1SLi Zefan 2931b145ae5SArnaldo Carvalho de Melo if (is_caller) { 2941b145ae5SArnaldo Carvalho de Melo addr = data->call_site; 2957707b6b6SLi Zefan if (!raw_ip) 2965c0541d5SArnaldo Carvalho de Melo sym = machine__find_kernel_function(machine, addr, &map, NULL); 2971b145ae5SArnaldo Carvalho de Melo } else 2981b145ae5SArnaldo Carvalho de Melo addr = data->ptr; 299ba77c9e1SLi Zefan 3001b145ae5SArnaldo Carvalho de Melo if (sym != NULL) 3019486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name, 30271cf8b8fSArnaldo Carvalho de Melo addr - map->unmap_ip(map, sym->start)); 3031b145ae5SArnaldo Carvalho de Melo else 3049486aa38SArnaldo Carvalho de Melo snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr); 305079d3f65SLi Zefan printf(" %-34s |", buf); 3061b145ae5SArnaldo Carvalho de Melo 30747103277SPekka Enberg printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n", 308079d3f65SLi Zefan (unsigned long long)data->bytes_alloc, 309ba77c9e1SLi Zefan (unsigned long)data->bytes_alloc / data->hit, 310ba77c9e1SLi Zefan (unsigned long long)data->bytes_req, 311ba77c9e1SLi Zefan (unsigned long)data->bytes_req / data->hit, 312ba77c9e1SLi Zefan (unsigned long)data->hit, 313079d3f65SLi Zefan (unsigned long)data->pingpong, 314ba77c9e1SLi Zefan fragmentation(data->bytes_req, data->bytes_alloc)); 315ba77c9e1SLi Zefan 316ba77c9e1SLi Zefan next = rb_next(next); 317ba77c9e1SLi Zefan } 318ba77c9e1SLi Zefan 319ba77c9e1SLi Zefan if (n_lines == -1) 320079d3f65SLi Zefan printf(" ... | ... | ... | ... | ... | ... \n"); 321ba77c9e1SLi Zefan 322079d3f65SLi Zefan printf("%.102s\n", graph_dotted_line); 323ba77c9e1SLi Zefan } 324ba77c9e1SLi Zefan 325ba77c9e1SLi Zefan static void print_summary(void) 326ba77c9e1SLi Zefan { 327ba77c9e1SLi Zefan printf("\nSUMMARY\n=======\n"); 328ba77c9e1SLi Zefan printf("Total bytes requested: %lu\n", total_requested); 329ba77c9e1SLi Zefan printf("Total bytes allocated: %lu\n", total_allocated); 330ba77c9e1SLi Zefan printf("Total bytes wasted on internal fragmentation: %lu\n", 331ba77c9e1SLi Zefan total_allocated - total_requested); 332ba77c9e1SLi Zefan printf("Internal fragmentation: %f%%\n", 333ba77c9e1SLi Zefan fragmentation(total_requested, total_allocated)); 3347d0d3945SLi Zefan printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); 335ba77c9e1SLi Zefan } 336ba77c9e1SLi Zefan 3374aa65636SArnaldo Carvalho de Melo static void print_result(struct perf_session *session) 338ba77c9e1SLi Zefan { 339ba77c9e1SLi Zefan if (caller_flag) 3404aa65636SArnaldo Carvalho de Melo __print_result(&root_caller_sorted, session, caller_lines, 1); 341ba77c9e1SLi Zefan if (alloc_flag) 3424aa65636SArnaldo Carvalho de Melo __print_result(&root_alloc_sorted, session, alloc_lines, 0); 343ba77c9e1SLi Zefan print_summary(); 344ba77c9e1SLi Zefan } 345ba77c9e1SLi Zefan 34629b3e152SLi Zefan struct sort_dimension { 34729b3e152SLi Zefan const char name[20]; 34829b3e152SLi Zefan sort_fn_t cmp; 34929b3e152SLi Zefan struct list_head list; 35029b3e152SLi Zefan }; 35129b3e152SLi Zefan 35229b3e152SLi Zefan static LIST_HEAD(caller_sort); 35329b3e152SLi Zefan static LIST_HEAD(alloc_sort); 35429b3e152SLi Zefan 355ba77c9e1SLi Zefan static void sort_insert(struct rb_root *root, struct alloc_stat *data, 35629b3e152SLi Zefan struct list_head *sort_list) 357ba77c9e1SLi Zefan { 358ba77c9e1SLi Zefan struct rb_node **new = &(root->rb_node); 359ba77c9e1SLi Zefan struct rb_node *parent = NULL; 36029b3e152SLi Zefan struct sort_dimension *sort; 361ba77c9e1SLi Zefan 362ba77c9e1SLi Zefan while (*new) { 363ba77c9e1SLi Zefan struct alloc_stat *this; 36429b3e152SLi Zefan int cmp = 0; 365ba77c9e1SLi Zefan 366ba77c9e1SLi Zefan this = rb_entry(*new, struct alloc_stat, node); 367ba77c9e1SLi Zefan parent = *new; 368ba77c9e1SLi Zefan 36929b3e152SLi Zefan list_for_each_entry(sort, sort_list, list) { 37029b3e152SLi Zefan cmp = sort->cmp(data, this); 37129b3e152SLi Zefan if (cmp) 37229b3e152SLi Zefan break; 37329b3e152SLi Zefan } 374ba77c9e1SLi Zefan 375ba77c9e1SLi Zefan if (cmp > 0) 376ba77c9e1SLi Zefan new = &((*new)->rb_left); 377ba77c9e1SLi Zefan else 378ba77c9e1SLi Zefan new = &((*new)->rb_right); 379ba77c9e1SLi Zefan } 380ba77c9e1SLi Zefan 381ba77c9e1SLi Zefan rb_link_node(&data->node, parent, new); 382ba77c9e1SLi Zefan rb_insert_color(&data->node, root); 383ba77c9e1SLi Zefan } 384ba77c9e1SLi Zefan 385ba77c9e1SLi Zefan static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, 38629b3e152SLi Zefan struct list_head *sort_list) 387ba77c9e1SLi Zefan { 388ba77c9e1SLi Zefan struct rb_node *node; 389ba77c9e1SLi Zefan struct alloc_stat *data; 390ba77c9e1SLi Zefan 391ba77c9e1SLi Zefan for (;;) { 392ba77c9e1SLi Zefan node = rb_first(root); 393ba77c9e1SLi Zefan if (!node) 394ba77c9e1SLi Zefan break; 395ba77c9e1SLi Zefan 396ba77c9e1SLi Zefan rb_erase(node, root); 397ba77c9e1SLi Zefan data = rb_entry(node, struct alloc_stat, node); 39829b3e152SLi Zefan sort_insert(root_sorted, data, sort_list); 399ba77c9e1SLi Zefan } 400ba77c9e1SLi Zefan } 401ba77c9e1SLi Zefan 402ba77c9e1SLi Zefan static void sort_result(void) 403ba77c9e1SLi Zefan { 40429b3e152SLi Zefan __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort); 40529b3e152SLi Zefan __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); 406ba77c9e1SLi Zefan } 407ba77c9e1SLi Zefan 4082b2b2c68SNamhyung Kim static int __cmd_kmem(struct perf_session *session) 409ba77c9e1SLi Zefan { 410d549c769SArnaldo Carvalho de Melo int err = -EINVAL; 4110f7d2f1bSArnaldo Carvalho de Melo const struct perf_evsel_str_handler kmem_tracepoints[] = { 4120f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmalloc", perf_evsel__process_alloc_event, }, 4130f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, 4140f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmalloc_node", perf_evsel__process_alloc_node_event, }, 4150f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, }, 4160f7d2f1bSArnaldo Carvalho de Melo { "kmem:kfree", perf_evsel__process_free_event, }, 4170f7d2f1bSArnaldo Carvalho de Melo { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, 4180f7d2f1bSArnaldo Carvalho de Melo }; 419ba77c9e1SLi Zefan 420d549c769SArnaldo Carvalho de Melo if (!perf_session__has_traces(session, "kmem record")) 4212b2b2c68SNamhyung Kim goto out; 422d549c769SArnaldo Carvalho de Melo 4230f7d2f1bSArnaldo Carvalho de Melo if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { 4240f7d2f1bSArnaldo Carvalho de Melo pr_err("Initializing perf session tracepoint handlers failed\n"); 4252b2b2c68SNamhyung Kim goto out; 4260f7d2f1bSArnaldo Carvalho de Melo } 4270f7d2f1bSArnaldo Carvalho de Melo 4284aa65636SArnaldo Carvalho de Melo setup_pager(); 429*b7b61cbeSArnaldo Carvalho de Melo err = perf_session__process_events(session); 4304aa65636SArnaldo Carvalho de Melo if (err != 0) 4312b2b2c68SNamhyung Kim goto out; 4324aa65636SArnaldo Carvalho de Melo sort_result(); 4334aa65636SArnaldo Carvalho de Melo print_result(session); 4342b2b2c68SNamhyung Kim out: 4354aa65636SArnaldo Carvalho de Melo return err; 436ba77c9e1SLi Zefan } 437ba77c9e1SLi Zefan 438ba77c9e1SLi Zefan static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) 439ba77c9e1SLi Zefan { 440ba77c9e1SLi Zefan if (l->ptr < r->ptr) 441ba77c9e1SLi Zefan return -1; 442ba77c9e1SLi Zefan else if (l->ptr > r->ptr) 443ba77c9e1SLi Zefan return 1; 444ba77c9e1SLi Zefan return 0; 445ba77c9e1SLi Zefan } 446ba77c9e1SLi Zefan 44729b3e152SLi Zefan static struct sort_dimension ptr_sort_dimension = { 44829b3e152SLi Zefan .name = "ptr", 44929b3e152SLi Zefan .cmp = ptr_cmp, 45029b3e152SLi Zefan }; 45129b3e152SLi Zefan 452ba77c9e1SLi Zefan static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) 453ba77c9e1SLi Zefan { 454ba77c9e1SLi Zefan if (l->call_site < r->call_site) 455ba77c9e1SLi Zefan return -1; 456ba77c9e1SLi Zefan else if (l->call_site > r->call_site) 457ba77c9e1SLi Zefan return 1; 458ba77c9e1SLi Zefan return 0; 459ba77c9e1SLi Zefan } 460ba77c9e1SLi Zefan 46129b3e152SLi Zefan static struct sort_dimension callsite_sort_dimension = { 46229b3e152SLi Zefan .name = "callsite", 46329b3e152SLi Zefan .cmp = callsite_cmp, 46429b3e152SLi Zefan }; 46529b3e152SLi Zefan 466f3ced7cdSPekka Enberg static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r) 467f3ced7cdSPekka Enberg { 468f3ced7cdSPekka Enberg if (l->hit < r->hit) 469f3ced7cdSPekka Enberg return -1; 470f3ced7cdSPekka Enberg else if (l->hit > r->hit) 471f3ced7cdSPekka Enberg return 1; 472f3ced7cdSPekka Enberg return 0; 473f3ced7cdSPekka Enberg } 474f3ced7cdSPekka Enberg 47529b3e152SLi Zefan static struct sort_dimension hit_sort_dimension = { 47629b3e152SLi Zefan .name = "hit", 47729b3e152SLi Zefan .cmp = hit_cmp, 47829b3e152SLi Zefan }; 47929b3e152SLi Zefan 480ba77c9e1SLi Zefan static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) 481ba77c9e1SLi Zefan { 482ba77c9e1SLi Zefan if (l->bytes_alloc < r->bytes_alloc) 483ba77c9e1SLi Zefan return -1; 484ba77c9e1SLi Zefan else if (l->bytes_alloc > r->bytes_alloc) 485ba77c9e1SLi Zefan return 1; 486ba77c9e1SLi Zefan return 0; 487ba77c9e1SLi Zefan } 488ba77c9e1SLi Zefan 48929b3e152SLi Zefan static struct sort_dimension bytes_sort_dimension = { 49029b3e152SLi Zefan .name = "bytes", 49129b3e152SLi Zefan .cmp = bytes_cmp, 49229b3e152SLi Zefan }; 49329b3e152SLi Zefan 494f3ced7cdSPekka Enberg static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r) 495f3ced7cdSPekka Enberg { 496f3ced7cdSPekka Enberg double x, y; 497f3ced7cdSPekka Enberg 498f3ced7cdSPekka Enberg x = fragmentation(l->bytes_req, l->bytes_alloc); 499f3ced7cdSPekka Enberg y = fragmentation(r->bytes_req, r->bytes_alloc); 500f3ced7cdSPekka Enberg 501f3ced7cdSPekka Enberg if (x < y) 502f3ced7cdSPekka Enberg return -1; 503f3ced7cdSPekka Enberg else if (x > y) 504f3ced7cdSPekka Enberg return 1; 505f3ced7cdSPekka Enberg return 0; 506f3ced7cdSPekka Enberg } 507f3ced7cdSPekka Enberg 50829b3e152SLi Zefan static struct sort_dimension frag_sort_dimension = { 50929b3e152SLi Zefan .name = "frag", 51029b3e152SLi Zefan .cmp = frag_cmp, 51129b3e152SLi Zefan }; 51229b3e152SLi Zefan 513079d3f65SLi Zefan static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r) 514079d3f65SLi Zefan { 515079d3f65SLi Zefan if (l->pingpong < r->pingpong) 516079d3f65SLi Zefan return -1; 517079d3f65SLi Zefan else if (l->pingpong > r->pingpong) 518079d3f65SLi Zefan return 1; 519079d3f65SLi Zefan return 0; 520079d3f65SLi Zefan } 521079d3f65SLi Zefan 522079d3f65SLi Zefan static struct sort_dimension pingpong_sort_dimension = { 523079d3f65SLi Zefan .name = "pingpong", 524079d3f65SLi Zefan .cmp = pingpong_cmp, 525079d3f65SLi Zefan }; 526079d3f65SLi Zefan 52729b3e152SLi Zefan static struct sort_dimension *avail_sorts[] = { 52829b3e152SLi Zefan &ptr_sort_dimension, 52929b3e152SLi Zefan &callsite_sort_dimension, 53029b3e152SLi Zefan &hit_sort_dimension, 53129b3e152SLi Zefan &bytes_sort_dimension, 53229b3e152SLi Zefan &frag_sort_dimension, 533079d3f65SLi Zefan &pingpong_sort_dimension, 53429b3e152SLi Zefan }; 53529b3e152SLi Zefan 53649e4ba54SSasha Levin #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts)) 53729b3e152SLi Zefan 53829b3e152SLi Zefan static int sort_dimension__add(const char *tok, struct list_head *list) 53929b3e152SLi Zefan { 54029b3e152SLi Zefan struct sort_dimension *sort; 54129b3e152SLi Zefan int i; 54229b3e152SLi Zefan 54329b3e152SLi Zefan for (i = 0; i < NUM_AVAIL_SORTS; i++) { 54429b3e152SLi Zefan if (!strcmp(avail_sorts[i]->name, tok)) { 5458d9233f2SArnaldo Carvalho de Melo sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i])); 5462814eb05SArnaldo Carvalho de Melo if (!sort) { 5478d9233f2SArnaldo Carvalho de Melo pr_err("%s: memdup failed\n", __func__); 5482814eb05SArnaldo Carvalho de Melo return -1; 5492814eb05SArnaldo Carvalho de Melo } 55029b3e152SLi Zefan list_add_tail(&sort->list, list); 55129b3e152SLi Zefan return 0; 55229b3e152SLi Zefan } 55329b3e152SLi Zefan } 55429b3e152SLi Zefan 55529b3e152SLi Zefan return -1; 55629b3e152SLi Zefan } 55729b3e152SLi Zefan 55829b3e152SLi Zefan static int setup_sorting(struct list_head *sort_list, const char *arg) 55929b3e152SLi Zefan { 56029b3e152SLi Zefan char *tok; 56129b3e152SLi Zefan char *str = strdup(arg); 56229b3e152SLi Zefan 5632814eb05SArnaldo Carvalho de Melo if (!str) { 5642814eb05SArnaldo Carvalho de Melo pr_err("%s: strdup failed\n", __func__); 5652814eb05SArnaldo Carvalho de Melo return -1; 5662814eb05SArnaldo Carvalho de Melo } 56729b3e152SLi Zefan 56829b3e152SLi Zefan while (true) { 56929b3e152SLi Zefan tok = strsep(&str, ","); 57029b3e152SLi Zefan if (!tok) 57129b3e152SLi Zefan break; 57229b3e152SLi Zefan if (sort_dimension__add(tok, sort_list) < 0) { 57329b3e152SLi Zefan error("Unknown --sort key: '%s'", tok); 5741b22859dSNamhyung Kim free(str); 57529b3e152SLi Zefan return -1; 57629b3e152SLi Zefan } 57729b3e152SLi Zefan } 57829b3e152SLi Zefan 57929b3e152SLi Zefan free(str); 58029b3e152SLi Zefan return 0; 58129b3e152SLi Zefan } 58229b3e152SLi Zefan 5831d037ca1SIrina Tirdea static int parse_sort_opt(const struct option *opt __maybe_unused, 5841d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused) 585ba77c9e1SLi Zefan { 586ba77c9e1SLi Zefan if (!arg) 587ba77c9e1SLi Zefan return -1; 588ba77c9e1SLi Zefan 589ba77c9e1SLi Zefan if (caller_flag > alloc_flag) 59029b3e152SLi Zefan return setup_sorting(&caller_sort, arg); 591ba77c9e1SLi Zefan else 59229b3e152SLi Zefan return setup_sorting(&alloc_sort, arg); 593ba77c9e1SLi Zefan 594ba77c9e1SLi Zefan return 0; 595ba77c9e1SLi Zefan } 596ba77c9e1SLi Zefan 5971d037ca1SIrina Tirdea static int parse_caller_opt(const struct option *opt __maybe_unused, 5981d037ca1SIrina Tirdea const char *arg __maybe_unused, 5991d037ca1SIrina Tirdea int unset __maybe_unused) 600ba77c9e1SLi Zefan { 601ba77c9e1SLi Zefan caller_flag = (alloc_flag + 1); 60290b86a9fSLi Zefan return 0; 60390b86a9fSLi Zefan } 60490b86a9fSLi Zefan 6051d037ca1SIrina Tirdea static int parse_alloc_opt(const struct option *opt __maybe_unused, 6061d037ca1SIrina Tirdea const char *arg __maybe_unused, 6071d037ca1SIrina Tirdea int unset __maybe_unused) 60890b86a9fSLi Zefan { 60990b86a9fSLi Zefan alloc_flag = (caller_flag + 1); 610ba77c9e1SLi Zefan return 0; 611ba77c9e1SLi Zefan } 612ba77c9e1SLi Zefan 6131d037ca1SIrina Tirdea static int parse_line_opt(const struct option *opt __maybe_unused, 6141d037ca1SIrina Tirdea const char *arg, int unset __maybe_unused) 615ba77c9e1SLi Zefan { 616ba77c9e1SLi Zefan int lines; 617ba77c9e1SLi Zefan 618ba77c9e1SLi Zefan if (!arg) 619ba77c9e1SLi Zefan return -1; 620ba77c9e1SLi Zefan 621ba77c9e1SLi Zefan lines = strtoul(arg, NULL, 10); 622ba77c9e1SLi Zefan 623ba77c9e1SLi Zefan if (caller_flag > alloc_flag) 624ba77c9e1SLi Zefan caller_lines = lines; 625ba77c9e1SLi Zefan else 626ba77c9e1SLi Zefan alloc_lines = lines; 627ba77c9e1SLi Zefan 628ba77c9e1SLi Zefan return 0; 629ba77c9e1SLi Zefan } 630ba77c9e1SLi Zefan 6310433ffbeSArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv) 6320433ffbeSArnaldo Carvalho de Melo { 6330433ffbeSArnaldo Carvalho de Melo const char * const record_args[] = { 6344a4d371aSJiri Olsa "record", "-a", "-R", "-c", "1", 635ba77c9e1SLi Zefan "-e", "kmem:kmalloc", 636ba77c9e1SLi Zefan "-e", "kmem:kmalloc_node", 637ba77c9e1SLi Zefan "-e", "kmem:kfree", 638ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_alloc", 639ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_alloc_node", 640ba77c9e1SLi Zefan "-e", "kmem:kmem_cache_free", 641ba77c9e1SLi Zefan }; 642ba77c9e1SLi Zefan unsigned int rec_argc, i, j; 643ba77c9e1SLi Zefan const char **rec_argv; 644ba77c9e1SLi Zefan 645ba77c9e1SLi Zefan rec_argc = ARRAY_SIZE(record_args) + argc - 1; 646ba77c9e1SLi Zefan rec_argv = calloc(rec_argc + 1, sizeof(char *)); 647ba77c9e1SLi Zefan 648ce47dc56SChris Samuel if (rec_argv == NULL) 649ce47dc56SChris Samuel return -ENOMEM; 650ce47dc56SChris Samuel 651ba77c9e1SLi Zefan for (i = 0; i < ARRAY_SIZE(record_args); i++) 652ba77c9e1SLi Zefan rec_argv[i] = strdup(record_args[i]); 653ba77c9e1SLi Zefan 654ba77c9e1SLi Zefan for (j = 1; j < (unsigned int)argc; j++, i++) 655ba77c9e1SLi Zefan rec_argv[i] = argv[j]; 656ba77c9e1SLi Zefan 657ba77c9e1SLi Zefan return cmd_record(i, rec_argv, NULL); 658ba77c9e1SLi Zefan } 659ba77c9e1SLi Zefan 6601d037ca1SIrina Tirdea int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) 661ba77c9e1SLi Zefan { 6620433ffbeSArnaldo Carvalho de Melo const char * const default_sort_order = "frag,hit,bytes"; 6630433ffbeSArnaldo Carvalho de Melo const struct option kmem_options[] = { 6640433ffbeSArnaldo Carvalho de Melo OPT_STRING('i', "input", &input_name, "file", "input file name"), 6650433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, 6660433ffbeSArnaldo Carvalho de Melo "show per-callsite statistics", parse_caller_opt), 6670433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, 6680433ffbeSArnaldo Carvalho de Melo "show per-allocation statistics", parse_alloc_opt), 6690433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", 6700433ffbeSArnaldo Carvalho de Melo "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", 6710433ffbeSArnaldo Carvalho de Melo parse_sort_opt), 6720433ffbeSArnaldo Carvalho de Melo OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), 6730433ffbeSArnaldo Carvalho de Melo OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), 6740433ffbeSArnaldo Carvalho de Melo OPT_END() 6750433ffbeSArnaldo Carvalho de Melo }; 6763bca2354SRamkumar Ramachandra const char *const kmem_subcommands[] = { "record", "stat", NULL }; 6773bca2354SRamkumar Ramachandra const char *kmem_usage[] = { 6783bca2354SRamkumar Ramachandra NULL, 6790433ffbeSArnaldo Carvalho de Melo NULL 6800433ffbeSArnaldo Carvalho de Melo }; 6812b2b2c68SNamhyung Kim struct perf_session *session; 6822b2b2c68SNamhyung Kim struct perf_data_file file = { 6832b2b2c68SNamhyung Kim .path = input_name, 6842b2b2c68SNamhyung Kim .mode = PERF_DATA_MODE_READ, 6852b2b2c68SNamhyung Kim }; 6862b2b2c68SNamhyung Kim int ret = -1; 6872b2b2c68SNamhyung Kim 6883bca2354SRamkumar Ramachandra argc = parse_options_subcommand(argc, argv, kmem_options, 6893bca2354SRamkumar Ramachandra kmem_subcommands, kmem_usage, 0); 690ba77c9e1SLi Zefan 69190b86a9fSLi Zefan if (!argc) 692ba77c9e1SLi Zefan usage_with_options(kmem_usage, kmem_options); 693ba77c9e1SLi Zefan 6942b2b2c68SNamhyung Kim if (!strncmp(argv[0], "rec", 3)) { 6950a7e6d1bSNamhyung Kim symbol__init(NULL); 6962b2b2c68SNamhyung Kim return __cmd_record(argc, argv); 6972b2b2c68SNamhyung Kim } 6982b2b2c68SNamhyung Kim 6992b2b2c68SNamhyung Kim session = perf_session__new(&file, false, &perf_kmem); 7002b2b2c68SNamhyung Kim if (session == NULL) 70152e02834STaeung Song return -1; 7022b2b2c68SNamhyung Kim 7030a7e6d1bSNamhyung Kim symbol__init(&session->header.env); 704655000e7SArnaldo Carvalho de Melo 7052b2b2c68SNamhyung Kim if (!strcmp(argv[0], "stat")) { 7064b627957SDon Zickus if (cpu__setup_cpunode_map()) 7072b2b2c68SNamhyung Kim goto out_delete; 70890b86a9fSLi Zefan 70929b3e152SLi Zefan if (list_empty(&caller_sort)) 71029b3e152SLi Zefan setup_sorting(&caller_sort, default_sort_order); 71129b3e152SLi Zefan if (list_empty(&alloc_sort)) 71229b3e152SLi Zefan setup_sorting(&alloc_sort, default_sort_order); 713ba77c9e1SLi Zefan 7142b2b2c68SNamhyung Kim ret = __cmd_kmem(session); 715b00eca8cSPekka Enberg } else 716b00eca8cSPekka Enberg usage_with_options(kmem_usage, kmem_options); 717ba77c9e1SLi Zefan 7182b2b2c68SNamhyung Kim out_delete: 7192b2b2c68SNamhyung Kim perf_session__delete(session); 7202b2b2c68SNamhyung Kim 7212b2b2c68SNamhyung Kim return ret; 72290b86a9fSLi Zefan } 72390b86a9fSLi Zefan 724