Merge branch 'for-rmk/perf' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into devel-stable

This commit is contained in:
Russell King 2013-10-23 23:38:17 +01:00
commit 901e7e34f8
12 changed files with 252 additions and 22 deletions

View File

@ -51,6 +51,8 @@ config ARM
select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND
select HAVE_OPROFILE if (HAVE_PERF_EVENTS) select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
select HAVE_PERF_EVENTS select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_SYSCALL_TRACEPOINTS select HAVE_SYSCALL_TRACEPOINTS
select HAVE_UID16 select HAVE_UID16

View File

@ -7,6 +7,7 @@ header-y += hwcap.h
header-y += ioctls.h header-y += ioctls.h
header-y += kvm_para.h header-y += kvm_para.h
header-y += mman.h header-y += mman.h
header-y += perf_regs.h
header-y += posix_types.h header-y += posix_types.h
header-y += ptrace.h header-y += ptrace.h
header-y += setup.h header-y += setup.h

View File

@ -0,0 +1,23 @@
#ifndef _ASM_ARM_PERF_REGS_H
#define _ASM_ARM_PERF_REGS_H
enum perf_event_arm_regs {
PERF_REG_ARM_R0,
PERF_REG_ARM_R1,
PERF_REG_ARM_R2,
PERF_REG_ARM_R3,
PERF_REG_ARM_R4,
PERF_REG_ARM_R5,
PERF_REG_ARM_R6,
PERF_REG_ARM_R7,
PERF_REG_ARM_R8,
PERF_REG_ARM_R9,
PERF_REG_ARM_R10,
PERF_REG_ARM_FP,
PERF_REG_ARM_IP,
PERF_REG_ARM_SP,
PERF_REG_ARM_LR,
PERF_REG_ARM_PC,
PERF_REG_ARM_MAX,
};
#endif /* _ASM_ARM_PERF_REGS_H */

View File

@ -78,6 +78,7 @@ obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
obj-$(CONFIG_IWMMXT) += iwmmxt.o obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o

View File

@ -256,12 +256,11 @@ validate_event(struct pmu_hw_events *hw_events,
struct perf_event *event) struct perf_event *event)
{ {
struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct pmu *leader_pmu = event->group_leader->pmu;
if (is_software_event(event)) if (is_software_event(event))
return 1; return 1;
if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF) if (event->state < PERF_EVENT_STATE_OFF)
return 1; return 1;
if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)

View File

@ -0,0 +1,30 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/perf_event.h>
#include <linux/bug.h>
#include <asm/perf_regs.h>
#include <asm/ptrace.h>
u64 perf_reg_value(struct pt_regs *regs, int idx)
{
if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM_MAX))
return 0;
return regs->uregs[idx];
}
#define REG_RESERVED (~((1ULL << PERF_REG_ARM_MAX) - 1))
int perf_reg_validate(u64 mask)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
return 0;
}
u64 perf_reg_abi(struct task_struct *task)
{
return PERF_SAMPLE_REGS_ABI_32;
}

View File

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

View File

@ -0,0 +1,54 @@
#ifndef ARCH_PERF_REGS_H
#define ARCH_PERF_REGS_H
#include <stdlib.h>
#include "../../util/types.h"
#include <asm/perf_regs.h>
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1)
#define PERF_REG_IP PERF_REG_ARM_PC
#define PERF_REG_SP PERF_REG_ARM_SP
static inline const char *perf_reg_name(int id)
{
switch (id) {
case PERF_REG_ARM_R0:
return "r0";
case PERF_REG_ARM_R1:
return "r1";
case PERF_REG_ARM_R2:
return "r2";
case PERF_REG_ARM_R3:
return "r3";
case PERF_REG_ARM_R4:
return "r4";
case PERF_REG_ARM_R5:
return "r5";
case PERF_REG_ARM_R6:
return "r6";
case PERF_REG_ARM_R7:
return "r7";
case PERF_REG_ARM_R8:
return "r8";
case PERF_REG_ARM_R9:
return "r9";
case PERF_REG_ARM_R10:
return "r10";
case PERF_REG_ARM_FP:
return "fp";
case PERF_REG_ARM_IP:
return "ip";
case PERF_REG_ARM_SP:
return "sp";
case PERF_REG_ARM_LR:
return "lr";
case PERF_REG_ARM_PC:
return "pc";
default:
return NULL;
}
return NULL;
}
#endif /* ARCH_PERF_REGS_H */

View File

@ -0,0 +1,48 @@
#include <errno.h>
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
int unwind__arch_reg_id(int regnum)
{
switch (regnum) {
case UNW_ARM_R0:
return PERF_REG_ARM_R0;
case UNW_ARM_R1:
return PERF_REG_ARM_R1;
case UNW_ARM_R2:
return PERF_REG_ARM_R2;
case UNW_ARM_R3:
return PERF_REG_ARM_R3;
case UNW_ARM_R4:
return PERF_REG_ARM_R4;
case UNW_ARM_R5:
return PERF_REG_ARM_R5;
case UNW_ARM_R6:
return PERF_REG_ARM_R6;
case UNW_ARM_R7:
return PERF_REG_ARM_R7;
case UNW_ARM_R8:
return PERF_REG_ARM_R8;
case UNW_ARM_R9:
return PERF_REG_ARM_R9;
case UNW_ARM_R10:
return PERF_REG_ARM_R10;
case UNW_ARM_R11:
return PERF_REG_ARM_FP;
case UNW_ARM_R12:
return PERF_REG_ARM_IP;
case UNW_ARM_R13:
return PERF_REG_ARM_SP;
case UNW_ARM_R14:
return PERF_REG_ARM_LR;
case UNW_ARM_R15:
return PERF_REG_ARM_PC;
default:
pr_err("unwind: invalid reg id %d\n", regnum);
return -EINVAL;
}
return -EINVAL;
}

