mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-28 14:58:52 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										14
									
								
								tools/perf/arch/arm/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tools/perf/arch/arm/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| 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 | ||||
							
								
								
									
										59
									
								
								tools/perf/arch/arm/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tools/perf/arch/arm/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 */ | ||||
							
								
								
									
										61
									
								
								tools/perf/arch/arm/tests/dwarf-unwind.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tools/perf/arch/arm/tests/dwarf-unwind.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| #include <string.h> | ||||
| #include "perf_regs.h" | ||||
| #include "thread.h" | ||||
| #include "map.h" | ||||
| #include "event.h" | ||||
| #include "debug.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); | ||||
| } | ||||
							
								
								
									
										58
									
								
								tools/perf/arch/arm/tests/regs_load.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tools/perf/arch/arm/tests/regs_load.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										64
									
								
								tools/perf/arch/arm/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								tools/perf/arch/arm/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| /*
 | ||||
|  * 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(%r##num), .dwarfnum = num} | ||||
| #define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} | ||||
| 
 | ||||
| /*
 | ||||
|  * Reference: | ||||
|  * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.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), | ||||
| 	REG_DWARFNUM_NAME("%fp", 11), | ||||
| 	REG_DWARFNUM_NAME("%ip", 12), | ||||
| 	REG_DWARFNUM_NAME("%sp", 13), | ||||
| 	REG_DWARFNUM_NAME("%lr", 14), | ||||
| 	REG_DWARFNUM_NAME("%pc", 15), | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										36
									
								
								tools/perf/arch/arm/util/unwind-libdw.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tools/perf/arch/arm/util/unwind-libdw.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
| } | ||||
							
								
								
									
										49
									
								
								tools/perf/arch/arm/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tools/perf/arch/arm/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| 
 | ||||
| #include <errno.h> | ||||
| #include <libunwind.h> | ||||
| #include "perf_regs.h" | ||||
| #include "../../util/unwind.h" | ||||
| #include "../../util/debug.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; | ||||
| } | ||||
							
								
								
									
										7
									
								
								tools/perf/arch/arm64/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tools/perf/arch/arm64/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										90
									
								
								tools/perf/arch/arm64/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								tools/perf/arch/arm64/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | |||
| #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_REGS_MAX	PERF_REG_ARM64_MAX | ||||
| 
 | ||||
| #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 */ | ||||
							
								
								
									
										80
									
								
								tools/perf/arch/arm64/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								tools/perf/arch/arm64/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										83
									
								
								tools/perf/arch/arm64/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								tools/perf/arch/arm64/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| 
 | ||||
| #include <errno.h> | ||||
| #include <libunwind.h> | ||||
| #include "perf_regs.h" | ||||
| #include "../../util/unwind.h" | ||||
| #include "../../util/debug.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; | ||||
| } | ||||
							
								
								
									
										220
									
								
								tools/perf/arch/common.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								tools/perf/arch/common.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,220 @@ | |||
| #include <stdio.h> | ||||
| #include <sys/utsname.h> | ||||
| #include "common.h" | ||||
| #include "../util/debug.h" | ||||
| 
 | ||||
| const char *const arm_triplets[] = { | ||||
| 	"arm-eabi-", | ||||
| 	"arm-linux-androideabi-", | ||||
| 	"arm-unknown-linux-", | ||||
| 	"arm-unknown-linux-gnu-", | ||||
| 	"arm-unknown-linux-gnueabi-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const arm64_triplets[] = { | ||||
| 	"aarch64-linux-android-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const powerpc_triplets[] = { | ||||
| 	"powerpc-unknown-linux-gnu-", | ||||
| 	"powerpc64-unknown-linux-gnu-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const s390_triplets[] = { | ||||
| 	"s390-ibm-linux-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const sh_triplets[] = { | ||||
| 	"sh-unknown-linux-gnu-", | ||||
| 	"sh64-unknown-linux-gnu-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const sparc_triplets[] = { | ||||
| 	"sparc-unknown-linux-gnu-", | ||||
| 	"sparc64-unknown-linux-gnu-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const x86_triplets[] = { | ||||
| 	"x86_64-pc-linux-gnu-", | ||||
| 	"x86_64-unknown-linux-gnu-", | ||||
| 	"i686-pc-linux-gnu-", | ||||
| 	"i586-pc-linux-gnu-", | ||||
| 	"i486-pc-linux-gnu-", | ||||
| 	"i386-pc-linux-gnu-", | ||||
| 	"i686-linux-android-", | ||||
| 	"i686-android-linux-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| const char *const mips_triplets[] = { | ||||
| 	"mips-unknown-linux-gnu-", | ||||
| 	"mipsel-linux-android-", | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static bool lookup_path(char *name) | ||||
| { | ||||
| 	bool found = false; | ||||
| 	char *path, *tmp; | ||||
| 	char buf[PATH_MAX]; | ||||
| 	char *env = getenv("PATH"); | ||||
| 
 | ||||
| 	if (!env) | ||||
| 		return false; | ||||
| 
 | ||||
| 	env = strdup(env); | ||||
| 	if (!env) | ||||
| 		return false; | ||||
| 
 | ||||
| 	path = strtok_r(env, ":", &tmp); | ||||
| 	while (path) { | ||||
| 		scnprintf(buf, sizeof(buf), "%s/%s", path, name); | ||||
| 		if (access(buf, F_OK) == 0) { | ||||
| 			found = true; | ||||
| 			break; | ||||
| 		} | ||||
| 		path = strtok_r(NULL, ":", &tmp); | ||||
| 	} | ||||
| 	free(env); | ||||
| 	return found; | ||||
| } | ||||
| 
 | ||||
| static int lookup_triplets(const char *const *triplets, const char *name) | ||||
| { | ||||
| 	int i; | ||||
| 	char buf[PATH_MAX]; | ||||
| 
 | ||||
| 	for (i = 0; triplets[i] != NULL; i++) { | ||||
| 		scnprintf(buf, sizeof(buf), "%s%s", triplets[i], name); | ||||
| 		if (lookup_path(buf)) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return architecture name in a normalized form. | ||||
|  * The conversion logic comes from the Makefile. | ||||
|  */ | ||||
| static const char *normalize_arch(char *arch) | ||||
| { | ||||
| 	if (!strcmp(arch, "x86_64")) | ||||
| 		return "x86"; | ||||
| 	if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6') | ||||
| 		return "x86"; | ||||
| 	if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) | ||||
| 		return "sparc"; | ||||
| 	if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64")) | ||||
| 		return "arm64"; | ||||
| 	if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) | ||||
| 		return "arm"; | ||||
| 	if (!strncmp(arch, "s390", 4)) | ||||
| 		return "s390"; | ||||
| 	if (!strncmp(arch, "parisc", 6)) | ||||
| 		return "parisc"; | ||||
| 	if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3)) | ||||
| 		return "powerpc"; | ||||
| 	if (!strncmp(arch, "mips", 4)) | ||||
| 		return "mips"; | ||||
| 	if (!strncmp(arch, "sh", 2) && isdigit(arch[2])) | ||||
| 		return "sh"; | ||||
| 
 | ||||
