aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/perf/Makefile1
-rw-r--r--tools/perf/builtin-report.c5
-rw-r--r--tools/perf/perf.h5
-rw-r--r--tools/perf/util/callchain.c174
-rw-r--r--tools/perf/util/callchain.h33
5 files changed, 213 insertions, 5 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index d3887ed51a64..1c1296d8a64b 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -329,6 +329,7 @@ LIB_OBJS += util/symbol.o
329LIB_OBJS += util/color.o 329LIB_OBJS += util/color.o
330LIB_OBJS += util/pager.o 330LIB_OBJS += util/pager.o
331LIB_OBJS += util/header.o 331LIB_OBJS += util/header.o
332LIB_OBJS += util/callchain.o
332 333
333BUILTIN_OBJS += builtin-annotate.o 334BUILTIN_OBJS += builtin-annotate.o
334BUILTIN_OBJS += builtin-help.o 335BUILTIN_OBJS += builtin-help.o
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 681c2233f882..28d1cb2127e9 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -62,11 +62,6 @@ struct ip_event {
62 unsigned char __more_data[]; 62 unsigned char __more_data[];
63}; 63};
64 64
65struct ip_callchain {
66 u64 nr;
67 u64 ips[0];
68};
69
70struct mmap_event { 65struct mmap_event {
71 struct perf_event_header header; 66 struct perf_event_header header;
72 u32 pid, tid; 67 u32 pid, tid;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 16c84fd73c86..a49842b69a59 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -66,4 +66,9 @@ sys_perf_counter_open(struct perf_counter_attr *attr,
66#define MAX_COUNTERS 256 66#define MAX_COUNTERS 256
67#define MAX_NR_CPUS 256 67#define MAX_NR_CPUS 256
68 68
69struct ip_callchain {
70 u64 nr;
71 u64 ips[0];
72};
73
69#endif 74#endif
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
new file mode 100644
index 000000000000..ad3c28578961
--- /dev/null
+++ b/tools/perf/util/callchain.c
@@ -0,0 +1,174 @@
1/*
2 * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com>
3 *
4 * Handle the callchains from the stream in an ad-hoc radix tree and then
5 * sort them in an rbtree.
6 *
7 */
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <stdbool.h>
12#include <errno.h>
13
14#include "callchain.h"
15
16
17static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain)
18{
19 struct rb_node **p = &root->rb_node;
20 struct rb_node *parent = NULL;
21 struct callchain_node *rnode;
22
23 while (*p) {
24 parent = *p;
25 rnode = rb_entry(parent, struct callchain_node, rb_node);
26
27 if (rnode->hit < chain->hit)
28 p = &(*p)->rb_left;
29 else
30 p = &(*p)->rb_right;
31 }
32
33 rb_link_node(&chain->rb_node, parent, p);
34 rb_insert_color(&chain->rb_node, root);
35}
36
37/*
38 * Once we get every callchains from the stream, we can now
39 * sort them by hit
40 */
41void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node)
42{
43 struct callchain_node *child;
44
45 list_for_each_entry(child, &node->children, brothers)
46 sort_chain_to_rbtree(rb_root, child);
47
48 if (node->hit)
49 rb_insert_callchain(rb_root, node);
50}
51
52static struct callchain_node *create_child(struct callchain_node *parent)
53{
54 struct callchain_node *new;
55
56 new = malloc(sizeof(*new));
57 if (!new) {
58 perror("not enough memory to create child for code path tree");
59 return NULL;
60 }
61 new->parent = parent;
62 INIT_LIST_HEAD(&new->children);
63 INIT_LIST_HEAD(&new->val);
64 list_add_tail(&new->brothers, &parent->children);
65
66 return new;
67}
68
69static void
70fill_node(struct callchain_node *node, struct ip_callchain *chain, int start)
71{
72 int i;
73
74 for (i = start; i < chain->nr; i++) {
75 struct callchain_list *call;
76
77 call = malloc(sizeof(*chain));
78 if (!call) {
79 perror("not enough memory for the code path tree");
80 return;
81 }
82 call->ip = chain->ips[i];
83 list_add_tail(&call->list, &node->val);
84 }
85 node->val_nr = i - start;
86}
87
88static void add_child(struct callchain_node *parent, struct ip_callchain *chain)
89{
90 struct callchain_node *new;
91
92 new = create_child(parent);
93 fill_node(new, chain, parent->val_nr);
94
95 new->hit = 1;
96}
97
98static void
99split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
100 struct callchain_list *to_split, int idx)
101{
102 struct callchain_node *new;
103
104 /* split */
105 new = create_child(parent);
106 list_move_tail(&to_split->list, &new->val);
107 new->hit = parent->hit;
108 parent->hit = 0;
109 parent->val_nr = idx;
110
111 /* create the new one */
112 add_child(parent, chain);
113}
114
115static int
116__append_chain(struct callchain_node *root, struct ip_callchain *chain,
117 int start);
118
119static int
120__append_chain_children(struct callchain_node *root, struct ip_callchain *chain)
121{
122 struct callchain_node *rnode;
123
124 /* lookup in childrens */
125 list_for_each_entry(rnode, &root->children, brothers) {
126 int ret = __append_chain(rnode, chain, root->val_nr);
127 if (!ret)
128 return 0;
129 }
130 return -1;
131}
132
133static int
134__append_chain(struct callchain_node *root, struct ip_callchain *chain,
135 int start)
136{
137 struct callchain_list *cnode;
138 int i = start;
139 bool found = false;
140
141 /* lookup in the current node */
142 list_for_each_entry(cnode, &root->val, list) {
143 if (cnode->ip != chain->ips[i++])
144 break;
145 if (!found)
146 found = true;
147 if (i == chain->nr)
148 break;
149 }
150
151 /* matches not, relay on the parent */
152 if (!found)
153 return -1;
154
155 /* we match only a part of the node. Split it and add the new chain */
156 if (i < root->val_nr) {
157 split_add_child(root, chain, cnode, i);
158 return 0;
159 }
160
161 /* we match 100% of the path, increment the hit */
162 if (i == root->val_nr) {
163 root->hit++;
164 return 0;
165 }
166
167 return __append_chain_children(root, chain);
168}
169
170void append_chain(struct callchain_node *root, struct ip_callchain *chain)
171{
172 if (__append_chain_children(root, chain) == -1)
173 add_child(root, chain);
174}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
new file mode 100644
index 000000000000..fa1cd2f71fd3
--- /dev/null
+++ b/tools/perf/util/callchain.h
@@ -0,0 +1,33 @@
1#ifndef __PERF_CALLCHAIN_H
2#define __PERF_CALLCHAIN_H
3
4#include "../perf.h"
5#include "list.h"
6#include "rbtree.h"
7
8
9struct callchain_node {
10 struct callchain_node *parent;
11 struct list_head brothers;
12 struct list_head children;
13 struct list_head val;
14 struct rb_node rb_node;
15 int val_nr;
16 int hit;
17};
18
19struct callchain_list {
20 unsigned long ip;
21 struct list_head list;
22};
23
24static inline void callchain_init(struct callchain_node *node)
25{
26 INIT_LIST_HEAD(&node->brothers);
27 INIT_LIST_HEAD(&node->children);
28 INIT_LIST_HEAD(&node->val);
29}
30
31void append_chain(struct callchain_node *root, struct ip_callchain *chain);
32void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node);
33#endif