diff options
Diffstat (limited to 'tools/perf/arch/x86/util')
| -rw-r--r-- | tools/perf/arch/x86/util/dwarf-regs.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/header.c | 59 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/tsc.c | 59 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/tsc.h | 20 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/unwind-libdw.c | 51 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/unwind-libunwind.c | 111 | 
6 files changed, 301 insertions, 1 deletions
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index a794d308192..be22dd46323 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c @@ -20,7 +20,7 @@   *   */ -#include <libio.h> +#include <stddef.h>  #include <dwarf-regs.h>  /* diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c new file mode 100644 index 00000000000..146d12a1cec --- /dev/null +++ b/tools/perf/arch/x86/util/header.c @@ -0,0 +1,59 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../../util/header.h" + +static inline void +cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, +      unsigned int *d) +{ +	__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t" +			      "movl %%ebx, %%esi\n\t.byte 0x5b" +			: "=a" (*a), +			"=S" (*b), +			"=c" (*c), +			"=d" (*d) +			: "a" (op)); +} + +int +get_cpuid(char *buffer, size_t sz) +{ +	unsigned int a, b, c, d, lvl; +	int family = -1, model = -1, step = -1; +	int nb; +	char vendor[16]; + +	cpuid(0, &lvl, &b, &c, &d); +	strncpy(&vendor[0], (char *)(&b), 4); +	strncpy(&vendor[4], (char *)(&d), 4); +	strncpy(&vendor[8], (char *)(&c), 4); +	vendor[12] = '\0'; + +	if (lvl >= 1) { +		cpuid(1, &a, &b, &c, &d); + +		family = (a >> 8) & 0xf;  /* bits 11 - 8 */ +		model  = (a >> 4) & 0xf;  /* Bits  7 - 4 */ +		step   = a & 0xf; + +		/* extended family */ +		if (family == 0xf) +			family += (a >> 20) & 0xff; + +		/* extended model */ +		if (family >= 0x6) +			model += ((a >> 16) & 0xf) << 4; +	} +	nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); + +	/* look for end marker to ensure the entire data fit */ +	if (strchr(buffer, '$')) { +		buffer[nb-1] = '\0'; +		return 0; +	} +	return -1; +} diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c new file mode 100644 index 00000000000..40021fa3129 --- /dev/null +++ b/tools/perf/arch/x86/util/tsc.c @@ -0,0 +1,59 @@ +#include <stdbool.h> +#include <errno.h> + +#include <linux/perf_event.h> + +#include "../../perf.h" +#include <linux/types.h> +#include "../../util/debug.h" +#include "tsc.h" + +u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc) +{ +	u64 t, quot, rem; + +	t = ns - tc->time_zero; +	quot = t / tc->time_mult; +	rem  = t % tc->time_mult; +	return (quot << tc->time_shift) + +	       (rem << tc->time_shift) / tc->time_mult; +} + +u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc) +{ +	u64 quot, rem; + +	quot = cyc >> tc->time_shift; +	rem  = cyc & ((1 << tc->time_shift) - 1); +	return tc->time_zero + quot * tc->time_mult + +	       ((rem * tc->time_mult) >> tc->time_shift); +} + +int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, +			     struct perf_tsc_conversion *tc) +{ +	bool cap_user_time_zero; +	u32 seq; +	int i = 0; + +	while (1) { +		seq = pc->lock; +		rmb(); +		tc->time_mult = pc->time_mult; +		tc->time_shift = pc->time_shift; +		tc->time_zero = pc->time_zero; +		cap_user_time_zero = pc->cap_user_time_zero; +		rmb(); +		if (pc->lock == seq && !(seq & 1)) +			break; +		if (++i > 10000) { +			pr_debug("failed to get perf_event_mmap_page lock\n"); +			return -EINVAL; +		} +	} + +	if (!cap_user_time_zero) +		return -EOPNOTSUPP; + +	return 0; +} diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h new file mode 100644 index 00000000000..2affe0366b5 --- /dev/null +++ b/tools/perf/arch/x86/util/tsc.h @@ -0,0 +1,20 @@ +#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ +#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ + +#include <linux/types.h> + +struct perf_tsc_conversion { +	u16 time_shift; +	u32 time_mult; +	u64 time_zero; +}; + +struct perf_event_mmap_page; + +int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, +			     struct perf_tsc_conversion *tc); + +u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); +u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); + +#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */ 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-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c new file mode 100644 index 00000000000..3261f68c6a7 --- /dev/null +++ b/tools/perf/arch/x86/util/unwind-libunwind.c @@ -0,0 +1,111 @@ + +#include <errno.h> +#include <libunwind.h> +#include "perf_regs.h" +#include "../../util/unwind.h" + +#ifdef HAVE_ARCH_X86_64_SUPPORT +int libunwind__arch_reg_id(int regnum) +{ +	int id; + +	switch (regnum) { +	case UNW_X86_64_RAX: +		id = PERF_REG_X86_AX; +		break; +	case UNW_X86_64_RDX: +		id = PERF_REG_X86_DX; +		break; +	case UNW_X86_64_RCX: +		id = PERF_REG_X86_CX; +		break; +	case UNW_X86_64_RBX: +		id = PERF_REG_X86_BX; +		break; +	case UNW_X86_64_RSI: +		id = PERF_REG_X86_SI; +		break; +	case UNW_X86_64_RDI: +		id = PERF_REG_X86_DI; +		break; +	case UNW_X86_64_RBP: +		id = PERF_REG_X86_BP; +		break; +	case UNW_X86_64_RSP: +		id = PERF_REG_X86_SP; +		break; +	case UNW_X86_64_R8: +		id = PERF_REG_X86_R8; +		break; +	case UNW_X86_64_R9: +		id = PERF_REG_X86_R9; +		break; +	case UNW_X86_64_R10: +		id = PERF_REG_X86_R10; +		break; +	case UNW_X86_64_R11: +		id = PERF_REG_X86_R11; +		break; +	case UNW_X86_64_R12: +		id = PERF_REG_X86_R12; +		break; +	case UNW_X86_64_R13: +		id = PERF_REG_X86_R13; +		break; +	case UNW_X86_64_R14: +		id = PERF_REG_X86_R14; +		break; +	case UNW_X86_64_R15: +		id = PERF_REG_X86_R15; +		break; +	case UNW_X86_64_RIP: +		id = PERF_REG_X86_IP; +		break; +	default: +		pr_err("unwind: invalid reg id %d\n", regnum); +		return -EINVAL; +	} + +	return id; +} +#else +int libunwind__arch_reg_id(int regnum) +{ +	int id; + +	switch (regnum) { +	case UNW_X86_EAX: +		id = PERF_REG_X86_AX; +		break; +	case UNW_X86_EDX: +		id = PERF_REG_X86_DX; +		break; +	case UNW_X86_ECX: +		id = PERF_REG_X86_CX; +		break; +	case UNW_X86_EBX: +		id = PERF_REG_X86_BX; +		break; +	case UNW_X86_ESI: +		id = PERF_REG_X86_SI; +		break; +	case UNW_X86_EDI: +		id = PERF_REG_X86_DI; +		break; +	case UNW_X86_EBP: +		id = PERF_REG_X86_BP; +		break; +	case UNW_X86_ESP: +		id = PERF_REG_X86_SP; +		break; +	case UNW_X86_EIP: +		id = PERF_REG_X86_IP; +		break; +	default: +		pr_err("unwind: invalid reg id %d\n", regnum); +		return -EINVAL; +	} + +	return id; +} +#endif /* HAVE_ARCH_X86_64_SUPPORT */  | 
