/*
* Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com>
*
* Handle the callchains from the stream in an ad-hoc radix tree and then
* sort them in an rbtree.
*
* 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.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <math.h>
#include "asm/bug.h"
#include "hist.h"
#include "util.h"
#include "sort.h"
#include "machine.h"
#include "callchain.h"
__thread struct callchain_cursor callchain_cursor;
#ifdef HAVE_DWARF_UNWIND_SUPPORT
static int get_stack_size(const char *str, unsigned long *_size)
{
char *endptr;
unsigned long size;
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
size = strtoul(str, &endptr, 0);
do {
if (*endptr)
break;
size = round_up(size, sizeof(u64));
if (!size || size > max_size)
break;
*_size = size;
return 0;
} while (0);
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
max_size, str);
return -1;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
int parse_callchain_record_opt(const char *arg)
{
char *tok, *name, *saveptr = NULL;
char *buf;
int ret = -1;
/* We need buffer that we know we can write to. */
buf = malloc(strlen(arg) + 1);
if (!buf)
return -ENOMEM;
strcpy(buf, arg);
tok = strtok_r((char *)buf, ",", &saveptr);
name = tok ? : (char *)buf;
do {
/* Framepointer style */
if (!strncmp(name, "fp", sizeof("fp"))) {
if (!strtok_r(NULL, ",", &saveptr)) {
callchain_param.record_mode = CALLCHAIN_FP;
ret = 0;
} else
pr_err("callchain: No more arguments "
"needed for --call-graph fp\n");
break;
#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
ret = 0;
callchain_param.record_mode = CALLCHAIN_DWARF;
callchain_param.dump_size = default_stack_dump_size;
tok = strtok_r(NULL, ",", &saveptr);
if (tok) {
unsigned long size = 0;
ret = get_stack_size(tok, &size);
callchain_param.dump_size = size;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
} else if (!strncmp(name, "lbr", sizeof("lbr"))) {
if (!strtok_r(NULL, ",", &saveptr)) {
callchain_param.record_mode = CALLCHAIN_LBR;
ret = 0;
} else
pr_err("callchain: No more arguments "
"needed for --call-graph lbr\n");
break;
} else {
pr_err("callchain: Unknown --call-graph option "
"value: %s\n", arg);
break;
}
} while (0);
free(buf);
return ret;
}
static int parse_callchain_mode(const char *value)
{
if (!strncmp(value, "graph", strlen(value))) {
callchain_param.mode = CHAIN_GRAPH_ABS;
return 0;
}
if (!strncmp(value, "flat", strlen(value))) {
callchain_param.mode = CHAIN_FLAT;
return 0;
}
if (!strncmp(value, "fractal", strlen(value))) {
callchain_param.mode = CHAIN_GRAPH_REL;
return 0;
}
return -1;
}
static int parse_callchain_order(const char *value)
{
if (!strncmp(value, "caller", strlen(value))) {
callchain_param.order = ORDER_CALLER;
return 0;
}
if (!strncmp(value, "callee", strlen(value))) {
callchain_param.order = ORDER_CALLEE;
return 0;
}
return -1;
}
static int parse_callchain_sort_key(const char *value)
{
if (!strncmp(value, "function", strlen(value))) {
callchain_param.key = CCKEY_FUNCTION;
return 0;
}
if (!strncmp(value, "address", strlen(value))) {
callchain_param.key = CCKEY_ADDRESS;
return 0;
}
if (!strncmp(value, "branch", strlen(value)))