| 	return arch; | ||||
| } | ||||
| 
 | ||||
| static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | ||||
| 						  const char *name, | ||||
| 						  const char **path) | ||||
| { | ||||
| 	int idx; | ||||
| 	const char *arch, *cross_env; | ||||
| 	struct utsname uts; | ||||
| 	const char *const *path_list; | ||||
| 	char *buf = NULL; | ||||
| 
 | ||||
| 	arch = normalize_arch(env->arch); | ||||
| 
 | ||||
| 	if (uname(&uts) < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We don't need to try to find objdump path for native system. | ||||
| 	 * Just use default binutils path (e.g.: "objdump"). | ||||
| 	 */ | ||||
| 	if (!strcmp(normalize_arch(uts.machine), arch)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	cross_env = getenv("CROSS_COMPILE"); | ||||
| 	if (cross_env) { | ||||
| 		if (asprintf(&buf, "%s%s", cross_env, name) < 0) | ||||
| 			goto out_error; | ||||
| 		if (buf[0] == '/') { | ||||
| 			if (access(buf, F_OK) == 0) | ||||
| 				goto out; | ||||
| 			goto out_error; | ||||
| 		} | ||||
| 		if (lookup_path(buf)) | ||||
| 			goto out; | ||||
| 		zfree(&buf); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!strcmp(arch, "arm")) | ||||
| 		path_list = arm_triplets; | ||||
| 	else if (!strcmp(arch, "arm64")) | ||||
| 		path_list = arm64_triplets; | ||||
| 	else if (!strcmp(arch, "powerpc")) | ||||
| 		path_list = powerpc_triplets; | ||||
| 	else if (!strcmp(arch, "sh")) | ||||
| 		path_list = sh_triplets; | ||||
| 	else if (!strcmp(arch, "s390")) | ||||
| 		path_list = s390_triplets; | ||||
| 	else if (!strcmp(arch, "sparc")) | ||||
| 		path_list = sparc_triplets; | ||||
| 	else if (!strcmp(arch, "x86")) | ||||
| 		path_list = x86_triplets; | ||||
| 	else if (!strcmp(arch, "mips")) | ||||
| 		path_list = mips_triplets; | ||||
| 	else { | ||||
| 		ui__error("binutils for %s not supported.\n", arch); | ||||
| 		goto out_error; | ||||
| 	} | ||||
| 
 | ||||
| 	idx = lookup_triplets(path_list, name); | ||||
| 	if (idx < 0) { | ||||
| 		ui__error("Please install %s for %s.\n" | ||||
| 			  "You can add it to PATH, set CROSS_COMPILE or " | ||||
| 			  "override the default using --%s.\n", | ||||
| 			  name, arch, name); | ||||
| 		goto out_error; | ||||
| 	} | ||||
| 
 | ||||
| 	if (asprintf(&buf, "%s%s", path_list[idx], name) < 0) | ||||
| 		goto out_error; | ||||
| 
 | ||||
| out: | ||||
| 	*path = buf; | ||||
| 	return 0; | ||||
| out_error: | ||||
| 	free(buf); | ||||
| 	*path = NULL; | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| int perf_session_env__lookup_objdump(struct perf_session_env *env) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * For live mode, env->arch will be NULL and we can use | ||||
| 	 * the native objdump tool. | ||||
| 	 */ | ||||
| 	if (env->arch == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return perf_session_env__lookup_binutils_path(env, "objdump", | ||||
| 						      &objdump_path); | ||||
| } | ||||
							
								
								
									
										10
									
								
								tools/perf/arch/common.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tools/perf/arch/common.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| #ifndef ARCH_PERF_COMMON_H | ||||
| #define ARCH_PERF_COMMON_H | ||||
| 
 | ||||
| #include "../util/session.h" | ||||
| 
 | ||||
| extern const char *objdump_path; | ||||
| 
 | ||||
| int perf_session_env__lookup_objdump(struct perf_session_env *env); | ||||
| 
 | ||||
| #endif /* ARCH_PERF_COMMON_H */ | ||||
							
								
								
									
										6
									
								
								tools/perf/arch/powerpc/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tools/perf/arch/powerpc/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| ifndef NO_DWARF | ||||
| PERF_HAVE_DWARF_REGS := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o | ||||
| endif | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||||
							
								
								
									
										88
									
								
								tools/perf/arch/powerpc/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								tools/perf/arch/powerpc/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | |||
| /*
 | ||||
|  * Mapping of DWARF debug register numbers into register names. | ||||
|  * | ||||
|  * Copyright (C) 2010 Ian Munsie, IBM Corporation. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * as published by the Free Software Foundation; either version | ||||
|  * 2 of the License, or (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #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(%gpr##num), .dwarfnum = num} | ||||
| #define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} | ||||
| 
 | ||||
| /*
 | ||||
|  * Reference: | ||||
|  * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
 | ||||
|  */ | ||||
| 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), | ||||
| 	GPR_DWARFNUM_NAME(30), | ||||
| 	GPR_DWARFNUM_NAME(31), | ||||
| 	REG_DWARFNUM_NAME("%msr",   66), | ||||
| 	REG_DWARFNUM_NAME("%ctr",   109), | ||||
| 	REG_DWARFNUM_NAME("%link",  108), | ||||
| 	REG_DWARFNUM_NAME("%xer",   101), | ||||
| 	REG_DWARFNUM_NAME("%dar",   119), | ||||
| 	REG_DWARFNUM_NAME("%dsisr", 118), | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										34
									
								
								tools/perf/arch/powerpc/util/header.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								tools/perf/arch/powerpc/util/header.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "../../util/header.h" | ||||
| #include "../../util/util.h" | ||||
| 
 | ||||
| #define mfspr(rn)       ({unsigned long rval; \ | ||||
| 			 asm volatile("mfspr %0," __stringify(rn) \ | ||||
| 				      : "=r" (rval)); rval; }) | ||||
| 
 | ||||
