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:
commit
7e74efcf76
|
@ -0,0 +1,5 @@
|
|||
#include "../../../include/linux/hash.h"
|
||||
|
||||
#ifndef _TOOLS_LINUX_HASH_H
|
||||
#define _TOOLS_LINUX_HASH_H
|
||||
#endif
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
|
@ -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__ */
|
|
@ -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
|
||||
|
|
|
@ -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)"' $<
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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(¶ms.line_range);
|
||||
return line_range__init(¶ms.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;
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "util.h"
|
||||
#include "fs.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include "../perf.h"
|
||||
#include "cpumap.h"
|
||||
#include <assert.h>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#ifndef __PERF_FS
|
||||
#define __PERF_FS
|
||||
|
||||
const char *sysfs__mountpoint(void);
|
||||
const char *procfs__mountpoint(void);
|
||||
|
||||
#endif /* __PERF_FS */
|
|
@ -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);
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#include "../../../../include/linux/hash.h"
|
||||
|
||||
#ifndef PERF_HASH_H
|
||||
#define PERF_HASH_H
|
||||
#endif
|
|
@ -1,5 +1,4 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include "../../../../include/linux/list.h"
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef PERF_LINUX_PREFETCH_H
|
||||
#define PERF_LINUX_PREFETCH_H
|
||||
|
||||
static inline void prefetch(void *a __attribute__((unused))) { }
|
||||
|
||||
#endif
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue