perf/core improvements and fixes

User Visible:
 
 . Allow setting preferred callchain method in .perfconfig (Jiri Olsa)
 
 . Show in what binaries/modules 'perf probe's are set (Masami Hiramatsu)
 
 . Support distro-style debuginfo for uprobe in 'perf probe' (Masami Hiramatsu)
 
 Developer stuff:
 
 . Move some hashing and fs related code from tools/perf/util/
   to tools/lib/ so that it can be used by more tools/ living
   utilities (Borislav Petkov)
 
 . Prepare DWARF unwinding code for using an elfutils alternative
   unwinding library (Jiri Olsa)
 
 . Fix DWARF unwind max_stack processing (Jiri Olsa)
 
 . Add dwarf unwind 'perf test' entry (Jiri Olsa)
 
 . 'perf probe' improvements including memory leak fixes,
   sharing the intlist class with other tools, uprobes/kprobes
   code sharing and use of ref_reloc_sym (Masami Hiramatsu)
 
 . Shorten sample symbol resolving by adding cpumode to
   struct addr_location (Arnaldo Carvalho de Melo)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJTA8jXAAoJENZQFvNTUqpA9OcQAKxb1RilOI8zIzjqSDcU/GBB
 gmD3LRH3y2IWhy6kuuOnxsloxNrLZkbVskd3pLYWPDnttAP9plvXzVqAnwSyL93c
 hC0T7zDuTG4A3rfjyFFhhxp53FbRDZvMUECtljDgJLsYq813AGzNzJ0LpkJoTIp8
 Rc3P5Qf7+8bWzw2Z119x8lWW6H2K6dj1PwqNUnLCwuTApiDPFAkC0vvpQccSQAN7
 16tX8yrCicD/gP42sKIVlUm91KTve5jPxLbPoPONHZm/SqabsCLtMRQjDRrXqNYB
 F2stQPL7DEsssiHf/RUNiP92eFqWDWDstgtigtiYq/ZVVraWF2aXcbjE/oh4QVx4
 SIjp0s1f5b9Y0Jk6fxBwfRweVaONgi51SPG/AIMxiNtcoFUrgkVQqioF9SNzhIQ4
 /f30ZdRypzYVDYfQTQ1P3+BeISy8jEwnlqFF5wEVx7xdWW0r/aEXq4+IyJkCrvPo
 4T7soDDRcMuazkQYkZlDN5JTjFO5fZN67mJ/W1sJ9vc8Xw2w/ZIAXfmw7L+VZVtN
 SpGJFL7c7zpEkD551hONz+wsoSb6D0aX+c0HSXJU5SmRdgBClGD0kv3gLjgwUn6E
 eZXU6/DoXKkBin3tAQbcIfVyXXuRKFg3fIA8KQcKrbfzqWInVbJrka1G7tCFePwj
 gXJT9RflDIR10ROJz7jY
 =cdsk
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo' 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:

User Visible:

  * Allow setting preferred callchain method in .perfconfig (Jiri Olsa)

  * Show in what binaries/modules 'perf probe's are set (Masami Hiramatsu)

  * Support distro-style debuginfo for uprobe in 'perf probe' (Masami Hiramatsu)

Developer stuff:

  * Move some hashing and fs related code from tools/perf/util/
    to tools/lib/ so that it can be used by more tools/ living
    utilities (Borislav Petkov)

  * Prepare DWARF unwinding code for using an elfutils alternative
    unwinding library (Jiri Olsa)

  * Fix DWARF unwind max_stack processing (Jiri Olsa)

  * Add dwarf unwind 'perf test' entry (Jiri Olsa)

  * 'perf probe' improvements including memory leak fixes,
    sharing the intlist class with other tools, uprobes/kprobes
    code sharing and use of ref_reloc_sym (Masami Hiramatsu)

  * Shorten sample symbol resolving by adding cpumode to
    struct addr_location (Arnaldo Carvalho de Melo)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2014-02-22 17:26:24 +01:00
commit 7e74efcf76
55 changed files with 1130 additions and 724 deletions

View File

@ -0,0 +1,5 @@
#include "../../../include/linux/hash.h"
#ifndef _TOOLS_LINUX_HASH_H
#define _TOOLS_LINUX_HASH_H
#endif

View File

@ -9,8 +9,10 @@ LIB_H=
LIB_OBJS=
LIB_H += fs/debugfs.h
LIB_H += fs/fs.h
LIB_OBJS += $(OUTPUT)fs/debugfs.o
LIB_OBJS += $(OUTPUT)fs/fs.o
LIBFILE = libapikfs.a

View File

