4
4
* Handle the callchains from the stream in an ad-hoc radix tree and then
5
5
* sort them in an rbtree.
6
6
*
7
+ * Using a radix for code path provides a fast retrieval and factorizes
8
+ * memory use. Also that lets us use the paths in a hierarchical graph view.
9
+ *
7
10
*/
8
11
9
12
#include <stdlib.h>
14
17
#include "callchain.h"
15
18
16
19
17
- static void rb_insert_callchain (struct rb_root * root , struct callchain_node * chain )
20
+ static void
21
+ rb_insert_callchain (struct rb_root * root , struct callchain_node * chain )
18
22
{
19
23
struct rb_node * * p = & root -> rb_node ;
20
24
struct rb_node * parent = NULL ;
@@ -49,7 +53,12 @@ void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node)
49
53
rb_insert_callchain (rb_root , node );
50
54
}
51
55
52
- static struct callchain_node * create_child (struct callchain_node * parent )
56
+ /*
57
+ * Create a child for a parent. If inherit_children, then the new child
58
+ * will become the new parent of it's parent children
59
+ */
60
+ static struct callchain_node *
61
+ create_child (struct callchain_node * parent , bool inherit_children )
53
62
{
54
63
struct callchain_node * new ;
55
64
@@ -61,14 +70,27 @@ static struct callchain_node *create_child(struct callchain_node *parent)
61
70
new -> parent = parent ;
62
71
INIT_LIST_HEAD (& new -> children );
63
72
INIT_LIST_HEAD (& new -> val );
73
+
74
+ if (inherit_children ) {
75
+ struct callchain_node * next ;
76
+
77
+ list_splice (& parent -> children , & new -> children );
78
+ INIT_LIST_HEAD (& parent -> children );
79
+
80
+ list_for_each_entry (next , & new -> children , brothers )
81
+ next -> parent = new ;
82
+ }
64
83
list_add_tail (& new -> brothers , & parent -> children );
65
84
66
85
return new ;
67
86
}
68
87
88
+ /*
89
+ * Fill the node with callchain values
90
+ */
69
91
static void
70
- fill_node (struct callchain_node * node , struct ip_callchain * chain , int start ,
71
- struct symbol * * syms )
92
+ fill_node (struct callchain_node * node , struct ip_callchain * chain ,
93
+ int start , struct symbol * * syms )
72
94
{
73
95
int i ;
74
96
@@ -84,54 +106,80 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, int start,
84
106
call -> sym = syms [i ];
85
107
list_add_tail (& call -> list , & node -> val );
86
108
}
87
- node -> val_nr = i - start ;
109
+ node -> val_nr = chain -> nr - start ;
110
+ if (!node -> val_nr )
111
+ printf ("Warning: empty node in callchain tree\n" );
88
112
}
89
113
90
- static void add_child (struct callchain_node * parent , struct ip_callchain * chain ,
91
- struct symbol * * syms )
114
+ static void
115
+ add_child (struct callchain_node * parent , struct ip_callchain * chain ,
116
+ int start , struct symbol * * syms )
92
117
{
93
118
struct callchain_node * new ;
94
119
95
- new = create_child (parent );
96
- fill_node (new , chain , parent -> val_nr , syms );
120
+ new = create_child (parent , false );
121
+ fill_node (new , chain , start , syms );
97
122
98
123
new -> hit = 1 ;
99
124
}
100
125
126
+ /*
127
+ * Split the parent in two parts (a new child is created) and
128
+ * give a part of its callchain to the created child.
129
+ * Then create another child to host the given callchain of new branch
130
+ */
101
131
static void
102
132
split_add_child (struct callchain_node * parent , struct ip_callchain * chain ,
103
- struct callchain_list * to_split , int idx , struct symbol * * syms )
133
+ struct callchain_list * to_split , int idx_parents , int idx_local ,
134
+ struct symbol * * syms )
104
135
{
105
136
struct callchain_node * new ;
137
+ struct list_head * old_tail ;
138
+ int idx_total = idx_parents + idx_local ;
106
139
107
140
/* split */
108
- new = create_child (parent );
109
- list_move_tail (& to_split -> list , & new -> val );
110
- new -> hit = parent -> hit ;
111
- parent -> hit = 0 ;
112
- parent -> val_nr = idx ;
141
+ new = create_child (parent , true);
142
+
143
+ /* split the callchain and move a part to the new child */
144
+ old_tail = parent -> val .prev ;
145
+ list_del_range (& to_split -> list , old_tail );
146
+ new -> val .next = & to_split -> list ;
147
+ new -> val .prev = old_tail ;
148
+ to_split -> list .prev = & new -> val ;
149
+ old_tail -> next = & new -> val ;
113
150
114
- /* create the new one */
115
- add_child (parent , chain , syms );
151
+ /* split the hits */
152
+ new -> hit = parent -> hit ;
153
+ new -> val_nr = parent -> val_nr - idx_local ;
154
+ parent -> val_nr = idx_local ;
155
+
156
+ /* create a new child for the new branch if any */
157
+ if (idx_total < chain -> nr ) {
158
+ parent -> hit = 0 ;
159
+ add_child (parent , chain , idx_total , syms );
160
+ } else {
161
+ parent -> hit = 1 ;
162
+ }
116
163
}
117
164
118
165
static int
119
166
__append_chain (struct callchain_node * root , struct ip_callchain * chain ,
120
167
int start , struct symbol * * syms );
121
168
122
- static int
169
+ static void
123
170
__append_chain_children (struct callchain_node * root , struct ip_callchain * chain ,
124
- struct symbol * * syms )
171
+ struct symbol * * syms , int start )
125
172
{
126
173
struct callchain_node * rnode ;
127
174
128
175
/* lookup in childrens */
129
176
list_for_each_entry (rnode , & root -> children , brothers ) {
130
- int ret = __append_chain (rnode , chain , root -> val_nr , syms );
177
+ int ret = __append_chain (rnode , chain , start , syms );
131
178
if (!ret )
132
- return 0 ;
179
+ return ;
133
180
}
134
- return -1 ;
181
+ /* nothing in children, add to the current node */
182
+ add_child (root , chain , start , syms );
135
183
}
136
184
137
185
static int
@@ -142,38 +190,48 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
142
190
int i = start ;
143
191
bool found = false;
144
192
145
- /* lookup in the current node */
193
+ /*
194
+ * Lookup in the current node
195
+ * If we have a symbol, then compare the start to match
196
+ * anywhere inside a function.
197
+ */
146
198
list_for_each_entry (cnode , & root -> val , list ) {
147
- if (cnode -> ip != chain -> ips [i ++ ])
199
+ if (i == chain -> nr )
200
+ break ;
201
+ if (cnode -> sym && syms [i ]) {
202
+ if (cnode -> sym -> start != syms [i ]-> start )
203
+ break ;
204
+ } else if (cnode -> ip != chain -> ips [i ])
148
205
break ;
149
206
if (!found )
150
207
found = true;
151
- if (i == chain -> nr )
152
- break ;
208
+ i ++ ;
153
209
}
154
210
155
211
/* matches not, relay on the parent */
156
212
if (!found )
157
213
return -1 ;
158
214
159
215
/* we match only a part of the node. Split it and add the new chain */
160
- if (i < root -> val_nr ) {
161
- split_add_child (root , chain , cnode , i , syms );
216
+ if (i - start < root -> val_nr ) {
217
+ split_add_child (root , chain , cnode , start , i - start , syms );
162
218
return 0 ;
163
219
}
164
220
165
221
/* we match 100% of the path, increment the hit */
166
- if (i == root -> val_nr ) {
222
+ if (i - start == root -> val_nr && i == chain -> nr ) {
167
223
root -> hit ++ ;
168
224
return 0 ;
169
225
}
170
226
171
- return __append_chain_children (root , chain , syms );
227
+ /* We match the node and still have a part remaining */
228
+ __append_chain_children (root , chain , syms , i );
229
+
230
+ return 0 ;
172
231
}
173
232
174
233
void append_chain (struct callchain_node * root , struct ip_callchain * chain ,
175
234
struct symbol * * syms )
176
235
{
177
- if (__append_chain_children (root , chain , syms ) == -1 )
178
- add_child (root , chain , syms );
236
+ __append_chain_children (root , chain , syms , 0 );
179
237
}
0 commit comments