| #define SPRN_PVR        0x11F	/* Processor Version Register */ | ||||
| #define PVR_VER(pvr)    (((pvr) >>  16) & 0xFFFF) /* Version field */ | ||||
| #define PVR_REV(pvr)    (((pvr) >>   0) & 0xFFFF) /* Revison field */ | ||||
| 
 | ||||
| int | ||||
| get_cpuid(char *buffer, size_t sz) | ||||
| { | ||||
| 	unsigned long pvr; | ||||
| 	int nb; | ||||
| 
 | ||||
| 	pvr = mfspr(SPRN_PVR); | ||||
| 
 | ||||
| 	nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); | ||||
| 
 | ||||
| 	/* look for end marker to ensure the entire data fit */ | ||||
| 	if (strchr(buffer, '$')) { | ||||
| 		buffer[nb-1] = '\0'; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
							
								
								
									
										267
									
								
								tools/perf/arch/powerpc/util/skip-callchain-idx.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								tools/perf/arch/powerpc/util/skip-callchain-idx.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,267 @@ | |||
| /*
 | ||||
|  * Use DWARF Debug information to skip unnecessary callchain entries. | ||||
|  * | ||||
|  * Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation. | ||||
|  * Copyright (C) 2014 Ulrich Weigand, IBM Corporation. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * as published by the Free Software Foundation; either version | ||||
|  * 2 of the License, or (at your option) any later version. | ||||
|  */ | ||||
| #include <inttypes.h> | ||||
| #include <dwarf.h> | ||||
| #include <elfutils/libdwfl.h> | ||||
| 
 | ||||
| #include "util/thread.h" | ||||
| #include "util/callchain.h" | ||||
| #include "util/debug.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * When saving the callchain on Power, the kernel conservatively saves | ||||
|  * excess entries in the callchain. A few of these entries are needed | ||||
|  * in some cases but not others. If the unnecessary entries are not | ||||
|  * ignored, we end up with duplicate arcs in the call-graphs. Use | ||||
|  * DWARF debug information to skip over any unnecessary callchain | ||||
|  * entries. | ||||
|  * | ||||
|  * See function header for arch_adjust_callchain() below for more details. | ||||
|  * | ||||
|  * The libdwfl code in this file is based on code from elfutils | ||||
|  * (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc). | ||||
|  */ | ||||
| static char *debuginfo_path; | ||||
| 
 | ||||
| static const Dwfl_Callbacks offline_callbacks = { | ||||
| 	.debuginfo_path = &debuginfo_path, | ||||
| 	.find_debuginfo = dwfl_standard_find_debuginfo, | ||||
| 	.section_address = dwfl_offline_section_address, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Use the DWARF expression for the Call-frame-address and determine | ||||
|  * if return address is in LR and if a new frame was allocated. | ||||
|  */ | ||||
| static int check_return_reg(int ra_regno, Dwarf_Frame *frame) | ||||
| { | ||||
| 	Dwarf_Op ops_mem[2]; | ||||
| 	Dwarf_Op dummy; | ||||
| 	Dwarf_Op *ops = &dummy; | ||||
| 	size_t nops; | ||||
| 	int result; | ||||
| 
 | ||||
| 	result = dwarf_frame_register(frame, ra_regno, ops_mem, &ops, &nops); | ||||
| 	if (result < 0) { | ||||
| 		pr_debug("dwarf_frame_register() %s\n", dwarf_errmsg(-1)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check if return address is on the stack. | ||||
| 	 */ | ||||
| 	if (nops != 0 || ops != NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Return address is in LR. Check if a frame was allocated | ||||
| 	 * but not-yet used. | ||||
| 	 */ | ||||
| 	result = dwarf_frame_cfa(frame, &ops, &nops); | ||||
| 	if (result < 0) { | ||||
| 		pr_debug("dwarf_frame_cfa() returns %d, %s\n", result, | ||||
| 					dwarf_errmsg(-1)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If call frame address is in r1, no new frame was allocated. | ||||
| 	 */ | ||||
| 	if (nops == 1 && ops[0].atom == DW_OP_bregx && ops[0].number == 1 && | ||||
| 				ops[0].number2 == 0) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A new frame was allocated but has not yet been used. | ||||
| 	 */ | ||||
| 	return 2; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the DWARF frame from the .eh_frame section. | ||||
|  */ | ||||
| static Dwarf_Frame *get_eh_frame(Dwfl_Module *mod, Dwarf_Addr pc) | ||||
| { | ||||
| 	int		result; | ||||
| 	Dwarf_Addr	bias; | ||||
| 	Dwarf_CFI	*cfi; | ||||
| 	Dwarf_Frame	*frame; | ||||
| 
 | ||||
| 	cfi = dwfl_module_eh_cfi(mod, &bias); | ||||
| 	if (!cfi) { | ||||
| 		pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	result = dwarf_cfi_addrframe(cfi, pc, &frame); | ||||
| 	if (result) { | ||||
| 		pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return frame; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the DWARF frame from the .debug_frame section. | ||||
|  */ | ||||
| static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc) | ||||
| { | ||||
| 	Dwarf_CFI       *cfi; | ||||
| 	Dwarf_Addr      bias; | ||||
| 	Dwarf_Frame     *frame; | ||||
| 	int             result; | ||||
| 
 | ||||
| 	cfi = dwfl_module_dwarf_cfi(mod, &bias); | ||||
| 	if (!cfi) { | ||||
| 		pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	result = dwarf_cfi_addrframe(cfi, pc, &frame); | ||||
| 	if (result) { | ||||
| 		pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return frame; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return: | ||||
|  *	0 if return address for the program counter @pc is on stack | ||||
|  *	1 if return address is in LR and no new stack frame was allocated | ||||
|  *	2 if return address is in LR and a new frame was allocated (but not | ||||
|  *		yet used) | ||||
|  *	-1 in case of errors | ||||
|  */ | ||||
| static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | ||||
| { | ||||
| 	int		rc = -1; | ||||
| 	Dwfl		*dwfl; | ||||
| 	Dwfl_Module	*mod; | ||||
| 	Dwarf_Frame	*frame; | ||||
| 	int		ra_regno; | ||||
| 	Dwarf_Addr	start = pc; | ||||
| 	Dwarf_Addr	end = pc; | ||||
| 	bool		signalp; | ||||
| 
 | ||||
| 	dwfl = dwfl_begin(&offline_callbacks); | ||||
| 	if (!dwfl) { | ||||
| 		pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dwfl_report_offline(dwfl, "",  exec_file, -1) == NULL) { | ||||
| 		pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1)); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	mod = dwfl_addrmodule(dwfl, pc); | ||||
| 	if (!mod) { | ||||
| 		pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1)); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * To work with split debug info files (eg: glibc), check both | ||||
| 	 * .eh_frame and .debug_frame sections of the ELF header. | ||||
| 	 */ | ||||
| 	frame = get_eh_frame(mod, pc); | ||||
| 	if (!frame) { | ||||
| 		frame = get_dwarf_frame(mod, pc); | ||||
| 		if (!frame) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ra_regno = dwarf_frame_info(frame, &start, &end, &signalp); | ||||
| 	if (ra_regno < 0) { | ||||
| 		pr_debug("Return address register unavailable: %s\n", | ||||
| 				dwarf_errmsg(-1)); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = check_return_reg(ra_regno, frame); | ||||
| 
 | ||||
| out: | ||||
| 	dwfl_end(dwfl); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The callchain saved by the kernel always includes the link register (LR). | ||||
|  * | ||||
|  *	0:	PERF_CONTEXT_USER | ||||
|  *	1:	Program counter (Next instruction pointer) | ||||
|  *	2:	LR value | ||||
|  *	3:	Caller's caller | ||||
|  *	4:	... | ||||
|  * | ||||
|  * The value in LR is only needed when it holds a return address. If the | ||||
|  * return address is on the stack, we should ignore the LR value. | ||||
|  * | ||||
|  * Further, when the return address is in the LR, if a new frame was just | ||||
|  * allocated but the LR was not saved into it, then the LR contains the | ||||
|  * caller, slot 4: contains the caller's caller and the contents of slot 3: | ||||
|  * (chain->ips[3]) is undefined and must be ignored. | ||||
|  * | ||||
|  * Use DWARF debug information to determine if any entries need to be skipped. | ||||
|  * | ||||
|  * Return: | ||||
|  *	index:	of callchain entry that needs to be ignored (if any) | ||||
|  *	-1	if no entry needs to be ignored or in case of errors | ||||
|  */ | ||||
| int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | ||||
| 				struct ip_callchain *chain) | ||||
| { | ||||
| 	struct addr_location al; | ||||
| 	struct dso *dso = NULL; | ||||
| 	int rc; | ||||
| 	u64 ip; | ||||
| 	u64 skip_slot = -1; | ||||
| 
 | ||||
| 	if (chain->nr < 3) | ||||
| 		return skip_slot; | ||||
| 
 | ||||
| 	ip = chain->ips[2]; | ||||
| 
 | ||||
| 	thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER, | ||||
| 			MAP__FUNCTION, ip, &al); | ||||
| 
 | ||||
| 	if (al.map) | ||||
| 		dso = al.map->dso; | ||||
| 
 | ||||
| 	if (!dso) { | ||||
| 		pr_debug("%" PRIx64 " dso is NULL\n", ip); | ||||
| 		return skip_slot; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = check_return_addr(dso->long_name, ip); | ||||
| 
 | ||||
| 	pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", | ||||
| 				dso->long_name, chain->nr, ip, rc); | ||||
| 
 | ||||
| 	if (rc == 0) { | ||||
| 		/*
 | ||||
| 		 * Return address on stack. Ignore LR value in callchain | ||||
| 		 */ | ||||
| 		skip_slot = 2; | ||||
| 	} else if (rc == 2) { | ||||
| 		/*
 | ||||
| 		 * New frame allocated but return address still in LR. | ||||
| 		 * Ignore the caller's caller entry in callchain. | ||||
| 		 */ | ||||
| 		skip_slot = 3; | ||||
| 	} | ||||
| 	return skip_slot; | ||||
| } | ||||
							
								
								
									
										7
									
								
								tools/perf/arch/s390/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tools/perf/arch/s390/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| ifndef NO_DWARF | ||||
| PERF_HAVE_DWARF_REGS := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||||
| endif | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||||
| HAVE_KVM_STAT_SUPPORT := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o | ||||
							
								
								
									
										22
									
								
								tools/perf/arch/s390/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tools/perf/arch/s390/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| /*
 | ||||
|  * Mapping of DWARF debug register numbers into register names. | ||||
|  * | ||||
|  *    Copyright IBM Corp. 2010 | ||||
|  *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <dwarf-regs.h> | ||||
| 
 | ||||
| #define NUM_GPRS 16 | ||||
| 
 | ||||
| static const char *gpr_names[NUM_GPRS] = { | ||||
| 	"%r0", "%r1",  "%r2",  "%r3",  "%r4",  "%r5",  "%r6",  "%r7", | ||||
| 	"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", | ||||
| }; | ||||
| 
 | ||||
| const char *get_arch_regstr(unsigned int n) | ||||
| { | ||||
| 	return (n >= NUM_GPRS) ? NULL : gpr_names[n]; | ||||
| } | ||||
							
								
								
									
										28
									
								
								tools/perf/arch/s390/util/header.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tools/perf/arch/s390/util/header.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /*
 | ||||
|  * Implementation of get_cpuid(). | ||||
|  * | ||||
|  * Copyright 2014 IBM Corp. | ||||
|  * Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License (version 2 only) | ||||
|  * as published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "../../util/header.h" | ||||
| 
 | ||||
| int get_cpuid(char *buffer, size_t sz) | ||||
| { | ||||
| 	const char *cpuid = "IBM/S390"; | ||||
| 
 | ||||
| 	if (strlen(cpuid) + 1 > sz) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	strcpy(buffer, cpuid); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										105
									
								
								tools/perf/arch/s390/util/kvm-stat.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								tools/perf/arch/s390/util/kvm-stat.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | |||
| /*
 | ||||
|  * Arch specific functions for perf kvm stat. | ||||
|  * | ||||
|  * Copyright 2014 IBM Corp. | ||||
|  * Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License (version 2 only) | ||||
|  * as published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #include "../../util/kvm-stat.h" | ||||
| #include <asm/kvm_perf.h> | ||||
| 
 | ||||
| define_exit_reasons_table(sie_exit_reasons, sie_intercept_code); | ||||
| define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes); | ||||
| define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes); | ||||
| define_exit_reasons_table(sie_diagnose_codes, diagnose_codes); | ||||
| define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes); | ||||
| 
 | ||||
| static void event_icpt_insn_get_key(struct perf_evsel *evsel, | ||||
| 				    struct perf_sample *sample, | ||||
| 				    struct event_key *key) | ||||
| { | ||||
| 	unsigned long insn; | ||||
| 
 | ||||
| 	insn = perf_evsel__intval(evsel, sample, "instruction"); | ||||
| 	key->key = icpt_insn_decoder(insn); | ||||
| 	key->exit_reasons = sie_icpt_insn_codes; | ||||
| } | ||||
| 
 | ||||
| static void event_sigp_get_key(struct perf_evsel *evsel, | ||||
| 			       struct perf_sample *sample, | ||||
| 			       struct event_key *key) | ||||
| { | ||||
| 	key->key = perf_evsel__intval(evsel, sample, "order_code"); | ||||
| 	key->exit_reasons = sie_sigp_order_codes; | ||||
| } | ||||
| 
 | ||||
| static void event_diag_get_key(struct perf_evsel *evsel, | ||||
| 			       struct perf_sample *sample, | ||||
| 			       struct event_key *key) | ||||
| { | ||||
| 	key->key = perf_evsel__intval(evsel, sample, "code"); | ||||
| 	key->exit_reasons = sie_diagnose_codes; | ||||
| } | ||||
| 
 | ||||
| static void event_icpt_prog_get_key(struct perf_evsel *evsel, | ||||
| 				    struct perf_sample *sample, | ||||
| 				    struct event_key *key) | ||||
| { | ||||
| 	key->key = perf_evsel__intval(evsel, sample, "code"); | ||||
| 	key->exit_reasons = sie_icpt_prog_codes; | ||||
| } | ||||
| 
 | ||||
| static struct child_event_ops child_events[] = { | ||||
| 	{ .name = "kvm:kvm_s390_intercept_instruction", | ||||
| 	  .get_key = event_icpt_insn_get_key }, | ||||
| 	{ .name = "kvm:kvm_s390_handle_sigp", | ||||
| 	  .get_key = event_sigp_get_key }, | ||||
| 	{ .name = "kvm:kvm_s390_handle_diag", | ||||
| 	  .get_key = event_diag_get_key }, | ||||
| 	{ .name = "kvm:kvm_s390_intercept_prog", | ||||
| 	  .get_key = event_icpt_prog_get_key }, | ||||
| 	{ NULL, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static struct kvm_events_ops exit_events = { | ||||
| 	.is_begin_event = exit_event_begin, | ||||
| 	.is_end_event = exit_event_end, | ||||
| 	.child_ops = child_events, | ||||
| 	.decode_key = exit_event_decode_key, | ||||
| 	.name = "VM-EXIT" | ||||
| }; | ||||
| 
 | ||||
| const char * const kvm_events_tp[] = { | ||||
| 	"kvm:kvm_s390_sie_enter", | ||||
| 	"kvm:kvm_s390_sie_exit", | ||||
| 	"kvm:kvm_s390_intercept_instruction", | ||||
| 	"kvm:kvm_s390_handle_sigp", | ||||
| 	"kvm:kvm_s390_handle_diag", | ||||
| 	"kvm:kvm_s390_intercept_prog", | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| struct kvm_reg_events_ops kvm_reg_events_ops[] = { | ||||
| 	{ .name = "vmexit", .ops = &exit_events }, | ||||
| 	{ NULL, NULL }, | ||||
| }; | ||||
| 
 | ||||
| const char * const kvm_skip_events[] = { | ||||
| 	"Wait state", | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) | ||||
| { | ||||
| 	if (strstr(cpuid, "IBM/S390")) { | ||||
| 		kvm->exit_reasons = sie_exit_reasons; | ||||
| 		kvm->exit_reasons_isa = "SIE"; | ||||
| 	} else | ||||
| 		return -ENOTSUP; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										4
									
								
								tools/perf/arch/sh/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tools/perf/arch/sh/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| ifndef NO_DWARF | ||||
| PERF_HAVE_DWARF_REGS := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||||
| endif | ||||
							
								
								
									
										55
									
								
								tools/perf/arch/sh/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								tools/perf/arch/sh/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| /*
 | ||||
|  * Mapping of DWARF debug register numbers into register names. | ||||
|  * | ||||
|  * Copyright (C) 2010 Matt Fleming <matt@console-pimps.org> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <dwarf-regs.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Generic dwarf analysis helpers | ||||
|  */ | ||||
| 
 | ||||
| #define SH_MAX_REGS 18 | ||||
| const char *sh_regs_table[SH_MAX_REGS] = { | ||||
| 	"r0", | ||||
| 	"r1", | ||||
| 	"r2", | ||||
| 	"r3", | ||||
| 	"r4", | ||||
| 	"r5", | ||||
| 	"r6", | ||||
| 	"r7", | ||||
| 	"r8", | ||||
| 	"r9", | ||||
| 	"r10", | ||||
| 	"r11", | ||||
| 	"r12", | ||||
| 	"r13", | ||||
| 	"r14", | ||||
| 	"r15", | ||||
| 	"pc", | ||||
| 	"pr", | ||||
| }; | ||||
| 
 | ||||
| /* Return architecture dependent register string (for kprobe-tracer) */ | ||||
| const char *get_arch_regstr(unsigned int n) | ||||
| { | ||||
| 	return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL; | ||||
| } | ||||
							
								
								
									
										4
									
								
								tools/perf/arch/sparc/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tools/perf/arch/sparc/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| ifndef NO_DWARF | ||||
| PERF_HAVE_DWARF_REGS := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||||
| endif | ||||
							
								
								
									
										43
									
								
								tools/perf/arch/sparc/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tools/perf/arch/sparc/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| /*
 | ||||
|  * Mapping of DWARF debug register numbers into register names. | ||||
|  * | ||||
|  * Copyright (C) 2010 David S. Miller <davem@davemloft.net> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * as published by the Free Software Foundation; either version | ||||
|  * 2 of the License, or (at your option) any later version. | ||||
|  */ | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <dwarf-regs.h> | ||||
| 
 | ||||
| #define SPARC_MAX_REGS	96 | ||||
| 
 | ||||
| const char *sparc_regs_table[SPARC_MAX_REGS] = { | ||||
| 	"%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", | ||||
| 	"%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", | ||||
| 	"%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", | ||||
| 	"%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", | ||||
| 	"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", | ||||
| 	"%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", | ||||
| 	"%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", | ||||
| 	"%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", | ||||
| 	"%f32", "%f33", "%f34", "%f35", "%f36", "%f37", "%f38", "%f39", | ||||
| 	"%f40", "%f41", "%f42", "%f43", "%f44", "%f45", "%f46", "%f47", | ||||
| 	"%f48", "%f49", "%f50", "%f51", "%f52", "%f53", "%f54", "%f55", | ||||
| 	"%f56", "%f57", "%f58", "%f59", "%f60", "%f61", "%f62", "%f63", | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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) | ||||
| { | ||||
| 	return (n <= SPARC_MAX_REGS) ? sparc_regs_table[n] : NULL; | ||||
| } | ||||
							
								
								
									
										19
									
								
								tools/perf/arch/x86/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tools/perf/arch/x86/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| 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 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o | ||||
| LIB_H += arch/$(ARCH)/util/tsc.h | ||||
| HAVE_KVM_STAT_SUPPORT := 1 | ||||
| LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o | ||||
							
								
								
									
										86
									
								
								tools/perf/arch/x86/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tools/perf/arch/x86/include/perf_regs.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| #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); | ||||
| 
 | ||||
| #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 | ||||
| 
 | ||||
| static inline const char *perf_reg_name(int id) | ||||
| { | ||||
| 	switch (id) { | ||||
| 	case PERF_REG_X86_AX: | ||||
| 		return "AX"; | ||||
| 	case PERF_REG_X86_BX: | ||||
| 		return "BX"; | ||||
| 	case PERF_REG_X86_CX: | ||||
| 		return "CX"; | ||||
| 	case PERF_REG_X86_DX: | ||||
| 		return "DX"; | ||||
| 	case PERF_REG_X86_SI: | ||||
| 		return "SI"; | ||||
| 	case PERF_REG_X86_DI: | ||||
| 		return "DI"; | ||||
| 	case PERF_REG_X86_BP: | ||||
| 		return "BP"; | ||||
| 	case PERF_REG_X86_SP: | ||||
| 		return "SP"; | ||||
| 	case PERF_REG_X86_IP: | ||||
| 		return "IP"; | ||||
| 	case PERF_REG_X86_FLAGS: | ||||
| 		return "FLAGS"; | ||||
| 	case PERF_REG_X86_CS: | ||||
| 		return "CS"; | ||||
| 	case PERF_REG_X86_SS: | ||||
| 		return "SS"; | ||||
| 	case PERF_REG_X86_DS: | ||||
| 		return "DS"; | ||||
| 	case PERF_REG_X86_ES: | ||||
| 		return "ES"; | ||||
| 	case PERF_REG_X86_FS: | ||||
| 		return "FS"; | ||||
| 	case PERF_REG_X86_GS: | ||||
| 		return "GS"; | ||||
| #ifdef HAVE_ARCH_X86_64_SUPPORT | ||||
| 	case PERF_REG_X86_R8: | ||||
| 		return "R8"; | ||||
| 	case PERF_REG_X86_R9: | ||||
| 		return "R9"; | ||||
| 	case PERF_REG_X86_R10: | ||||
| 		return "R10"; | ||||
| 	case PERF_REG_X86_R11: | ||||
| 		return "R11"; | ||||
| 	case PERF_REG_X86_R12: | ||||
| 		return "R12"; | ||||
| 	case PERF_REG_X86_R13: | ||||
| 		return "R13"; | ||||
| 	case PERF_REG_X86_R14: | ||||
| 		return "R14"; | ||||
| 	case PERF_REG_X86_R15: | ||||
| 		return "R15"; | ||||
| #endif /* HAVE_ARCH_X86_64_SUPPORT */ | ||||
| 	default: | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| #endif /* ARCH_PERF_REGS_H */ | ||||
							
								
								
									
										61
									
								
								tools/perf/arch/x86/tests/dwarf-unwind.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tools/perf/arch/x86/tests/dwarf-unwind.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| #include <string.h> | ||||
| #include "perf_regs.h" | ||||
| #include "thread.h" | ||||
| #include "map.h" | ||||
| #include "event.h" | ||||
| #include "debug.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); | ||||
| } | ||||
							
								
								
									
										98
									
								
								tools/perf/arch/x86/tests/regs_load.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tools/perf/arch/x86/tests/regs_load.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||
							
								
								
									
										75
									
								
								tools/perf/arch/x86/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								tools/perf/arch/x86/util/dwarf-regs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| /*
 | ||||
|  * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. | ||||
|  * Extracted from probe-finder.c | ||||
|  * | ||||
|  * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <dwarf-regs.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Generic dwarf analysis helpers | ||||
|  */ | ||||
| 
 | ||||
| #define X86_32_MAX_REGS 8 | ||||
| const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||||
| 	"%ax", | ||||
| 	"%cx", | ||||
| 	"%dx", | ||||
| 	"%bx", | ||||
| 	"$stack",	/* Stack address instead of %sp */ | ||||
| 	"%bp", | ||||
| 	"%si", | ||||
| 	"%di", | ||||
| }; | ||||
| 
 | ||||
| #define X86_64_MAX_REGS 16 | ||||
| const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||||
| 	"%ax", | ||||
| 	"%dx", | ||||
| 	"%cx", | ||||
| 	"%bx", | ||||
| 	"%si", | ||||
| 	"%di", | ||||
| 	"%bp", | ||||
| 	"%sp", | ||||
| 	"%r8", | ||||
| 	"%r9", | ||||
| 	"%r10", | ||||
| 	"%r11", | ||||
| 	"%r12", | ||||
| 	"%r13", | ||||
| 	"%r14", | ||||
| 	"%r15", | ||||
| }; | ||||
| 
 | ||||
| /* TODO: switching by dwarf address size */ | ||||
| #ifdef __x86_64__ | ||||
| #define ARCH_MAX_REGS X86_64_MAX_REGS | ||||
| #define arch_regs_table x86_64_regs_table | ||||
| #else | ||||
| #define ARCH_MAX_REGS X86_32_MAX_REGS | ||||
| #define arch_regs_table x86_32_regs_table | ||||
| #endif | ||||
| 
 | ||||
| /* Return architecture dependent register string (for kprobe-tracer) */ | ||||
| const char *get_arch_regstr(unsigned int n) | ||||
| { | ||||
| 	return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||||
| } | ||||
							
								
								
									
										59
									
								
								tools/perf/arch/x86/util/header.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tools/perf/arch/x86/util/header.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										156
									
								
								tools/perf/arch/x86/util/kvm-stat.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								tools/perf/arch/x86/util/kvm-stat.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | |||
| #include "../../util/kvm-stat.h" | ||||
| #include <asm/kvm_perf.h> | ||||
| 
 | ||||
| define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); | ||||
| define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); | ||||
| 
 | ||||
| static struct kvm_events_ops exit_events = { | ||||
| 	.is_begin_event = exit_event_begin, | ||||
| 	.is_end_event = exit_event_end, | ||||
| 	.decode_key = exit_event_decode_key, | ||||
| 	.name = "VM-EXIT" | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * For the mmio events, we treat: | ||||
|  * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry | ||||
|  * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). | ||||
|  */ | ||||
| static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, | ||||
| 			       struct event_key *key) | ||||
| { | ||||
| 	key->key  = perf_evsel__intval(evsel, sample, "gpa"); | ||||
| 	key->info = perf_evsel__intval(evsel, sample, "type"); | ||||
| } | ||||
| 
 | ||||
| #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 | ||||
| #define KVM_TRACE_MMIO_READ 1 | ||||
| #define KVM_TRACE_MMIO_WRITE 2 | ||||
| 
 | ||||
| static bool mmio_event_begin(struct perf_evsel *evsel, | ||||
| 			     struct perf_sample *sample, struct event_key *key) | ||||
| { | ||||
| 	/* MMIO read begin event in kernel. */ | ||||
| 	if (kvm_exit_event(evsel)) | ||||
| 		return true; | ||||
| 
 | ||||
| 	/* MMIO write begin event in kernel. */ | ||||
| 	if (!strcmp(evsel->name, "kvm:kvm_mmio") && | ||||
| 	    perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { | ||||
| 		mmio_event_get_key(evsel, sample, key); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, | ||||
| 			   struct event_key *key) | ||||
| { | ||||
| 	/* MMIO write end event in kernel. */ | ||||
| 	if (kvm_entry_event(evsel)) | ||||
| 		return true; | ||||
| 
 | ||||
| 	/* MMIO read end event in kernel.*/ | ||||
| 	if (!strcmp(evsel->name, "kvm:kvm_mmio") && | ||||
| 	    perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { | ||||
| 		mmio_event_get_key(evsel, sample, key); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | ||||
| 				  struct event_key *key, | ||||
| 				  char *decode) | ||||
| { | ||||
| 	scnprintf(decode, DECODE_STR_LEN, "%#lx:%s", | ||||
| 		  (unsigned long)key->key, | ||||
| 		  key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); | ||||
| } | ||||
| 
 | ||||
| static struct kvm_events_ops mmio_events = { | ||||
| 	.is_begin_event = mmio_event_begin, | ||||
| 	.is_end_event = mmio_event_end, | ||||
| 	.decode_key = mmio_event_decode_key, | ||||
| 	.name = "MMIO Access" | ||||
| }; | ||||
| 
 | ||||
|  /* The time of emulation pio access is from kvm_pio to kvm_entry. */ | ||||
| static void ioport_event_get_key(struct perf_evsel *evsel, | ||||
| 				 struct perf_sample *sample, | ||||
| 				 struct event_key *key) | ||||
| { | ||||
| 	key->key  = perf_evsel__intval(evsel, sample, "port"); | ||||
| 	key->info = perf_evsel__intval(evsel, sample, "rw"); | ||||
| } | ||||
| 
 | ||||
| static bool ioport_event_begin(struct perf_evsel *evsel, | ||||
| 			       struct perf_sample *sample, | ||||
| 			       struct event_key *key) | ||||
| { | ||||
| 	if (!strcmp(evsel->name, "kvm:kvm_pio")) { | ||||
| 		ioport_event_get_key(evsel, sample, key); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static bool ioport_event_end(struct perf_evsel *evsel, | ||||
| 			     struct perf_sample *sample __maybe_unused, | ||||
| 			     struct event_key *key __maybe_unused) | ||||
| { | ||||
| 	return kvm_entry_event(evsel); | ||||
| } | ||||
| 
 | ||||
| static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | ||||
| 				    struct event_key *key, | ||||
| 				    char *decode) | ||||
| { | ||||
| 	scnprintf(decode, DECODE_STR_LEN, "%#llx:%s", | ||||
| 		  (unsigned long long)key->key, | ||||
| 		  key->info ? "POUT" : "PIN"); | ||||
| } | ||||
| 
 | ||||
| static struct kvm_events_ops ioport_events = { | ||||
| 	.is_begin_event = ioport_event_begin, | ||||
| 	.is_end_event = ioport_event_end, | ||||
| 	.decode_key = ioport_event_decode_key, | ||||
| 	.name = "IO Port Access" | ||||
| }; | ||||
| 
 | ||||
| const char * const kvm_events_tp[] = { | ||||
| 	"kvm:kvm_entry", | ||||
| 	"kvm:kvm_exit", | ||||
| 	"kvm:kvm_mmio", | ||||
| 	"kvm:kvm_pio", | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| struct kvm_reg_events_ops kvm_reg_events_ops[] = { | ||||
| 	{ .name = "vmexit", .ops = &exit_events }, | ||||
| 	{ .name = "mmio", .ops = &mmio_events }, | ||||
| 	{ .name = "ioport", .ops = &ioport_events }, | ||||
| 	{ NULL, NULL }, | ||||
| }; | ||||
| 
 | ||||
| const char * const kvm_skip_events[] = { | ||||
| 	"HLT", | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) | ||||
| { | ||||
| 	if (strstr(cpuid, "Intel")) { | ||||
| 		kvm->exit_reasons = vmx_exit_reasons; | ||||
| 		kvm->exit_reasons_isa = "VMX"; | ||||
| 	} else if (strstr(cpuid, "AMD")) { | ||||
| 		kvm->exit_reasons = svm_exit_reasons; | ||||
| 		kvm->exit_reasons_isa = "SVM"; | ||||
| 	} else | ||||
| 		return -ENOTSUP; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										48
									
								
								tools/perf/arch/x86/util/tsc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								tools/perf/arch/x86/util/tsc.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| #include <stdbool.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <linux/perf_event.h> | ||||
| 
 | ||||
| #include "../../perf.h" | ||||
| #include <linux/types.h> | ||||
| #include "../../util/debug.h" | ||||
| #include "../../util/tsc.h" | ||||
| #include "tsc.h" | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| u64 rdtsc(void) | ||||
| { | ||||
| 	unsigned int low, high; | ||||
| 
 | ||||
| 	asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||||
| 
 | ||||
| 	return low | ((u64)high) << 32; | ||||
| } | ||||
							
								
								
									
										17
									
								
								tools/perf/arch/x86/util/tsc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tools/perf/arch/x86/util/tsc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| #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); | ||||
| 
 | ||||
| #endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */ | ||||
							
								
								
									
										51
									
								
								tools/perf/arch/x86/util/unwind-libdw.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tools/perf/arch/x86/util/unwind-libdw.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
| } | ||||
							
								
								
									
										112
									
								
								tools/perf/arch/x86/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								tools/perf/arch/x86/util/unwind-libunwind.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| 
 | ||||
| #include <errno.h> | ||||
| #include <libunwind.h> | ||||
| #include "perf_regs.h" | ||||
| #include "../../util/unwind.h" | ||||
| #include "../../util/debug.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 */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228