diff options
Diffstat (limited to 'tools/perf/util/callchain.c')
-rw-r--r-- | tools/perf/util/callchain.c | 121 |
1 files changed, 90 insertions, 31 deletions
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index b3b71258272..21a52e0a443 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> |
3 | * | 3 | * |
4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then |
5 | * sort them in an rbtree. | 5 | * sort them in an rbtree. |
@@ -17,6 +17,13 @@ | |||
17 | 17 | ||
18 | #include "callchain.h" | 18 | #include "callchain.h" |
19 | 19 | ||
20 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) | ||
21 | { | ||
22 | unsigned int chain_size = event->header.size; | ||
23 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | ||
24 | return chain->nr * sizeof(u64) <= chain_size; | ||
25 | } | ||
26 | |||
20 | #define chain_for_each_child(child, parent) \ | 27 | #define chain_for_each_child(child, parent) \ |
21 | list_for_each_entry(child, &parent->children, brothers) | 28 | list_for_each_entry(child, &parent->children, brothers) |
22 | 29 | ||
@@ -160,7 +167,7 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
160 | { | 167 | { |
161 | struct callchain_node *new; | 168 | struct callchain_node *new; |
162 | 169 | ||
163 | new = malloc(sizeof(*new)); | 170 | new = zalloc(sizeof(*new)); |
164 | if (!new) { | 171 | if (!new) { |
165 | perror("not enough memory to create child for code path tree"); | 172 | perror("not enough memory to create child for code path tree"); |
166 | return NULL; | 173 | return NULL; |
@@ -183,25 +190,36 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
183 | return new; | 190 | return new; |
184 | } | 191 | } |
185 | 192 | ||
193 | |||
194 | struct resolved_ip { | ||
195 | u64 ip; | ||
196 | struct map_symbol ms; | ||
197 | }; | ||
198 | |||
199 | struct resolved_chain { | ||
200 | u64 nr; | ||
201 | struct resolved_ip ips[0]; | ||
202 | }; | ||
203 | |||
204 | |||
186 | /* | 205 | /* |
187 | * Fill the node with callchain values | 206 | * Fill the node with callchain values |
188 | */ | 207 | */ |
189 | static void | 208 | static void |
190 | fill_node(struct callchain_node *node, struct ip_callchain *chain, | 209 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) |
191 | int start, struct symbol **syms) | ||
192 | { | 210 | { |
193 | unsigned int i; | 211 | unsigned int i; |
194 | 212 | ||
195 | for (i = start; i < chain->nr; i++) { | 213 | for (i = start; i < chain->nr; i++) { |
196 | struct callchain_list *call; | 214 | struct callchain_list *call; |
197 | 215 | ||
198 | call = malloc(sizeof(*call)); | 216 | call = zalloc(sizeof(*call)); |
199 | if (!call) { | 217 | if (!call) { |
200 | perror("not enough memory for the code path tree"); | 218 | perror("not enough memory for the code path tree"); |
201 | return; | 219 | return; |
202 | } | 220 | } |
203 | call->ip = chain->ips[i]; | 221 | call->ip = chain->ips[i].ip; |
204 | call->sym = syms[i]; | 222 | call->ms = chain->ips[i].ms; |
205 | list_add_tail(&call->list, &node->val); | 223 | list_add_tail(&call->list, &node->val); |
206 | } | 224 | } |
207 | node->val_nr = chain->nr - start; | 225 | node->val_nr = chain->nr - start; |
@@ -210,13 +228,13 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
210 | } | 228 | } |
211 | 229 | ||
212 | static void | 230 | static void |
213 | add_child(struct callchain_node *parent, struct ip_callchain *chain, | 231 | add_child(struct callchain_node *parent, struct resolved_chain *chain, |
214 | int start, struct symbol **syms) | 232 | int start) |
215 | { | 233 | { |
216 | struct callchain_node *new; | 234 | struct callchain_node *new; |
217 | 235 | ||
218 | new = create_child(parent, false); | 236 | new = create_child(parent, false); |
219 | fill_node(new, chain, start, syms); | 237 | fill_node(new, chain, start); |
220 | 238 | ||
221 | new->children_hit = 0; | 239 | new->children_hit = 0; |
222 | new->hit = 1; | 240 | new->hit = 1; |
@@ -228,9 +246,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
228 | * Then create another child to host the given callchain of new branch | 246 | * Then create another child to host the given callchain of new branch |
229 | */ | 247 | */ |
230 | static void | 248 | static void |
231 | split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | 249 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, |
232 | struct callchain_list *to_split, int idx_parents, int idx_local, | 250 | struct callchain_list *to_split, int idx_parents, int idx_local) |
233 | struct symbol **syms) | ||
234 | { | 251 | { |
235 | struct callchain_node *new; | 252 | struct callchain_node *new; |
236 | struct list_head *old_tail; | 253 | struct list_head *old_tail; |
@@ -257,7 +274,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
257 | /* create a new child for the new branch if any */ | 274 | /* create a new child for the new branch if any */ |
258 | if (idx_total < chain->nr) { | 275 | if (idx_total < chain->nr) { |
259 | parent->hit = 0; | 276 | parent->hit = 0; |
260 | add_child(parent, chain, idx_total, syms); | 277 | add_child(parent, chain, idx_total); |
261 | parent->children_hit++; | 278 | parent->children_hit++; |
262 | } else { | 279 | } else { |
263 | parent->hit = 1; | 280 | parent->hit = 1; |
@@ -265,32 +282,33 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
265 | } | 282 | } |
266 | 283 | ||
267 | static int | 284 | static int |
268 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 285 | __append_chain(struct callchain_node *root, struct resolved_chain *chain, |
269 | unsigned int start, struct symbol **syms); | 286 | unsigned int start); |
270 | 287 | ||
271 | static void | 288 | static void |
272 | __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, | 289 | __append_chain_children(struct callchain_node *root, |
273 | struct symbol **syms, unsigned int start) | 290 | struct resolved_chain *chain, |
291 | unsigned int start) | ||
274 | { | 292 | { |
275 | struct callchain_node *rnode; | 293 | struct callchain_node *rnode; |
276 | 294 | ||
277 | /* lookup in childrens */ | 295 | /* lookup in childrens */ |
278 | chain_for_each_child(rnode, root) { | 296 | chain_for_each_child(rnode, root) { |
279 | unsigned int ret = __append_chain(rnode, chain, start, syms); | 297 | unsigned int ret = __append_chain(rnode, chain, start); |
280 | 298 | ||
281 | if (!ret) | 299 | if (!ret) |
282 | goto inc_children_hit; | 300 | goto inc_children_hit; |
283 | } | 301 | } |
284 | /* nothing in children, add to the current node */ | 302 | /* nothing in children, add to the current node */ |
285 | add_child(root, chain, start, syms); | 303 | add_child(root, chain, start); |
286 | 304 | ||
287 | inc_children_hit: | 305 | inc_children_hit: |
288 | root->children_hit++; | 306 | root->children_hit++; |
289 | } | 307 | } |
290 | 308 | ||
291 | static int | 309 | static int |
292 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 310 | __append_chain(struct callchain_node *root, struct resolved_chain *chain, |
293 | unsigned int start, struct symbol **syms) | 311 | unsigned int start) |
294 | { | 312 | { |
295 | struct callchain_list *cnode; | 313 | struct callchain_list *cnode; |
296 | unsigned int i = start; | 314 | unsigned int i = start; |
@@ -302,13 +320,19 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
302 | * anywhere inside a function. | 320 | * anywhere inside a function. |
303 | */ | 321 | */ |
304 | list_for_each_entry(cnode, &root->val, list) { | 322 | list_for_each_entry(cnode, &root->val, list) { |
323 | struct symbol *sym; | ||
324 | |||
305 | if (i == chain->nr) | 325 | if (i == chain->nr) |
306 | break; | 326 | break; |
307 | if (cnode->sym && syms[i]) { | 327 | |
308 | if (cnode->sym->start != syms[i]->start) | 328 | sym = chain->ips[i].ms.sym; |
329 | |||
330 | if (cnode->ms.sym && sym) { | ||
331 | if (cnode->ms.sym->start != sym->start) | ||
309 | break; | 332 | break; |
310 | } else if (cnode->ip != chain->ips[i]) | 333 | } else if (cnode->ip != chain->ips[i].ip) |
311 | break; | 334 | break; |
335 | |||
312 | if (!found) | 336 | if (!found) |
313 | found = true; | 337 | found = true; |
314 | i++; | 338 | i++; |
@@ -320,7 +344,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
320 | 344 | ||
321 | /* we match only a part of the node. Split it and add the new chain */ | 345 | /* we match only a part of the node. Split it and add the new chain */ |
322 | if (i - start < root->val_nr) { | 346 | if (i - start < root->val_nr) { |
323 | split_add_child(root, chain, cnode, start, i - start, syms); | 347 | split_add_child(root, chain, cnode, start, i - start); |
324 | return 0; | 348 | return 0; |
325 | } | 349 | } |
326 | 350 | ||
@@ -331,15 +355,50 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
331 | } | 355 | } |
332 | 356 | ||
333 | /* We match the node and still have a part remaining */ | 357 | /* We match the node and still have a part remaining */ |
334 | __append_chain_children(root, chain, syms, i); | 358 | __append_chain_children(root, chain, i); |
335 | 359 | ||
336 | return 0; | 360 | return 0; |
337 | } | 361 | } |
338 | 362 | ||
339 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 363 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, |
340 | struct symbol **syms) | 364 | struct map_symbol *syms) |
341 | { | 365 | { |
366 | int i, j = 0; | ||
367 | |||
368 | for (i = 0; i < (int)old->nr; i++) { | ||
369 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
370 | continue; | ||
371 | |||
372 | new->ips[j].ip = old->ips[i]; | ||
373 | new->ips[j].ms = syms[i]; | ||
374 | j++; | ||
375 | } | ||
376 | |||
377 | new->nr = j; | ||
378 | } | ||
379 | |||
380 | |||
381 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
382 | struct map_symbol *syms) | ||
383 | { | ||
384 | struct resolved_chain *filtered; | ||
385 | |||
342 | if (!chain->nr) | 386 | if (!chain->nr) |
343 | return; | 387 | return 0; |
344 | __append_chain_children(root, chain, syms, 0); | 388 | |
389 | filtered = zalloc(sizeof(*filtered) + | ||
390 | chain->nr * sizeof(struct resolved_ip)); | ||
391 | if (!filtered) | ||
392 | return -ENOMEM; | ||
393 | |||
394 | filter_context(chain, filtered, syms); | ||
395 | |||
396 | if (!filtered->nr) | ||
397 | goto end; | ||
398 | |||
399 | __append_chain_children(root, filtered, 0); | ||
400 | end: | ||
401 | free(filtered); | ||
402 | |||
403 | return 0; | ||
345 | } | 404 | } |