perf kmem: Measure kmalloc/kfree CPU ping-pong call-sites
authorLi Zefan <lizf@cn.fujitsu.com>
Tue, 24 Nov 2009 05:26:55 +0000 (13:26 +0800)
committerIngo Molnar <mingo@elte.hu>
Tue, 24 Nov 2009 07:49:50 +0000 (08:49 +0100)
Show statistics for allocations and frees on different cpus:

------------------------------------------------------------------------------------------------------
Callsite                           | Total_alloc/Per | Total_req/Per   | Hit   | Ping-pong | Frag
------------------------------------------------------------------------------------------------------
 perf_event_alloc.clone.0+0         |      7504/682   |      7128/648   |     11 |        0 |  5.011%
 alloc_buffer_head+16               |       288/57    |       280/56    |      5 |        0 |  2.778%
 radix_tree_preload+51              |       296/296   |       288/288   |      1 |        0 |  2.703%
 tracepoint_add_probe+32e           |       157/31    |       154/30    |      5 |        0 |  1.911%
 do_maps_open+0                     |       796/12    |       792/12    |     66 |        0 |  0.503%
 sock_alloc_send_pskb+16e           |     23780/495   |     23744/494   |     48 |       38 |  0.151%
 anon_vma_prepare+9a                |      3744/44    |      3740/44    |     85 |        0 |  0.107%
 d_alloc+21                         |     64948/164   |     64944/164   |    396 |        0 |  0.006%
 proc_alloc_inode+23                |    262292/676   |    262288/676   |    388 |        0 |  0.002%
 create_object+28                   |    459600/200   |    459600/200   |   2298 |       71 |  0.000%
 journal_start+67                   |     14440/40    |     14440/40    |    361 |        0 |  0.000%
 get_empty_filp+df                  |     53504/256   |     53504/256   |    209 |        0 |  0.000%
 getname+2a                         |    823296/4096  |    823296/4096  |    201 |        0 |  0.000%
 seq_read+2b0                       |    544768/4096  |    544768/4096  |    133 |        0 |  0.000%
 seq_open+6d                        |     17024/128   |     17024/128   |    133 |        0 |  0.000%
 mmap_region+2e6                    |     11704/88    |     11704/88    |    133 |        0 |  0.000%
 single_open+0                      |      1072/16    |      1072/16    |     67 |        0 |  0.000%
 __alloc_skb+2e                     |     12544/256   |     12544/256   |     49 |       38 |  0.000%
 __sigqueue_alloc+4a                |      1296/144   |      1296/144   |      9 |        8 |  0.000%
 tracepoint_add_probe+6f            |        80/16    |        80/16    |      5 |        0 |  0.000%
------------------------------------------------------------------------------------------------------
...

Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Cc: Eduard - Gabriel Munteanu <eduard.munteanu@linux360.ro>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-mm@kvack.org <linux-mm@kvack.org>
LKML-Reference: <4B0B6E9F.6020309@cn.fujitsu.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
tools/perf/builtin-kmem.c

index 1ecf3f4..173d6db 100644 (file)
@@ -40,13 +40,14 @@ static int                  *cpunode_map;
 static int                     max_cpu_num;
 
 struct alloc_stat {
-       union {
-               u64     call_site;
-               u64     ptr;
-       };
+       u64     call_site;
+       u64     ptr;
        u64     bytes_req;
        u64     bytes_alloc;
        u32     hit;
+       u32     pingpong;
+
+       short   alloc_cpu;
 
        struct rb_node node;
 };
@@ -144,16 +145,13 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
        return 0;
 }
 
