perf/core improvements and fixes:
New features: - Handle inline functions in callchains (Jin Yao) - Enable sorting by srcline as key (Milian Wolff) Fixes: - Fix no_size logic in addr_filter__resolve_kernel_syms() in the auxtrace code (Adrian Hunter) - Fix some thread refcount leaks in 'perf trace' (Arnaldo Carvalho de Melo) - Fix divide by zero when calculating percent for an event in a group in the annotate by source line code (Taeung Song) - build-id files now aren't anymore symlinks, their parent directories are, so readlink the later (Taeung Song) - Assorted fixes for null termination problems, mostly related to readlink, detected by valgrind (Tommi Rantala) Infrastructure: - Make vfs_getname probe point logic in 'perf trace' more robust wrt length of pathname (Arnaldo Carvalho de Melo) - Remove unused 'prefix' parameter from builtins main functions (Arnaldo Carvalho de Melo) - Show 'perf list sdt' option in man page (Ravi Bangoria) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJY2WNUAAoJENZQFvNTUqpAZ3AQAIn/Q+Y665oP57RbikedeifL He8vdMUkD/haRo0atbvuu5tRrwiRUabkUa6GKPHNCDl8GUD6UbkztUirL4Cq4v9s 7ONbCHXzaPnPZbDbl/W7Yx4vADow3YMR9EyNkL8/i2ApZqMCPQ9mUBhxJlSDp7RY agYcOugUlYuvHsKVX59fTyvTAq8btfyFQTqhJ+NPddcxsyR5jam9XxxvgMURdFJr h6OLO9wqCxlMctqlGXU+6tpqiAR+bp8UZgzDKwabGR4mZR+uLBYGf0FUQz52vf2A 83ufaZ5UrQUsSnVeYXBPW+i8+Ixu8pEOFDMDcSpk/wQXunLlN52LmuatSCkPBEV1 jFth8SX3IAX349hpaRBNuLk5UuqS6NKBztYzlaVsKMpuIw4hRPVE3VvqKefZD/hx Vdlr1v6fPXMcRUcc3lFFiVCIvs0hRV4IDDIimGjJHf8dm+GFMHH+bk+tfiSQAlmZ q3aSKMImUM3vlD01E4BmTVr4IEZHTd3mv0Ml+nbQGNj6Bu2364eBsFRnNHJWwGmt c9tcnmeRv6JzrmprVXMuOUyyTcml+b5/vincEEmTxUdbxCbYFkQS3JzPxfpxqFI/ zM5rlJJ9KKWXmwD6OgUoXT5IUzq4BuIVyJ3DxwuL2rrQggsv0zORxQtVduY+IJSj ZD/Qu7SOiFfnAFM6kLwP =Lm/M -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-4.12-20170327' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: New features: - Handle inline functions in callchains (Jin Yao) - Enable sorting by srcline as key (Milian Wolff) Fixes: - Fix no_size logic in addr_filter__resolve_kernel_syms() in the auxtrace code (Adrian Hunter) - Fix some thread refcount leaks in 'perf trace' (Arnaldo Carvalho de Melo) - Fix divide by zero when calculating percent for an event in a group in the annotate by source line code (Taeung Song) - build-id files now aren't anymore symlinks, their parent directories are, so readlink the later (Taeung Song) - Assorted fixes for null termination problems, mostly related to readlink, detected by valgrind (Tommi Rantala) Infrastructure changes: - Make vfs_getname probe point logic in 'perf trace' more robust wrt length of pathname (Arnaldo Carvalho de Melo) - Remove unused 'prefix' parameter from builtins main functions (Arnaldo Carvalho de Melo) - Show 'perf list sdt' option in man page (Ravi Bangoria) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
3906a13a6b
@ -8,7 +8,7 @@ perf-list - List all symbolic event types
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|event_glob]
|
||||
'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|sdt|event_glob]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -244,6 +244,8 @@ To limit the list use:
|
||||
|
||||
. 'pmu' to print the kernel supplied PMU events.
|
||||
|
||||
. 'sdt' to list all Statically Defined Tracepoint events.
|
||||
|
||||
. If none of the above is matched, it will apply the supplied glob to all
|
||||
events, printing the ones that match.
|
||||
|
||||
|
@ -235,6 +235,7 @@ OPTIONS
|
||||
sort_key can be:
|
||||
- function: compare on functions (default)
|
||||
- address: compare on individual code addresses
|
||||
- srcline: compare on source filename and line number
|
||||
|
||||
branch can be:
|
||||
- branch: include last branch information in callgraph when available.
|
||||
@ -430,6 +431,10 @@ include::itrace.txt[]
|
||||
--hierarchy::
|
||||
Enable hierarchical output.
|
||||
|
||||
--inline::
|
||||
If a callgraph address belongs to an inlined function, the inline stack
|
||||
will be printed. Each entry is function name or file/line.
|
||||
|
||||
include::callchain-overhead-calculation.txt[]
|
||||
|
||||
SEE ALSO
|
||||
|
@ -25,17 +25,17 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
int bench_numa(int argc, const char **argv, const char *prefix);
|
||||
int bench_sched_messaging(int argc, const char **argv, const char *prefix);
|
||||
int bench_sched_pipe(int argc, const char **argv, const char *prefix);
|
||||
int bench_mem_memcpy(int argc, const char **argv, const char *prefix);
|
||||
int bench_mem_memset(int argc, const char **argv, const char *prefix);
|
||||
int bench_futex_hash(int argc, const char **argv, const char *prefix);
|
||||
int bench_futex_wake(int argc, const char **argv, const char *prefix);
|
||||
int bench_futex_wake_parallel(int argc, const char **argv, const char *prefix);
|
||||
int bench_futex_requeue(int argc, const char **argv, const char *prefix);
|
||||
int bench_numa(int argc, const char **argv);
|
||||
int bench_sched_messaging(int argc, const char **argv);
|
||||
int bench_sched_pipe(int argc, const char **argv);
|
||||
int bench_mem_memcpy(int argc, const char **argv);
|
||||
int bench_mem_memset(int argc, const char **argv);
|
||||
int bench_futex_hash(int argc, const char **argv);
|
||||
int bench_futex_wake(int argc, const char **argv);
|
||||
int bench_futex_wake_parallel(int argc, const char **argv);
|
||||
int bench_futex_requeue(int argc, const char **argv);
|
||||
/* pi futexes */
|
||||
int bench_futex_lock_pi(int argc, const char **argv, const char *prefix);
|
||||
int bench_futex_lock_pi(int argc, const char **argv);
|
||||
|
||||
#define BENCH_FORMAT_DEFAULT_STR "default"
|
||||
#define BENCH_FORMAT_DEFAULT 0
|
||||
|
@ -114,8 +114,7 @@ static void print_summary(void)
|
||||
(int) runtime.tv_sec);
|
||||
}
|
||||
|
||||
int bench_futex_hash(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_futex_hash(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
cpu_set_t cpu;
|
||||
|
@ -140,8 +140,7 @@ static void create_threads(struct worker *w, pthread_attr_t thread_attr)
|
||||
}
|
||||
}
|
||||
|
||||
int bench_futex_lock_pi(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_futex_lock_pi(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
|
@ -109,8 +109,7 @@ static void toggle_done(int sig __maybe_unused,
|
||||
done = true;
|
||||
}
|
||||
|
||||
int bench_futex_requeue(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_futex_requeue(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
|
@ -197,8 +197,7 @@ static void toggle_done(int sig __maybe_unused,
|
||||
done = true;
|
||||
}
|
||||
|
||||
int bench_futex_wake_parallel(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_futex_wake_parallel(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
|
@ -115,8 +115,7 @@ static void toggle_done(int sig __maybe_unused,
|
||||
done = true;
|
||||
}
|
||||
|
||||
int bench_futex_wake(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_futex_wake(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i, j;
|
||||
|
@ -284,7 +284,7 @@ static const char * const bench_mem_memcpy_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
int bench_mem_memcpy(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int bench_mem_memcpy(int argc, const char **argv)
|
||||
{
|
||||
struct bench_mem_info info = {
|
||||
.functions = memcpy_functions,
|
||||
@ -358,7 +358,7 @@ static const struct function memset_functions[] = {
|
||||
{ .name = NULL, }
|
||||
};
|
||||
|
||||
int bench_mem_memset(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int bench_mem_memset(int argc, const char **argv)
|
||||
{
|
||||
struct bench_mem_info info = {
|
||||
.functions = memset_functions,
|
||||
|
@ -1767,7 +1767,7 @@ static int bench_all(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bench_numa(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int bench_numa(int argc, const char **argv)
|
||||
{
|
||||
init_params(&p0, "main,", argc, argv);
|
||||
argc = parse_options(argc, argv, options, bench_numa_usage, 0);
|
||||
|
@ -260,8 +260,7 @@ static const char * const bench_sched_message_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
int bench_sched_messaging(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int bench_sched_messaging(int argc, const char **argv)
|
||||
{
|
||||
unsigned int i, total_children;
|
||||
struct timeval start, stop, diff;
|
||||
|
@ -76,7 +76,7 @@ static void *worker_thread(void *__tdata)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bench_sched_pipe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int bench_sched_pipe(int argc, const char **argv)
|
||||
{
|
||||
struct thread_data threads[2], *td;
|
||||
int pipe_1[2], pipe_2[2];
|
||||
|
@ -383,7 +383,7 @@ static const char * const annotate_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_annotate(int argc, const char **argv)
|
||||
{
|
||||
struct perf_annotate annotate = {
|
||||
.tool = {
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
typedef int (*bench_fn_t)(int argc, const char **argv, const char *prefix);
|
||||
typedef int (*bench_fn_t)(int argc, const char **argv);
|
||||
|
||||
struct bench {
|
||||
const char *name;
|
||||
@ -155,7 +155,7 @@ static int bench_str2int(const char *str)
|
||||
* to something meaningful:
|
||||
*/
|
||||
static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t fn,
|
||||
int argc, const char **argv, const char *prefix)
|
||||
int argc, const char **argv)
|
||||
{
|
||||
int size;
|
||||
char *name;
|
||||
@ -171,7 +171,7 @@ static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t f
|
||||
prctl(PR_SET_NAME, name);
|
||||
argv[0] = name;
|
||||
|
||||
ret = fn(argc, argv, prefix);
|
||||
ret = fn(argc, argv);
|
||||
|
||||
free(name);
|
||||
|
||||
@ -198,7 +198,7 @@ static void run_collection(struct collection *coll)
|
||||
fflush(stdout);
|
||||
|
||||
argv[1] = bench->name;
|
||||
run_bench(coll->name, bench->name, bench->fn, 1, argv, NULL);
|
||||
run_bench(coll->name, bench->name, bench->fn, 1, argv);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@ -211,7 +211,7 @@ static void run_all_collections(void)
|
||||
run_collection(coll);
|
||||
}
|
||||
|
||||
int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_bench(int argc, const char **argv)
|
||||
{
|
||||
struct collection *coll;
|
||||
int ret = 0;
|
||||
@ -270,7 +270,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (bench_format == BENCH_FORMAT_DEFAULT)
|
||||
printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name);
|
||||
fflush(stdout);
|
||||
ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1, prefix);
|
||||
ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -276,8 +276,7 @@ static int build_id_cache__update_file(const char *filename)
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmd_buildid_cache(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int cmd_buildid_cache(int argc, const char **argv)
|
||||
{
|
||||
struct strlist *list;
|
||||
struct str_node *pos;
|
||||
|
@ -87,8 +87,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_buildid_list(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int cmd_buildid_list(int argc, const char **argv)
|
||||
{
|
||||
bool show_kernel = false;
|
||||
bool with_hits = false;
|
||||
|
@ -2755,12 +2755,12 @@ static int perf_c2c__record(int argc, const char **argv)
|
||||
pr_debug("\n");
|
||||
}
|
||||
|
||||
ret = cmd_record(i, rec_argv, NULL);
|
||||
ret = cmd_record(i, rec_argv);
|
||||
free(rec_argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_c2c(int argc, const char **argv)
|
||||
{
|
||||
argc = parse_options(argc, argv, c2c_options, c2c_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
@ -154,7 +154,7 @@ static int parse_config_arg(char *arg, char **var, char **value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_config(int argc, const char **argv)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct perf_config_set *set;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "data-convert.h"
|
||||
#include "data-convert-bt.h"
|
||||
|
||||
typedef int (*data_cmd_fn_t)(int argc, const char **argv, const char *prefix);
|
||||
typedef int (*data_cmd_fn_t)(int argc, const char **argv);
|
||||
|
||||
struct data_cmd {
|
||||
const char *name;
|
||||
@ -50,8 +50,7 @@ static const char * const data_convert_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_data_convert(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
static int cmd_data_convert(int argc, const char **argv)
|
||||
{
|
||||
const char *to_ctf = NULL;
|
||||
struct perf_data_convert_opts opts = {
|
||||
@ -98,7 +97,7 @@ static struct data_cmd data_cmds[] = {
|
||||
{ .name = NULL, },
|
||||
};
|
||||
|
||||
int cmd_data(int argc, const char **argv, const char *prefix)
|
||||
int cmd_data(int argc, const char **argv)
|
||||
{
|
||||
struct data_cmd *cmd;
|
||||
const char *cmdstr;
|
||||
@ -118,7 +117,7 @@ int cmd_data(int argc, const char **argv, const char *prefix)
|
||||
if (strcmp(cmd->name, cmdstr))
|
||||
continue;
|
||||
|
||||
return cmd->fn(argc, argv, prefix);
|
||||
return cmd->fn(argc, argv);
|
||||
}
|
||||
|
||||
pr_err("Unknown command: %s\n", cmdstr);
|
||||
|
@ -1321,7 +1321,7 @@ static int diff__config(const char *var, const char *value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_diff(int argc, const char **argv)
|
||||
{
|
||||
int ret = hists__init();
|
||||
|
||||
|
@ -46,7 +46,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_evlist(int argc, const char **argv)
|
||||
{
|
||||
struct perf_attr_details details = { .verbose = false, };
|
||||
const struct option options[] = {
|
||||
|
@ -304,7 +304,7 @@ static int perf_ftrace_config(const char *var, const char *value, void *cb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_ftrace(int argc, const char **argv)
|
||||
{
|
||||
int ret;
|
||||
struct perf_ftrace ftrace = {
|
||||
|
@ -418,7 +418,7 @@ static int show_html_page(const char *perf_cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_help(int argc, const char **argv)
|
||||
{
|
||||
bool show_all = false;
|
||||
enum help_format help_format = HELP_FORMAT_MAN;
|
||||
|
@ -738,7 +738,7 @@ static int __cmd_inject(struct perf_inject *inject)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_inject(int argc, const char **argv)
|
||||
{
|
||||
struct perf_inject inject = {
|
||||
.tool = {
|
||||
|
@ -43,7 +43,7 @@ static int __cmd_kallsyms(int argc, const char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_kallsyms(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_kallsyms(int argc, const char **argv)
|
||||
{
|
||||
const struct option options[] = {
|
||||
OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
|
||||
|
@ -1866,7 +1866,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
for (j = 1; j < (unsigned int)argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
return cmd_record(i, rec_argv);
|
||||
}
|
||||
|
||||
static int kmem_config(const char *var, const char *value, void *cb __maybe_unused)
|
||||
@ -1885,7 +1885,7 @@ static int kmem_config(const char *var, const char *value, void *cb __maybe_unus
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_kmem(int argc, const char **argv)
|
||||
{
|
||||
const char * const default_slab_sort = "frag,hit,bytes";
|
||||
const char * const default_page_sort = "bytes,hit";
|
||||
|
@ -1209,7 +1209,7 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
|
||||
set_option_flag(record_options, 0, "transaction", PARSE_OPT_DISABLED);
|
||||
|
||||
record_usage = kvm_stat_record_usage;
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
return cmd_record(i, rec_argv);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1477,7 +1477,7 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)
|
||||
#endif
|
||||
|
||||
perf_stat:
|
||||
return cmd_stat(argc, argv, NULL);
|
||||
return cmd_stat(argc, argv);
|
||||
}
|
||||
#endif /* HAVE_KVM_STAT_SUPPORT */
|
||||
|
||||
@ -1496,7 +1496,7 @@ static int __cmd_record(const char *file_name, int argc, const char **argv)
|
||||
|
||||
BUG_ON(i != rec_argc);
|
||||
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
return cmd_record(i, rec_argv);
|
||||
}
|
||||
|
||||
static int __cmd_report(const char *file_name, int argc, const char **argv)
|
||||
@ -1514,7 +1514,7 @@ static int __cmd_report(const char *file_name, int argc, const char **argv)
|
||||
|
||||
BUG_ON(i != rec_argc);
|
||||
|
||||
return cmd_report(i, rec_argv, NULL);
|
||||
return cmd_report(i, rec_argv);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1533,10 +1533,10 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv)
|
||||
|
||||
BUG_ON(i != rec_argc);
|
||||
|
||||
return cmd_buildid_list(i, rec_argv, NULL);
|
||||
return cmd_buildid_list(i, rec_argv);
|
||||
}
|
||||
|
||||
int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_kvm(int argc, const char **argv)
|
||||
{
|
||||
const char *file_name = NULL;
|
||||
const struct option kvm_options[] = {
|
||||
@ -1591,9 +1591,9 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
else if (!strncmp(argv[0], "rep", 3))
|
||||
return __cmd_report(file_name, argc, argv);
|
||||
else if (!strncmp(argv[0], "diff", 4))
|
||||
return cmd_diff(argc, argv, NULL);
|
||||
return cmd_diff(argc, argv);
|
||||
else if (!strncmp(argv[0], "top", 3))
|
||||
return cmd_top(argc, argv, NULL);
|
||||
return cmd_top(argc, argv);
|
||||
else if (!strncmp(argv[0], "buildid-list", 12))
|
||||
return __cmd_buildid_list(file_name, argc, argv);
|
||||
#ifdef HAVE_KVM_STAT_SUPPORT
|
||||
|
@ -20,7 +20,7 @@
|
||||
static bool desc_flag = true;
|
||||
static bool details_flag;
|
||||
|
||||
int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_list(int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
bool raw_dump = false;
|
||||
|
@ -941,12 +941,12 @@ static int __cmd_record(int argc, const char **argv)
|
||||
|
||||
BUG_ON(i != rec_argc);
|
||||
|
||||
ret = cmd_record(i, rec_argv, NULL);
|
||||
ret = cmd_record(i, rec_argv);
|
||||
free(rec_argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_lock(int argc, const char **argv)
|
||||
{
|
||||
const struct option lock_options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
@ -1009,7 +1009,7 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
rc = __cmd_report(false);
|
||||
} else if (!strcmp(argv[0], "script")) {
|
||||
/* Aliased to 'perf script' */
|
||||
return cmd_script(argc, argv, prefix);
|
||||
return cmd_script(argc, argv);
|
||||
} else if (!strcmp(argv[0], "info")) {
|
||||
if (argc) {
|
||||
argc = parse_options(argc, argv,
|
||||
|
@ -129,7 +129,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
pr_debug("\n");
|
||||
}
|
||||
|
||||
ret = cmd_record(i, rec_argv, NULL);
|
||||
ret = cmd_record(i, rec_argv);
|
||||
free(rec_argv);
|
||||
return ret;
|
||||
}
|
||||
@ -256,7 +256,7 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rep_argv[i] = argv[j];
|
||||
|
||||
ret = cmd_report(i, rep_argv, NULL);
|
||||
ret = cmd_report(i, rep_argv);
|
||||
free(rep_argv);
|
||||
return ret;
|
||||
}
|
||||
@ -330,7 +330,7 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_mem(int argc, const char **argv)
|
||||
{
|
||||
struct stat st;
|
||||
struct perf_mem mem = {
|
||||
|
@ -468,7 +468,7 @@ out:
|
||||
|
||||
|
||||
static int
|
||||
__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
__cmd_probe(int argc, const char **argv)
|
||||
{
|
||||
const char * const probe_usage[] = {
|
||||
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
|
||||
@ -687,13 +687,13 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix)
|
||||
int cmd_probe(int argc, const char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = init_params();
|
||||
if (!ret) {
|
||||
ret = __cmd_probe(argc, argv, prefix);
|
||||
ret = __cmd_probe(argc, argv);
|
||||
cleanup_params();
|
||||
}
|
||||
|
||||
|
@ -1667,7 +1667,7 @@ static struct option __record_options[] = {
|
||||
|
||||
struct option *record_options = __record_options;
|
||||
|
||||
int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_record(int argc, const char **argv)
|
||||
{
|
||||
int err;
|
||||
struct record *rec = &record;
|
||||
|
@ -681,7 +681,7 @@ const char report_callchain_help[] = "Display call graph (stack chain/backtrace)
|
||||
CALLCHAIN_REPORT_HELP
|
||||
"\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
|
||||
|
||||
int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_report(int argc, const char **argv)
|
||||
{
|
||||
struct perf_session *session;
|
||||
struct itrace_synth_opts itrace_synth_opts = { .set = 0, };
|
||||
@ -845,6 +845,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
stdio__config_color, "always"),
|
||||
OPT_STRING(0, "time", &report.time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name,
|
||||
"Show inline function"),
|
||||
OPT_END()
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
|
@ -3272,10 +3272,10 @@ static int __cmd_record(int argc, const char **argv)
|
||||
|
||||
BUG_ON(i != rec_argc);
|
||||
|
||||
return cmd_record(i, rec_argv, NULL);
|
||||
return cmd_record(i, rec_argv);
|
||||
}
|
||||
|
||||
int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_sched(int argc, const char **argv)
|
||||
{
|
||||
const char default_sort_order[] = "avg, max, switch, runtime";
|
||||
struct perf_sched sched = {
|
||||
@ -3412,7 +3412,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
* Aliased to 'perf script' for now:
|
||||
*/
|
||||
if (!strcmp(argv[0], "script"))
|
||||
return cmd_script(argc, argv, prefix);
|
||||
return cmd_script(argc, argv);
|
||||
|
||||
if (!strncmp(argv[0], "rec", 3)) {
|
||||
return __cmd_record(argc, argv);
|
||||
|
@ -2359,7 +2359,7 @@ int process_cpu_map_event(struct perf_tool *tool __maybe_unused,
|
||||
return set_maps(script);
|
||||
}
|
||||
|
||||
int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_script(int argc, const char **argv)
|
||||
{
|
||||
bool show_full_info = false;
|
||||
bool header = false;
|
||||
@ -2504,7 +2504,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
|
||||
rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
|
||||
if (!rec_script_path)
|
||||
return cmd_record(argc, argv, NULL);
|
||||
return cmd_record(argc, argv);
|
||||
}
|
||||
|
||||
if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
|
||||
|
@ -2478,7 +2478,7 @@ static void setup_system_wide(int forks)
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_stat(int argc, const char **argv)
|
||||
{
|
||||
const char * const stat_usage[] = {
|
||||
"perf stat [<options>] [<command>]",
|
||||
|
@ -1773,7 +1773,7 @@ static int timechart__io_record(int argc, const char **argv)
|
||||
for (i = 0; i < (unsigned int)argc; i++)
|
||||
*p++ = argv[i];
|
||||
|
||||
return cmd_record(rec_argc, rec_argv, NULL);
|
||||
return cmd_record(rec_argc, rec_argv);
|
||||
}
|
||||
|
||||
|
||||
@ -1864,7 +1864,7 @@ static int timechart__record(struct timechart *tchart, int argc, const char **ar
|
||||
for (j = 0; j < (unsigned int)argc; j++)
|
||||
*p++ = argv[j];
|
||||
|
||||
return cmd_record(rec_argc, rec_argv, NULL);
|
||||
return cmd_record(rec_argc, rec_argv);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1917,8 +1917,7 @@ parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_timechart(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
int cmd_timechart(int argc, const char **argv)
|
||||
{
|
||||
struct timechart tchart = {
|
||||
.tool = {
|
||||
|
@ -1075,7 +1075,7 @@ parse_percent_limit(const struct option *opt, const char *arg,
|
||||
const char top_callchain_help[] = CALLCHAIN_RECORD_HELP CALLCHAIN_REPORT_HELP
|
||||
"\n\t\t\t\tDefault: fp,graph,0.5,caller,function";
|
||||
|
||||
int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_top(int argc, const char **argv)
|
||||
{
|
||||
char errbuf[BUFSIZ];
|
||||
struct perf_top top = {
|
||||
|
@ -1653,15 +1653,17 @@ static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
|
||||
|
||||
ttrace = thread__priv(thread);
|
||||
if (!ttrace)
|
||||
goto out;
|
||||
goto out_put;
|
||||
|
||||
filename_len = strlen(filename);
|
||||
if (filename_len == 0)
|
||||
goto out_put;
|
||||
|
||||
if (ttrace->filename.namelen < filename_len) {
|
||||
char *f = realloc(ttrace->filename.name, filename_len + 1);
|
||||
|
||||
if (f == NULL)
|
||||
goto out;
|
||||
goto out_put;
|
||||
|
||||
ttrace->filename.namelen = filename_len;
|
||||
ttrace->filename.name = f;
|
||||
@ -1671,12 +1673,12 @@ static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
|
||||
ttrace->filename.pending_open = true;
|
||||
|
||||
if (!ttrace->filename.ptr)
|
||||
goto out;
|
||||
goto out_put;
|
||||
|
||||
entry_str_len = strlen(ttrace->entry_str);
|
||||
remaining_space = trace__entry_str_size - entry_str_len - 1; /* \0 */
|
||||
if (remaining_space <= 0)
|
||||
goto out;
|
||||
goto out_put;
|
||||
|
||||
if (filename_len > (size_t)remaining_space) {
|
||||
filename += filename_len - remaining_space;
|
||||
@ -1690,6 +1692,8 @@ static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
|
||||
|
||||
ttrace->filename.ptr = 0;
|
||||
ttrace->filename.entry_str_pos = 0;
|
||||
out_put:
|
||||
thread__put(thread);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
@ -1710,6 +1714,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs
|
||||
|
||||
ttrace->runtime_ms += runtime_ms;
|
||||
trace->runtime_ms += runtime_ms;
|
||||
out_put:
|
||||
thread__put(thread);
|
||||
return 0;
|
||||
|
||||
@ -1720,8 +1725,7 @@ out_dump:
|
||||
(pid_t)perf_evsel__intval(evsel, sample, "pid"),
|
||||
runtime,
|
||||
perf_evsel__intval(evsel, sample, "vruntime"));
|
||||
thread__put(thread);
|
||||
return 0;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
static void bpf_output__printer(enum binary_printer_ops op,
|
||||
@ -1920,7 +1924,7 @@ static int trace__process_sample(struct perf_tool *tool,
|
||||
|
||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||
if (thread && thread__is_filtered(thread))
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
trace__set_base_time(trace, evsel, sample);
|
||||
|
||||
@ -1928,7 +1932,8 @@ static int trace__process_sample(struct perf_tool *tool,
|
||||
++trace->nr_events;
|
||||
handler(trace, evsel, event, sample);
|
||||
}
|
||||
|
||||
out:
|
||||
thread__put(thread);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1988,7 +1993,7 @@ static int trace__record(struct trace *trace, int argc, const char **argv)
|
||||
for (i = 0; i < (unsigned int)argc; i++)
|
||||
rec_argv[j++] = argv[i];
|
||||
|
||||
return cmd_record(j, rec_argv, NULL);
|
||||
return cmd_record(j, rec_argv);
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
|
||||
@ -2786,7 +2791,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_trace(int argc, const char **argv)
|
||||
{
|
||||
const char *trace_usage[] = {
|
||||
"perf trace [<options>] [<command>]",
|
||||
|
@ -2,8 +2,7 @@
|
||||
#include "builtin.h"
|
||||
#include "perf.h"
|
||||
|
||||
int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused,
|
||||
const char *prefix __maybe_unused)
|
||||
int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused)
|
||||
{
|
||||
printf("perf version %s\n", perf_version_string);
|
||||
return 0;
|
||||
|
@ -13,35 +13,35 @@ void prune_packed_objects(int);
|
||||
int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
int check_pager_config(const char *cmd);
|
||||
|
||||
int cmd_annotate(int argc, const char **argv, const char *prefix);
|
||||
int cmd_bench(int argc, const char **argv, const char *prefix);
|
||||
int cmd_buildid_cache(int argc, const char **argv, const char *prefix);
|
||||
int cmd_buildid_list(int argc, const char **argv, const char *prefix);
|
||||
int cmd_config(int argc, const char **argv, const char *prefix);
|
||||
int cmd_c2c(int argc, const char **argv, const char *prefix);
|
||||
int cmd_diff(int argc, const char **argv, const char *prefix);
|
||||
int cmd_evlist(int argc, const char **argv, const char *prefix);
|
||||
int cmd_help(int argc, const char **argv, const char *prefix);
|
||||
int cmd_sched(int argc, const char **argv, const char *prefix);
|
||||
int cmd_kallsyms(int argc, const char **argv, const char *prefix);
|
||||
int cmd_list(int argc, const char **argv, const char *prefix);
|
||||
int cmd_record(int argc, const char **argv, const char *prefix);
|
||||
int cmd_report(int argc, const char **argv, const char *prefix);
|
||||
int cmd_stat(int argc, const char **argv, const char *prefix);
|
||||
int cmd_timechart(int argc, const char **argv, const char *prefix);
|
||||
int cmd_top(int argc, const char **argv, const char *prefix);
|
||||
int cmd_script(int argc, const char **argv, const char *prefix);
|
||||
int cmd_version(int argc, const char **argv, const char *prefix);
|
||||
int cmd_probe(int argc, const char **argv, const char *prefix);
|
||||
int cmd_kmem(int argc, const char **argv, const char *prefix);
|
||||
int cmd_lock(int argc, const char **argv, const char *prefix);
|
||||
int cmd_kvm(int argc, const char **argv, const char *prefix);
|
||||
int cmd_test(int argc, const char **argv, const char *prefix);
|
||||
int cmd_trace(int argc, const char **argv, const char *prefix);
|
||||
int cmd_inject(int argc, const char **argv, const char *prefix);
|
||||
int cmd_mem(int argc, const char **argv, const char *prefix);
|
||||
int cmd_data(int argc, const char **argv, const char *prefix);
|
||||
int cmd_ftrace(int argc, const char **argv, const char *prefix);
|
||||
int cmd_annotate(int argc, const char **argv);
|
||||
int cmd_bench(int argc, const char **argv);
|
||||
int cmd_buildid_cache(int argc, const char **argv);
|
||||
int cmd_buildid_list(int argc, const char **argv);
|
||||
int cmd_config(int argc, const char **argv);
|
||||
int cmd_c2c(int argc, const char **argv);
|
||||
int cmd_diff(int argc, const char **argv);
|
||||
int cmd_evlist(int argc, const char **argv);
|
||||
int cmd_help(int argc, const char **argv);
|
||||
int cmd_sched(int argc, const char **argv);
|
||||
int cmd_kallsyms(int argc, const char **argv);
|
||||
int cmd_list(int argc, const char **argv);
|
||||
int cmd_record(int argc, const char **argv);
|
||||
int cmd_report(int argc, const char **argv);
|
||||
int cmd_stat(int argc, const char **argv);
|
||||
int cmd_timechart(int argc, const char **argv);
|
||||
int cmd_top(int argc, const char **argv);
|
||||
int cmd_script(int argc, const char **argv);
|
||||
int cmd_version(int argc, const char **argv);
|
||||
int cmd_probe(int argc, const char **argv);
|
||||
int cmd_kmem(int argc, const char **argv);
|
||||
int cmd_lock(int argc, const char **argv);
|
||||
int cmd_kvm(int argc, const char **argv);
|
||||
int cmd_test(int argc, const char **argv);
|
||||
int cmd_trace(int argc, const char **argv);
|
||||
int cmd_inject(int argc, const char **argv);
|
||||
int cmd_mem(int argc, const char **argv);
|
||||
int cmd_data(int argc, const char **argv);
|
||||
int cmd_ftrace(int argc, const char **argv);
|
||||
|
||||
int find_scripts(char **scripts_array, char **scripts_path_array);
|
||||
#endif
|
||||
|
@ -34,7 +34,7 @@ const char *input_name;
|
||||
|
||||
struct cmd_struct {
|
||||
const char *cmd;
|
||||
int (*fn)(int, const char **, const char *);
|
||||
int (*fn)(int, const char **);
|
||||
int option;
|
||||
};
|
||||
|
||||
@ -339,13 +339,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
{
|
||||
int status;
|
||||
struct stat st;
|
||||
const char *prefix;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
prefix = NULL;
|
||||
if (p->option & RUN_SETUP)
|
||||
prefix = NULL; /* setup_perf_directory(); */
|
||||
|
||||
if (use_browser == -1)
|
||||
use_browser = check_browser_config(p->cmd);
|
||||
|
||||
@ -356,7 +351,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
commit_pager_choice();
|
||||
|
||||
perf_env__set_cmdline(&perf_env, argc, argv);
|
||||
status = p->fn(argc, argv, prefix);
|
||||
status = p->fn(argc, argv);
|
||||
perf_config__exit();
|
||||
exit_browser(status);
|
||||
perf_env__exit(&perf_env);
|
||||
@ -566,7 +561,7 @@ int main(int argc, const char **argv)
|
||||
#ifdef HAVE_LIBAUDIT_SUPPORT
|
||||
setup_path();
|
||||
argv[0] = "trace";
|
||||
return cmd_trace(argc, argv, NULL);
|
||||
return cmd_trace(argc, argv);
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"trace command not available: missing audit-libs devel package at build time.\n");
|
||||
|
@ -464,7 +464,7 @@ static int perf_test__list(int argc, const char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
int cmd_test(int argc, const char **argv)
|
||||
{
|
||||
const char *test_usage[] = {
|
||||
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
|
||||
|
@ -43,7 +43,7 @@ static char *get_self_path(void)
|
||||
{
|
||||
char *buf = calloc(PATH_MAX, sizeof(char));
|
||||
|
||||
if (buf && readlink("/proc/self/exe", buf, PATH_MAX) < 0) {
|
||||
if (buf && readlink("/proc/self/exe", buf, PATH_MAX - 1) < 0) {
|
||||
pr_debug("Failed to get correct path of perf\n");
|
||||
free(buf);
|
||||
return NULL;
|
||||
|
@ -144,9 +144,60 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
|
||||
cl->unfolded = unfold ? cl->has_children : false;
|
||||
}
|
||||
|
||||
static struct inline_node *inline_node__create(struct map *map, u64 ip)
|
||||
{
|
||||
struct dso *dso;
|
||||
struct inline_node *node;
|
||||
|
||||
if (map == NULL)
|
||||
return NULL;
|
||||
|
||||
dso = map->dso;
|
||||
if (dso == NULL)
|
||||
return NULL;
|
||||
|
||||
if (dso->kernel != DSO_TYPE_USER)
|
||||
return NULL;
|
||||
|
||||
node = dso__parse_addr_inlines(dso,
|
||||
map__rip_2objdump(map, ip));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static int inline__count_rows(struct inline_node *node)
|
||||
{
|
||||
struct inline_list *ilist;
|
||||
int i = 0;
|
||||
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(ilist, &node->val, list) {
|
||||
if ((ilist->filename != NULL) || (ilist->funcname != NULL))
|
||||
i++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int callchain_list__inline_rows(struct callchain_list *chain)
|
||||
{
|
||||
struct inline_node *node;
|
||||
int rows;
|
||||
|
||||
node = inline_node__create(chain->ms.map, chain->ip);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
rows = inline__count_rows(node);
|
||||
inline_node__delete(node);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
|
||||
{
|
||||
int n = 0;
|
||||
int n = 0, inline_rows;
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
|
||||
@ -156,6 +207,13 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
++n;
|
||||
|
||||
if (symbol_conf.inline_name) {
|
||||
inline_rows =
|
||||
callchain_list__inline_rows(chain);
|
||||
n += inline_rows;
|
||||
}
|
||||
|
||||
/* We need this because we may not have children */
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
if (folded_sign == '+')
|
||||
@ -207,7 +265,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
bool unfolded = false;
|
||||
int n = 0;
|
||||
int n = 0, inline_rows;
|
||||
|
||||
if (callchain_param.mode == CHAIN_FLAT)
|
||||
return callchain_node__count_flat_rows(node);
|
||||
@ -216,6 +274,11 @@ static int callchain_node__count_rows(struct callchain_node *node)
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
++n;
|
||||
if (symbol_conf.inline_name) {
|
||||
inline_rows = callchain_list__inline_rows(chain);
|
||||
n += inline_rows;
|
||||
}
|
||||
|
||||
unfolded = chain->unfolded;
|
||||
}
|
||||
|
||||
@ -362,6 +425,19 @@ static void hist_entry__init_have_children(struct hist_entry *he)
|
||||
he->init_have_children = true;
|
||||
}
|
||||
|
||||
static void hist_entry_init_inline_node(struct hist_entry *he)
|
||||
{
|
||||
if (he->inline_node)
|
||||
return;
|
||||
|
||||
he->inline_node = inline_node__create(he->ms.map, he->ip);
|
||||
|
||||
if (he->inline_node == NULL)
|
||||
return;
|
||||
|
||||
he->has_children = true;
|
||||
}
|
||||
|
||||
static bool hist_browser__toggle_fold(struct hist_browser *browser)
|
||||
{
|
||||
struct hist_entry *he = browser->he_selection;
|
||||
@ -393,7 +469,12 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
|
||||
|
||||
if (he->unfolded) {
|
||||
if (he->leaf)
|
||||
he->nr_rows = callchain__count_rows(&he->sorted_chain);
|
||||
if (he->inline_node)
|
||||
he->nr_rows = inline__count_rows(
|
||||
he->inline_node);
|
||||
else
|
||||
he->nr_rows = callchain__count_rows(
|
||||
&he->sorted_chain);
|
||||
else
|
||||
he->nr_rows = hierarchy_count_rows(browser, he, false);
|
||||
|
||||
@ -753,6 +834,71 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u
|
||||
|
||||
#define LEVEL_OFFSET_STEP 3
|
||||
|
||||
static int hist_browser__show_inline(struct hist_browser *browser,
|
||||
struct inline_node *node,
|
||||
unsigned short row,
|
||||
int offset)
|
||||
{
|
||||
struct inline_list *ilist;
|
||||
char buf[1024];
|
||||
int color, width, first_row;
|
||||
|
||||
first_row = row;
|
||||
width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
|
||||
list_for_each_entry(ilist, &node->val, list) {
|
||||
if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
|
||||
color = HE_COLORSET_NORMAL;
|
||||
if (ui_browser__is_current_entry(&browser->b, row))
|
||||
color = HE_COLORSET_SELECTED;
|
||||
|
||||
if (callchain_param.key == CCKEY_ADDRESS ||
|
||||
callchain_param.key == CCKEY_SRCLINE) {
|
||||
if (ilist->filename != NULL)
|
||||
scnprintf(buf, sizeof(buf),
|
||||
"%s:%d (inline)",
|
||||
ilist->filename,
|
||||
ilist->line_nr);
|
||||
else
|
||||
scnprintf(buf, sizeof(buf), "??");
|
||||
} else if (ilist->funcname != NULL)
|
||||
scnprintf(buf, sizeof(buf), "%s (inline)",
|
||||
ilist->funcname);
|
||||
else if (ilist->filename != NULL)
|
||||
scnprintf(buf, sizeof(buf),
|
||||
"%s:%d (inline)",
|
||||
ilist->filename,
|
||||
ilist->line_nr);
|
||||
else
|
||||
scnprintf(buf, sizeof(buf), "??");
|
||||
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
ui_browser__write_nstring(&browser->b, " ",
|
||||
LEVEL_OFFSET_STEP + offset);
|
||||
ui_browser__write_nstring(&browser->b, buf, width);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
static size_t show_inline_list(struct hist_browser *browser, struct map *map,
|
||||
u64 ip, int row, int offset)
|
||||
{
|
||||
struct inline_node *node;
|
||||
int ret;
|
||||
|
||||
node = inline_node__create(map, ip);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
ret = hist_browser__show_inline(browser, node, row, offset);
|
||||
|
||||
inline_node__delete(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hist_browser__show_callchain_list(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
struct callchain_list *chain,
|
||||
@ -764,6 +910,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
|
||||
char bf[1024], *alloc_str;
|
||||
char buf[64], *alloc_str2;
|
||||
const char *str;
|
||||
int inline_rows = 0, ret = 1;
|
||||
|
||||
if (arg->row_offset != 0) {
|
||||
arg->row_offset--;
|
||||
@ -801,10 +948,15 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
|
||||
}
|
||||
|
||||
print(browser, chain, str, offset, row, arg);
|
||||
|
||||
free(alloc_str);
|
||||
free(alloc_str2);
|
||||
return 1;
|
||||
|
||||
if (symbol_conf.inline_name) {
|
||||
inline_rows = show_inline_list(browser, chain->ms.map,
|
||||
chain->ip, row + 1, offset);
|
||||
}
|
||||
|
||||
return ret + inline_rows;
|
||||
}
|
||||
|
||||
static bool check_percent_display(struct rb_node *node, u64 parent_total)
|
||||
@ -1228,6 +1380,12 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
folded_sign = hist_entry__folded(entry);
|
||||
}
|
||||
|
||||
if (symbol_conf.inline_name &&
|
||||
(!entry->has_children)) {
|
||||
hist_entry_init_inline_node(entry);
|
||||
folded_sign = hist_entry__folded(entry);
|
||||
}
|
||||
|
||||
if (row_offset == 0) {
|
||||
struct hpp_arg arg = {
|
||||
.b = &browser->b,
|
||||
@ -1259,7 +1417,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
}
|
||||
|
||||
if (first) {
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (symbol_conf.use_callchain ||
|
||||
symbol_conf.inline_name) {
|
||||
ui_browser__printf(&browser->b, "%c ", folded_sign);
|
||||
width -= 2;
|
||||
}
|
||||
@ -1301,8 +1460,14 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
.is_current_entry = current_entry,
|
||||
};
|
||||
|
||||
printed += hist_browser__show_callchain(browser, entry, 1, row,
|
||||
hist_browser__show_callchain_entry, &arg,
|
||||
if (entry->inline_node)
|
||||
printed += hist_browser__show_inline(browser,
|
||||
entry->inline_node, row, 0);
|
||||
else
|
||||
printed += hist_browser__show_callchain(browser,
|
||||
entry, 1, row,
|
||||
hist_browser__show_callchain_entry,
|
||||
&arg,
|
||||
hist_browser__check_output_full);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,67 @@ static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
|
||||
int depth, int depth_mask, FILE *fp)
|
||||
{
|
||||
struct dso *dso;
|
||||
struct inline_node *node;
|
||||
struct inline_list *ilist;
|
||||
int ret = 0, i;
|
||||
|
||||
if (map == NULL)
|
||||
return 0;
|
||||
|
||||
dso = map->dso;
|
||||
if (dso == NULL)
|
||||
return 0;
|
||||
|
||||
if (dso->kernel != DSO_TYPE_USER)
|
||||
return 0;
|
||||
|
||||
node = dso__parse_addr_inlines(dso,
|
||||
map__rip_2objdump(map, ip));
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(ilist, &node->val, list) {
|
||||
if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
|
||||
for (i = 0; i < depth; i++) {
|
||||
if (depth_mask & (1 << i))
|
||||
ret += fprintf(fp, "|");
|
||||
else
|
||||
ret += fprintf(fp, " ");
|
||||
ret += fprintf(fp, " ");
|
||||
}
|
||||
|
||||
if (callchain_param.key == CCKEY_ADDRESS ||
|
||||
callchain_param.key == CCKEY_SRCLINE) {
|
||||
if (ilist->filename != NULL)
|
||||
ret += fprintf(fp, "%s:%d (inline)",
|
||||
ilist->filename,
|
||||
ilist->line_nr);
|
||||
else
|
||||
ret += fprintf(fp, "??");
|
||||
} else if (ilist->funcname != NULL)
|
||||
ret += fprintf(fp, "%s (inline)",
|
||||
ilist->funcname);
|
||||
else if (ilist->filename != NULL)
|
||||
ret += fprintf(fp, "%s:%d (inline)",
|
||||
ilist->filename,
|
||||
ilist->line_nr);
|
||||
else
|
||||
ret += fprintf(fp, "??");
|
||||
|
||||
ret += fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline_node__delete(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
|
||||
int left_margin)
|
||||
{
|
||||
@ -78,6 +139,10 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
|
||||
fputs(str, fp);
|
||||
fputc('\n', fp);
|
||||
free(alloc_str);
|
||||
|
||||
if (symbol_conf.inline_name)
|
||||
ret += inline__fprintf(chain->ms.map, chain->ip,
|
||||
left_margin, depth, depth_mask, fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -229,6 +294,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
|
||||
if (!i++ && field_order == NULL &&
|
||||
sort_order && !prefixcmp(sort_order, "sym"))
|
||||
continue;
|
||||
|
||||
if (!printed) {
|
||||
ret += callchain__fprintf_left_margin(fp, left_margin);
|
||||
ret += fprintf(fp, "|\n");
|
||||
@ -251,6 +317,13 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
|
||||
|
||||
if (++entries_printed == callchain_param.print_limit)
|
||||
break;
|
||||
|
||||
if (symbol_conf.inline_name)
|
||||
ret += inline__fprintf(chain->ms.map,
|
||||
chain->ip,
|
||||
left_margin,
|
||||
0, 0,
|
||||
fp);
|
||||
}
|
||||
root = &cnode->rb_root;
|
||||
}
|
||||
@ -529,6 +602,8 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
bool use_callchain)
|
||||
{
|
||||
int ret;
|
||||
int callchain_ret = 0;
|
||||
int inline_ret = 0;
|
||||
struct perf_hpp hpp = {
|
||||
.buf = bf,
|
||||
.size = size,
|
||||
@ -547,7 +622,16 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
ret = fprintf(fp, "%s\n", bf);
|
||||
|
||||
if (use_callchain)
|
||||
ret += hist_entry_callchain__fprintf(he, total_period, 0, fp);
|
||||
callchain_ret = hist_entry_callchain__fprintf(he, total_period,
|
||||
0, fp);
|
||||
|
||||
if (callchain_ret == 0 && symbol_conf.inline_name) {
|
||||
inline_ret = inline__fprintf(he->ms.map, he->ip, 0, 0, 0, fp);
|
||||
ret += inline_ret;
|
||||
if (inline_ret > 0)
|
||||
ret += fprintf(fp, "\n");
|
||||
} else
|
||||
ret += callchain_ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1307,6 +1307,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
|
||||
{
|
||||
char linkname[PATH_MAX];
|
||||
char *build_id_filename;
|
||||
char *build_id_path = NULL;
|
||||
|
||||
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
|
||||
!dso__is_kcore(dso))
|
||||
@ -1322,8 +1323,14 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
build_id_path = strdup(filename);
|
||||
if (!build_id_path)
|
||||
return -1;
|
||||
|
||||
dirname(build_id_path);
|
||||
|
||||
if (dso__is_kcore(dso) ||
|
||||
readlink(filename, linkname, sizeof(linkname)) < 0 ||
|
||||
readlink(build_id_path, linkname, sizeof(linkname)) < 0 ||
|
||||
strstr(linkname, DSO__NAME_KALLSYMS) ||
|
||||
access(filename, R_OK)) {
|
||||
fallback:
|
||||
@ -1335,6 +1342,7 @@ fallback:
|
||||
__symbol__join_symfs(filename, filename_size, dso->long_name);
|
||||
}
|
||||
|
||||
free(build_id_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1663,18 +1671,23 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
||||
src_line->nr_pcnt = nr_pcnt;
|
||||
|
||||
for (k = 0; k < nr_pcnt; k++) {
|
||||
h = annotation__histogram(notes, evidx + k);
|
||||
src_line->samples[k].percent = 100.0 * h->addr[i] / h->sum;
|
||||
double percent = 0.0;
|
||||
|
||||
if (src_line->samples[k].percent > percent_max)
|
||||
percent_max = src_line->samples[k].percent;
|
||||
h = annotation__histogram(notes, evidx + k);
|
||||
if (h->sum)
|
||||
percent = 100.0 * h->addr[i] / h->sum;
|
||||
|
||||
if (percent > percent_max)
|
||||
percent_max = percent;
|
||||
src_line->samples[k].percent = percent;
|
||||
}
|
||||
|
||||
if (percent_max <= 0.5)
|
||||
goto next;
|
||||
|
||||
offset = start + i;
|
||||
src_line->path = get_srcline(map->dso, offset, NULL, false);
|
||||
src_line->path = get_srcline(map->dso, offset, NULL,
|
||||
false, true);
|
||||
insert_source_line(&tmp_root, src_line);
|
||||
|
||||
next:
|
||||
|
@ -1826,7 +1826,7 @@ static int addr_filter__resolve_kernel_syms(struct addr_filter *filt)
|
||||
filt->addr = start;
|
||||
if (filt->range && !filt->size && !filt->sym_to) {
|
||||
filt->size = size;
|
||||
no_size = !!size;
|
||||
no_size = !size;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1840,7 +1840,7 @@ static int addr_filter__resolve_kernel_syms(struct addr_filter *filt)
|
||||
if (err)
|
||||
return err;
|
||||
filt->size = start + size - filt->addr;
|
||||
no_size = !!size;
|
||||
no_size = !size;
|
||||
}
|
||||
|
||||
/* The very last symbol in kallsyms does not imply a particular size */
|
||||
|
@ -182,13 +182,17 @@ char *build_id_cache__origname(const char *sbuild_id)
|
||||
char buf[PATH_MAX];
|
||||
char *ret = NULL, *p;
|
||||
size_t offs = 5; /* == strlen("../..") */
|
||||
ssize_t len;
|
||||
|
||||
linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
|
||||
if (!linkname)
|
||||
return NULL;
|
||||
|
||||
if (readlink(linkname, buf, PATH_MAX) < 0)
|
||||
len = readlink(linkname, buf, sizeof(buf) - 1);
|
||||
if (len <= 0)
|
||||
goto out;
|
||||
buf[len] = '\0';
|
||||
|
||||
/* The link should be "../..<origpath>/<sbuild_id>" */
|
||||
p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */
|
||||
if (p && (p > buf + offs)) {
|
||||
@ -690,7 +694,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
|
||||
err = 0;
|
||||
|
||||
/* Update SDT cache : error is just warned */
|
||||
if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
|
||||
if (realname && build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
|
||||
pr_debug4("Failed to update/scan SDT cache for %s\n", realname);
|
||||
|
||||
out_free:
|
||||
|
@ -80,6 +80,10 @@ static int parse_callchain_sort_key(const char *value)
|
||||
callchain_param.key = CCKEY_ADDRESS;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "srcline", strlen(value))) {
|
||||
callchain_param.key = CCKEY_SRCLINE;
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(value, "branch", strlen(value))) {
|
||||
callchain_param.branch_callstack = 1;
|
||||
return 0;
|
||||
@ -510,14 +514,51 @@ enum match_result {
|
||||
MATCH_GT,
|
||||
};
|
||||
|
||||
static enum match_result match_chain_srcline(struct callchain_cursor_node *node,
|
||||
struct callchain_list *cnode)
|
||||
{
|
||||
char *left = get_srcline(cnode->ms.map->dso,
|
||||
map__rip_2objdump(cnode->ms.map, cnode->ip),
|
||||
cnode->ms.sym, true, false);
|
||||
char *right = get_srcline(node->map->dso,
|
||||
map__rip_2objdump(node->map, node->ip),
|
||||
node->sym, true, false);
|
||||
enum match_result ret = MATCH_EQ;
|
||||
int cmp;
|
||||
|
||||
if (left && right)
|
||||
cmp = strcmp(left, right);
|
||||
else if (!left && right)
|
||||
cmp = 1;
|
||||
else if (left && !right)
|
||||
cmp = -1;
|
||||
else if (cnode->ip == node->ip)
|
||||
cmp = 0;
|
||||
else
|
||||
cmp = (cnode->ip < node->ip) ? -1 : 1;
|
||||
|
||||
if (cmp != 0)
|
||||
ret = cmp < 0 ? MATCH_LT : MATCH_GT;
|
||||
|
||||
free_srcline(left);
|
||||
free_srcline(right);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum match_result match_chain(struct callchain_cursor_node *node,
|
||||
struct callchain_list *cnode)
|
||||
{
|
||||
struct symbol *sym = node->sym;
|
||||
u64 left, right;
|
||||
|
||||
if (cnode->ms.sym && sym &&
|
||||
callchain_param.key == CCKEY_FUNCTION) {
|
||||
if (callchain_param.key == CCKEY_SRCLINE) {
|
||||
enum match_result match = match_chain_srcline(node, cnode);
|
||||
|
||||
if (match != MATCH_ERROR)
|
||||
return match;
|
||||
}
|
||||
|
||||
if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) {
|
||||
left = cnode->ms.sym->start;
|
||||
right = sym->start;
|
||||
} else {
|
||||
@ -911,15 +952,16 @@ out:
|
||||
char *callchain_list__sym_name(struct callchain_list *cl,
|
||||
char *bf, size_t bfsize, bool show_dso)
|
||||
{
|
||||
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
|
||||
bool show_srcline = show_addr || callchain_param.key == CCKEY_SRCLINE;
|
||||
int printed;
|
||||
|
||||
if (cl->ms.sym) {
|
||||
if (callchain_param.key == CCKEY_ADDRESS &&
|
||||
cl->ms.map && !cl->srcline)
|
||||
if (show_srcline && cl->ms.map && !cl->srcline)
|
||||
cl->srcline = get_srcline(cl->ms.map->dso,
|
||||
map__rip_2objdump(cl->ms.map,
|
||||
cl->ip),
|
||||
cl->ms.sym, false);
|
||||
cl->ms.sym, false, show_addr);
|
||||
if (cl->srcline)
|
||||
printed = scnprintf(bf, bfsize, "%s %s",
|
||||
cl->ms.sym->name, cl->srcline);
|
||||
|
@ -77,7 +77,8 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
|
||||
|
||||
enum chain_key {
|
||||
CCKEY_FUNCTION,
|
||||
CCKEY_ADDRESS
|
||||
CCKEY_ADDRESS,
|
||||
CCKEY_SRCLINE
|
||||
};
|
||||
|
||||
enum chain_value {
|
||||
|
@ -370,15 +370,11 @@ static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
|
||||
struct perf_evlist *evlist __maybe_unused)
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
char proc[32];
|
||||
u32 n;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* actual atual path to perf binary
|
||||
*/
|
||||
sprintf(proc, "/proc/%d/exe", getpid());
|
||||
ret = readlink(proc, buf, sizeof(buf));
|
||||
/* actual path to perf binary */
|
||||
ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
|
||||
if (ret <= 0)
|
||||
return -1;
|
||||
|
||||
|
@ -1136,6 +1136,11 @@ void hist_entry__delete(struct hist_entry *he)
|
||||
zfree(&he->mem_info);
|
||||
}
|
||||
|
||||
if (he->inline_node) {
|
||||
inline_node__delete(he->inline_node);
|
||||
he->inline_node = NULL;
|
||||
}
|
||||
|
||||
zfree(&he->stat_acc);
|
||||
free_srcline(he->srcline);
|
||||
if (he->srcfile && he->srcfile[0])
|
||||
|
@ -405,7 +405,8 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
|
||||
|
||||
if (map && map->dso) {
|
||||
srcline = get_srcline(map->dso,
|
||||
map__rip_2objdump(map, addr), NULL, true);
|
||||
map__rip_2objdump(map, addr), NULL,
|
||||
true, true);
|
||||
if (srcline != SRCLINE_UNKNOWN)
|
||||
ret = fprintf(fp, "%s%s", prefix, srcline);
|
||||
free_srcline(srcline);
|
||||
|
@ -323,7 +323,7 @@ char *hist_entry__get_srcline(struct hist_entry *he)
|
||||
return SRCLINE_UNKNOWN;
|
||||
|
||||
return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
|
||||
he->ms.sym, true);
|
||||
he->ms.sym, true, true);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
@ -366,7 +366,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
left->branch_info->srcline_from = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
left->branch_info->from.al_addr),
|
||||
left->branch_info->from.sym, true);
|
||||
left->branch_info->from.sym,
|
||||
true, true);
|
||||
}
|
||||
if (!right->branch_info->srcline_from) {
|
||||
struct map *map = right->branch_info->from.map;
|
||||
@ -376,7 +377,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
right->branch_info->srcline_from = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
right->branch_info->from.al_addr),
|
||||
right->branch_info->from.sym, true);
|
||||
right->branch_info->from.sym,
|
||||
true, true);
|
||||
}
|
||||
return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
|
||||
}
|
||||
@ -407,7 +409,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
left->branch_info->srcline_to = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
left->branch_info->to.al_addr),
|
||||
left->branch_info->from.sym, true);
|
||||
left->branch_info->from.sym,
|
||||
true, true);
|
||||
}
|
||||
if (!right->branch_info->srcline_to) {
|
||||
struct map *map = right->branch_info->to.map;
|
||||
@ -417,7 +420,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
right->branch_info->srcline_to = get_srcline(map->dso,
|
||||
map__rip_2objdump(map,
|
||||
right->branch_info->to.al_addr),
|
||||
right->branch_info->to.sym, true);
|
||||
right->branch_info->to.sym,
|
||||
true, true);
|
||||
}
|
||||
return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
|
||||
}
|
||||
@ -448,7 +452,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
|
||||
return no_srcfile;
|
||||
|
||||
sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
|
||||
e->ms.sym, false, true);
|
||||
e->ms.sym, false, true, true);
|
||||
if (!strcmp(sf, SRCLINE_UNKNOWN))
|
||||
return no_srcfile;
|
||||
p = strchr(sf, ':');
|
||||
|
@ -128,6 +128,7 @@ struct hist_entry {
|
||||
};
|
||||
char *srcline;
|
||||
char *srcfile;
|
||||
struct inline_node *inline_node;
|
||||
struct symbol *parent;
|
||||
struct branch_info *branch_info;
|
||||
struct hists *hists;
|
||||
|
@ -7,11 +7,58 @@
|
||||
#include "util/dso.h"
|
||||
#include "util/util.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/callchain.h"
|
||||
|
||||
#include "symbol.h"
|
||||
|
||||
bool srcline_full_filename;
|
||||
|
||||
static const char *dso__name(struct dso *dso)
|
||||
{
|
||||
const char *dso_name;
|
||||
|
||||
if (dso->symsrc_filename)
|
||||
dso_name = dso->symsrc_filename;
|
||||
else
|
||||
dso_name = dso->long_name;
|
||||
|
||||
if (dso_name[0] == '[')
|
||||
return NULL;
|
||||
|
||||
if (!strncmp(dso_name, "/tmp/perf-", 10))
|
||||
return NULL;
|
||||
|
||||
return dso_name;
|
||||
}
|
||||
|
||||
static int inline_list__append(char *filename, char *funcname, int line_nr,
|
||||
struct inline_node *node, struct dso *dso)
|
||||
{
|
||||
struct inline_list *ilist;
|
||||
char *demangled;
|
||||
|
||||
ilist = zalloc(sizeof(*ilist));
|
||||
if (ilist == NULL)
|
||||
return -1;
|
||||
|
||||
ilist->filename = filename;
|
||||
ilist->line_nr = line_nr;
|
||||
|
||||
if (dso != NULL) {
|
||||
demangled = dso__demangle_sym(dso, 0, funcname);
|
||||
if (demangled == NULL) {
|
||||
ilist->funcname = funcname;
|
||||
} else {
|
||||
ilist->funcname = demangled;
|
||||
free(funcname);
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&ilist->list, &node->val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBBFD_SUPPORT
|
||||
|
||||
/*
|
||||
@ -151,9 +198,17 @@ static void addr2line_cleanup(struct a2l_data *a2l)
|
||||
|
||||
#define MAX_INLINE_NEST 1024
|
||||
|
||||
static void inline_list__reverse(struct inline_node *node)
|
||||
{
|
||||
struct inline_list *ilist, *n;
|
||||
|
||||
list_for_each_entry_safe_reverse(ilist, n, &node->val, list)
|
||||
list_move_tail(&ilist->list, &node->val);
|
||||
}
|
||||
|
||||
static int addr2line(const char *dso_name, u64 addr,
|
||||
char **file, unsigned int *line, struct dso *dso,
|
||||
bool unwind_inlines)
|
||||
bool unwind_inlines, struct inline_node *node)
|
||||
{
|
||||
int ret = 0;
|
||||
struct a2l_data *a2l = dso->a2l;
|
||||
@ -178,8 +233,21 @@ static int addr2line(const char *dso_name, u64 addr,
|
||||
|
||||
while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
|
||||
&a2l->funcname, &a2l->line) &&
|
||||
cnt++ < MAX_INLINE_NEST)
|
||||
;
|
||||
cnt++ < MAX_INLINE_NEST) {
|
||||
|
||||
if (node != NULL) {
|
||||
if (inline_list__append(strdup(a2l->filename),
|
||||
strdup(a2l->funcname),
|
||||
a2l->line, node,
|
||||
dso) != 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((node != NULL) &&
|
||||
(callchain_param.order != ORDER_CALLEE)) {
|
||||
inline_list__reverse(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (a2l->found && a2l->filename) {
|
||||
@ -205,18 +273,68 @@ void dso__free_a2l(struct dso *dso)
|
||||
dso->a2l = NULL;
|
||||
}
|
||||
|
||||
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
||||
struct dso *dso)
|
||||
{
|
||||
char *file = NULL;
|
||||
unsigned int line = 0;
|
||||
struct inline_node *node;
|
||||
|
||||
node = zalloc(sizeof(*node));
|
||||
if (node == NULL) {
|
||||
perror("not enough memory for the inline node");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->val);
|
||||
node->addr = addr;
|
||||
|
||||
if (!addr2line(dso_name, addr, &file, &line, dso, TRUE, node))
|
||||
goto out_free_inline_node;
|
||||
|
||||
if (list_empty(&node->val))
|
||||
goto out_free_inline_node;
|
||||
|
||||
return node;
|
||||
|
||||
out_free_inline_node:
|
||||
inline_node__delete(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else /* HAVE_LIBBFD_SUPPORT */
|
||||
|
||||
static int filename_split(char *filename, unsigned int *line_nr)
|
||||
{
|
||||
char *sep;
|
||||
|
||||
sep = strchr(filename, '\n');
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
|
||||
if (!strcmp(filename, "??:0"))
|
||||
return 0;
|
||||
|
||||
sep = strchr(filename, ':');
|
||||
if (sep) {
|
||||
*sep++ = '\0';
|
||||
*line_nr = strtoul(sep, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int addr2line(const char *dso_name, u64 addr,
|
||||
char **file, unsigned int *line_nr,
|
||||
struct dso *dso __maybe_unused,
|
||||
bool unwind_inlines __maybe_unused)
|
||||
bool unwind_inlines __maybe_unused,
|
||||
struct inline_node *node __maybe_unused)
|
||||
{
|
||||
FILE *fp;
|
||||
char cmd[PATH_MAX];
|
||||
char *filename = NULL;
|
||||
size_t len;
|
||||
char *sep;
|
||||
int ret = 0;
|
||||
|
||||
scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64,
|
||||
@ -233,23 +351,14 @@ static int addr2line(const char *dso_name, u64 addr,
|
||||
goto out;
|
||||
}
|
||||
|
||||
sep = strchr(filename, '\n');
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
|
||||
if (!strcmp(filename, "??:0")) {
|
||||
pr_debug("no debugging info in %s\n", dso_name);
|
||||
ret = filename_split(filename, line_nr);
|
||||
if (ret != 1) {
|
||||
free(filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sep = strchr(filename, ':');
|
||||
if (sep) {
|
||||
*sep++ = '\0';
|
||||
*file = filename;
|
||||
*line_nr = strtoul(sep, NULL, 0);
|
||||
ret = 1;
|
||||
}
|
||||
*file = filename;
|
||||
|
||||
out:
|
||||
pclose(fp);
|
||||
return ret;
|
||||
@ -259,6 +368,58 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
||||
struct dso *dso __maybe_unused)
|
||||
{
|
||||
FILE *fp;
|
||||
char cmd[PATH_MAX];
|
||||
struct inline_node *node;
|
||||
char *filename = NULL;
|
||||
size_t len;
|
||||
unsigned int line_nr = 0;
|
||||
|
||||
scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i %016"PRIx64,
|
||||
dso_name, addr);
|
||||
|
||||
fp = popen(cmd, "r");
|
||||
if (fp == NULL) {
|
||||
pr_err("popen failed for %s\n", dso_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = zalloc(sizeof(*node));
|
||||
if (node == NULL) {
|
||||
perror("not enough memory for the inline node");
|
||||
goto out;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->val);
|
||||
node->addr = addr;
|
||||
|
||||
while (getline(&filename, &len, fp) != -1) {
|
||||
if (filename_split(filename, &line_nr) != 1) {
|
||||
free(filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (inline_list__append(filename, NULL, line_nr, node,
|
||||
NULL) != 0)
|
||||
goto out;
|
||||
|
||||
filename = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
pclose(fp);
|
||||
|
||||
if (list_empty(&node->val)) {
|
||||
inline_node__delete(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBBFD_SUPPORT */
|
||||
|
||||
/*
|
||||
@ -268,7 +429,7 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
|
||||
#define A2L_FAIL_LIMIT 123
|
||||
|
||||
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
bool show_sym, bool unwind_inlines)
|
||||
bool show_sym, bool show_addr, bool unwind_inlines)
|
||||
{
|
||||
char *file = NULL;
|
||||
unsigned line = 0;
|
||||
@ -278,18 +439,11 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
if (!dso->has_srcline)
|
||||
goto out;
|
||||
|
||||
if (dso->symsrc_filename)
|
||||
dso_name = dso->symsrc_filename;
|
||||
else
|
||||
dso_name = dso->long_name;
|
||||
|
||||
if (dso_name[0] == '[')
|
||||
dso_name = dso__name(dso);
|
||||
if (dso_name == NULL)
|
||||
goto out;
|
||||
|
||||
if (!strncmp(dso_name, "/tmp/perf-", 10))
|
||||
goto out;
|
||||
|
||||
if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines))
|
||||
if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL))
|
||||
goto out;
|
||||
|
||||
if (asprintf(&srcline, "%s:%u",
|
||||
@ -309,6 +463,11 @@ out:
|
||||
dso->has_srcline = 0;
|
||||
dso__free_a2l(dso);
|
||||
}
|
||||
|
||||
if (!show_addr)
|
||||
return (show_sym && sym) ?
|
||||
strndup(sym->name, sym->namelen) : NULL;
|
||||
|
||||
if (sym) {
|
||||
if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
|
||||
addr - sym->start) < 0)
|
||||
@ -325,7 +484,32 @@ void free_srcline(char *srcline)
|
||||
}
|
||||
|
||||
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
bool show_sym)
|
||||
bool show_sym, bool show_addr)
|
||||
{
|
||||
return __get_srcline(dso, addr, sym, show_sym, false);
|
||||
return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
|
||||
}
|
||||
|
||||
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
|
||||
{
|
||||
const char *dso_name;
|
||||
|
||||
dso_name = dso__name(dso);
|
||||
if (dso_name == NULL)
|
||||
return NULL;
|
||||
|
||||
return addr2inlines(dso_name, addr, dso);
|
||||
}
|
||||
|
||||
void inline_node__delete(struct inline_node *node)
|
||||
{
|
||||
struct inline_list *ilist, *tmp;
|
||||
|
||||
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
|
||||
list_del_init(&ilist->list);
|
||||
zfree(&ilist->filename);
|
||||
zfree(&ilist->funcname);
|
||||
free(ilist);
|
||||
}
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
@ -390,6 +390,11 @@ out_elf_end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name)
|
||||
{
|
||||
return demangle_sym(dso, kmodule, elf_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Align offset to 4 bytes as needed for note name and descriptor data.
|
||||
*/
|
||||
|
@ -373,3 +373,10 @@ int kcore_copy(const char *from_dir __maybe_unused,
|
||||
void symbol__elf_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
char *dso__demangle_sym(struct dso *dso __maybe_unused,
|
||||
int kmodule __maybe_unused,
|
||||
char *elf_name __maybe_unused)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -118,7 +118,8 @@ struct symbol_conf {
|
||||
show_ref_callgraph,
|
||||
hide_unresolved,
|
||||
raw_trace,
|
||||
report_hierarchy;
|
||||
report_hierarchy,
|
||||
inline_name;
|
||||
const char *vmlinux_name,
|
||||
*kallsyms_name,
|
||||
*source_prefix,
|
||||
@ -305,6 +306,8 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
||||
int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss,
|
||||
struct map *map);
|
||||
|
||||
char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name);
|
||||
|
||||
void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel);
|
||||
void symbols__insert(struct rb_root *symbols, struct symbol *sym);
|
||||
void symbols__fixup_duplicate(struct rb_root *symbols);
|
||||
|
@ -192,7 +192,7 @@ static int read_ftrace_printk(struct pevent *pevent)
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
buf = malloc(size);
|
||||
buf = malloc(size + 1);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
@ -201,6 +201,8 @@ static int read_ftrace_printk(struct pevent *pevent)
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[size] = '\0';
|
||||
|
||||
parse_ftrace_printk(pevent, buf, size);
|
||||
|
||||
free(buf);
|
||||
|
@ -287,9 +287,9 @@ struct symbol;
|
||||
|
||||
extern bool srcline_full_filename;
|
||||
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
bool show_sym);
|
||||
bool show_sym, bool show_addr);
|
||||
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
bool show_sym, bool unwind_inlines);
|
||||
bool show_sym, bool show_addr, bool unwind_inlines);
|
||||
void free_srcline(char *srcline);
|
||||
|
||||
int perf_event_paranoid(void);
|
||||
@ -364,4 +364,20 @@ int is_printable_array(char *p, unsigned int len);
|
||||
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
|
||||
|
||||
int unit_number__scnprintf(char *buf, size_t size, u64 n);
|
||||
|
||||
struct inline_list {
|
||||
char *filename;
|
||||
char *funcname;
|
||||
unsigned int line_nr;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct inline_node {
|
||||
u64 addr;
|
||||
struct list_head val;
|
||||
};
|
||||
|
||||
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr);
|
||||
void inline_node__delete(struct inline_node *node);
|
||||
|
||||
#endif /* GIT_COMPAT_UTIL_H */
|
||||
|
Loading…
Reference in New Issue
Block a user