2009-06-26 16:28:00 +02:00
/*
2010-03-22 13:09:33 -03:00
* Copyright ( C ) 2009 - 2010 , Frederic Weisbecker < fweisbec @ gmail . com >
2009-06-26 16:28:00 +02:00
*
* Handle the callchains from the stream in an ad - hoc radix tree and then
* sort them in an rbtree .
*
2009-07-01 05:35:15 +02:00
* Using a radix for code path provides a fast retrieval and factorizes
* memory use . Also that lets us use the paths in a hierarchical graph view .
*
2009-06-26 16:28:00 +02:00
*/
# include <stdlib.h>
# include <stdio.h>
# include <stdbool.h>
# include <errno.h>
2009-08-09 04:19:15 +02:00
# include <math.h>
2009-06-26 16:28:00 +02:00
# include "callchain.h"
2010-05-09 11:47:13 -03:00
bool ip_callchain__valid ( struct ip_callchain * chain , event_t * event )
{
unsigned int chain_size = event - > header . size ;
chain_size - = ( unsigned long ) & event - > ip . __more_data - ( unsigned long ) event ;
return chain - > nr * sizeof ( u64 ) < = chain_size ;
}
2009-07-02 17:58:19 +02:00
# define chain_for_each_child(child, parent) \
list_for_each_entry ( child , & parent - > children , brothers )
2009-07-01 05:35:15 +02:00
static void
2009-07-02 17:58:21 +02:00
rb_insert_callchain ( struct rb_root * root , struct callchain_node * chain ,
enum chain_mode mode )
2009-06-26 16:28:00 +02:00
{
struct rb_node * * p = & root - > rb_node ;
struct rb_node * parent = NULL ;
struct callchain_node * rnode ;
2009-08-07 07:11:05 +02:00
u64 chain_cumul = cumul_hits ( chain ) ;
2009-06-26 16:28:00 +02:00
while ( * p ) {
2009-08-07 07:11:05 +02:00
u64 rnode_cumul ;
2009-06-26 16:28:00 +02:00
parent = * p ;
rnode = rb_entry ( parent , struct callchain_node , rb_node ) ;
2009-08-07 07:11:05 +02:00
rnode_cumul = cumul_hits ( rnode ) ;
2009-06-26 16:28:00 +02:00
2009-07-02 17:58:21 +02:00
switch ( mode ) {
2009-07-05 07:39:21 +02:00
case CHAIN_FLAT :
2009-07-02 17:58:21 +02:00
if ( rnode - > hit < chain - > hit )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
break ;
2009-07-05 07:39:21 +02:00
case CHAIN_GRAPH_ABS : /* Falldown */
case CHAIN_GRAPH_REL :
2009-08-07 07:11:05 +02:00
if ( rnode_cumul < chain_cumul )
2009-07-02 17:58:21 +02:00
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
break ;
2009-08-15 12:26:57 +02:00
case CHAIN_NONE :
2009-07-02 17:58:21 +02:00
default :
break ;
}
2009-06-26 16:28:00 +02:00
}
rb_link_node ( & chain - > rb_node , parent , p ) ;
rb_insert_color ( & chain - > rb_node , root ) ;
}
2009-07-05 07:39:21 +02:00
static void
__sort_chain_flat ( struct rb_root * rb_root , struct callchain_node * node ,
u64 min_hit )
{
struct callchain_node * child ;
chain_for_each_child ( child , node )
__sort_chain_flat ( rb_root , child , min_hit ) ;
if ( node - > hit & & node - > hit > = min_hit )
rb_insert_callchain ( rb_root , node , CHAIN_FLAT ) ;
}
2009-06-26 16:28:00 +02:00
/*
* Once we get every callchains from the stream , we can now
* sort them by hit
*/
2009-07-05 07:39:21 +02:00
static void
sort_chain_flat ( struct rb_root * rb_root , struct callchain_node * node ,
u64 min_hit , struct callchain_param * param __used )
{
__sort_chain_flat ( rb_root , node , min_hit ) ;
}
static void __sort_chain_graph_abs ( struct callchain_node * node ,
u64 min_hit )
2009-06-26 16:28:00 +02:00
{
struct callchain_node * child ;
2009-07-05 07:39:21 +02:00
node - > rb_root = RB_ROOT ;
2009-06-26 16:28:00 +02:00
2009-07-05 07:39:21 +02:00
chain_for_each_child ( child , node ) {
__sort_chain_graph_abs ( child , min_hit ) ;
2009-08-07 07:11:05 +02:00
if ( cumul_hits ( child ) > = min_hit )
2009-07-05 07:39:21 +02:00
rb_insert_callchain ( & node - > rb_root , child ,
CHAIN_GRAPH_ABS ) ;
}
}
static void
sort_chain_graph_abs ( struct rb_root * rb_root , struct callchain_node * chain_root ,
u64 min_hit , struct callchain_param * param __used )
{
__sort_chain_graph_abs ( chain_root , min_hit ) ;
rb_root - > rb_node = chain_root - > rb_root . rb_node ;
2009-07-02 17:58:21 +02:00
}
2009-07-05 07:39:21 +02:00
static void __sort_chain_graph_rel ( struct callchain_node * node ,
double min_percent )
2009-07-02 17:58:21 +02:00
{
struct callchain_node * child ;
2009-07-05 07:39:21 +02:00
u64 min_hit ;
2009-07-02 17:58:21 +02:00
node - > rb_root = RB_ROOT ;
2009-08-09 04:19:15 +02:00
min_hit = ceil ( node - > children_hit * min_percent ) ;
2009-07-02 17:58:21 +02:00
chain_for_each_child ( child , node ) {
2009-07-05 07:39:21 +02:00
__sort_chain_graph_rel ( child , min_percent ) ;
2009-08-07 07:11:05 +02:00
if ( cumul_hits ( child ) > = min_hit )
2009-07-05 07:39:21 +02:00
rb_insert_callchain ( & node - > rb_root , child ,
CHAIN_GRAPH_REL ) ;
2009-07-02 17:58:21 +02:00
}
}
2009-07-05 07:39:21 +02:00
static void
sort_chain_graph_rel ( struct rb_root * rb_root , struct callchain_node * chain_root ,
u64 min_hit __used , struct callchain_param * param )
2009-07-02 17:58:21 +02:00
{
2009-08-09 04:19:15 +02:00
__sort_chain_graph_rel ( chain_root , param - > min_percent / 100.0 ) ;
2009-07-02 17:58:21 +02:00
rb_root - > rb_node = chain_root - > rb_root . rb_node ;
2009-06-26 16:28:00 +02:00
}
2009-07-05 07:39:21 +02:00
int register_callchain_param ( struct callchain_param * param )
{
switch ( param - > mode ) {
case CHAIN_GRAPH_ABS :
param - > sort = sort_chain_graph_abs ;
break ;
case CHAIN_GRAPH_REL :
param - > sort = sort_chain_graph_rel ;
break ;
case CHAIN_FLAT :
param - > sort = sort_chain_flat ;
break ;
2009-08-15 12:26:57 +02:00
case CHAIN_NONE :
2009-07-05 07:39:21 +02:00
default :
return - 1 ;
}
return 0 ;
}
2009-07-01 05:35:15 +02:00
/*
* Create a child for a parent . If inherit_children , then the new child
* will become the new parent of it ' s parent children
*/
static struct callchain_node *
create_child ( struct callchain_node * parent , bool inherit_children )
2009-06-26 16:28:00 +02:00
{
struct callchain_node * new ;
2010-05-10 10:56:50 -03:00
new = zalloc ( sizeof ( * new ) ) ;
2009-06-26 16:28:00 +02:00
if ( ! new ) {
perror ( " not enough memory to create child for code path tree " ) ;
return NULL ;
}
new - > parent = parent ;
INIT_LIST_HEAD ( & new - > children ) ;
INIT_LIST_HEAD ( & new - > val ) ;
2009-07-01 05:35:15 +02:00
if ( inherit_children ) {
struct callchain_node * next ;
list_splice ( & parent - > children , & new - > children ) ;
INIT_LIST_HEAD ( & parent - > children ) ;
2009-07-02 17:58:19 +02:00
chain_for_each_child ( next , new )
2009-07-01 05:35:15 +02:00
next - > parent = new ;
}
2009-06-26 16:28:00 +02:00
list_add_tail ( & new - > brothers , & parent - > children ) ;
return new ;
}
2010-03-22 13:09:33 -03:00
struct resolved_ip {
2010-03-24 16:40:18 -03:00
u64 ip ;
struct map_symbol ms ;
2010-03-22 13:09:33 -03:00
} ;
struct resolved_chain {
u64 nr ;
struct resolved_ip ips [ 0 ] ;
} ;
2009-07-01 05:35:15 +02:00
/*
* Fill the node with callchain values
*/
2009-06-26 16:28:00 +02:00
static void
2010-03-22 13:09:33 -03:00
fill_node ( struct callchain_node * node , struct resolved_chain * chain , int start )
2009-06-26 16:28:00 +02:00
{
2009-07-01 12:37:06 +02:00
unsigned int i ;
2009-06-26 16:28:00 +02:00
for ( i = start ; i < chain - > nr ; i + + ) {
struct callchain_list * call ;
2010-05-10 10:56:50 -03:00
call = zalloc ( sizeof ( * call ) ) ;
2009-06-26 16:28:00 +02:00
if ( ! call ) {
perror ( " not enough memory for the code path tree " ) ;
return ;
}
2010-03-22 13:09:33 -03:00
call - > ip = chain - > ips [ i ] . ip ;
2010-03-24 16:40:18 -03:00
call - > ms = chain - > ips [ i ] . ms ;
2009-06-26 16:28:00 +02:00
list_add_tail ( & call - > list , & node - > val ) ;
}
2009-07-01 05:35:15 +02:00
node - > val_nr = chain - > nr - start ;
if ( ! node - > val_nr )
2009-10-21 17:34:06 -02:00
pr_warning ( " Warning: empty node in callchain tree \n " ) ;
2009-06-26 16:28:00 +02:00
}
2009-07-01 05:35:15 +02:00
static void
2010-03-22 13:09:33 -03:00
add_child ( struct callchain_node * parent , struct resolved_chain * chain ,
int start )
2009-06-26 16:28:00 +02:00
{
struct callchain_node * new ;
2009-07-01 05:35:15 +02:00
new = create_child ( parent , false ) ;
2010-03-22 13:09:33 -03:00
fill_node ( new , chain , start ) ;
2009-06-26 16:28:00 +02:00
2009-08-07 07:11:05 +02:00
new - > children_hit = 0 ;
new - > hit = 1 ;
2009-06-26 16:28:00 +02:00
}
2009-07-01 05:35:15 +02:00
/*
* Split the parent in two parts ( a new child is created ) and
* give a part of its callchain to the created child .
* Then create another child to host the given callchain of new branch
*/
2009-06-26 16:28:00 +02:00
static void
2010-03-22 13:09:33 -03:00
split_add_child ( struct callchain_node * parent , struct resolved_chain * chain ,
struct callchain_list * to_split , int idx_parents , int idx_local )
2009-06-26 16:28:00 +02:00
{
struct callchain_node * new ;
2009-07-01 05:35:15 +02:00
struct list_head * old_tail ;
2009-07-01 12:37:06 +02:00
unsigned int idx_total = idx_parents + idx_local ;
2009-06-26 16:28:00 +02:00
/* split */
2009-07-01 05:35:15 +02:00
new = create_child ( parent , true ) ;
/* split the callchain and move a part to the new child */
old_tail = parent - > val . prev ;
list_del_range ( & to_split - > list , old_tail ) ;
new - > val . next = & to_split - > list ;
new - > val . prev = old_tail ;
to_split - > list . prev = & new - > val ;
old_tail - > next = & new - > val ;
2009-06-26 16:28:00 +02:00
2009-07-01 05:35:15 +02:00
/* split the hits */
new - > hit = parent - > hit ;
2009-08-07 07:11:05 +02:00
new - > children_hit = parent - > children_hit ;
parent - > children_hit = cumul_hits ( new ) ;
2009-07-01 05:35:15 +02:00
new - > val_nr = parent - > val_nr - idx_local ;
parent - > val_nr = idx_local ;
/* create a new child for the new branch if any */
if ( idx_total < chain - > nr ) {
parent - > hit = 0 ;
2010-03-22 13:09:33 -03:00
add_child ( parent , chain , idx_total ) ;
2009-08-07 07:11:05 +02:00
parent - > children_hit + + ;
2009-07-01 05:35:15 +02:00
} else {
parent - > hit = 1 ;
}
2009-06-26 16:28:00 +02:00
}
static int
2010-03-22 13:09:33 -03:00
__append_chain ( struct callchain_node * root , struct resolved_chain * chain ,
unsigned int start ) ;
2009-06-26 16:28:00 +02:00
2009-07-01 05:35:15 +02:00
static void
2010-03-22 13:09:33 -03:00
__append_chain_children ( struct callchain_node * root ,
struct resolved_chain * chain ,
unsigned int start )
2009-06-26 16:28:00 +02:00
{
struct callchain_node * rnode ;
/* lookup in childrens */
2009-07-02 17:58:19 +02:00
chain_for_each_child ( rnode , root ) {
2010-03-22 13:09:33 -03:00
unsigned int ret = __append_chain ( rnode , chain , start ) ;
2009-07-01 12:37:06 +02:00
2009-06-26 16:28:00 +02:00
if ( ! ret )
2009-08-07 07:11:05 +02:00
goto inc_children_hit ;
2009-06-26 16:28:00 +02:00
}
2009-07-01 05:35:15 +02:00
/* nothing in children, add to the current node */
2010-03-22 13:09:33 -03:00
add_child ( root , chain , start ) ;
2009-07-05 07:39:20 +02:00
2009-08-07 07:11:05 +02:00
inc_children_hit :
root - > children_hit + + ;
2009-06-26 16:28:00 +02:00
}
static int
2010-03-22 13:09:33 -03:00
__append_chain ( struct callchain_node * root , struct resolved_chain * chain ,
unsigned int start )
2009-06-26 16:28:00 +02:00
{
struct callchain_list * cnode ;
2009-07-01 12:37:06 +02:00
unsigned int i = start ;
2009-06-26 16:28:00 +02:00
bool found = false ;
2009-07-01 05:35:15 +02:00
/*
* Lookup in the current node
* If we have a symbol , then compare the start to match
* anywhere inside a function .
*/
2009-06-26 16:28:00 +02:00
list_for_each_entry ( cnode , & root - > val , list ) {
2010-03-22 13:09:33 -03:00
struct symbol * sym ;
2009-07-01 05:35:15 +02:00
if ( i = = chain - > nr )
break ;
2010-03-22 13:09:33 -03:00
2010-03-24 16:40:18 -03:00
sym = chain - > ips [ i ] . ms . sym ;
2010-03-22 13:09:33 -03:00
2010-03-24 16:40:18 -03:00
if ( cnode - > ms . sym & & sym ) {
if ( cnode - > ms . sym - > start ! = sym - > start )
2009-07-01 05:35:15 +02:00
break ;
2010-03-22 13:09:33 -03:00
} else if ( cnode - > ip ! = chain - > ips [ i ] . ip )
2009-06-26 16:28:00 +02:00
break ;
2010-03-22 13:09:33 -03:00
2009-06-26 16:28:00 +02:00
if ( ! found )
found = true ;
2009-07-01 05:35:15 +02:00
i + + ;
2009-06-26 16:28:00 +02:00
}
/* matches not, relay on the parent */
if ( ! found )
return - 1 ;
/* we match only a part of the node. Split it and add the new chain */
2009-07-01 05:35:15 +02:00
if ( i - start < root - > val_nr ) {
2010-03-22 13:09:33 -03:00
split_add_child ( root , chain , cnode , start , i - start ) ;
2009-06-26 16:28:00 +02:00
return 0 ;
}
/* we match 100% of the path, increment the hit */
2009-07-01 05:35:15 +02:00
if ( i - start = = root - > val_nr & & i = = chain - > nr ) {
2009-06-26 16:28:00 +02:00
root - > hit + + ;
return 0 ;
}
2009-07-01 05:35:15 +02:00
/* We match the node and still have a part remaining */
2010-03-22 13:09:33 -03:00
__append_chain_children ( root , chain , i ) ;
2009-07-01 05:35:15 +02:00
return 0 ;
2009-06-26 16:28:00 +02:00
}
2010-03-24 16:40:18 -03:00
static void filter_context ( struct ip_callchain * old , struct resolved_chain * new ,
struct map_symbol * syms )
2010-03-22 13:09:33 -03:00
{
int i , j = 0 ;
for ( i = 0 ; i < ( int ) old - > nr ; i + + ) {
if ( old - > ips [ i ] > = PERF_CONTEXT_MAX )
continue ;
new - > ips [ j ] . ip = old - > ips [ i ] ;
2010-03-24 16:40:18 -03:00
new - > ips [ j ] . ms = syms [ i ] ;
2010-03-22 13:09:33 -03:00
j + + ;
}
new - > nr = j ;
}
int append_chain ( struct callchain_node * root , struct ip_callchain * chain ,
2010-03-24 16:40:18 -03:00
struct map_symbol * syms )
2009-06-26 16:28:00 +02:00
{
2010-03-22 13:09:33 -03:00
struct resolved_chain * filtered ;
2009-08-08 02:16:23 +02:00
if ( ! chain - > nr )
2010-03-22 13:09:33 -03:00
return 0 ;
2010-05-10 10:56:50 -03:00
filtered = zalloc ( sizeof ( * filtered ) +
2010-03-22 13:09:33 -03:00
chain - > nr * sizeof ( struct resolved_ip ) ) ;
if ( ! filtered )
return - ENOMEM ;
filter_context ( chain , filtered , syms ) ;
if ( ! filtered - > nr )
goto end ;
__append_chain_children ( root , filtered , 0 ) ;
end :
free ( filtered ) ;
return 0 ;
2009-06-26 16:28:00 +02:00
}