diff options
Diffstat (limited to 'tools/perf/arch')
| -rw-r--r-- | tools/perf/arch/arm/Makefile | 10 | ||||
| -rw-r--r-- | tools/perf/arch/arm/include/perf_regs.h | 59 | ||||
| -rw-r--r-- | tools/perf/arch/arm/tests/dwarf-unwind.c | 60 | ||||
| -rw-r--r-- | tools/perf/arch/arm/tests/regs_load.S | 58 | ||||
| -rw-r--r-- | tools/perf/arch/arm/util/unwind-libdw.c | 36 | ||||
| -rw-r--r-- | tools/perf/arch/arm/util/unwind-libunwind.c | 48 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/Makefile | 7 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/include/perf_regs.h | 88 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/util/dwarf-regs.c | 80 | ||||
| -rw-r--r-- | tools/perf/arch/arm64/util/unwind-libunwind.c | 82 | ||||
| -rw-r--r-- | tools/perf/arch/common.c | 3 | ||||
| -rw-r--r-- | tools/perf/arch/x86/Makefile | 9 | ||||
| -rw-r--r-- | tools/perf/arch/x86/include/perf_regs.h | 14 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/dwarf-unwind.c | 60 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/regs_load.S | 98 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/tsc.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/tsc.h | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/unwind-libdw.c | 51 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/unwind-libunwind.c (renamed from tools/perf/arch/x86/util/unwind.c) | 8 | 
19 files changed, 762 insertions, 13 deletions
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 15130b50dfe..09d62153d38 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile @@ -2,3 +2,13 @@ ifndef NO_DWARF  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-libunwind.o +endif +ifndef NO_LIBDW_DWARF_UNWIND +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o +endif +ifndef NO_DWARF_UNWIND +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o +endif diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h new file mode 100644 index 00000000000..f619c9c5a4b --- /dev/null +++ b/tools/perf/arch/arm/include/perf_regs.h @@ -0,0 +1,59 @@ +#ifndef ARCH_PERF_REGS_H +#define ARCH_PERF_REGS_H + +#include <stdlib.h> +#include <linux/types.h> +#include <asm/perf_regs.h> + +void perf_regs_load(u64 *regs); + +#define PERF_REGS_MASK	((1ULL << PERF_REG_ARM_MAX) - 1) +#define PERF_REGS_MAX	PERF_REG_ARM_MAX +#define PERF_SAMPLE_REGS_ABI	PERF_SAMPLE_REGS_ABI_32 + +#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 */ diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c new file mode 100644 index 00000000000..9f870d27cb3 --- /dev/null +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c @@ -0,0 +1,60 @@ +#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_ARM_SP]; + +	map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp); +	if (!map) { +		pr_debug("failed to get stack map\n"); +		free(buf); +		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 = calloc(1, 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); +} diff --git a/tools/perf/arch/arm/tests/regs_load.S b/tools/perf/arch/arm/tests/regs_load.S new file mode 100644 index 00000000000..e09e983946f --- /dev/null +++ b/tools/perf/arch/arm/tests/regs_load.S @@ -0,0 +1,58 @@ +#include <linux/linkage.h> + +#define R0 0x00 +#define R1 0x08 +#define R2 0x10 +#define R3 0x18 +#define R4 0x20 +#define R5 0x28 +#define R6 0x30 +#define R7 0x38 +#define R8 0x40 +#define R9 0x48 +#define SL 0x50 +#define FP 0x58 +#define IP 0x60 +#define SP 0x68 +#define LR 0x70 +#define PC 0x78 + +/* + * Implementation of void perf_regs_load(u64 *regs); + * + * This functions fills in the 'regs' buffer from the actual registers values, + * in the way the perf built-in unwinding test expects them: + * - the PC at the time at the call to this function. Since this function + *   is called using a bl instruction, the PC value is taken from LR. + * The built-in unwinding test then unwinds the call stack from the dwarf + * information in unwind__get_entries. + * + * Notes: + * - the 8 bytes stride in the registers offsets comes from the fact + * that the registers are stored in an u64 array (u64 *regs), + * - the regs buffer needs to be zeroed before the call to this function, + * in this case using a calloc in dwarf-unwind.c. + */ + +.text +.type perf_regs_load,%function +ENTRY(perf_regs_load) +	str r0, [r0, #R0] +	str r1, [r0, #R1] +	str r2, [r0, #R2] +	str r3, [r0, #R3] +	str r4, [r0, #R4] +	str r5, [r0, #R5] +	str r6, [r0, #R6] +	str r7, [r0, #R7] +	str r8, [r0, #R8] +	str r9, [r0, #R9] +	str sl, [r0, #SL] +	str fp, [r0, #FP] +	str ip, [r0, #IP] +	str sp, [r0, #SP] +	str lr, [r0, #LR] +	str lr, [r0, #PC]	// store pc as lr in order to skip the call +	                        //  to this function +	mov pc, lr +ENDPROC(perf_regs_load) diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c new file mode 100644 index 00000000000..b4176c60117 --- /dev/null +++ b/tools/perf/arch/arm/util/unwind-libdw.c @@ -0,0 +1,36 @@ +#include <elfutils/libdwfl.h> +#include "../../util/unwind-libdw.h" +#include "../../util/perf_regs.h" + +bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) +{ +	struct unwind_info *ui = arg; +	struct regs_dump *user_regs = &ui->sample->user_regs; +	Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX]; + +#define REG(r) ({						\ +	Dwarf_Word val = 0;					\ +	perf_reg_value(&val, user_regs, PERF_REG_ARM_##r);	\ +	val;							\ +}) + +	dwarf_regs[0]  = REG(R0); +	dwarf_regs[1]  = REG(R1); +	dwarf_regs[2]  = REG(R2); +	dwarf_regs[3]  = REG(R3); +	dwarf_regs[4]  = REG(R4); +	dwarf_regs[5]  = REG(R5); +	dwarf_regs[6]  = REG(R6); +	dwarf_regs[7]  = REG(R7); +	dwarf_regs[8]  = REG(R8); +	dwarf_regs[9]  = REG(R9); +	dwarf_regs[10] = REG(R10); +	dwarf_regs[11] = REG(FP); +	dwarf_regs[12] = REG(IP); +	dwarf_regs[13] = REG(SP); +	dwarf_regs[14] = REG(LR); +	dwarf_regs[15] = REG(PC); + +	return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX, +					   dwarf_regs); +} diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c new file mode 100644 index 00000000000..729ed69a666 --- /dev/null +++ b/tools/perf/arch/arm/util/unwind-libunwind.c @@ -0,0 +1,48 @@ + +#include <errno.h> +#include <libunwind.h> +#include "perf_regs.h" +#include "../../util/unwind.h" + +int libunwind__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; +} diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile new file mode 100644 index 00000000000..67e9b3d38e8 --- /dev/null +++ b/tools/perf/arch/arm64/Makefile @@ -0,0 +1,7 @@ +ifndef NO_DWARF +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-libunwind.o +endif diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h new file mode 100644 index 00000000000..e9441b9e2a3 --- /dev/null +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -0,0 +1,88 @@ +#ifndef ARCH_PERF_REGS_H +#define ARCH_PERF_REGS_H + +#include <stdlib.h> +#include <linux/types.h> +#include <asm/perf_regs.h> + +#define PERF_REGS_MASK	((1ULL << PERF_REG_ARM64_MAX) - 1) +#define PERF_REG_IP	PERF_REG_ARM64_PC +#define PERF_REG_SP	PERF_REG_ARM64_SP + +static inline const char *perf_reg_name(int id) +{ +	switch (id) { +	case PERF_REG_ARM64_X0: +		return "x0"; +	case PERF_REG_ARM64_X1: +		return "x1"; +	case PERF_REG_ARM64_X2: +		return "x2"; +	case PERF_REG_ARM64_X3: +		return "x3"; +	case PERF_REG_ARM64_X4: +		return "x4"; +	case PERF_REG_ARM64_X5: +		return "x5"; +	case PERF_REG_ARM64_X6: +		return "x6"; +	case PERF_REG_ARM64_X7: +		return "x7"; +	case PERF_REG_ARM64_X8: +		return "x8"; +	case PERF_REG_ARM64_X9: +		return "x9"; +	case PERF_REG_ARM64_X10: +		return "x10"; +	case PERF_REG_ARM64_X11: +		return "x11"; +	case PERF_REG_ARM64_X12: +		return "x12"; +	case PERF_REG_ARM64_X13: +		return "x13"; +	case PERF_REG_ARM64_X14: +		return "x14"; +	case PERF_REG_ARM64_X15: +		return "x15"; +	case PERF_REG_ARM64_X16: +		return "x16"; +	case PERF_REG_ARM64_X17: +		return "x17"; +	case PERF_REG_ARM64_X18: +		return "x18"; +	case PERF_REG_ARM64_X19: +		return "x19"; +	case PERF_REG_ARM64_X20: +		return "x20"; +	case PERF_REG_ARM64_X21: +		return "x21"; +	case PERF_REG_ARM64_X22: +		return "x22"; +	case PERF_REG_ARM64_X23: +		return "x23"; +	case PERF_REG_ARM64_X24: +		return "x24"; +	case PERF_REG_ARM64_X25: +		return "x25"; +	case PERF_REG_ARM64_X26: +		return "x26"; +	case PERF_REG_ARM64_X27: +		return "x27"; +	case PERF_REG_ARM64_X28: +		return "x28"; +	case PERF_REG_ARM64_X29: +		return "x29"; +	case PERF_REG_ARM64_SP: +		return "sp"; +	case PERF_REG_ARM64_LR: +		return "lr"; +	case PERF_REG_ARM64_PC: +		return "pc"; +	default: +		return NULL; +	} + +	return NULL; +} + +#endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c new file mode 100644 index 00000000000..d49efeb8172 --- /dev/null +++ b/tools/perf/arch/arm64/util/dwarf-regs.c @@ -0,0 +1,80 @@ +/* + * Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2010 Will Deacon, ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <stddef.h> +#include <dwarf-regs.h> + +struct pt_regs_dwarfnum { +	const char *name; +	unsigned int dwarfnum; +}; + +#define STR(s) #s +#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} +#define GPR_DWARFNUM_NAME(num) \ +	{.name = STR(%x##num), .dwarfnum = num} +#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} + +/* + * Reference: + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf + */ +static const struct pt_regs_dwarfnum regdwarfnum_table[] = { +	GPR_DWARFNUM_NAME(0), +	GPR_DWARFNUM_NAME(1), +	GPR_DWARFNUM_NAME(2), +	GPR_DWARFNUM_NAME(3), +	GPR_DWARFNUM_NAME(4), +	GPR_DWARFNUM_NAME(5), +	GPR_DWARFNUM_NAME(6), +	GPR_DWARFNUM_NAME(7), +	GPR_DWARFNUM_NAME(8), +	GPR_DWARFNUM_NAME(9), +	GPR_DWARFNUM_NAME(10), +	GPR_DWARFNUM_NAME(11), +	GPR_DWARFNUM_NAME(12), +	GPR_DWARFNUM_NAME(13), +	GPR_DWARFNUM_NAME(14), +	GPR_DWARFNUM_NAME(15), +	GPR_DWARFNUM_NAME(16), +	GPR_DWARFNUM_NAME(17), +	GPR_DWARFNUM_NAME(18), +	GPR_DWARFNUM_NAME(19), +	GPR_DWARFNUM_NAME(20), +	GPR_DWARFNUM_NAME(21), +	GPR_DWARFNUM_NAME(22), +	GPR_DWARFNUM_NAME(23), +	GPR_DWARFNUM_NAME(24), +	GPR_DWARFNUM_NAME(25), +	GPR_DWARFNUM_NAME(26), +	GPR_DWARFNUM_NAME(27), +	GPR_DWARFNUM_NAME(28), +	GPR_DWARFNUM_NAME(29), +	REG_DWARFNUM_NAME("%lr", 30), +	REG_DWARFNUM_NAME("%sp", 31), +	REG_DWARFNUM_END, +}; + +/** + * get_arch_regstr() - lookup register name from it's DWARF register number + * @n:	the DWARF register number + * + * get_arch_regstr() returns the name of the register in struct + * regdwarfnum_table from it's DWARF register number. If the register is not + * found in the table, this returns NULL; + */ +const char *get_arch_regstr(unsigned int n) +{ +	const struct pt_regs_dwarfnum *roff; +	for (roff = regdwarfnum_table; roff->name != NULL; roff++) +		if (roff->dwarfnum == n) +			return roff->name; +	return NULL; +} diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c new file mode 100644 index 00000000000..436ee43859d --- /dev/null +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c @@ -0,0 +1,82 @@ + +#include <errno.h> +#include <libunwind.h> +#include "perf_regs.h" +#include "../../util/unwind.h" + +int libunwind__arch_reg_id(int regnum) +{ +	switch (regnum) { +	case UNW_AARCH64_X0: +		return PERF_REG_ARM64_X0; +	case UNW_AARCH64_X1: +		return PERF_REG_ARM64_X1; +	case UNW_AARCH64_X2: +		return PERF_REG_ARM64_X2; +	case UNW_AARCH64_X3: +		return PERF_REG_ARM64_X3; +	case UNW_AARCH64_X4: +		return PERF_REG_ARM64_X4; +	case UNW_AARCH64_X5: +		return PERF_REG_ARM64_X5; +	case UNW_AARCH64_X6: +		return PERF_REG_ARM64_X6; +	case UNW_AARCH64_X7: +		return PERF_REG_ARM64_X7; +	case UNW_AARCH64_X8: +		return PERF_REG_ARM64_X8; +	case UNW_AARCH64_X9: +		return PERF_REG_ARM64_X9; +	case UNW_AARCH64_X10: +		return PERF_REG_ARM64_X10; +	case UNW_AARCH64_X11: +		return PERF_REG_ARM64_X11; +	case UNW_AARCH64_X12: +		return PERF_REG_ARM64_X12; +	case UNW_AARCH64_X13: +		return PERF_REG_ARM64_X13; +	case UNW_AARCH64_X14: +		return PERF_REG_ARM64_X14; +	case UNW_AARCH64_X15: +		return PERF_REG_ARM64_X15; +	case UNW_AARCH64_X16: +		return PERF_REG_ARM64_X16; +	case UNW_AARCH64_X17: +		return PERF_REG_ARM64_X17; +	case UNW_AARCH64_X18: +		return PERF_REG_ARM64_X18; +	case UNW_AARCH64_X19: +		return PERF_REG_ARM64_X19; +	case UNW_AARCH64_X20: +		return PERF_REG_ARM64_X20; +	case UNW_AARCH64_X21: +		return PERF_REG_ARM64_X21; +	case UNW_AARCH64_X22: +		return PERF_REG_ARM64_X22; +	case UNW_AARCH64_X23: +		return PERF_REG_ARM64_X23; +	case UNW_AARCH64_X24: +		return PERF_REG_ARM64_X24; +	case UNW_AARCH64_X25: +		return PERF_REG_ARM64_X25; +	case UNW_AARCH64_X26: +		return PERF_REG_ARM64_X26; +	case UNW_AARCH64_X27: +		return PERF_REG_ARM64_X27; +	case UNW_AARCH64_X28: +		return PERF_REG_ARM64_X28; +	case UNW_AARCH64_X29: +		return PERF_REG_ARM64_X29; +	case UNW_AARCH64_X30: +		return PERF_REG_ARM64_LR; +	case UNW_AARCH64_SP: +		return PERF_REG_ARM64_SP; +	case UNW_AARCH64_PC: +		return PERF_REG_ARM64_PC; +	default: +		pr_err("unwind: invalid reg id %d\n", regnum); +		return -EINVAL; +	} + +	return -EINVAL; +} diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index aacef07ebf3..42faf369211 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -154,8 +154,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env,  		}  		if (lookup_path(buf))  			goto out; -		free(buf); -		buf = NULL; +		zfree(&buf);  	}  	if (!strcmp(arch, "arm")) diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 8801fe02f20..1641542e363 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile @@ -3,7 +3,14 @@ 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 +ifndef NO_LIBDW_DWARF_UNWIND +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o +endif +ifndef NO_DWARF_UNWIND +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 diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index 7fcdcdbee91..7df517acfef 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -2,17 +2,23 @@  #define ARCH_PERF_REGS_H  #include <stdlib.h> -#include "../../util/types.h" +#include <linux/types.h>  #include <asm/perf_regs.h> -#ifndef ARCH_X86_64 +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 @@ -52,7 +58,7 @@ static inline const char *perf_reg_name(int id)  		return "FS";  	case PERF_REG_X86_GS:  		return "GS"; -#ifdef ARCH_X86_64 +#ifdef HAVE_ARCH_X86_64_SUPPORT  	case PERF_REG_X86_R8:  		return "R8";  	case PERF_REG_X86_R9: @@ -69,7 +75,7 @@ static inline const char *perf_reg_name(int id)  		return "R14";  	case PERF_REG_X86_R15:  		return "R15"; -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */  	default:  		return NULL;  	} diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c new file mode 100644 index 00000000000..9f89f899ccc --- /dev/null +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -0,0 +1,60 @@ +#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__VARIABLE, (u64) sp); +	if (!map) { +		pr_debug("failed to get stack map\n"); +		free(buf); +		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); +} diff --git a/tools/perf/arch/x86/tests/regs_load.S b/tools/perf/arch/x86/tests/regs_load.S new file mode 100644 index 00000000000..60875d5c556 --- /dev/null +++ b/tools/perf/arch/x86/tests/regs_load.S @@ -0,0 +1,98 @@ +#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 + +/* + * We need to provide note.GNU-stack section, saying that we want + * NOT executable stack. Otherwise the final linking will assume that + * the ELF stack should not be restricted at all and set it RWX. + */ +.section .note.GNU-stack,"",@progbits diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index b2519e49424..40021fa3129 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -4,7 +4,7 @@  #include <linux/perf_event.h>  #include "../../perf.h" -#include "../../util/types.h" +#include <linux/types.h>  #include "../../util/debug.h"  #include "tsc.h" diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h index a24dec81c79..2affe0366b5 100644 --- a/tools/perf/arch/x86/util/tsc.h +++ b/tools/perf/arch/x86/util/tsc.h @@ -1,7 +1,7 @@  #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__  #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ -#include "../../util/types.h" +#include <linux/types.h>  struct perf_tsc_conversion {  	u16 time_shift; diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/arch/x86/util/unwind-libdw.c new file mode 100644 index 00000000000..c4b72176ca8 --- /dev/null +++ b/tools/perf/arch/x86/util/unwind-libdw.c @@ -0,0 +1,51 @@ +#include <elfutils/libdwfl.h> +#include "../../util/unwind-libdw.h" +#include "../../util/perf_regs.h" + +bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) +{ +	struct unwind_info *ui = arg; +	struct regs_dump *user_regs = &ui->sample->user_regs; +	Dwarf_Word dwarf_regs[17]; +	unsigned nregs; + +#define REG(r) ({						\ +	Dwarf_Word val = 0;					\ +	perf_reg_value(&val, user_regs, PERF_REG_X86_##r);	\ +	val;							\ +}) + +	if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) { +		dwarf_regs[0] = REG(AX); +		dwarf_regs[1] = REG(CX); +		dwarf_regs[2] = REG(DX); +		dwarf_regs[3] = REG(BX); +		dwarf_regs[4] = REG(SP); +		dwarf_regs[5] = REG(BP); +		dwarf_regs[6] = REG(SI); +		dwarf_regs[7] = REG(DI); +		dwarf_regs[8] = REG(IP); +		nregs = 9; +	} else { +		dwarf_regs[0]  = REG(AX); +		dwarf_regs[1]  = REG(DX); +		dwarf_regs[2]  = REG(CX); +		dwarf_regs[3]  = REG(BX); +		dwarf_regs[4]  = REG(SI); +		dwarf_regs[5]  = REG(DI); +		dwarf_regs[6]  = REG(BP); +		dwarf_regs[7]  = REG(SP); +		dwarf_regs[8]  = REG(R8); +		dwarf_regs[9]  = REG(R9); +		dwarf_regs[10] = REG(R10); +		dwarf_regs[11] = REG(R11); +		dwarf_regs[12] = REG(R12); +		dwarf_regs[13] = REG(R13); +		dwarf_regs[14] = REG(R14); +		dwarf_regs[15] = REG(R15); +		dwarf_regs[16] = REG(IP); +		nregs = 17; +	} + +	return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs); +} diff --git a/tools/perf/arch/x86/util/unwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c index 78d956eff96..3261f68c6a7 100644 --- a/tools/perf/arch/x86/util/unwind.c +++ b/tools/perf/arch/x86/util/unwind-libunwind.c @@ -4,8 +4,8 @@  #include "perf_regs.h"  #include "../../util/unwind.h" -#ifdef ARCH_X86_64 -int unwind__arch_reg_id(int regnum) +#ifdef HAVE_ARCH_X86_64_SUPPORT +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; @@ -108,4 +108,4 @@ int unwind__arch_reg_id(int regnum)  	return id;  } -#endif /* ARCH_X86_64 */ +#endif /* HAVE_ARCH_X86_64_SUPPORT */  | 
