Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/util/callchain.c
10821 views
1
/*
2
* Copyright (C) 2009-2011, Frederic Weisbecker <[email protected]>
3
*
4
* Handle the callchains from the stream in an ad-hoc radix tree and then
5
* sort them in an rbtree.
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
*
10
*/
11
12
#include <stdlib.h>
13
#include <stdio.h>
14
#include <stdbool.h>
15
#include <errno.h>
16
#include <math.h>
17
18
#include "util.h"
19
#include "callchain.h"
20
21
bool ip_callchain__valid(struct ip_callchain *chain,
22
const union perf_event *event)
23
{
24
unsigned int chain_size = event->header.size;
25
chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
26
return chain->nr * sizeof(u64) <= chain_size;
27
}
28
29
#define chain_for_each_child(child, parent) \
30
list_for_each_entry(child, &parent->children, siblings)
31
32
#define chain_for_each_child_safe(child, next, parent) \
33
list_for_each_entry_safe(child, next, &parent->children, siblings)
34
35
static void
36
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
37
enum chain_mode mode)
38
{
39
struct rb_node **p = &root->rb_node;
40
struct rb_node *parent = NULL;
41
struct callchain_node *rnode;
42
u64 chain_cumul = callchain_cumul_hits(chain);
43
44
while (*p) {
45
u64 rnode_cumul;
46
47
parent = *p;
48
rnode = rb_entry(parent, struct callchain_node, rb_node);
49
rnode_cumul = callchain_cumul_hits(rnode);
50
51
switch (mode) {
52
case CHAIN_FLAT:
53
if (rnode->hit < chain->hit)
54
p = &(*p)->rb_left;
55
else
56
p = &(*p)->rb_right;
57
break;
58
case CHAIN_GRAPH_ABS: /* Falldown */
59
case CHAIN_GRAPH_REL:
60
if (rnode_cumul < chain_cumul)
61
p = &(*p)->rb_left;
62
else
63
p = &(*p)->rb_right;
64
break;
65
case CHAIN_NONE:
66
default:
67
break;
68
}
69
}
70
71
rb_link_node(&chain->rb_node, parent, p);
72
rb_insert_color(&chain->rb_node, root);
73
}
74
75
static void
76
__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
77
u64 min_hit)
78
{
79
struct callchain_node *child;
80
81
chain_for_each_child(child, node)
82
__sort_chain_flat(rb_root, child, min_hit);
83
84
if (node->hit && node->hit >= min_hit)
85
rb_insert_callchain(rb_root, node, CHAIN_FLAT);
86
}
87
88
/*
89
* Once we get every callchains from the stream, we can now
90
* sort them by hit
91
*/
92
static void
93
sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
94
u64 min_hit, struct callchain_param *param __used)
95
{
96
__sort_chain_flat(rb_root, &root->node, min_hit);
97
}
98
99
static void __sort_chain_graph_abs(struct callchain_node *node,
100
u64 min_hit)
101
{
102
struct callchain_node *child;
103
104
node->rb_root = RB_ROOT;
105
106
chain_for_each_child(child, node) {
107
__sort_chain_graph_abs(child, min_hit);
108
if (callchain_cumul_hits(child) >= min_hit)
109
rb_insert_callchain(&node->rb_root, child,
110
CHAIN_GRAPH_ABS);
111
}
112
}
113
114
static void
115
sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
116
u64 min_hit, struct callchain_param *param __used)
117
{
118
__sort_chain_graph_abs(&chain_root->node, min_hit);
119
rb_root->rb_node = chain_root->node.rb_root.rb_node;
120
}
121
122
static void __sort_chain_graph_rel(struct callchain_node *node,
123
double min_percent)
124
{
125
struct callchain_node *child;
126
u64 min_hit;
127
128
node->rb_root = RB_ROOT;
129
min_hit = ceil(node->children_hit * min_percent);
130
131
chain_for_each_child(child, node) {
132
__sort_chain_graph_rel(child, min_percent);
133
if (callchain_cumul_hits(child) >= min_hit)
134
rb_insert_callchain(&node->rb_root, child,
135
CHAIN_GRAPH_REL);
136
}
137
}
138
139
static void
140
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
141
u64 min_hit __used, struct callchain_param *param)
142
{
143
__sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
144
rb_root->rb_node = chain_root->node.rb_root.rb_node;
145
}
146
147
int callchain_register_param(struct callchain_param *param)
148
{
149
switch (param->mode) {
150
case CHAIN_GRAPH_ABS:
151
param->sort = sort_chain_graph_abs;
152
break;
153
case CHAIN_GRAPH_REL:
154
param->sort = sort_chain_graph_rel;
155
break;
156
case CHAIN_FLAT:
157
param->sort = sort_chain_flat;
158
break;
159
case CHAIN_NONE:
160
default:
161
return -1;
162
}
163
return 0;
164
}
165
166
/*
167
* Create a child for a parent. If inherit_children, then the new child
168
* will become the new parent of it's parent children
169
*/
170
static struct callchain_node *
171
create_child(struct callchain_node *parent, bool inherit_children)
172
{
173
struct callchain_node *new;
174
175
new = zalloc(sizeof(*new));
176
if (!new) {
177
perror("not enough memory to create child for code path tree");
178
return NULL;
179
}
180
new->parent = parent;
181
INIT_LIST_HEAD(&new->children);
182
INIT_LIST_HEAD(&new->val);
183
184
if (inherit_children) {
185
struct callchain_node *next;
186
187
list_splice(&parent->children, &new->children);
188
INIT_LIST_HEAD(&parent->children);
189
190
chain_for_each_child(next, new)
191
next->parent = new;
192
}
193
list_add_tail(&new->siblings, &parent->children);
194
195
return new;
196
}
197
198
199
/*
200
* Fill the node with callchain values
201
*/
202
static void
203
fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
204
{
205
struct callchain_cursor_node *cursor_node;
206
207
node->val_nr = cursor->nr - cursor->pos;
208
if (!node->val_nr)
209
pr_warning("Warning: empty node in callchain tree\n");
210
211
cursor_node = callchain_cursor_current(cursor);
212
213
while (cursor_node) {
214
struct callchain_list *call;
215
216
call = zalloc(sizeof(*call));
217
if (!call) {
218
perror("not enough memory for the code path tree");
219
return;
220
}
221
call->ip = cursor_node->ip;
222
call->ms.sym = cursor_node->sym;
223
call->ms.map = cursor_node->map;
224
list_add_tail(&call->list, &node->val);
225
226
callchain_cursor_advance(cursor);
227
cursor_node = callchain_cursor_current(cursor);
228
}
229
}
230
231
static void
232
add_child(struct callchain_node *parent,
233
struct callchain_cursor *cursor,
234
u64 period)
235
{
236
struct callchain_node *new;
237
238
new = create_child(parent, false);
239
fill_node(new, cursor);
240
241
new->children_hit = 0;
242
new->hit = period;
243
}
244
245
/*
246
* Split the parent in two parts (a new child is created) and
247
* give a part of its callchain to the created child.
248
* Then create another child to host the given callchain of new branch
249
*/
250
static void
251
split_add_child(struct callchain_node *parent,
252
struct callchain_cursor *cursor,
253
struct callchain_list *to_split,
254
u64 idx_parents, u64 idx_local, u64 period)
255
{
256
struct callchain_node *new;
257
struct list_head *old_tail;
258
unsigned int idx_total = idx_parents + idx_local;
259
260
/* split */
261
new = create_child(parent, true);
262
263
/* split the callchain and move a part to the new child */
264
old_tail = parent->val.prev;
265
list_del_range(&to_split->list, old_tail);
266
new->val.next = &to_split->list;
267
new->val.prev = old_tail;
268
to_split->list.prev = &new->val;
269
old_tail->next = &new->val;
270
271
/* split the hits */
272
new->hit = parent->hit;
273
new->children_hit = parent->children_hit;
274
parent->children_hit = callchain_cumul_hits(new);
275
new->val_nr = parent->val_nr - idx_local;
276
parent->val_nr = idx_local;
277
278
/* create a new child for the new branch if any */
279
if (idx_total < cursor->nr) {
280
parent->hit = 0;
281
add_child(parent, cursor, period);
282
parent->children_hit += period;
283
} else {
284
parent->hit = period;
285
}
286
}
287
288
static int
289
append_chain(struct callchain_node *root,
290
struct callchain_cursor *cursor,
291
u64 period);
292
293
static void
294
append_chain_children(struct callchain_node *root,
295
struct callchain_cursor *cursor,
296
u64 period)
297
{
298
struct callchain_node *rnode;
299
300
/* lookup in childrens */
301
chain_for_each_child(rnode, root) {
302
unsigned int ret = append_chain(rnode, cursor, period);
303
304
if (!ret)
305
goto inc_children_hit;
306
}
307
/* nothing in children, add to the current node */
308
add_child(root, cursor, period);
309
310
inc_children_hit:
311
root->children_hit += period;
312
}
313
314
static int
315
append_chain(struct callchain_node *root,
316
struct callchain_cursor *cursor,
317
u64 period)
318
{
319
struct callchain_cursor_node *curr_snap = cursor->curr;
320
struct callchain_list *cnode;
321
u64 start = cursor->pos;
322
bool found = false;
323
u64 matches;
324
325
/*
326
* Lookup in the current node
327
* If we have a symbol, then compare the start to match
328
* anywhere inside a function.
329
*/
330
list_for_each_entry(cnode, &root->val, list) {
331
struct callchain_cursor_node *node;
332
struct symbol *sym;
333
334
node = callchain_cursor_current(cursor);
335
if (!node)
336
break;
337
338
sym = node->sym;
339
340
if (cnode->ms.sym && sym) {
341
if (cnode->ms.sym->start != sym->start)
342
break;
343
} else if (cnode->ip != node->ip)
344
break;
345
346
if (!found)
347
found = true;
348
349
callchain_cursor_advance(cursor);
350
}
351
352
/* matches not, relay on the parent */
353
if (!found) {
354
cursor->curr = curr_snap;
355
cursor->pos = start;
356
return -1;
357
}
358
359
matches = cursor->pos - start;
360
361
/* we match only a part of the node. Split it and add the new chain */
362
if (matches < root->val_nr) {
363
split_add_child(root, cursor, cnode, start, matches, period);
364
return 0;
365
}
366
367
/* we match 100% of the path, increment the hit */
368
if (matches == root->val_nr && cursor->pos == cursor->nr) {
369
root->hit += period;
370
return 0;
371
}
372
373
/* We match the node and still have a part remaining */
374
append_chain_children(root, cursor, period);
375
376
return 0;
377
}
378
379
int callchain_append(struct callchain_root *root,
380
struct callchain_cursor *cursor,
381
u64 period)
382
{
383
if (!cursor->nr)
384
return 0;
385
386
callchain_cursor_commit(cursor);
387
388
append_chain_children(&root->node, cursor, period);
389
390
if (cursor->nr > root->max_depth)
391
root->max_depth = cursor->nr;
392
393
return 0;
394
}
395
396
static int
397
merge_chain_branch(struct callchain_cursor *cursor,
398
struct callchain_node *dst, struct callchain_node *src)
399
{
400
struct callchain_cursor_node **old_last = cursor->last;
401
struct callchain_node *child, *next_child;
402
struct callchain_list *list, *next_list;
403
int old_pos = cursor->nr;
404
int err = 0;
405
406
list_for_each_entry_safe(list, next_list, &src->val, list) {
407
callchain_cursor_append(cursor, list->ip,
408
list->ms.map, list->ms.sym);
409
list_del(&list->list);
410
free(list);
411
}
412
413
if (src->hit) {
414
callchain_cursor_commit(cursor);
415
append_chain_children(dst, cursor, src->hit);
416
}
417
418
chain_for_each_child_safe(child, next_child, src) {
419
err = merge_chain_branch(cursor, dst, child);
420
if (err)
421
break;
422
423
list_del(&child->siblings);
424
free(child);
425
}
426
427
cursor->nr = old_pos;
428
cursor->last = old_last;
429
430
return err;
431
}
432
433
int callchain_merge(struct callchain_cursor *cursor,
434
struct callchain_root *dst, struct callchain_root *src)
435
{
436
return merge_chain_branch(cursor, &dst->node, &src->node);
437
}
438
439
int callchain_cursor_append(struct callchain_cursor *cursor,
440
u64 ip, struct map *map, struct symbol *sym)
441
{
442
struct callchain_cursor_node *node = *cursor->last;
443
444
if (!node) {
445
node = calloc(sizeof(*node), 1);
446
if (!node)
447
return -ENOMEM;
448
449
*cursor->last = node;
450
}
451
452
node->ip = ip;
453
node->map = map;
454
node->sym = sym;
455
456
cursor->nr++;
457
458
cursor->last = &node->next;
459
460
return 0;
461
}
462
463