View File

@ -29,6 +29,10 @@ ifeq ($(ARCH),x86_64)
NO_PERF_REGS := 0 NO_PERF_REGS := 0
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
endif endif
ifeq ($(ARCH),arm)
NO_PERF_REGS := 0
LIBUNWIND_LIBS = -lunwind -lunwind-arm
endif
ifeq ($(NO_PERF_REGS),0) ifeq ($(NO_PERF_REGS),0)
CFLAGS += -DHAVE_PERF_REGS CFLAGS += -DHAVE_PERF_REGS
@ -208,8 +212,7 @@ ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y)
endif # try-cc endif # try-cc
endif # NO_LIBELF endif # NO_LIBELF
# There's only x86 (both 32 and 64) support for CFI unwind so far ifeq ($(LIBUNWIND_LIBS),)
ifneq ($(ARCH),x86)
NO_LIBUNWIND := 1 NO_LIBUNWIND := 1
endif endif
@ -223,9 +226,13 @@ endif
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y)
msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1);
NO_LIBUNWIND := 1 NO_LIBUNWIND := 1
endif # Libunwind support endif # Libunwind support
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND_DEBUG_FRAME),$(FLAGS_UNWIND),libunwind debug_frame),y)
msg := $(warning No debug_frame support found in libunwind);
CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
endif # debug_frame support in libunwind
endif # NO_LIBUNWIND endif # NO_LIBUNWIND
ifndef NO_LIBUNWIND ifndef NO_LIBUNWIND

View File

@ -185,7 +185,6 @@ extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
unw_proc_info_t *pi, unw_proc_info_t *pi,
int need_unwind_info, void *arg); int need_unwind_info, void *arg);
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
int main(void) int main(void)
@ -197,6 +196,26 @@ int main(void)
return 0; return 0;
} }
endef endef
define SOURCE_LIBUNWIND_DEBUG_FRAME
#include <libunwind.h>
#include <stdlib.h>
extern int
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
unw_word_t ip, unw_word_t segbase,
const char *obj_name, unw_word_t start,
unw_word_t end);
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
int main(void)
{
dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
return 0;
}
endef
endif endif
ifndef NO_BACKTRACE ifndef NO_BACKTRACE

View File

@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
extern int
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
unw_word_t ip,
unw_word_t segbase,
const char *obj_name, unw_word_t start,
unw_word_t end);
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
return 0; return 0;
} }
static int read_unwind_spec(struct dso *dso, struct machine *machine, static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
u64 *table_data, u64 *segbase, u64 *fde_count) u64 *table_data, u64 *segbase,
u64 *fde_count)
{ {
int ret = -EINVAL, fd; int ret = -EINVAL, fd;
u64 offset; u64 offset;
@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
if (fd < 0) if (fd < 0)
return -EINVAL; return -EINVAL;
/* Check the .eh_frame section for unwinding info */
offset = elf_section_offset(fd, ".eh_frame_hdr"); offset = elf_section_offset(fd, ".eh_frame_hdr");
close(fd); close(fd);
@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
table_data, segbase, table_data, segbase,
fde_count); fde_count);
/* TODO .debug_frame check if eh_frame_hdr fails */
return ret; return ret;
} }
#ifndef NO_LIBUNWIND_DEBUG_FRAME
static int read_unwind_spec_debug_frame(struct dso *dso,
struct machine *machine, u64 *offset)
{
int fd = dso__data_fd(dso, machine);
if (fd < 0)
return -EINVAL;
/* Check the .debug_frame section for unwinding info */
*offset = elf_section_offset(fd, ".debug_frame");
close(fd);
if (*offset)
return 0;
return -EINVAL;
}
#endif
static struct map *find_map(unw_word_t ip, struct unwind_info *ui) static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
{ {
struct addr_location al; struct addr_location al;
@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
if (read_unwind_spec(map->dso, ui->machine, /* Check the .eh_frame section for unwinding info */
&table_data, &segbase, &fde_count)) if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
return -EINVAL; &table_data, &segbase, &fde_count)) {
memset(&di, 0, sizeof(di));
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
di.start_ip = map->start;
di.end_ip = map->end;
di.u.rti.segbase = map->start + segbase;
di.u.rti.table_data = map->start + table_data;
di.u.rti.table_len = fde_count * sizeof(struct table_entry)
/ sizeof(unw_word_t);
return dwarf_search_unwind_table(as, ip, &di, pi,
need_unwind_info, arg);
}
memset(&di, 0, sizeof(di)); #ifndef NO_LIBUNWIND_DEBUG_FRAME
di.format = UNW_INFO_FORMAT_REMOTE_TABLE; /* Check the .debug_frame section for unwinding info */
di.start_ip = map->start; if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
di.end_ip = map->end; memset(&di, 0, sizeof(di));
di.u.rti.segbase = map->start + segbase; dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name,
di.u.rti.table_data = map->start + table_data; map->start, map->end);
di.u.rti.table_len = fde_count * sizeof(struct table_entry) return dwarf_search_unwind_table(as, ip, &di, pi,
/ sizeof(unw_word_t); need_unwind_info, arg);
return dwarf_search_unwind_table(as, ip, &di, pi, }
need_unwind_info, arg); #endif
return -EINVAL;
} }
static int access_fpreg(unw_addr_space_t __maybe_unused as, static int access_fpreg(unw_addr_space_t __maybe_unused as,