-static void insert_alloc_stat(unsigned long ptr,
-                             int bytes_req, int bytes_alloc)
+static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
+                             int bytes_req, int bytes_alloc, int cpu)
 {
        struct rb_node **node = &root_alloc_stat.rb_node;
        struct rb_node *parent = NULL;
        struct alloc_stat *data = NULL;
 
-       if (!alloc_flag)
-               return;
-
        while (*node) {
                parent = *node;
                data = rb_entry(*node, struct alloc_stat, node);
@@ -172,7 +170,10 @@ static void insert_alloc_stat(unsigned long ptr,
                data->bytes_alloc += bytes_req;
        } else {
                data = malloc(sizeof(*data));
+               if (!data)
+                       die("malloc");
                data->ptr = ptr;
+               data->pingpong = 0;
                data->hit = 1;
                data->bytes_req = bytes_req;
                data->bytes_alloc = bytes_alloc;
@@ -180,6 +181,8 @@ static void insert_alloc_stat(unsigned long ptr,
                rb_link_node(&data->node, parent, node);
                rb_insert_color(&data->node, &root_alloc_stat);
        }
+       data->call_site = call_site;
+       data->alloc_cpu = cpu;
 }
 
 static void insert_caller_stat(unsigned long call_site,
@@ -189,9 +192,6 @@ static void insert_caller_stat(unsigned long call_site,
        struct rb_node *parent = NULL;
        struct alloc_stat *data = NULL;
 
-       if (!caller_flag)
-               return;
-
        while (*node) {
                parent = *node;
                data = rb_entry(*node, struct alloc_stat, node);
@@ -210,7 +210,10 @@ static void insert_caller_stat(unsigned long call_site,
                data->bytes_alloc += bytes_req;
        } else {
                data = malloc(sizeof(*data));
+               if (!data)
+                       die("malloc");
                data->call_site = call_site;
+               data->pingpong = 0;
                data->hit = 1;
                data->bytes_req = bytes_req;
                data->bytes_alloc = bytes_alloc;
@@ -238,7 +241,7 @@ static void process_alloc_event(struct raw_event_sample *raw,
        bytes_req = raw_field_value(event, "bytes_req", raw->data);
        bytes_alloc = raw_field_value(event, "bytes_alloc", raw->data);
 
-       insert_alloc_stat(ptr, bytes_req, bytes_alloc);
+       insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
        insert_caller_stat(call_site, bytes_req, bytes_alloc);
 
        total_requested += bytes_req;
@@ -253,12 +256,58 @@ static void process_alloc_event(struct raw_event_sample *raw,
        nr_allocs++;
 }
 
-static void process_free_event(struct raw_event_sample *raw __used,
-                              struct event *event __used,
-                              int cpu __used,
+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;
+}
+
+static void process_free_event(struct raw_event_sample *raw,
+                              struct event *event,
+                              int cpu,
                               u64 timestamp __used,
                               struct thread *thread __used)
 {
+       unsigned long ptr;
+       struct alloc_stat *s_alloc, *s_caller;
+
+       ptr = raw_field_value(event, "ptr", raw->data);
+
+       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;
 }
 
 static void
@@ -379,10 +428,10 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
 {
        struct rb_node *next;
 
-       printf("%.78s\n", graph_dotted_line);
-       printf("%-28s|",  is_caller ? "Callsite": "Alloc Ptr");
-       printf("Total_alloc/Per | Total_req/Per | Hit  | Frag\n");
-       printf("%.78s\n", graph_dotted_line);
+       printf("%.102s\n", graph_dotted_line);
+       printf(" %-34s |",  is_caller ? "Callsite": "Alloc Ptr");
+       printf(" Total_alloc/Per | Total_req/Per   | Hit   | Ping-pong | Frag\n");
+       printf("%.102s\n", graph_dotted_line);
 
        next = rb_first(root);
 
@@ -390,7 +439,7 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
                struct alloc_stat *data = rb_entry(next, struct alloc_stat,
                                                   node);
                struct symbol *sym = NULL;
-               char bf[BUFSIZ];
+               char buf[BUFSIZ];
                u64 addr;
 
                if (is_caller) {
@@ -402,26 +451,28 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
                        addr = data->ptr;
 
                if (sym != NULL)
-                       snprintf(bf, sizeof(bf), "%s+%Lx", sym->name,
+                       snprintf(buf, sizeof(buf), "%s+%Lx", sym->name,
                                 addr - sym->start);
                else
-                       snprintf(bf, sizeof(bf), "%#Lx", addr);
+                       snprintf(buf, sizeof(buf), "%#Lx", addr);
+               printf(" %-34s |", buf);
 
-               printf("%-28s|%8llu/%-6lu |%8llu/%-6lu|%6lu|%8.3f%%\n",
-                      bf, (unsigned long long)data->bytes_alloc,
+               printf(" %9llu/%-5lu | %9llu/%-5lu | %6lu | %8lu | %6.3f%%\n",
+                      (unsigned long long)data->bytes_alloc,
                       (unsigned long)data->bytes_alloc / data->hit,
                       (unsigned long long)data->bytes_req,
                       (unsigned long)data->bytes_req / data->hit,
                       (unsigned long)data->hit,
+                      (unsigned long)data->pingpong,
                       fragmentation(data->bytes_req, data->bytes_alloc));
 
                next = rb_next(next);
        }
 
        if (n_lines == -1)
-               printf(" ...                        | ...            | ...           | ...    | ...   \n");
+               printf(" ...                                | ...             | ...             | ...    | ...      | ...   \n");
 
-       printf("%.78s\n", graph_dotted_line);
+       printf("%.102s\n", graph_dotted_line);
 }
 
 static void print_summary(void)
@@ -597,12 +648,27 @@ static struct sort_dimension frag_sort_dimension = {
        .cmp    = frag_cmp,
 };
 
+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,
+};
+
 static struct sort_dimension *avail_sorts[] = {
        &ptr_sort_dimension,
        &callsite_sort_dimension,
        &hit_sort_dimension,
        &bytes_sort_dimension,
        &frag_sort_dimension,
+       &pingpong_sort_dimension,
 };
 
 #define NUM_AVAIL_SORTS        \
@@ -703,7 +769,7 @@ static const struct option kmem_options[] = {
                     "stat selector, Pass 'alloc' or 'caller'.",
                     parse_stat_opt),
        OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
-                    "sort by key(s): ptr, call_site, bytes, hit, frag",
+                    "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
                     parse_sort_opt),
        OPT_CALLBACK('l', "line", NULL, "num",
                     "show n lins",