@ -1,8 +1,13 @@
/* TODO merge/factor in debugfs.c here */
/* TODO merge/factor into tools/lib/lk/debugfs.c */
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/vfs.h>
#include "util.h"
#include "util/fs.h"
#include "debugfs.h"
#include "fs.h"
static const char * const sysfs__fs_known_mountpoints[] = {
"/sys",

View File

@ -1,9 +1,5 @@
#ifndef _PERF_LINUX_MAGIC_H_
#define _PERF_LINUX_MAGIC_H_
#ifndef DEBUGFS_MAGIC
#define DEBUGFS_MAGIC 0x64626720
#endif
#ifndef __API_FS__
#define __API_FS__
#ifndef SYSFS_MAGIC
#define SYSFS_MAGIC 0x62656572
@ -13,4 +9,6 @@
#define PROC_SUPER_MAGIC 0x9fa0
#endif
#endif
const char *sysfs__mountpoint(void);
const char *procfs__mountpoint(void);
#endif /* __API_FS__ */

View File

@ -6,6 +6,7 @@ tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h
tools/include/asm/bug.h
tools/include/linux/compiler.h
tools/include/linux/hash.h
include/linux/const.h
include/linux/perf_event.h
include/linux/rbtree.h

View File

@ -208,7 +208,7 @@ LIB_H += ../../include/uapi/linux/perf_event.h
LIB_H += ../../include/linux/rbtree.h
LIB_H += ../../include/linux/list.h
LIB_H += ../../include/uapi/linux/const.h
LIB_H += ../../include/linux/hash.h
LIB_H += ../include/linux/hash.h
LIB_H += ../../include/linux/stringify.h
LIB_H += util/include/linux/bitmap.h
LIB_H += util/include/linux/bitops.h
@ -218,9 +218,7 @@ LIB_H += util/include/linux/ctype.h
LIB_H += util/include/linux/kernel.h
LIB_H += util/include/linux/list.h
LIB_H += util/include/linux/export.h
LIB_H += util/include/linux/magic.h
LIB_H += util/include/linux/poison.h
LIB_H += util/include/linux/prefetch.h
LIB_H += util/include/linux/rbtree.h
LIB_H += util/include/linux/rbtree_augmented.h
LIB_H += util/include/linux/string.h
@ -244,7 +242,6 @@ LIB_H += util/cache.h
LIB_H += util/callchain.h
LIB_H += util/build-id.h
LIB_H += util/debug.h
LIB_H += util/fs.h
LIB_H += util/pmu.h
LIB_H += util/event.h
LIB_H += util/evsel.h
@ -306,7 +303,6 @@ LIB_OBJS += $(OUTPUT)util/annotate.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/fs.o
LIB_OBJS += $(OUTPUT)util/pmu.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
@ -408,6 +404,11 @@ endif
LIB_OBJS += $(OUTPUT)tests/code-reading.o
LIB_OBJS += $(OUTPUT)tests/sample-parsing.o
LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o
ifndef NO_LIBUNWIND
ifeq ($(ARCH),x86)
LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o
endif
endif
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
@ -476,7 +477,7 @@ endif # NO_DWARF
endif # NO_LIBELF
ifndef NO_LIBUNWIND
LIB_OBJS += $(OUTPUT)util/unwind.o
LIB_OBJS += $(OUTPUT)util/unwind-libunwind.o
endif
LIB_OBJS += $(OUTPUT)tests/keep-tracking.o
@ -533,6 +534,7 @@ ifeq ($(NO_PERF_REGS),0)
ifeq ($(ARCH),x86)
LIB_H += arch/x86/include/perf_regs.h
endif
LIB_OBJS += $(OUTPUT)util/perf_regs.o
endif
ifndef NO_LIBNUMA
@ -655,6 +657,9 @@ $(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS
-DPYTHON='"$(PYTHON_WORD)"' \
$<
$(OUTPUT)tests/dwarf-unwind.o: tests/dwarf-unwind.c
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -fno-optimize-sibling-calls $<
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<

View File

@ -3,5 +3,5 @@ PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
ifndef NO_LIBUNWIND
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
endif

View File

@ -4,7 +4,7 @@
#include "perf_regs.h"
#include "../../util/unwind.h"
int unwind__arch_reg_id(int regnum)
int libunwind__arch_reg_id(int regnum)
{
switch (regnum) {
case UNW_ARM_R0:

View File

@ -3,7 +3,9 @@ PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
ifndef NO_LIBUNWIND
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o
endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o

View File

@ -5,14 +5,20 @@
#include "../../util/types.h"
#include <asm/perf_regs.h>
void perf_regs_load(u64 *regs);
#ifndef HAVE_ARCH_X86_64_SUPPORT
#define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1)
#define PERF_REGS_MAX PERF_REG_X86_32_MAX
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
#else
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
(1ULL << PERF_REG_X86_ES) | \
(1ULL << PERF_REG_X86_FS) | \
(1ULL << PERF_REG_X86_GS))
#define PERF_REGS_MASK (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~REG_NOSUPPORT)
#define PERF_REGS_MAX PERF_REG_X86_64_MAX
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
#endif
#define PERF_REG_IP PERF_REG_X86_IP
#define PERF_REG_SP PERF_REG_X86_SP

View File

@ -0,0 +1,59 @@
#include <string.h>
#include "perf_regs.h"
#include "thread.h"
#include "map.h"
#include "event.h"
#include "tests/tests.h"
#define STACK_SIZE 8192
static int sample_ustack(struct perf_sample *sample,
struct thread *thread, u64 *regs)
{
struct stack_dump *stack = &sample->user_stack;
struct map *map;
unsigned long sp;
u64 stack_size, *buf;
buf = malloc(STACK_SIZE);
if (!buf) {
pr_debug("failed to allocate sample uregs data\n");
return -1;
}
sp = (unsigned long) regs[PERF_REG_X86_SP];
map = map_groups__find(&thread->mg, MAP__FUNCTION, (u64) sp);
if (!map) {
pr_debug("failed to get stack map\n");
return -1;
}
stack_size = map->end - sp;
stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
memcpy(buf, (void *) sp, stack_size);
stack->data = (char *) buf;
stack->size = stack_size;
return 0;
}
int test__arch_unwind_sample(struct perf_sample *sample,
struct thread *thread)
{
struct regs_dump *regs = &sample->user_regs;
u64 *buf;
buf = malloc(sizeof(u64) * PERF_REGS_MAX);
if (!buf) {
pr_debug("failed to allocate sample uregs data\n");
return -1;
}
perf_regs_load(buf);
regs->abi = PERF_SAMPLE_REGS_ABI;
regs->regs = buf;
regs->mask = PERF_REGS_MASK;
return sample_ustack(sample, thread, buf);
}

View File

@ -0,0 +1,92 @@
#include <linux/linkage.h>
#define AX 0
#define BX 1 * 8
#define CX 2 * 8
#define DX 3 * 8
#define SI 4 * 8
#define DI 5 * 8
#define BP 6 * 8
#define SP 7 * 8
#define IP 8 * 8
#define FLAGS 9 * 8
#define CS 10 * 8
#define SS 11 * 8
#define DS 12 * 8
#define ES 13 * 8
#define FS 14 * 8
#define GS 15 * 8
#define R8 16 * 8
#define R9 17 * 8
#define R10 18 * 8
#define R11 19 * 8
#define R12 20 * 8
#define R13 21 * 8
#define R14 22 * 8
#define R15 23 * 8
.text
#ifdef HAVE_ARCH_X86_64_SUPPORT
ENTRY(perf_regs_load)
movq %rax, AX(%rdi)
movq %rbx, BX(%rdi)
movq %rcx, CX(%rdi)
movq %rdx, DX(%rdi)
movq %rsi, SI(%rdi)
movq %rdi, DI(%rdi)
movq %rbp, BP(%rdi)
leaq 8(%rsp), %rax /* exclude this call. */
movq %rax, SP(%rdi)
movq 0(%rsp), %rax
movq %rax, IP(%rdi)
movq $0, FLAGS(%rdi)
movq $0, CS(%rdi)
movq $0, SS(%rdi)
movq $0, DS(%rdi)
movq $0, ES(%rdi)
movq $0, FS(%rdi)
movq $0, GS(%rdi)
movq %r8, R8(%rdi)
movq %r9, R9(%rdi)
movq %r10, R10(%rdi)
movq %r11, R11(%rdi)
movq %r12, R12(%rdi)
movq %r13, R13(%rdi)
movq %r14, R14(%rdi)
movq %r15, R15(%rdi)
ret
ENDPROC(perf_regs_load)
#else
ENTRY(perf_regs_load)
push %edi
movl 8(%esp), %edi
movl %eax, AX(%edi)
movl %ebx, BX(%edi)
movl %ecx, CX(%edi)
movl %edx, DX(%edi)
movl %esi, SI(%edi)
pop %eax
movl %eax, DI(%edi)
movl %ebp, BP(%edi)
leal 4(%esp), %eax /* exclude this call. */
movl %eax, SP(%edi)
movl 0(%esp), %eax
movl %eax, IP(%edi)
movl $0, FLAGS(%edi)
movl $0, CS(%edi)
movl $0, SS(%edi)
movl $0, DS(%edi)
movl $0, ES(%edi)
movl $0, FS(%edi)
movl $0, GS(%edi)
ret
ENDPROC(perf_regs_load)
#endif

View File

@ -5,7 +5,7 @@
#include "../../util/unwind.h"
#ifdef HAVE_ARCH_X86_64_SUPPORT
int unwind__arch_reg_id(int regnum)
int libunwind__arch_reg_id(int regnum)
{
int id;
@ -69,7 +69,7 @@ int unwind__arch_reg_id(int regnum)
return id;
}
#else
int unwind__arch_reg_id(int regnum)
int libunwind__arch_reg_id(int regnum)
{
int id;

View File

@ -312,7 +312,6 @@ found:
sample_sw.period = sample->period;
sample_sw.time = sample->time;
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
evsel->attr.sample_regs_user,
evsel->attr.read_format, &sample_sw,
false);
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);

View File

@ -268,9 +268,9 @@ static int opt_set_filter(const struct option *opt __maybe_unused,
return 0;
}
static void init_params(void)
static int init_params(void)
{
line_range__init(&params.line_range);
return line_range__init(&params.line_range);
}
static void cleanup_params(void)
@ -515,9 +515,11 @@ int cmd_probe(int argc, const char **argv, const char *prefix)
{
int ret;
init_params();
ret = __cmd_probe(argc, argv, prefix);
cleanup_params();
ret = init_params();
if (!ret) {
ret = __cmd_probe(argc, argv, prefix);
cleanup_params();
}
return ret;
}

View File

@ -649,7 +649,7 @@ error:
return ret;
}
#ifdef HAVE_LIBUNWIND_SUPPORT
#ifdef HAVE_DWARF_UNWIND_SUPPORT
static int get_stack_size(char *str, unsigned long *_size)
{
char *endptr;
@ -675,7 +675,7 @@ static int get_stack_size(char *str, unsigned long *_size)
max_size, str);
return -1;
}
#endif /* HAVE_LIBUNWIND_SUPPORT */
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
int record_parse_callchain(const char *arg, struct record_opts *opts)
{
@ -704,7 +704,7 @@ int record_parse_callchain(const char *arg, struct record_opts *opts)
"needed for -g fp\n");
break;
#ifdef HAVE_LIBUNWIND_SUPPORT
#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
@ -720,7 +720,7 @@ int record_parse_callchain(const char *arg, struct record_opts *opts)
ret = get_stack_size(tok, &size);
opts->stack_dump_size = size;
}
#endif /* HAVE_LIBUNWIND_SUPPORT */
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
} else {
pr_err("callchain: Unknown --call-graph option "
"value: %s\n", arg);
@ -735,7 +735,9 @@ int record_parse_callchain(const char *arg, struct record_opts *opts)
static void callchain_debug(struct record_opts *opts)
{
pr_debug("callchain: type %d\n", opts->call_graph);
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" };
pr_debug("callchain: type %s\n", str[opts->call_graph]);
if (opts->call_graph == CALLCHAIN_DWARF)
pr_debug("callchain: stack dump size %d\n",
@ -749,6 +751,8 @@ int record_parse_callchain_opt(const struct option *opt,
struct record_opts *opts = opt->value;
int ret;
opts->call_graph_enabled = !unset;
/* --no-call-graph */
if (unset) {
opts->call_graph = CALLCHAIN_NONE;
@ -769,6 +773,8 @@ int record_callchain_opt(const struct option *opt,
{
struct record_opts *opts = opt->value;
opts->call_graph_enabled = !unset;
if (opts->call_graph == CALLCHAIN_NONE)
opts->call_graph = CALLCHAIN_FP;
@ -776,6 +782,16 @@ int record_callchain_opt(const struct option *opt,
return 0;
}
static int perf_record_config(const char *var, const char *value, void *cb)
{
struct record *rec = cb;
if (!strcmp(var, "record.call-graph"))
return record_parse_callchain(value, &rec->opts);
return perf_default_config(var, value, cb);
}
static const char * const record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
@ -807,7 +823,7 @@ static struct record record = {
#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
#ifdef HAVE_LIBUNWIND_SUPPORT
#ifdef HAVE_DWARF_UNWIND_SUPPORT
const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
#else
const char record_callchain_help[] = CALLCHAIN_HELP "fp";
@ -907,6 +923,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (rec->evlist == NULL)
return -ENOMEM;
perf_config(perf_record_config, rec);
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target__none(&rec->opts.target))

View File

@ -75,13 +75,10 @@ static int report__config(const char *var, const char *value, void *cb)
return perf_default_config(var, value, cb);
}
static int report__add_mem_hist_entry(struct perf_tool *tool, struct addr_location *al,
struct perf_sample *sample, struct perf_evsel *evsel,
union perf_event *event)
static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al,
struct perf_sample *sample, struct perf_evsel *evsel)
{
struct report *rep = container_of(tool, struct report, tool);
struct symbol *parent = NULL;
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
struct hist_entry *he;
struct mem_info *mi, *mx;
uint64_t cost;
@ -90,7 +87,7 @@ static int report__add_mem_hist_entry(struct perf_tool *tool, struct addr_locati
if (err)
return err;
mi = machine__resolve_mem(al->machine, al->thread, sample, cpumode);
mi = sample__resolve_mem(sample, al);
if (!mi)
return -ENOMEM;
@ -129,10 +126,9 @@ out:
return err;
}
static int report__add_branch_hist_entry(struct perf_tool *tool, struct addr_location *al,
static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al,
struct perf_sample *sample, struct perf_evsel *evsel)
{
struct report *rep = container_of(tool, struct report, tool);
struct symbol *parent = NULL;
unsigned i;
struct hist_entry *he;
@ -142,8 +138,7 @@ static int report__add_branch_hist_entry(struct perf_tool *tool, struct addr_loc
if (err)
return err;
bi = machine__resolve_bstack(al->machine, al->thread,
sample->branch_stack);
bi = sample__resolve_bstack(sample, al);
if (!bi)
return -ENOMEM;
@ -184,10 +179,9 @@ out:
return err;
}
static int report__add_hist_entry(struct perf_tool *tool, struct perf_evsel *evsel,
static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
struct addr_location *al, struct perf_sample *sample)
{
struct report *rep = container_of(tool, struct report, tool);
struct symbol *parent = NULL;
struct hist_entry *he;
int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
@ -236,18 +230,18 @@ static int process_sample_event(struct perf_tool *tool,
return 0;
if (sort__mode == SORT_MODE__BRANCH) {
ret = report__add_branch_hist_entry(tool, &al, sample, evsel);
ret = report__add_branch_hist_entry(rep, &al, sample, evsel);
if (ret < 0)
pr_debug("problem adding lbr entry, skipping event\n");
} else if (rep->mem_mode == 1) {
ret = report__add_mem_hist_entry(tool, &al, sample, evsel, event);
ret = report__add_mem_hist_entry(rep, &al, sample, evsel);
if (ret < 0)
pr_debug("problem adding mem entry, skipping event\n");
} else {
if (al.map != NULL)
al.map->dso->hit = 1;
ret = report__add_hist_entry(tool, evsel, &al, sample);
ret = report__add_hist_entry(rep, evsel, &al, sample);
if (ret < 0)
pr_debug("problem incrementing symbol period, skipping event\n");
}

View File

@ -991,6 +991,16 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
return record_parse_callchain_opt(opt, arg, unset);
}
static int perf_top_config(const char *var, const char *value, void *cb)
{
struct perf_top *top = cb;
if (!strcmp(var, "top.call-graph"))
return record_parse_callchain(value, &top->record_opts);
return perf_default_config(var, value, cb);
}
static int
parse_percent_limit(const struct option *opt, const char *arg,
int unset __maybe_unused)
@ -1115,6 +1125,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
if (top.evlist == NULL)
return -ENOMEM;
perf_config(perf_top_config, &top);
argc = parse_options(argc, argv, options, top_usage, 0);
if (argc)
usage_with_options(top_usage, options);

View File

@ -37,6 +37,10 @@
# define MADV_UNMERGEABLE 13
#endif
#ifndef EFD_SEMAPHORE
# define EFD_SEMAPHORE 1
#endif
struct tp_field {
int offset;
union {
@ -279,6 +283,11 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size,
#define SCA_STRARRAY syscall_arg__scnprintf_strarray
#if defined(__i386__) || defined(__x86_64__)
/*
* FIXME: Make this available to all arches as soon as the ioctl beautifier
* gets rewritten to support all arches.
*/
static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size,
struct syscall_arg *arg)
{
@ -286,6 +295,7 @@ static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size,
}
#define SCA_STRHEXARRAY syscall_arg__scnprintf_strhexarray
#endif /* defined(__i386__) || defined(__x86_64__) */
static size_t syscall_arg__scnprintf_fd(char *bf, size_t size,
struct syscall_arg *arg);
@ -839,6 +849,10 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal
#define SCA_SIGNUM syscall_arg__scnprintf_signum
#if defined(__i386__) || defined(__x86_64__)
/*
* FIXME: Make this available to all arches.
*/
#define TCGETS 0x5401
static const char *tioctls[] = {
@ -860,6 +874,7 @@ static const char *tioctls[] = {
};
static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401);
#endif /* defined(__i386__) || defined(__x86_64__) */
#define STRARRAY(arg, name, array) \
.arg_scnprintf = { [arg] = SCA_STRARRAY, }, \
@ -941,9 +956,16 @@ static struct syscall_fmt {
{ .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), },
{ .name = "ioctl", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FD, /* fd */
#if defined(__i386__) || defined(__x86_64__)
/*
* FIXME: Make this available to all arches.
*/
[1] = SCA_STRHEXARRAY, /* cmd */
[2] = SCA_HEX, /* arg */ },
.arg_parm = { [1] = &strarray__tioctls, /* cmd */ }, },
#else
[2] = SCA_HEX, /* arg */ }, },
#endif
{ .name = "kill", .errmsg = true,
.arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },
{ .name = "linkat", .errmsg = true,

View File

@ -338,7 +338,7 @@ ifndef NO_LIBUNWIND
CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
endif
CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
CFLAGS += -DHAVE_DWARF_UNWIND_SUPPORT -DHAVE_LIBUNWIND_SUPPORT
EXTLIBS += $(LIBUNWIND_LIBS)
CFLAGS += $(LIBUNWIND_CFLAGS)
LDFLAGS += $(LIBUNWIND_LDFLAGS)

View File

@ -251,12 +251,14 @@ void pthread__unblock_sigwinch(void);
enum perf_call_graph_mode {
CALLCHAIN_NONE,
CALLCHAIN_FP,
CALLCHAIN_DWARF
CALLCHAIN_DWARF,
CALLCHAIN_MAX
};
struct record_opts {
struct target target;
int call_graph;
bool call_graph_enabled;
bool group;
bool inherit_stat;
bool no_buffering;

View File

@ -115,6 +115,14 @@ static struct test {
.desc = "Test parsing with no sample_id_all bit set",
.func = test__parse_no_sample_id_all,
},
#if defined(__x86_64__) || defined(__i386__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
{
.desc = "Test dwarf unwind",
.func = test__dwarf_unwind,
},
#endif
#endif
{
.func = NULL,
},

View File

@ -0,0 +1,144 @@
#include <linux/compiler.h>
#include <sys/types.h>
#include <unistd.h>
#include "tests.h"
#include "debug.h"
#include "machine.h"
#include "event.h"
#include "unwind.h"
#include "perf_regs.h"
#include "map.h"
#include "thread.h"
static int mmap_handler(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
return machine__process_mmap_event(machine, event, NULL);
}
static int init_live_machine(struct machine *machine)
{
union perf_event event;
pid_t pid = getpid();
return perf_event__synthesize_mmap_events(NULL, &event, pid, pid,
mmap_handler, machine, true);
}
#define MAX_STACK 6
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
unsigned long *cnt = (unsigned long *) arg;
char *symbol = entry->sym ? entry->sym->name : NULL;
static const char *funcs[MAX_STACK] = {
"test__arch_unwind_sample",
"unwind_thread",
"krava_3",
"krava_2",
"krava_1",
"test__dwarf_unwind"
};
if (*cnt >= MAX_STACK) {
pr_debug("failed: crossed the max stack value %d\n", MAX_STACK);
return -1;
}
if (!symbol) {
pr_debug("failed: got unresolved address 0x%" PRIx64 "\n",
entry->ip);
return -1;
}
pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip);
return strcmp((const char *) symbol, funcs[(*cnt)++]);
}
__attribute__ ((noinline))
static int unwind_thread(struct thread *thread, struct machine *machine)
{
struct perf_sample sample;
unsigned long cnt = 0;
int err = -1;
memset(&sample, 0, sizeof(sample));
if (test__arch_unwind_sample(&sample, thread)) {
pr_debug("failed to get unwind sample\n");
goto out;
}
err = unwind__get_entries(unwind_entry, &cnt, machine, thread,
&sample, MAX_STACK);
if (err)
pr_debug("unwind failed\n");
else if (cnt != MAX_STACK) {
pr_debug("got wrong number of stack entries %lu != %d\n",
cnt, MAX_STACK);
err = -1;
}
out:
free(sample.user_stack.data);
free(sample.user_regs.regs);
return err;
}
__attribute__ ((noinline))
static int krava_3(struct thread *thread, struct machine *machine)
{
return unwind_thread(thread, machine);
}
__attribute__ ((noinline))
static int krava_2(struct thread *thread, struct machine *machine)
{
return krava_3(thread, machine);
}
__attribute__ ((noinline))
static int krava_1(struct thread *thread, struct machine *machine)
{
return krava_2(thread, machine);
}
int test__dwarf_unwind(void)
{
struct machines machines;
struct machine *machine;
struct thread *thread;
int err = -1;
machines__init(&machines);
machine = machines__find(&machines, HOST_KERNEL_ID);
if (!machine) {
pr_err("Could not get machine\n");
return -1;
}
if (init_live_machine(machine)) {
pr_err("Could not init machine\n");
goto out;
}
if (verbose > 1)
machine__fprintf(machine, stderr);
thread = machine__find_thread(machine, getpid());
if (!thread) {
pr_err("Could not get thread\n");
goto out;
}
err = krava_1(thread, machine);
out:
machine__delete_threads(machine);
machine__exit(machine);
machines__exit(&machines);
return err;
}

View File

@ -2,7 +2,7 @@
#include "parse-events.h"
#include "evsel.h"
#include "evlist.h"
#include "fs.h"
#include <api/fs/fs.h>
#include <api/fs/debugfs.h>
#include "tests.h"
#include <linux/hw_breakpoint.h>

View File

@ -22,8 +22,8 @@
} while (0)
static bool samples_same(const struct perf_sample *s1,
const struct perf_sample *s2, u64 type, u64 regs_user,
u64 read_format)
const struct perf_sample *s2,
u64 type, u64 read_format)
{
size_t i;
@ -95,8 +95,9 @@ static bool samples_same(const struct perf_sample *s1,
}
if (type & PERF_SAMPLE_REGS_USER) {
size_t sz = hweight_long(regs_user) * sizeof(u64);
size_t sz = hweight_long(s1->user_regs.mask) * sizeof(u64);
COMP(user_regs.mask);
COMP(user_regs.abi);
if (s1->user_regs.abi &&
(!s1->user_regs.regs || !s2->user_regs.regs ||
@ -174,6 +175,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
.branch_stack = &branch_stack.branch_stack,
.user_regs = {
.abi = PERF_SAMPLE_REGS_ABI_64,
.mask = sample_regs_user,
.regs = user_regs,
},
.user_stack = {
@ -201,8 +203,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
sample.read.one.id = 99;
}
sz = perf_event__sample_event_size(&sample, sample_type,
sample_regs_user, read_format);
sz = perf_event__sample_event_size(&sample, sample_type, read_format);
bufsz = sz + 4096; /* Add a bit for overrun checking */
event = malloc(bufsz);
if (!event) {
@ -215,8 +216,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
event->header.misc = 0;
event->header.size = sz;
err = perf_event__synthesize_sample(event, sample_type,
sample_regs_user, read_format,
err = perf_event__synthesize_sample(event, sample_type, read_format,
&sample, false);
if (err) {
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
@ -244,8 +244,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
goto out_free;
}
if (!samples_same(&sample, &sample_out, sample_type,
sample_regs_user, read_format)) {
if (!samples_same(&sample, &sample_out, sample_type, read_format)) {
pr_debug("parsing failed for sample_type %#"PRIx64"\n",
sample_type);
goto out_free;

View File

@ -40,5 +40,14 @@ int test__code_reading(void);
int test__sample_parsing(void);
int test__keep_tracking(void);
int test__parse_no_sample_id_all(void);
int test__dwarf_unwind(void);
#if defined(__x86_64__) || defined(__i386__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
struct perf_sample;
int test__arch_unwind_sample(struct perf_sample *sample,
struct thread *thread);
#endif
#endif
#endif /* TESTS_H */

View File

@ -1,5 +1,5 @@
#include "util.h"
#include "fs.h"
#include <api/fs/fs.h>
#include "../perf.h"
#include "cpumap.h"
#include <assert.h>

View File

@ -102,6 +102,16 @@ struct dso {
char name[0];
};
/* dso__for_each_symbol - iterate over the symbols of given type
*
* @dso: the 'struct dso *' in which symbols itereated
* @pos: the 'struct symbol *' to use as a loop cursor
* @n: the 'struct rb_node *' to use as a temporary storage
* @type: the 'enum map_type' type of symbols
*/
#define dso__for_each_symbol(dso, pos, n, type) \
symbols__for_each_entry(&(dso)->symbols[(type)], pos, n)
static inline void dso__set_loaded(struct dso *dso, enum map_type type)
{
dso->loaded |= (1 << type);

View File

@ -85,6 +85,7 @@ struct sample_event {
struct regs_dump {
u64 abi;
u64 mask;
u64 *regs;
};
@ -259,9 +260,9 @@ int perf_event__preprocess_sample(const union perf_event *event,
const char *perf_event__name(unsigned int id);
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
u64 sample_regs_user, u64 read_format);
u64 read_format);
int perf_event__synthesize_sample(union perf_event *event, u64 type,
u64 sample_regs_user, u64 read_format,
u64 read_format,
const struct perf_sample *sample,
bool swapped);

View File

@ -595,7 +595,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
attr->mmap_data = track;
}
if (opts->call_graph) {
if (opts->call_graph_enabled) {
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
if (opts->call_graph == CALLCHAIN_DWARF) {
@ -1220,7 +1220,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
memset(data, 0, sizeof(*data));
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
data->period = 1;
data->period = evsel->attr.sample_period;
data->weight = 0;
if (event->header.type != PERF_RECORD_SAMPLE) {
@ -1396,10 +1396,11 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
array++;
if (data->user_regs.abi) {
u64 regs_user = evsel->attr.sample_regs_user;
u64 mask = evsel->attr.sample_regs_user;
sz = hweight_long(regs_user) * sizeof(u64);
sz = hweight_long(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
data->user_regs.mask = mask;
data->user_regs.regs = (u64 *)array;
array = (void *)array + sz;
}
@ -1451,7 +1452,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
}
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
u64 sample_regs_user, u64 read_format)
u64 read_format)
{
size_t sz, result = sizeof(struct sample_event);
@ -1517,7 +1518,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
if (type & PERF_SAMPLE_REGS_USER) {
if (sample->user_regs.abi) {
result += sizeof(u64);
sz = hweight_long(sample_regs_user) * sizeof(u64);
sz = hweight_long(sample->user_regs.mask) * sizeof(u64);
result += sz;
} else {
result += sizeof(u64);
@ -1546,7 +1547,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
}
int perf_event__synthesize_sample(union perf_event *event, u64 type,
u64 sample_regs_user, u64 read_format,
u64 read_format,
const struct perf_sample *sample,
bool swapped)
{
@ -1687,7 +1688,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
if (type & PERF_SAMPLE_REGS_USER) {
if (sample->user_regs.abi) {
*array++ = sample->user_regs.abi;
sz = hweight_long(sample_regs_user) * sizeof(u64);
sz = hweight_long(sample->user_regs.mask) * sizeof(u64);
memcpy(array, sample->user_regs.regs, sz);
array = (void *)array + sz;
} else {

View File

@ -1,7 +0,0 @@
#ifndef __PERF_FS
#define __PERF_FS
const char *sysfs__mountpoint(void);
const char *procfs__mountpoint(void);
#endif /* __PERF_FS */

View File

@ -290,7 +290,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
if (he->branch_info) {
/*
* This branch info is (a part of) allocated from
* machine__resolve_bstack() and will be freed after
* sample__resolve_bstack() and will be freed after
* adding new entries. So we need to save a copy.
*/
he->branch_info = malloc(sizeof(*he->branch_info));
@ -369,7 +369,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
he_stat__add_period(&he->stat, period, weight);
/*
* This mem info was allocated from machine__resolve_mem
* This mem info was allocated from sample__resolve_mem
* and will not be used anymore.
*/
zfree(&entry->mem_info);

View File

@ -1,5 +0,0 @@
#include "../../../../include/linux/hash.h"
#ifndef PERF_HASH_H
#define PERF_HASH_H
#endif

View File

@ -1,5 +1,4 @@
#include <linux/kernel.h>
#include <linux/prefetch.h>
#include "../../../../include/linux/list.h"

View File

@ -1,6 +0,0 @@
#ifndef PERF_LINUX_PREFETCH_H
#define PERF_LINUX_PREFETCH_H
static inline void prefetch(void *a __attribute__((unused))) { }
#endif

View File

@ -1238,37 +1238,35 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,
ams->map = al.map;
}
struct mem_info *machine__resolve_mem(struct machine *machine,
struct thread *thr,
struct perf_sample *sample,
u8 cpumode)
struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al)
{
struct mem_info *mi = zalloc(sizeof(*mi));
if (!mi)
return NULL;
ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip);
ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr);
ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip);
ip__resolve_data(al->machine, al->thread, al->cpumode,
&mi->daddr, sample->addr);
mi->data_src.val = sample->data_src;
return mi;
}
struct branch_info *machine__resolve_bstack(struct machine *machine,
struct thread *thr,
struct branch_stack *bs)
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al)
{
struct branch_info *bi;
unsigned int i;
const struct branch_stack *bs = sample->branch_stack;
struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info));
bi = calloc(bs->nr, sizeof(struct branch_info));
if (!bi)
return NULL;
for (i = 0; i < bs->nr; i++) {
ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to);
ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from);
ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to);
ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags;
}
return bi;
@ -1385,8 +1383,7 @@ int machine__resolve_callchain(struct machine *machine,
return 0;
return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
thread, evsel->attr.sample_regs_user,
sample, max_stack);
thread, sample, max_stack);
}

View File

@ -91,12 +91,10 @@ void machine__delete_dead_threads(struct machine *machine);
void machine__delete_threads(struct machine *machine);
void machine__delete(struct machine *machine);
struct branch_info *machine__resolve_bstack(struct machine *machine,
struct thread *thread,
struct branch_stack *bs);
struct mem_info *machine__resolve_mem(struct machine *machine,
struct thread *thread,
struct perf_sample *sample, u8 cpumode);
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al);
struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al);
int machine__resolve_callchain(struct machine *machine,
struct perf_evsel *evsel,
struct thread *thread,

View File

@ -90,6 +90,16 @@ u64 map__objdump_2mem(struct map *map, u64 ip);
struct symbol;
/* map__for_each_symbol - iterate over the symbols in the given map
*
* @map: the 'struct map *' in which symbols itereated
* @pos: the 'struct symbol *' to use as a loop cursor
* @n: the 'struct rb_node *' to use as a temporary storage
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
#define map__for_each_symbol(map, pos, n) \
dso__for_each_symbol(map->dso, pos, n, map->type)
typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
void map__init(struct map *map, enum map_type type,

View File

@ -1091,12 +1091,12 @@ int is_valid_tracepoint(const char *event_string)
static bool is_event_supported(u8 type, unsigned config)
{
bool ret = true;
int open_return;
struct perf_evsel *evsel;
struct perf_event_attr attr = {
.type = type,
.config = config,
.disabled = 1,
.exclude_kernel = 1,
};
struct {
struct thread_map map;
@ -1108,7 +1108,20 @@ static bool is_event_supported(u8 type, unsigned config)
evsel = perf_evsel__new(&attr);
if (evsel) {
ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0;
open_return = perf_evsel__open(evsel, NULL, &tmap.map);
ret = open_return >= 0;
if (open_return == -EACCES) {
/*
* This happens if the paranoid value
* /proc/sys/kernel/perf_event_paranoid is set to 2
* Re-run with exclude_kernel set; we don't do that
* by default as some ARM machines do not support it.
*
*/
evsel->attr.exclude_kernel = 1;
ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0;
}
perf_evsel__delete(evsel);
}

View File

@ -0,0 +1,19 @@
#include <errno.h>
#include "perf_regs.h"
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
{
int i, idx = 0;
u64 mask = regs->mask;
if (!(mask & (1 << id)))
return -EINVAL;
for (i = 0; i < id; i++) {
if (mask & (1 << i))
idx++;
}
*valp = regs->regs[idx];
return 0;
}

View File

@ -1,8 +1,14 @@
#ifndef __PERF_REGS_H
#define __PERF_REGS_H
#include "types.h"
#include "event.h"
#ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h>
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
#else
#define PERF_REGS_MASK 0
@ -10,5 +16,12 @@ static inline const char *perf_reg_name(int id __maybe_unused)
{
return NULL;
}
static inline int perf_reg_value(u64 *valp __maybe_unused,
struct regs_dump *regs __maybe_unused,
int id __maybe_unused)
{
return 0;
}
#endif /* HAVE_PERF_REGS_SUPPORT */
#endif /* __PERF_REGS_H */

View File

@ -3,7 +3,7 @@
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include "fs.h"
#include <api/fs/fs.h>
#include <locale.h>
#include "util.h"
#include "pmu.h"

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
#define _PROBE_EVENT_H
#include <stdbool.h>
#include "intlist.h"
#include "strlist.h"
#include "strfilter.h"
@ -76,13 +77,6 @@ struct perf_probe_event {
struct perf_probe_arg *args; /* Arguments */
};
/* Line number container */
struct line_node {
struct list_head list;
int line;
};
/* Line range */
struct line_range {
char *file; /* File name */
@ -92,7 +86,7 @@ struct line_range {
int offset; /* Start line offset */
char *path; /* Real path name */
char *comp_dir; /* Compile directory */
struct list_head line_list; /* Visible lines */
struct intlist *line_list; /* Visible lines */
};
/* List of variables */
@ -124,7 +118,7 @@ extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
extern void line_range__clear(struct line_range *lr);
/* Initialize line range */
extern void line_range__init(struct line_range *lr);
extern int line_range__init(struct line_range *lr);
/* Internal use: Return kernel/module path */
extern const char *kernel_get_module_path(const char *module);

View File

@ -34,7 +34,9 @@
#include <linux/bitops.h>
#include "event.h"
#include "dso.h"
#include "debug.h"
#include "intlist.h"
#include "util.h"
#include "symbol.h"
#include "probe-finder.h"
@ -42,65 +44,6 @@
/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
/* Line number list operations */
/* Add a line to line number list */
static int line_list__add_line(struct list_head *head, int line)
{
struct line_node *ln;
struct list_head *p;
/* Reverse search, because new line will be the last one */
list_for_each_entry_reverse(ln, head, list) {
if (ln->line < line) {
p = &ln->list;
goto found;
} else if (ln->line == line) /* Already exist */
return 1;
}
/* List is empty, or the smallest entry */
p = head;
found:
pr_debug("line list: add a line %u\n", line);
ln = zalloc(sizeof(struct line_node));
if (ln == NULL)
return -ENOMEM;
ln->line = line;
INIT_LIST_HEAD(&ln->list);
list_add(&ln->list, p);
return 0;
}
/* Check if the line in line number list */
static int line_list__has_line(struct list_head *head, int line)
{
struct line_node *ln;
/* Reverse search, because new line will be the last one */
list_for_each_entry(ln, head, list)
if (ln->line == line)
return 1;
return 0;
}
/* Init line number list */
static void line_list__init(struct list_head *head)
{
INIT_LIST_HEAD(head);
}
/* Free line number list */
static void line_list__free(struct list_head *head)
{
struct line_node *ln;
while (!list_empty(head)) {
ln = list_first_entry(head, struct line_node, list);
list_del(&ln->list);
free(ln);
}
}
/* Dwarf FL wrappers */
static char *debuginfo_path; /* Currently dummy */
@ -147,80 +90,7 @@ error:
return -ENOENT;
}
#if _ELFUTILS_PREREQ(0, 148)
/* This method is buggy if elfutils is older than 0.148 */
static int __linux_kernel_find_elf(Dwfl_Module *mod,
void **userdata,
const char *module_name,
Dwarf_Addr base,
char **file_name, Elf **elfp)
{
int fd;
const char *path = kernel_get_module_path(module_name);
pr_debug2("Use file %s for %s\n", path, module_name);
if (path) {
fd = open(path, O_RDONLY);
if (fd >= 0) {
*file_name = strdup(path);
return fd;
}
}
/* If failed, try to call standard method */
return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
file_name, elfp);
}
static const Dwfl_Callbacks kernel_callbacks = {
.find_debuginfo = dwfl_standard_find_debuginfo,
.debuginfo_path = &debuginfo_path,
.find_elf = __linux_kernel_find_elf,
.section_address = dwfl_linux_kernel_module_section_address,
};
/* Get a Dwarf from live kernel image */
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg,
Dwarf_Addr addr)
{
dbg->dwfl = dwfl_begin(&kernel_callbacks);
if (!dbg->dwfl)
return -EINVAL;
/* Load the kernel dwarves: Don't care the result here */
dwfl_linux_kernel_report_kernel(dbg->dwfl);
dwfl_linux_kernel_report_modules(dbg->dwfl);
dbg->dbg = dwfl_addrdwarf(dbg->dwfl, addr, &dbg->bias);
/* Here, check whether we could get a real dwarf */
if (!dbg->dbg) {
pr_debug("Failed to find kernel dwarf at %lx\n",
(unsigned long)addr);
dwfl_end(dbg->dwfl);
memset(dbg, 0, sizeof(*dbg));
return -ENOENT;
}
return 0;
}
#else
/* With older elfutils, this just support kernel module... */
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg,
Dwarf_Addr addr __maybe_unused)
{
const char *path = kernel_get_module_path("kernel");
if (!path) {
pr_err("Failed to find vmlinux path\n");
return -ENOENT;
}
pr_debug2("Use file %s for debuginfo\n", path);
return debuginfo__init_offline_dwarf(dbg, path);
}
#endif
struct debuginfo *debuginfo__new(const char *path)
static struct debuginfo *__debuginfo__new(const char *path)
{
struct debuginfo *dbg = zalloc(sizeof(*dbg));
if (!dbg)
@ -228,21 +98,44 @@ struct debuginfo *debuginfo__new(const char *path)
if (debuginfo__init_offline_dwarf(dbg, path) < 0)
zfree(&dbg);
if (dbg)
pr_debug("Open Debuginfo file: %s\n", path);
return dbg;
}
struct debuginfo *debuginfo__new_online_kernel(unsigned long addr)
enum dso_binary_type distro_dwarf_types[] = {
DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__NOT_FOUND,
};
struct debuginfo *debuginfo__new(const char *path)
{
struct debuginfo *dbg = zalloc(sizeof(*dbg));
enum dso_binary_type *type;
char buf[PATH_MAX], nil = '\0';
struct dso *dso;
struct debuginfo *dinfo = NULL;
if (!dbg)
return NULL;
/* Try to open distro debuginfo files */
dso = dso__new(path);
if (!dso)
goto out;
if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0)
zfree(&dbg);
for (type = distro_dwarf_types;
!dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
type++) {
if (dso__read_binary_type_filename(dso, *type, &nil,
buf, PATH_MAX) < 0)
continue;
dinfo = __debuginfo__new(buf);
}
dso__delete(dso);
return dbg;
out:
/* if failed to open all distro debuginfo, open given binary */
return dinfo ? : __debuginfo__new(path);
}
void debuginfo__delete(struct debuginfo *dbg)
@ -880,7 +773,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
}
/* Find lines which match lazy pattern */
static int find_lazy_match_lines(struct list_head *head,
static int find_lazy_match_lines(struct intlist *list,
const char *fname, const char *pat)
{
FILE *fp;
@ -901,7 +794,7 @@ static int find_lazy_match_lines(struct list_head *head,
line[len - 1] = '\0';
if (strlazymatch(line, pat)) {
line_list__add_line(head, linenum);
intlist__add(list, linenum);
count++;
}
linenum++;
@ -924,7 +817,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
Dwarf_Die *sc_die, die_mem;
int ret;
if (!line_list__has_line(&pf->lcache, lineno) ||
if (!intlist__has_entry(pf->lcache, lineno) ||
strtailcmp(fname, pf->fname) != 0)
return 0;
@ -952,9 +845,9 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
{
int ret = 0;
if (list_empty(&pf->lcache)) {
if (intlist__empty(pf->lcache)) {
/* Matching lazy line pattern */
ret = find_lazy_match_lines(&pf->lcache, pf->fname,
ret = find_lazy_match_lines(pf->lcache, pf->fname,
pf->pev->point.lazy_line);
if (ret <= 0)
return ret;
@ -1096,7 +989,9 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
#endif
off = 0;
line_list__init(&pf->lcache);
pf->lcache = intlist__new(NULL);
if (!pf->lcache)
return -ENOMEM;
/* Fastpath: lookup by function name from .debug_pubnames section */
if (pp->function) {
@ -1149,7 +1044,8 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
}
found:
line_list__free(&pf->lcache);
intlist__delete(pf->lcache);
pf->lcache = NULL;
return ret;
}
@ -1537,7 +1433,7 @@ static int line_range_add_line(const char *src, unsigned int lineno,
if (lr->path == NULL)
return -ENOMEM;
}
return line_list__add_line(&lr->line_list, lineno);
return intlist__add(lr->line_list, lineno);
}
static int line_range_walk_cb(const char *fname, int lineno,
@ -1565,7 +1461,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
/* Update status */
if (ret >= 0)
if (!list_empty(&lf->lr->line_list))
if (!intlist__empty(lf->lr->line_list))
ret = lf->found = 1;
else
ret = 0; /* Lines are not found */

View File

@ -3,6 +3,7 @@
#include <stdbool.h>
#include "util.h"
#include "intlist.h"
#include "probe-event.h"
#define MAX_PROBE_BUFFER 1024
@ -29,8 +30,8 @@ struct debuginfo {
Dwarf_Addr bias;
};
/* This also tries to open distro debuginfo */
extern struct debuginfo *debuginfo__new(const char *path);
extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr);
extern void debuginfo__delete(struct debuginfo *dbg);
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
@ -66,7 +67,7 @@ struct probe_finder {
const char *fname; /* Real file name */
Dwarf_Die cu_die; /* Current CU */
Dwarf_Die sp_die;
struct list_head lcache; /* Line cache for lazy match */
struct intlist *lcache; /* Line cache for lazy match */
/* For variable searching */
#if _ELFUTILS_PREREQ(0, 142)

View File

@ -17,6 +17,6 @@ util/xyarray.c
util/cgroup.c
util/rblist.c
util/strlist.c
util/fs.c
../lib/api/fs/fs.c
util/trace-event.c
../../lib/rbtree.c

View File

@ -2,7 +2,7 @@
#include "evsel.h"
#include "cpumap.h"
#include "parse-events.h"
#include "fs.h"
#include <api/fs/fs.h>
#include "util.h"
typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel);

View File

@ -702,11 +702,12 @@ static void regs_dump__printf(u64 mask, u64 *regs)
}
}
static void regs_user__printf(struct perf_sample *sample, u64 mask)
static void regs_user__printf(struct perf_sample *sample)
{
struct regs_dump *user_regs = &sample->user_regs;
if (user_regs->regs) {
u64 mask = user_regs->mask;
printf("... user regs: mask 0x%" PRIx64 "\n", mask);
regs_dump__printf(mask, user_regs->regs);
}
@ -806,7 +807,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
branch_stack__printf(sample);
if (sample_type & PERF_SAMPLE_REGS_USER)
regs_user__printf(sample, evsel->attr.sample_regs_user);
regs_user__printf(sample);
if (sample_type & PERF_SAMPLE_STACK_USER)
stack_user__printf(&sample->user_stack);
@ -1008,6 +1009,12 @@ static int perf_session__process_user_event(struct perf_session *session, union
if (err == 0)
perf_session__set_id_hdr_size(session);
return err;
case PERF_RECORD_HEADER_EVENT_TYPE:
/*
* Depreceated, but we need to handle it for sake
* of old data files create in pipe mode.
*/
return 0;
case PERF_RECORD_HEADER_TRACING_DATA:
/* setup for reading amidst mmap */
lseek(fd, file_offset, SEEK_SET);

View File

@ -410,7 +410,7 @@ struct symbol *dso__find_symbol(struct dso *dso,
return symbols__find(&dso->symbols[type], addr);
}
struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)
static struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)
{
return symbols__first(&dso->symbols[type]);
}

View File

@ -79,6 +79,17 @@ struct symbol {
void symbol__delete(struct symbol *sym);
void symbols__delete(struct rb_root *symbols);
/* symbols__for_each_entry - iterate over symbols (rb_root)
*
* @symbols: the rb_root of symbols
* @pos: the 'struct symbol *' to use as a loop cursor
* @nd: the 'struct rb_node *' to use as a temporary storage
*/
#define symbols__for_each_entry(symbols, pos, nd) \
for (nd = rb_first(symbols); \
nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
nd = rb_next(nd))
static inline size_t symbol__size(const struct symbol *sym)
{
return sym->end - sym->start + 1;
@ -223,7 +234,6 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
u64 addr);
struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
const char *name);
struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);
int filename__read_build_id(const char *filename, void *bf, size_t size);
int sysfs__read_build_id(const char *filename, void *bf, size_t size);

View File

@ -126,6 +126,7 @@ void event_format__print(struct event_format *event,
trace_seq_init(&s);
pevent_event_info(&s, event, &record);
trace_seq_do_printf(&s);
trace_seq_destroy(&s);
}
void parse_proc_kallsyms(struct pevent *pevent,

View File

@ -86,7 +86,6 @@ struct unwind_info {
struct perf_sample *sample;
struct machine *machine;
struct thread *thread;
u64 sample_uregs;
};
#define dw_read(ptr, type, end) ({ \
@ -391,30 +390,13 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
return !(size == sizeof(*data));
}
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id,
u64 sample_regs)
{
int i, idx = 0;
if (!(sample_regs & (1 << id)))
return -EINVAL;
for (i = 0; i < id; i++) {
if (sample_regs & (1 << i))
idx++;
}
*valp = regs->regs[idx];
return 0;
}
static int access_mem(unw_addr_space_t __maybe_unused as,
unw_word_t addr, unw_word_t *valp,
int __write, void *arg)
{
struct unwind_info *ui = arg;
struct stack_dump *stack = &ui->sample->user_stack;
unw_word_t start, end;
u64 start, end;
int offset;
int ret;
@ -424,8 +406,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
return 0;
}
ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP,
ui->sample_uregs);
ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
if (ret)
return ret;
@ -438,8 +419,9 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
if (addr < start || addr + sizeof(unw_word_t) >= end) {
ret = access_dso_mem(ui, addr, valp);
if (ret) {
pr_debug("unwind: access_mem %p not inside range %p-%p\n",
(void *)addr, (void *)start, (void *)end);
pr_debug("unwind: access_mem %p not inside range"
" 0x%" PRIx64 "-0x%" PRIx64 "\n",
(void *) addr, start, end);
*valp = 0;
return ret;
}
@ -448,8 +430,8 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
offset = addr - start;
*valp = *(unw_word_t *)&stack->data[offset];
pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n",
(void *)addr, (unsigned long)*valp, offset);
pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
(void *) addr, (unsigned long)*valp, offset);
return 0;
}
@ -459,6 +441,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
{
struct unwind_info *ui = arg;
int id, ret;
u64 val;
/* Don't support write, I suspect we don't need it. */
if (__write) {
@ -471,16 +454,17 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
return 0;
}
id = unwind__arch_reg_id(regnum);
id = libunwind__arch_reg_id(regnum);
if (id < 0)
return -EINVAL;
ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs);
ret = perf_reg_value(&val, &ui->sample->user_regs, id);
if (ret) {
pr_err("unwind: can't read reg %d\n", regnum);
return ret;
}
*valp = (unw_word_t) val;
pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
return 0;
}
@ -563,7 +547,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
unw_word_t ip;
unw_get_reg(&c, UNW_REG_IP, &ip);
ret = entry(ip, ui->thread, ui->machine, cb, arg);
ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;
}
unw_destroy_addr_space(addr_space);
@ -572,13 +556,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct machine *machine, struct thread *thread,
u64 sample_uregs, struct perf_sample *data,
int max_stack)
struct perf_sample *data, int max_stack)
{
unw_word_t ip;
u64 ip;
struct unwind_info ui = {
.sample = data,
.sample_uregs = sample_uregs,
.thread = thread,
.machine = machine,
};
@ -587,7 +569,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (!data->user_regs.regs)
return -EINVAL;
ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs);
ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
if (ret)
return ret;
@ -595,5 +577,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (ret)
return -ENOMEM;
return get_entries(&ui, cb, arg, max_stack);
return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0;
}

View File

@ -13,24 +13,25 @@ struct unwind_entry {
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
#ifdef HAVE_LIBUNWIND_SUPPORT
#ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct machine *machine,
struct thread *thread,
u64 sample_uregs,
struct perf_sample *data, int max_stack);
int unwind__arch_reg_id(int regnum);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
int libunwind__arch_reg_id(int regnum);
#endif
#else
static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
void *arg __maybe_unused,
struct machine *machine __maybe_unused,
struct thread *thread __maybe_unused,
u64 sample_uregs __maybe_unused,
struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused)
{
return 0;
}
#endif /* HAVE_LIBUNWIND_SUPPORT */
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */

View File

@ -1,6 +1,6 @@
#include "../perf.h"
#include "util.h"
#include "fs.h"
#include <api/fs/fs.h>
#include <sys/mman.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>