Commit e2cf00c2 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-4.11-20170126' of...

Merge tag 'perf-core-for-mingo-4.11-20170126' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull the latest perf/core updates from Arnaldo Carvalho de Melo:

New features:

 - Introduce 'perf ftrace' a perf front end to the kernel's ftrace
   function and function_graph tracer, defaulting to the "function_graph"
   tracer, more work will be done in reviving this effort, forward porting
   it from its initial patch submission (Namhyung Kim)

 - Add 'e' and 'c' hotkeys to expand/collapse call chains for a single
   hist entry in the 'perf report' and 'perf top' TUI (Jiri Olsa)

Fixes:

 - Fix wrong register name for arm64, used in 'perf probe' (He Kuang)

 - Fix map offsets in relocation in libbpf (Joe Stringer)

 - Fix looking up dwarf unwind stack info (Matija Glavinic Pecotic)

Infrastructure changes:

 - libbpf prog functions sync with what is exported via uapi (Joe Stringer)

Trivial changes:

 - Remove unnecessary checks and assignments in 'perf probe's
   try_to_find_absolute_address() (Markus Elfring)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 47cd95a6 ec347870
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <linux/err.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -779,7 +780,7 @@ static int ...@@ -779,7 +780,7 @@ static int
bpf_program__collect_reloc(struct bpf_program *prog, bpf_program__collect_reloc(struct bpf_program *prog,
size_t nr_maps, GElf_Shdr *shdr, size_t nr_maps, GElf_Shdr *shdr,
Elf_Data *data, Elf_Data *symbols, Elf_Data *data, Elf_Data *symbols,
int maps_shndx) int maps_shndx, struct bpf_map *maps)
{ {
int i, nrels; int i, nrels;
...@@ -829,7 +830,15 @@ bpf_program__collect_reloc(struct bpf_program *prog, ...@@ -829,7 +830,15 @@ bpf_program__collect_reloc(struct bpf_program *prog,
return -LIBBPF_ERRNO__RELOC; return -LIBBPF_ERRNO__RELOC;
} }
map_idx = sym.st_value / sizeof(struct bpf_map_def); /* TODO: 'maps' is sorted. We can use bsearch to make it faster. */
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
if (maps[map_idx].offset == sym.st_value) {
pr_debug("relocation: find map %zd (%s) for insn %u\n",
map_idx, maps[map_idx].name, insn_idx);
break;
}
}
if (map_idx >= nr_maps) { if (map_idx >= nr_maps) {
pr_warning("bpf relocation: map_idx %d large than %d\n", pr_warning("bpf relocation: map_idx %d large than %d\n",
(int)map_idx, (int)nr_maps - 1); (int)map_idx, (int)nr_maps - 1);
...@@ -953,7 +962,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) ...@@ -953,7 +962,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
err = bpf_program__collect_reloc(prog, nr_maps, err = bpf_program__collect_reloc(prog, nr_maps,
shdr, data, shdr, data,
obj->efile.symbols, obj->efile.symbols,
obj->efile.maps_shndx); obj->efile.maps_shndx,
obj->maps);
if (err) if (err)
return err; return err;
} }
...@@ -1419,37 +1429,33 @@ static void bpf_program__set_type(struct bpf_program *prog, ...@@ -1419,37 +1429,33 @@ static void bpf_program__set_type(struct bpf_program *prog,
prog->type = type; prog->type = type;
} }
int bpf_program__set_tracepoint(struct bpf_program *prog)
{
if (!prog)
return -EINVAL;
bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
return 0;
}
int bpf_program__set_kprobe(struct bpf_program *prog)
{
if (!prog)
return -EINVAL;
bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE);
return 0;
}
static bool bpf_program__is_type(struct bpf_program *prog, static bool bpf_program__is_type(struct bpf_program *prog,
enum bpf_prog_type type) enum bpf_prog_type type)
{ {
return prog ? (prog->type == type) : false; return prog ? (prog->type == type) : false;
} }
bool bpf_program__is_tracepoint(struct bpf_program *prog) #define BPF_PROG_TYPE_FNS(NAME, TYPE) \
{ int bpf_program__set_##NAME(struct bpf_program *prog) \
return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT); { \
} if (!prog) \
return -EINVAL; \
bool bpf_program__is_kprobe(struct bpf_program *prog) bpf_program__set_type(prog, TYPE); \
{ return 0; \
return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE); } \
} \
bool bpf_program__is_##NAME(struct bpf_program *prog) \
{ \
return bpf_program__is_type(prog, TYPE); \
} \
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
int bpf_map__fd(struct bpf_map *map) int bpf_map__fd(struct bpf_map *map)
{ {
...@@ -1537,3 +1543,10 @@ bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset) ...@@ -1537,3 +1543,10 @@ bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
} }
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
long libbpf_get_error(const void *ptr)
{
if (IS_ERR(ptr))
return PTR_ERR(ptr);
return 0;
}
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#define __BPF_LIBBPF_H #define __BPF_LIBBPF_H
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <linux/err.h>
#include <sys/types.h> // for size_t #include <sys/types.h> // for size_t
enum libbpf_errno { enum libbpf_errno {
...@@ -174,11 +174,21 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n); ...@@ -174,11 +174,21 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n);
/* /*
* Adjust type of bpf program. Default is kprobe. * Adjust type of bpf program. Default is kprobe.
*/ */
int bpf_program__set_socket_filter(struct bpf_program *prog);
int bpf_program__set_tracepoint(struct bpf_program *prog); int bpf_program__set_tracepoint(struct bpf_program *prog);
int bpf_program__set_kprobe(struct bpf_program *prog); int bpf_program__set_kprobe(struct bpf_program *prog);
int bpf_program__set_sched_cls(struct bpf_program *prog);
int bpf_program__set_sched_act(struct bpf_program *prog);
int bpf_program__set_xdp(struct bpf_program *prog);
int bpf_program__set_perf_event(struct bpf_program *prog);
bool bpf_program__is_socket_filter(struct bpf_program *prog);
bool bpf_program__is_tracepoint(struct bpf_program *prog); bool bpf_program__is_tracepoint(struct bpf_program *prog);
bool bpf_program__is_kprobe(struct bpf_program *prog); bool bpf_program__is_kprobe(struct bpf_program *prog);
bool bpf_program__is_sched_cls(struct bpf_program *prog);
bool bpf_program__is_sched_act(struct bpf_program *prog);
bool bpf_program__is_xdp(struct bpf_program *prog);
bool bpf_program__is_perf_event(struct bpf_program *prog);
/* /*
* We don't need __attribute__((packed)) now since it is * We don't need __attribute__((packed)) now since it is
...@@ -224,4 +234,6 @@ int bpf_map__set_priv(struct bpf_map *map, void *priv, ...@@ -224,4 +234,6 @@ int bpf_map__set_priv(struct bpf_map *map, void *priv,
bpf_map_clear_priv_t clear_priv); bpf_map_clear_priv_t clear_priv);
void *bpf_map__priv(struct bpf_map *map); void *bpf_map__priv(struct bpf_map *map);
long libbpf_get_error(const void *ptr);
#endif #endif
...@@ -3,6 +3,7 @@ perf-y += builtin-annotate.o ...@@ -3,6 +3,7 @@ perf-y += builtin-annotate.o
perf-y += builtin-config.o perf-y += builtin-config.o
perf-y += builtin-diff.o perf-y += builtin-diff.o
perf-y += builtin-evlist.o perf-y += builtin-evlist.o
perf-y += builtin-ftrace.o
perf-y += builtin-help.o perf-y += builtin-help.o
perf-y += builtin-sched.o perf-y += builtin-sched.o
perf-y += builtin-buildid-list.o perf-y += builtin-buildid-list.o
......
...@@ -248,7 +248,7 @@ output fields set for caheline offsets output: ...@@ -248,7 +248,7 @@ output fields set for caheline offsets output:
Code address, Code symbol, Shared Object, Source line Code address, Code symbol, Shared Object, Source line
dso - coalesced by shared object dso - coalesced by shared object
By default the coalescing is setup with 'pid,tid,iaddr'. By default the coalescing is setup with 'pid,iaddr'.
STDIO OUTPUT STDIO OUTPUT
------------ ------------
......
perf-ftrace(1)
=============
NAME
----
perf-ftrace - simple wrapper for kernel's ftrace functionality
SYNOPSIS
--------
[verse]
'perf ftrace' <command>
DESCRIPTION
-----------
The 'perf ftrace' command is a simple wrapper of kernel's ftrace
functionality. It only supports single thread tracing currently and
just reads trace_pipe in text and then write it to stdout.
The following options apply to perf ftrace.
OPTIONS
-------
-t::
--tracer=::
Tracer to use: function_graph or function.
-v::
--verbose=::
Verbosity level.
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-trace[1]
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
/* This is included in perf/util/dwarf-regs.c */ /* This is included in perf/util/dwarf-regs.c */
static const char * const aarch64_regstr_tbl[] = { static const char * const aarch64_regstr_tbl[] = {
"%r0", "%r1", "%r2", "%r3", "%r4", "%x0", "%x1", "%x2", "%x3", "%x4",
"%r5", "%r6", "%r7", "%r8", "%r9", "%x5", "%x6", "%x7", "%x8", "%x9",
"%r10", "%r11", "%r12", "%r13", "%r14", "%x10", "%x11", "%x12", "%x13", "%x14",
"%r15", "%r16", "%r17", "%r18", "%r19", "%x15", "%x16", "%x17", "%x18", "%x19",
"%r20", "%r21", "%r22", "%r23", "%r24", "%x20", "%x21", "%x22", "%x23", "%x24",
"%r25", "%r26", "%r27", "%r28", "%r29", "%x25", "%x26", "%x27", "%x28", "%x29",
"%lr", "%sp", "%lr", "%sp",
}; };
#endif #endif
...@@ -58,7 +58,7 @@ struct c2c_hist_entry { ...@@ -58,7 +58,7 @@ struct c2c_hist_entry {
struct hist_entry he; struct hist_entry he;
}; };
static char const *coalesce_default = "pid,tid,iaddr"; static char const *coalesce_default = "pid,iaddr";
struct perf_c2c { struct perf_c2c {
struct perf_tool tool; struct perf_tool tool;
...@@ -2476,6 +2476,7 @@ static int build_cl_output(char *cl_sort, bool no_source) ...@@ -2476,6 +2476,7 @@ static int build_cl_output(char *cl_sort, bool no_source)
"mean_rmt," "mean_rmt,"
"mean_lcl," "mean_lcl,"
"mean_load," "mean_load,"
"tot_recs,"
"cpucnt,", "cpucnt,",
add_sym ? "symbol," : "", add_sym ? "symbol," : "",
add_dso ? "dso," : "", add_dso ? "dso," : "",
......
/*
* builtin-ftrace.c
*
* Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org>
*
* Released under the GPL v2.
*/
#include "builtin.h"
#include "perf.h"
#include <unistd.h>
#include <signal.h>
#include "debug.h"
#include <subcmd/parse-options.h>
#include "evlist.h"
#include "target.h"
#include "thread_map.h"
#define DEFAULT_TRACER "function_graph"
struct perf_ftrace {
struct perf_evlist *evlist;
struct target target;
const char *tracer;
};
static bool done;
static void sig_handler(int sig __maybe_unused)
{
done = true;
}
/*
* perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since
* we asked by setting its exec_error to the function below,
* ftrace__workload_exec_failed_signal.
*
* XXX We need to handle this more appropriately, emitting an error, etc.
*/
static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
siginfo_t *info __maybe_unused,
void *ucontext __maybe_unused)
{
/* workload_exec_errno = info->si_value.sival_int; */
done = true;
}
static int write_tracing_file(const char *name, const char *val)
{
char *file;
int fd, ret = -1;
ssize_t size = strlen(val);
file = get_tracing_file(name);
if (!file) {
pr_debug("cannot get tracing file: %s\n", name);
return -1;
}
fd = open(file, O_WRONLY);
if (fd < 0) {
pr_debug("cannot open tracing file: %s\n", name);
goto out;
}
if (write(fd, val, size) == size)
ret = 0;
else
pr_debug("write '%s' to tracing/%s failed\n", val, name);
close(fd);
out:
put_tracing_file(file);
return ret;
}
static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
{
if (write_tracing_file("tracing_on", "0") < 0)
return -1;
if (write_tracing_file("current_tracer", "nop") < 0)
return -1;
if (write_tracing_file("set_ftrace_pid", " ") < 0)
return -1;
return 0;
}
static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
{
char *trace_file;
int trace_fd;
char *trace_pid;
char buf[4096];
struct pollfd pollfd = {
.events = POLLIN,
};
if (geteuid() != 0) {
pr_err("ftrace only works for root!\n");
return -1;
}
if (argc < 1)
return -1;
signal(SIGINT, sig_handler);
signal(SIGUSR1, sig_handler);
signal(SIGCHLD, sig_handler);
reset_tracing_files(ftrace);
/* reset ftrace buffer */
if (write_tracing_file("trace", "0") < 0)
goto out;
if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target,
argv, false, ftrace__workload_exec_failed_signal) < 0)
goto out;
if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
goto out;
}
if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) {
pr_err("failed to allocate pid string\n");
goto out;
}
if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) {
pr_err("failed to set pid: %s\n", trace_pid);
goto out_free_pid;
}
trace_file = get_tracing_file("trace_pipe");
if (!trace_file) {
pr_err("failed to open trace_pipe\n");
goto out_free_pid;
}
trace_fd = open(trace_file, O_RDONLY);
put_tracing_file(trace_file);
if (trace_fd < 0) {
pr_err("failed to open trace_pipe\n");
goto out_free_pid;
}
fcntl(trace_fd, F_SETFL, O_NONBLOCK);
pollfd.fd = trace_fd;
if (write_tracing_file("tracing_on", "1") < 0) {
pr_err("can't enable tracing\n");
goto out_close_fd;
}
perf_evlist__start_workload(ftrace->evlist);
while (!done) {
if (poll(&pollfd, 1, -1) < 0)
break;
if (pollfd.revents & POLLIN) {
int n = read(trace_fd, buf, sizeof(buf));
if (n < 0)
break;
if (fwrite(buf, n, 1, stdout) != 1)
break;
}
}
write_tracing_file("tracing_on", "0");
/* read remaining buffer contents */
while (true) {
int n = read(trace_fd, buf, sizeof(buf));
if (n <= 0)
break;
if (fwrite(buf, n, 1, stdout) != 1)
break;
}
out_close_fd:
close(trace_fd);
out_free_pid:
free(trace_pid);
out:
reset_tracing_files(ftrace);
return done ? 0 : -1;
}
int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused)
{
int ret;
struct perf_ftrace ftrace = {
.tracer = "function_graph",
.target = { .uid = UINT_MAX, },
};
const char * const ftrace_usage[] = {
"perf ftrace [<options>] <command>",
"perf ftrace [<options>] -- <command> [<options>]",
NULL
};
const struct option ftrace_options[] = {
OPT_STRING('t', "tracer", &ftrace.tracer, "tracer",
"tracer to use: function_graph(default) or function"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose"),
OPT_END()
};
argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc)
usage_with_options(ftrace_usage, ftrace_options);
ftrace.evlist = perf_evlist__new();
if (ftrace.evlist == NULL)
return -ENOMEM;
ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
if (ret < 0)
goto out_delete_evlist;
if (ftrace.tracer == NULL)
ftrace.tracer = DEFAULT_TRACER;
ret = __cmd_ftrace(&ftrace, argc, argv);
out_delete_evlist:
perf_evlist__delete(ftrace.evlist);
return ret;
}
...@@ -41,6 +41,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix); ...@@ -41,6 +41,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix);
int cmd_inject(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_mem(int argc, const char **argv, const char *prefix);
int cmd_data(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 find_scripts(char **scripts_array, char **scripts_path_array); int find_scripts(char **scripts_array, char **scripts_path_array);
#endif #endif
...@@ -11,6 +11,7 @@ perf-data mainporcelain common ...@@ -11,6 +11,7 @@ perf-data mainporcelain common
perf-diff mainporcelain common perf-diff mainporcelain common
perf-config mainporcelain common perf-config mainporcelain common
perf-evlist mainporcelain common perf-evlist mainporcelain common
perf-ftrace mainporcelain common
perf-inject mainporcelain common perf-inject mainporcelain common
perf-kallsyms mainporcelain common perf-kallsyms mainporcelain common
perf-kmem mainporcelain common perf-kmem mainporcelain common
......
...@@ -71,6 +71,7 @@ static struct cmd_struct commands[] = { ...@@ -71,6 +71,7 @@ static struct cmd_struct commands[] = {
{ "inject", cmd_inject, 0 }, { "inject", cmd_inject, 0 },
{ "mem", cmd_mem, 0 }, { "mem", cmd_mem, 0 },
{ "data", cmd_data, 0 }, { "data", cmd_data, 0 },
{ "ftrace", cmd_ftrace, 0 },
}; };
struct pager_config { struct pager_config {
......
...@@ -13,7 +13,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) ...@@ -13,7 +13,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
struct bpf_object *obj; struct bpf_object *obj;
obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
if (IS_ERR(obj)) if (libbpf_get_error(obj))
return TEST_FAIL; return TEST_FAIL;
bpf_object__close(obj); bpf_object__close(obj);
return TEST_OK; return TEST_OK;
......
...@@ -501,8 +501,8 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, ...@@ -501,8 +501,8 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
return n; return n;
} }
static void hist_entry__set_folding(struct hist_entry *he, static void __hist_entry__set_folding(struct hist_entry *he,
struct hist_browser *hb, bool unfold) struct hist_browser *hb, bool unfold)
{ {
hist_entry__init_have_children(he); hist_entry__init_have_children(he);
he->unfolded = unfold ? he->has_children : false; he->unfolded = unfold ? he->has_children : false;
...@@ -520,12 +520,34 @@ static void hist_entry__set_folding(struct hist_entry *he, ...@@ -520,12 +520,34 @@ static void hist_entry__set_folding(struct hist_entry *he,
he->nr_rows = 0; he->nr_rows = 0;
} }
static void hist_entry__set_folding(struct hist_entry *he,
struct hist_browser *browser, bool unfold)
{
double percent;
percent = hist_entry__get_percent_limit(he);
if (he->filtered || percent < browser->min_pcnt)
return;
__hist_entry__set_folding(he, browser, unfold);
if (!he->depth || unfold)
browser->nr_hierarchy_entries++;
if (he->leaf)
browser->nr_callchain_rows += he->nr_rows;
else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
browser->nr_hierarchy_entries++;
he->has_no_entry = true;
he->nr_rows = 1;
} else
he->has_no_entry = false;
}
static void static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold) __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
{ {
struct rb_node *nd; struct rb_node *nd;
struct hist_entry *he; struct hist_entry *he;
double percent;
nd = rb_first(&browser->hists->entries); nd = rb_first(&browser->hists->entries);
while (nd) { while (nd) {
...@@ -535,21 +557,6 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold) ...@@ -535,21 +557,6 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
hist_entry__set_folding(he, browser, unfold); hist_entry__set_folding(he, browser, unfold);
percent = hist_entry__get_percent_limit(he);
if (he->filtered || percent < browser->min_pcnt)
continue;
if (!he->depth || unfold)
browser->nr_hierarchy_entries++;
if (he->leaf)
browser->nr_callchain_rows += he->nr_rows;
else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
browser->nr_hierarchy_entries++;
he->has_no_entry = true;
he->nr_rows = 1;
} else
he->has_no_entry = false;
} }
} }
...@@ -564,6 +571,15 @@ static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) ...@@ -564,6 +571,15 @@ static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
ui_browser__reset_index(&browser->b); ui_browser__reset_index(&browser->b);
} }
static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
{
if (!browser->he_selection)
return;
hist_entry__set_folding(browser->he_selection, browser, unfold);
browser->b.nr_entries = hist_browser__nr_entries(browser);
}
static void ui_browser__warn_lost_events(struct ui_browser *browser) static void ui_browser__warn_lost_events(struct ui_browser *browser)
{ {
ui_browser__warning(browser, 4, ui_browser__warning(browser, 4,
...@@ -637,10 +653,18 @@ int hist_browser__run(struct hist_browser *browser, const char *help) ...@@ -637,10 +653,18 @@ int hist_browser__run(struct hist_browser *browser, const char *help)
/* Collapse the whole world. */ /* Collapse the whole world. */
hist_browser__set_folding(browser, false); hist_browser__set_folding(browser, false);
break; break;
case 'c':
/* Collapse the selected entry. */
hist_browser__set_folding_selected(browser, false);
break;
case 'E': case 'E':
/* Expand the whole world. */ /* Expand the whole world. */
hist_browser__set_folding(browser, true); hist_browser__set_folding(browser, true);
break; break;
case 'e':
/* Expand the selected entry. */
hist_browser__set_folding_selected(browser, true);
break;
case 'H': case 'H':
browser->show_headers = !browser->show_headers; browser->show_headers = !browser->show_headers;
hist_browser__update_rows(browser); hist_browser__update_rows(browser);
......
...@@ -9,6 +9,13 @@ ...@@ -9,6 +9,13 @@
#include "debug.h" #include "debug.h"
#include "vdso.h" #include "vdso.h"
static const char * const debuglink_paths[] = {
"%.0s%s",
"%s/%s",
"%s/.debug/%s",
"/usr/lib/debug%s/%s"
};
char dso__symtab_origin(const struct dso *dso) char dso__symtab_origin(const struct dso *dso)
{ {
static const char origin[] = { static const char origin[] = {
...@@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso, ...@@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso,
size_t len; size_t len;
switch (type) { switch (type) {
case DSO_BINARY_TYPE__DEBUGLINK: { case DSO_BINARY_TYPE__DEBUGLINK:
char *debuglink; {
const char *last_slash;
char dso_dir[PATH_MAX];
char symfile[PATH_MAX];
unsigned int i;
len = __symbol__join_symfs(filename, size, dso->long_name); len = __symbol__join_symfs(filename, size, dso->long_name);
debuglink = filename + len; last_slash = filename + len;
while (debuglink != filename && *debuglink != '/') while (last_slash != filename && *last_slash != '/')
debuglink--; last_slash--;
if (*debuglink == '/')
debuglink++;
ret = -1; strncpy(dso_dir, filename, last_slash - filename);
if (!is_regular_file(filename)) dso_dir[last_slash-filename] = '\0';
if (!is_regular_file(filename)) {
ret = -1;
break;
}
ret = filename__read_debuglink(filename, symfile, PATH_MAX);
if (ret)
break; break;
ret = filename__read_debuglink(filename, debuglink, /* Check predefined locations where debug file might reside */
size - (debuglink - filename)); ret = -1;
for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) {
snprintf(filename, size,
debuglink_paths[i], dso_dir, symfile);
if (is_regular_file(filename)) {
ret = 0;
break;
}
} }
break; break;
}
case DSO_BINARY_TYPE__BUILD_ID_CACHE: case DSO_BINARY_TYPE__BUILD_ID_CACHE:
if (dso__build_id_filename(dso, filename, size) == NULL) if (dso__build_id_filename(dso, filename, size) == NULL)
ret = -1; ret = -1;
......
...@@ -2803,8 +2803,10 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, ...@@ -2803,8 +2803,10 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
} }
event = pevent_find_event(pevent, evsel->attr.config); event = pevent_find_event(pevent, evsel->attr.config);
if (event == NULL) if (event == NULL) {
pr_debug("cannot find event format for %d\n", (int)evsel->attr.config);
return -1; return -1;
}
if (!evsel->name) { if (!evsel->name) {
snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name);
......
...@@ -3023,20 +3023,17 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev, ...@@ -3023,20 +3023,17 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
tev->nargs = pev->nargs; tev->nargs = pev->nargs;
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
if (!tev->args) { if (!tev->args)
err = -ENOMEM;
goto errout; goto errout;
}
for (i = 0; i < tev->nargs; i++) for (i = 0; i < tev->nargs; i++)
copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]); copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]);
return 1; return 1;
errout: errout:
if (*tevs) { clear_probe_trace_events(*tevs, 1);
clear_probe_trace_events(*tevs, 1); *tevs = NULL;
*tevs = NULL;
}
return err; return err;
} }
......
...@@ -350,8 +350,10 @@ static void perl_process_tracepoint(struct perf_sample *sample, ...@@ -350,8 +350,10 @@ static void perl_process_tracepoint(struct perf_sample *sample,
if (evsel->attr.type != PERF_TYPE_TRACEPOINT) if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return; return;
if (!event) if (!event) {
die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
return;
}
pid = raw_field_value(event, "common_pid", data); pid = raw_field_value(event, "common_pid", data);
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
#include "evsel.h" #include "evsel.h"
#include "debug.h" #include "debug.h"
#define VERSION "0.5" #define VERSION "0.6"
static int output_fd; static int output_fd;
...@@ -379,6 +379,34 @@ static int record_ftrace_printk(void) ...@@ -379,6 +379,34 @@ static int record_ftrace_printk(void)
return err; return err;
} }
static int record_saved_cmdline(void)
{
unsigned int size;
char *path;
struct stat st;
int ret, err = 0;
path = get_tracing_file("saved_cmdlines");
if (!path) {
pr_debug("can't get tracing/saved_cmdline");
return -ENOMEM;
}
ret = stat(path, &st);
if (ret < 0) {
/* not found */
size = 0;
if (write(output_fd, &size, 8) != 8)
err = -EIO;
goto out;
}
err = record_file(path, 8);
out:
put_tracing_file(path);
return err;
}
static void static void
put_tracepoints_path(struct tracepoint_path *tps) put_tracepoints_path(struct tracepoint_path *tps)
{ {
...@@ -539,6 +567,9 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, ...@@ -539,6 +567,9 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
if (err) if (err)
goto out; goto out;
err = record_ftrace_printk(); err = record_ftrace_printk();
if (err)
goto out;
err = record_saved_cmdline();
out: out:
/* /*
......
...@@ -160,6 +160,23 @@ void parse_ftrace_printk(struct pevent *pevent, ...@@ -160,6 +160,23 @@ void parse_ftrace_printk(struct pevent *pevent,
} }
} }
void parse_saved_cmdline(struct pevent *pevent,
char *file, unsigned int size __maybe_unused)
{
char *comm;
char *line;
char *next = NULL;
int pid;
line = strtok_r(file, "\n", &next);
while (line) {
sscanf(line, "%d %ms", &pid, &comm);
pevent_register_comm(pevent, comm, pid);
free(comm);
line = strtok_r(NULL, "\n", &next);
}
}
int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size) int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size)
{ {
return pevent_parse_event(pevent, buf, size, "ftrace"); return pevent_parse_event(pevent, buf, size, "ftrace");
......
...@@ -260,39 +260,53 @@ static int read_header_files(struct pevent *pevent) ...@@ -260,39 +260,53 @@ static int read_header_files(struct pevent *pevent)
static int read_ftrace_file(struct pevent *pevent, unsigned long long size) static int read_ftrace_file(struct pevent *pevent, unsigned long long size)
{ {
int ret;
char *buf; char *buf;
buf = malloc(size); buf = malloc(size);
if (buf == NULL) if (buf == NULL) {
pr_debug("memory allocation failure\n");
return -1; return -1;
}
if (do_read(buf, size) < 0) { ret = do_read(buf, size);
free(buf); if (ret < 0) {
return -1; pr_debug("error reading ftrace file.\n");
goto out;
} }
parse_ftrace_file(pevent, buf, size); ret = parse_ftrace_file(pevent, buf, size);
if (ret < 0)
pr_debug("error parsing ftrace file.\n");
out:
free(buf); free(buf);
return 0; return ret;
} }
static int read_event_file(struct pevent *pevent, char *sys, static int read_event_file(struct pevent *pevent, char *sys,
unsigned long long size) unsigned long long size)
{ {
int ret;
char *buf; char *buf;
buf = malloc(size); buf = malloc(size);
if (buf == NULL) if (buf == NULL) {
pr_debug("memory allocation failure\n");
return -1; return -1;
}
if (do_read(buf, size) < 0) { ret = do_read(buf, size);
if (ret < 0) {
free(buf); free(buf);
return -1; goto out;
} }
parse_event_file(pevent, buf, size, sys); ret = parse_event_file(pevent, buf, size, sys);
if (ret < 0)
pr_debug("error parsing event file.\n");
out:
free(buf); free(buf);
return 0; return ret;
} }
static int read_ftrace_files(struct pevent *pevent) static int read_ftrace_files(struct pevent *pevent)
...@@ -341,6 +355,36 @@ static int read_event_files(struct pevent *pevent) ...@@ -341,6 +355,36 @@ static int read_event_files(struct pevent *pevent)
return 0; return 0;
} }
static int read_saved_cmdline(struct pevent *pevent)
{
unsigned long long size;
char *buf;
int ret;
/* it can have 0 size */
size = read8(pevent);
if (!size)
return 0;
buf = malloc(size + 1);
if (buf == NULL) {
pr_debug("memory allocation failure\n");
return -1;
}
ret = do_read(buf, size);
if (ret < 0) {
pr_debug("error reading saved cmdlines\n");
goto out;
}
parse_saved_cmdline(pevent, buf, size);
ret = 0;
out:
free(buf);
return ret;
}
ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
{ {
char buf[BUFSIZ]; char buf[BUFSIZ];
...@@ -379,10 +423,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) ...@@ -379,10 +423,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
return -1; return -1;
if (show_version) if (show_version)
printf("version = %s\n", version); printf("version = %s\n", version);
free(version);
if (do_read(buf, 1) < 0) if (do_read(buf, 1) < 0) {
free(version);
return -1; return -1;
}
file_bigendian = buf[0]; file_bigendian = buf[0];
host_bigendian = bigendian(); host_bigendian = bigendian();
...@@ -423,6 +468,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) ...@@ -423,6 +468,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
err = read_ftrace_printk(pevent); err = read_ftrace_printk(pevent);
if (err) if (err)
goto out; goto out;
if (atof(version) >= 0.6) {
err = read_saved_cmdline(pevent);
if (err)
goto out;
}
size = trace_data_size; size = trace_data_size;
repipe = false; repipe = false;
...@@ -438,5 +488,6 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) ...@@ -438,5 +488,6 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
out: out:
if (pevent) if (pevent)
trace_event__cleanup(tevent); trace_event__cleanup(tevent);
free(version);
return size; return size;
} }
...@@ -42,6 +42,7 @@ raw_field_value(struct event_format *event, const char *name, void *data); ...@@ -42,6 +42,7 @@ raw_field_value(struct event_format *event, const char *name, void *data);
void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size);
void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size);
void parse_saved_cmdline(struct pevent *pevent, char *file, unsigned int size);
ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "asm/bug.h" #include "asm/bug.h"
#include "dso.h"
extern int extern int
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
...@@ -297,15 +298,58 @@ static int read_unwind_spec_debug_frame(struct dso *dso, ...@@ -297,15 +298,58 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
int fd; int fd;
u64 ofs = dso->data.debug_frame_offset; u64 ofs = dso->data.debug_frame_offset;
/* debug_frame can reside in:
* - dso
* - debug pointed by symsrc_filename
* - gnu_debuglink, which doesn't necessary
* has to be pointed by symsrc_filename
*/
if (ofs == 0) { if (ofs == 0) {
fd = dso__data_get_fd(dso, machine); fd = dso__data_get_fd(dso, machine);
if (fd < 0) if (fd >= 0) {
return -EINVAL; ofs = elf_section_offset(fd, ".debug_frame");
dso__data_put_fd(dso);
}
if (ofs <= 0) {
fd = open(dso->symsrc_filename, O_RDONLY);
if (fd >= 0) {
ofs = elf_section_offset(fd, ".debug_frame");
close(fd);
}
}
if (ofs <= 0) {
char *debuglink = malloc(PATH_MAX);
int ret = 0;
ret = dso__read_binary_type_filename(
dso, DSO_BINARY_TYPE__DEBUGLINK,
machine->root_dir, debuglink, PATH_MAX);
if (!ret) {
fd = open(debuglink, O_RDONLY);
if (fd >= 0) {
ofs = elf_section_offset(fd,
".debug_frame");
close(fd);
}
}
if (ofs > 0) {
if (dso->symsrc_filename != NULL) {
pr_warning(
"%s: overwrite symsrc(%s,%s)\n",
__func__,
dso->symsrc_filename,
debuglink);
free(dso->symsrc_filename);
}
dso->symsrc_filename = debuglink;
} else {
free(debuglink);
}
}
/* Check the .debug_frame section for unwinding info */
ofs = elf_section_offset(fd, ".debug_frame");
dso->data.debug_frame_offset = ofs; dso->data.debug_frame_offset = ofs;
dso__data_put_fd(dso);
} }
*offset = ofs; *offset = ofs;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment