mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 10:13:57 +08:00
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: - libbpf prog functions sync with what is exported via uapi (Joe Stringer) Trivial: - Remove unnecessary checks and assignments in 'perf probe's try_to_find_absolute_address() (Markus Elfring) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJYig7UAAoJENZQFvNTUqpAhJQP/iI0T7A8TNekPGLv7j20c302 89N9+9TAFtVqjgr1hIzqQgGOqbOdAW1tU3VTPW92nNDBn9JV5qwuF9YWEiDaAVv2 0bmV5hLnrNlymddm3pdg/PbD1TVlwk2NFxtrkPxuf/vx0ZhEGqsSrRUCR/xGXbtQ TcMg3rQquspV9JNv4HzFdQC9nsG1CGNotZKsE1avRw70pWAqCtF81B0m8teb6OWo 5qnN+AMJlYcC+OGffROemUksuehkMvi5L8v1e/6RO/lU1qt9Jrc/2sT9cqvjVFNR k4c76cUgWOCYzDEotENMpU4bc6e/24DE2ydFeovihdXw8Qs4ajEA9LXKM4yW+ZoE MZE3GS153a8n+CvTfkB9Ow1QJ8rgmR/L0BuhmGb6bYW/MtuTRTShhSduZwOrIyap 9KckHYti4p3oN3CKFYGO9PN3DRUdx+Xqg/miwrgjkPo09QFp+lzfFFOk0P2/Zqw2 yfvdWeHxkkrwoWQIyMHVKp/E9jQPuyYqwnKdp68LCN+DgNiFpPpSA8id5e47RQDE otqrK8U/82ktakfrBijSPBI6EEqFg7ltip2KT/xlDMfnP9HtxgFhzrk52dyi6pM/ jkBhJaTQhVZTyaFvUXuaLmBSdPpcaaGM4KJ+2iAayA2r0KLiDj6IdzD5ROCRFOvJ SFA472mIxNxUjpQEUTtc =tYKN -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
e2cf00c257
@ -28,6 +28,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/list.h>
|
||||
@ -779,7 +780,7 @@ static int
|
||||
bpf_program__collect_reloc(struct bpf_program *prog,
|
||||
size_t nr_maps, GElf_Shdr *shdr,
|
||||
Elf_Data *data, Elf_Data *symbols,
|
||||
int maps_shndx)
|
||||
int maps_shndx, struct bpf_map *maps)
|
||||
{
|
||||
int i, nrels;
|
||||
|
||||
@ -829,7 +830,15 @@ bpf_program__collect_reloc(struct bpf_program *prog,
|
||||
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) {
|
||||
pr_warning("bpf relocation: map_idx %d large than %d\n",
|
||||
(int)map_idx, (int)nr_maps - 1);
|
||||
@ -953,7 +962,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
|
||||
err = bpf_program__collect_reloc(prog, nr_maps,
|
||||
shdr, data,
|
||||
obj->efile.symbols,
|
||||
obj->efile.maps_shndx);
|
||||
obj->efile.maps_shndx,
|
||||
obj->maps);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1419,37 +1429,33 @@ static void bpf_program__set_type(struct bpf_program *prog,
|
||||
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,
|
||||
enum bpf_prog_type type)
|
||||
{
|
||||
return prog ? (prog->type == type) : false;
|
||||
}
|
||||
|
||||
bool bpf_program__is_tracepoint(struct bpf_program *prog)
|
||||
{
|
||||
return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT);
|
||||
}
|
||||
#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
|
||||
int bpf_program__set_##NAME(struct bpf_program *prog) \
|
||||
{ \
|
||||
if (!prog) \
|
||||
return -EINVAL; \
|
||||
bpf_program__set_type(prog, TYPE); \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
bool bpf_program__is_##NAME(struct bpf_program *prog) \
|
||||
{ \
|
||||
return bpf_program__is_type(prog, TYPE); \
|
||||
} \
|
||||
|
||||
bool bpf_program__is_kprobe(struct bpf_program *prog)
|
||||
{
|
||||
return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE);
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -1537,3 +1543,10 @@ bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
|
||||
}
|
||||
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 @@
|
||||
#define __BPF_LIBBPF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/err.h>
|
||||
#include <sys/types.h> // for size_t
|
||||
|
||||
enum libbpf_errno {
|
||||
@ -174,11 +174,21 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n);
|
||||
/*
|
||||
* 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_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_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
|
||||
@ -224,4 +234,6 @@ int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
||||
bpf_map_clear_priv_t clear_priv);
|
||||
void *bpf_map__priv(struct bpf_map *map);
|
||||
|
||||
long libbpf_get_error(const void *ptr);
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@ perf-y += builtin-annotate.o
|
||||
perf-y += builtin-config.o
|
||||
perf-y += builtin-diff.o
|
||||
perf-y += builtin-evlist.o
|
||||
perf-y += builtin-ftrace.o
|
||||
perf-y += builtin-help.o
|
||||
perf-y += builtin-sched.o
|
||||
perf-y += builtin-buildid-list.o
|
||||
|
@ -248,7 +248,7 @@ output fields set for caheline offsets output:
|
||||
Code address, Code symbol, Shared Object, Source line
|
||||
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
|
||||
------------
|
||||
|
36
tools/perf/Documentation/perf-ftrace.txt
Normal file
36
tools/perf/Documentation/perf-ftrace.txt
Normal file
@ -0,0 +1,36 @@
|
||||
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 @@
|
||||
/* This is included in perf/util/dwarf-regs.c */
|
||||
|
||||
static const char * const aarch64_regstr_tbl[] = {
|
||||
"%r0", "%r1", "%r2", "%r3", "%r4",
|
||||
"%r5", "%r6", "%r7", "%r8", "%r9",
|
||||
"%r10", "%r11", "%r12", "%r13", "%r14",
|
||||
"%r15", "%r16", "%r17", "%r18", "%r19",
|
||||
"%r20", "%r21", "%r22", "%r23", "%r24",
|
||||
"%r25", "%r26", "%r27", "%r28", "%r29",
|
||||
"%x0", "%x1", "%x2", "%x3", "%x4",
|
||||
"%x5", "%x6", "%x7", "%x8", "%x9",
|
||||
"%x10", "%x11", "%x12", "%x13", "%x14",
|
||||
"%x15", "%x16", "%x17", "%x18", "%x19",
|
||||
"%x20", "%x21", "%x22", "%x23", "%x24",
|
||||
"%x25", "%x26", "%x27", "%x28", "%x29",
|
||||
"%lr", "%sp",
|
||||
};
|
||||
#endif
|
||||
|
@ -58,7 +58,7 @@ struct c2c_hist_entry {
|
||||
struct hist_entry he;
|
||||
};
|
||||
|
||||
static char const *coalesce_default = "pid,tid,iaddr";
|
||||
static char const *coalesce_default = "pid,iaddr";
|
||||
|
||||
struct perf_c2c {
|
||||
struct perf_tool tool;
|
||||
@ -2476,6 +2476,7 @@ static int build_cl_output(char *cl_sort, bool no_source)
|
||||
"mean_rmt,"
|
||||
"mean_lcl,"
|
||||
"mean_load,"
|
||||
"tot_recs,"
|
||||
"cpucnt,",
|
||||
add_sym ? "symbol," : "",
|
||||
add_dso ? "dso," : "",
|
||||
|
243
tools/perf/builtin-ftrace.c
Normal file
243
tools/perf/builtin-ftrace.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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);
|
||||
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 find_scripts(char **scripts_array, char **scripts_path_array);
|
||||
#endif
|
||||
|
@ -11,6 +11,7 @@ perf-data mainporcelain common
|
||||
perf-diff mainporcelain common
|
||||
perf-config mainporcelain common
|
||||
perf-evlist mainporcelain common
|
||||
perf-ftrace mainporcelain common
|
||||
perf-inject mainporcelain common
|
||||
perf-kallsyms mainporcelain common
|
||||
perf-kmem mainporcelain common
|
||||
|
@ -71,6 +71,7 @@ static struct cmd_struct commands[] = {
|
||||
{ "inject", cmd_inject, 0 },
|
||||
{ "mem", cmd_mem, 0 },
|
||||
{ "data", cmd_data, 0 },
|
||||
{ "ftrace", cmd_ftrace, 0 },
|
||||
};
|
||||
|
||||
struct pager_config {
|
||||
|
@ -13,7 +13,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
|
||||
struct bpf_object *obj;
|
||||
|
||||
obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
|
||||
if (IS_ERR(obj))
|
||||
if (libbpf_get_error(obj))
|
||||
return TEST_FAIL;
|
||||
bpf_object__close(obj);
|
||||
return TEST_OK;
|
||||
|
@ -501,8 +501,8 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
|
||||
return n;
|
||||
}
|
||||
|
||||
static void hist_entry__set_folding(struct hist_entry *he,
|
||||
struct hist_browser *hb, bool unfold)
|
||||
static void __hist_entry__set_folding(struct hist_entry *he,
|
||||
struct hist_browser *hb, bool unfold)
|
||||
{
|
||||
hist_entry__init_have_children(he);
|
||||
he->unfolded = unfold ? he->has_children : false;
|
||||
@ -520,12 +520,34 @@ static void hist_entry__set_folding(struct hist_entry *he,
|
||||
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
|
||||
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *he;
|
||||
double percent;
|
||||
|
||||
nd = rb_first(&browser->hists->entries);
|
||||
while (nd) {
|
||||
@ -535,21 +557,6 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
|
||||
nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
ui_browser__warning(browser, 4,
|
||||
@ -637,10 +653,18 @@ int hist_browser__run(struct hist_browser *browser, const char *help)
|
||||
/* Collapse the whole world. */
|
||||
hist_browser__set_folding(browser, false);
|
||||
break;
|
||||
case 'c':
|
||||
/* Collapse the selected entry. */
|
||||
hist_browser__set_folding_selected(browser, false);
|
||||
break;
|
||||
case 'E':
|
||||
/* Expand the whole world. */
|
||||
hist_browser__set_folding(browser, true);
|
||||
break;
|
||||
case 'e':
|
||||
/* Expand the selected entry. */
|
||||
hist_browser__set_folding_selected(browser, true);
|
||||
break;
|
||||
case 'H':
|
||||
browser->show_headers = !browser->show_headers;
|
||||
hist_browser__update_rows(browser);
|
||||
|
@ -9,6 +9,13 @@
|
||||
#include "debug.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)
|
||||
{
|
||||
static const char origin[] = {
|
||||
@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
||||
size_t len;
|
||||
|
||||
switch (type) {
|
||||
case DSO_BINARY_TYPE__DEBUGLINK: {
|
||||
char *debuglink;
|
||||
case DSO_BINARY_TYPE__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);
|
||||
debuglink = filename + len;
|
||||
while (debuglink != filename && *debuglink != '/')
|
||||
debuglink--;
|
||||
if (*debuglink == '/')
|
||||
debuglink++;
|
||||
last_slash = filename + len;
|
||||
while (last_slash != filename && *last_slash != '/')
|
||||
last_slash--;
|
||||
|
||||
ret = -1;
|
||||
if (!is_regular_file(filename))
|
||||
strncpy(dso_dir, filename, last_slash - 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;
|
||||
|
||||
ret = filename__read_debuglink(filename, debuglink,
|
||||
size - (debuglink - filename));
|
||||
/* Check predefined locations where debug file might reside */
|
||||
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;
|
||||
}
|
||||
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
|
||||
if (dso__build_id_filename(dso, filename, size) == NULL)
|
||||
ret = -1;
|
||||
|
@ -2803,8 +2803,10 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!evsel->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,
|
||||
|
||||
tev->nargs = pev->nargs;
|
||||
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
||||
if (!tev->args) {
|
||||
err = -ENOMEM;
|
||||
if (!tev->args)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
for (i = 0; i < tev->nargs; i++)
|
||||
copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]);
|
||||
|
||||
return 1;
|
||||
|
||||
errout:
|
||||
if (*tevs) {
|
||||
clear_probe_trace_events(*tevs, 1);
|
||||
*tevs = NULL;
|
||||
}
|
||||
clear_probe_trace_events(*tevs, 1);
|
||||
*tevs = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -350,8 +350,10 @@ static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
return;
|
||||
|
||||
if (!event)
|
||||
die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
|
||||
if (!event) {
|
||||
pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
|
||||
return;
|
||||
}
|
||||
|
||||
pid = raw_field_value(event, "common_pid", data);
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define VERSION "0.5"
|
||||
#define VERSION "0.6"
|
||||
|
||||
static int output_fd;
|
||||
|
||||
@ -379,6 +379,34 @@ out:
|
||||
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
|
||||
put_tracepoints_path(struct tracepoint_path *tps)
|
||||
{
|
||||
@ -539,6 +567,9 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
||||
if (err)
|
||||
goto out;
|
||||
err = record_ftrace_printk();
|
||||
if (err)
|
||||
goto out;
|
||||
err = record_saved_cmdline();
|
||||
|
||||
out:
|
||||
/*
|
||||
|
@ -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)
|
||||
{
|
||||
return pevent_parse_event(pevent, buf, size, "ftrace");
|
||||
|
@ -260,39 +260,53 @@ static int read_header_files(struct pevent *pevent)
|
||||
|
||||
static int read_ftrace_file(struct pevent *pevent, unsigned long long size)
|
||||
{
|
||||
int ret;
|
||||
char *buf;
|
||||
|
||||
buf = malloc(size);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
if (buf == NULL) {
|
||||
pr_debug("memory allocation failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_ftrace_file(pevent, buf, size);
|
||||
ret = do_read(buf, size);
|
||||
if (ret < 0) {
|
||||
pr_debug("error reading ftrace file.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parse_ftrace_file(pevent, buf, size);
|
||||
if (ret < 0)
|
||||
pr_debug("error parsing ftrace file.\n");
|
||||
out:
|
||||
free(buf);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_event_file(struct pevent *pevent, char *sys,
|
||||
unsigned long long size)
|
||||
{
|
||||
int ret;
|
||||
char *buf;
|
||||
|
||||
buf = malloc(size);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(buf, size) < 0) {
|
||||
free(buf);
|
||||
if (buf == NULL) {
|
||||
pr_debug("memory allocation failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse_event_file(pevent, buf, size, sys);
|
||||
ret = do_read(buf, size);
|
||||
if (ret < 0) {
|
||||
free(buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = parse_event_file(pevent, buf, size, sys);
|
||||
if (ret < 0)
|
||||
pr_debug("error parsing event file.\n");
|
||||
out:
|
||||
free(buf);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_ftrace_files(struct pevent *pevent)
|
||||
@ -341,6 +355,36 @@ static int read_event_files(struct pevent *pevent)
|
||||
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)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
@ -379,10 +423,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
|
||||
return -1;
|
||||
if (show_version)
|
||||
printf("version = %s\n", version);
|
||||
free(version);
|
||||
|
||||
if (do_read(buf, 1) < 0)
|
||||
if (do_read(buf, 1) < 0) {
|
||||
free(version);
|
||||
return -1;
|
||||
}
|
||||
file_bigendian = buf[0];
|
||||
host_bigendian = bigendian();
|
||||
|
||||
@ -423,6 +468,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
|
||||
err = read_ftrace_printk(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
if (atof(version) >= 0.6) {
|
||||
err = read_saved_cmdline(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = trace_data_size;
|
||||
repipe = false;
|
||||
@ -438,5 +488,6 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
|
||||
out:
|
||||
if (pevent)
|
||||
trace_event__cleanup(tevent);
|
||||
free(version);
|
||||
return size;
|
||||
}
|
||||
|
@ -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_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);
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "asm/bug.h"
|
||||
#include "dso.h"
|
||||
|
||||
extern int
|
||||
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,
|
||||
int fd;
|
||||
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) {
|
||||
fd = dso__data_get_fd(dso, machine);
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
if (fd >= 0) {
|
||||
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_put_fd(dso);
|
||||
}
|
||||
|
||||
*offset = ofs;
|
||||
|
Loading…
Reference in New Issue
Block a user