diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/annotate-data.c | 213 |
1 files changed, 169 insertions, 44 deletions
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 48fea0c716ef..83b5aa00f01c 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -24,6 +24,12 @@ #include "symbol_conf.h" #include "thread.h" +enum type_state_kind { + TSR_KIND_INVALID = 0, + TSR_KIND_TYPE, + TSR_KIND_PERCPU_BASE, +}; + #define pr_debug_dtp(fmt, ...) \ do { \ if (debug_type_profile) \ @@ -32,7 +38,7 @@ do { \ pr_debug3(fmt, ##__VA_ARGS__); \ } while (0) -static void pr_debug_type_name(Dwarf_Die *die) +static void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) { struct strbuf sb; char *str; @@ -40,6 +46,18 @@ static void pr_debug_type_name(Dwarf_Die *die) if (!debug_type_profile && verbose < 3) return; + switch (kind) { + case TSR_KIND_INVALID: + pr_info("\n"); + return; + case TSR_KIND_PERCPU_BASE: + pr_info(" percpu base\n"); + return; + case TSR_KIND_TYPE: + default: + break; + } + strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); str = strbuf_detach(&sb, NULL); @@ -53,8 +71,10 @@ static void pr_debug_type_name(Dwarf_Die *die) */ struct type_state_reg { Dwarf_Die type; + u32 imm_value; bool ok; bool caller_saved; + u8 kind; }; /* Type information in a stack location, dynamically allocated */ @@ -64,6 +84,7 @@ struct type_state_stack { int offset; int size; bool compound; + u8 kind; }; /* FIXME: This should be arch-dependent */ @@ -82,6 +103,8 @@ struct type_state { struct list_head stack_vars; /* return value register */ int ret_reg; + /* stack pointer register */ + int stack_reg; }; static bool has_reg_type(struct type_state *state, int reg) @@ -105,6 +128,7 @@ static void init_type_state(struct type_state *state, struct arch *arch) state->regs[10].caller_saved = true; state->regs[11].caller_saved = true; state->ret_reg = 0; + state->stack_reg = 7; } } @@ -350,7 +374,7 @@ static struct type_state_stack *find_stack_state(struct type_state *state, return NULL; } -static void set_stack_state(struct type_state_stack *stack, int offset, +static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, Dwarf_Die *type_die) { int tag; @@ -364,6 +388,7 @@ static void set_stack_state(struct type_state_stack *stack, int offset, stack->type = *type_die; stack->size = size; stack->offset = offset; + stack->kind = kind; switch (tag) { case DW_TAG_structure_type: @@ -377,34 +402,60 @@ static void set_stack_state(struct type_state_stack *stack, int offset, } static struct type_state_stack *findnew_stack_state(struct type_state *state, - int offset, Dwarf_Die *type_die) + int offset, u8 kind, + Dwarf_Die *type_die) { struct type_state_stack *stack = find_stack_state(state, offset); if (stack) { - set_stack_state(stack, offset, type_die); + set_stack_state(stack, offset, kind, type_die); return stack; } stack = malloc(sizeof(*stack)); if (stack) { - set_stack_state(stack, offset, type_die); + set_stack_state(stack, offset, kind, type_die); list_add(&stack->list, &state->stack_vars); } return stack; } +static bool get_global_var_info(struct data_loc_info *dloc, u64 addr, + const char **var_name, int *var_offset) +{ + struct addr_location al; + struct symbol *sym; + u64 mem_addr; + + /* Kernel symbols might be relocated */ + mem_addr = addr + map__reloc(dloc->ms->map); + + addr_location__init(&al); + sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode, + mem_addr, &al); + if (sym) { + *var_name = sym->name; + /* Calculate type offset from the start of variable */ + *var_offset = mem_addr - map__unmap_ip(al.map, sym->start); + } else { + *var_name = NULL; + } + addr_location__exit(&al); + if (*var_name == NULL) + return false; + + return true; +} + static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, u64 ip, u64 var_addr, int *var_offset, Dwarf_Die *type_die) { - u64 pc, mem_addr; + u64 pc; int offset; bool is_pointer = false; - const char *var_name = NULL; + const char *var_name; Dwarf_Die var_die; - struct addr_location al; - struct symbol *sym; /* Try to get the variable by address first */ if (die_find_variable_by_addr(cu_die, var_addr, &var_die, &offset) && @@ -413,19 +464,7 @@ static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, return true; } - /* Kernel symbols might be relocated */ - mem_addr = var_addr + map__reloc(dloc->ms->map); - - addr_location__init(&al); - sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode, - mem_addr, &al); - if (sym) { - var_name = sym->name; - /* Calculate type offset from the start of variable */ - *var_offset = mem_addr - map__unmap_ip(al.map, sym->start); - } - addr_location__exit(&al); - if (var_name == NULL) + if (!get_global_var_info(dloc, var_addr, &var_name, var_offset)) return false; pc = map__rip_2objdump(dloc->ms->map, ip); @@ -470,27 +509,30 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo continue; if (var->reg == DWARF_REG_FB) { - findnew_stack_state(state, var->offset, &mem_die); + findnew_stack_state(state, var->offset, TSR_KIND_TYPE, + &mem_die); pr_debug_dtp("var [%"PRIx64"] -%#x(stack)", insn_offset, -var->offset); - pr_debug_type_name(&mem_die); + pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } else if (var->reg == fbreg) { - findnew_stack_state(state, var->offset - fb_offset, &mem_die); + findnew_stack_state(state, var->offset - fb_offset, + TSR_KIND_TYPE, &mem_die); pr_debug_dtp("var [%"PRIx64"] -%#x(stack)", insn_offset, -var->offset + fb_offset); - pr_debug_type_name(&mem_die); + pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } else if (has_reg_type(state, var->reg) && var->offset == 0) { struct type_state_reg *reg; reg = &state->regs[var->reg]; reg->type = mem_die; + reg->kind = TSR_KIND_TYPE; reg->ok = true; pr_debug_dtp("var [%"PRIx64"] reg%d", insn_offset, var->reg); - pr_debug_type_name(&mem_die); + pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } } } @@ -533,11 +575,12 @@ static void update_insn_state_x86(struct type_state *state, if (die_find_func_rettype(cu_die, func->name, &type_die)) { tsr = &state->regs[state->ret_reg]; tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; tsr->ok = true; pr_debug_dtp("call [%x] return -> reg%d", insn_offset, state->ret_reg); - pr_debug_type_name(&type_die); + pr_debug_type_name(&type_die, tsr->kind); } return; } @@ -580,11 +623,12 @@ static void update_insn_state_x86(struct type_state *state, } tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; tsr->ok = true; pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d", insn_offset, var_addr, dst->reg1); - pr_debug_type_name(&tsr->type); + pr_debug_type_name(&tsr->type, tsr->kind); return; } @@ -595,11 +639,12 @@ static void update_insn_state_x86(struct type_state *state, } tsr->type = state->regs[src->reg1].type; + tsr->kind = state->regs[src->reg1].kind; tsr->ok = true; pr_debug_dtp("mov [%x] reg%d -> reg%d", insn_offset, src->reg1, dst->reg1); - pr_debug_type_name(&tsr->type); + pr_debug_type_name(&tsr->type, tsr->kind); } /* Case 2. memory to register transers */ if (src->mem_ref && !dst->mem_ref) { @@ -622,11 +667,13 @@ retry: return; } else if (!stack->compound) { tsr->type = stack->type; + tsr->kind = stack->kind; tsr->ok = true; } else if (die_get_member_type(&stack->type, offset - stack->offset, &type_die)) { tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; tsr->ok = true; } else { tsr->ok = false; @@ -635,18 +682,20 @@ retry: pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d", insn_offset, -offset, dst->reg1); - pr_debug_type_name(&tsr->type); + pr_debug_type_name(&tsr->type, tsr->kind); } /* And then dereference the pointer if it has one */ else if (has_reg_type(state, sreg) && state->regs[sreg].ok && + state->regs[sreg].kind == TSR_KIND_TYPE && die_deref_ptr_type(&state->regs[sreg].type, src->offset, &type_die)) { tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; tsr->ok = true; pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d", insn_offset, src->offset, sreg, dst->reg1); - pr_debug_type_name(&tsr->type); + pr_debug_type_name(&tsr->type, tsr->kind); } /* Or check if it's a global variable */ else if (sreg == DWARF_REG_PC) { @@ -665,11 +714,37 @@ retry: } tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; tsr->ok = true; pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d", insn_offset, addr, dst->reg1); - pr_debug_type_name(&type_die); + pr_debug_type_name(&type_die, tsr->kind); + } + /* And check percpu access with base register */ + else if (has_reg_type(state, sreg) && + state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) { + u64 ip = dloc->ms->sym->start + dl->al.offset; + int offset; + + /* + * In kernel, %gs points to a per-cpu region for the + * current CPU. Access with a constant offset should + * be treated as a global variable access. + */ + if (get_global_var_type(cu_die, dloc, ip, src->offset, + &offset, &type_die) && + die_get_member_type(&type_die, offset, &type_die)) { + tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; + tsr->ok = true; + + pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d type=", + insn_offset, src->offset, sreg, dst->reg1); + pr_debug_type_name(&tsr->type, tsr->kind); + } else { + tsr->ok = false; + } } /* Or try another register if any */ else if (src->multi_regs && sreg == src->reg1 && @@ -677,8 +752,22 @@ retry: sreg = src->reg2; goto retry; } - /* It failed to get a type info, mark it as invalid */ else { + int offset; + const char *var_name = NULL; + + /* it might be per-cpu variable (in kernel) access */ + if (src->offset < 0) { + if (get_global_var_info(dloc, (s64)src->offset, + &var_name, &offset) && + !strcmp(var_name, "__per_cpu_offset")) { + tsr->kind = TSR_KIND_PERCPU_BASE; + + pr_debug_dtp("mov [%x] percpu base reg%d\n", + insn_offset, dst->reg1); + } + } + tsr->ok = false; } } @@ -693,6 +782,8 @@ retry: struct type_state_stack *stack; int offset = dst->offset - fboff; + tsr = &state->regs[src->reg1]; + stack = find_stack_state(state, offset); if (stack) { /* @@ -703,16 +794,16 @@ retry: * die_get_member_type(). */ if (!stack->compound) - set_stack_state(stack, offset, - &state->regs[src->reg1].type); + set_stack_state(stack, offset, tsr->kind, + &tsr->type); } else { - findnew_stack_state(state, offset, - &state->regs[src->reg1].type); + findnew_stack_state(state, offset, tsr->kind, + &tsr->type); } pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)", insn_offset, src->reg1, -offset); - pr_debug_type_name(&state->regs[src->reg1].type); + pr_debug_type_name(&tsr->type, tsr->kind); } /* * Ignore other transfers since it'd set a value in a struct @@ -824,10 +915,11 @@ static bool check_matching_type(struct type_state *state, Dwarf_Word size; u32 insn_offset = dloc->ip - dloc->ms->sym->start; - pr_debug_dtp("chk [%x] reg%d offset=%#x ok=%d", - insn_offset, reg, dloc->op->offset, state->regs[reg].ok); + pr_debug_dtp("chk [%x] reg%d offset=%#x ok=%d kind=%d", + insn_offset, reg, dloc->op->offset, + state->regs[reg].ok, state->regs[reg].kind); - if (state->regs[reg].ok) { + if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_TYPE) { int tag = dwarf_tag(&state->regs[reg].type); pr_debug_dtp("\n"); @@ -893,10 +985,25 @@ static bool check_matching_type(struct type_state *state, return true; } + if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) { + u64 var_addr = dloc->op->offset; + int var_offset; + + pr_debug_dtp(" percpu var\n"); + + if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr, + &var_offset, type_die)) { + dloc->type_offset = var_offset; + return true; + } + return false; + } + if (map__dso(dloc->ms->map)->kernel && arch__is(dloc->arch, "x86")) { u64 addr; int offset; + /* Direct this-cpu access like "%gs:0x34740" */ if (dloc->op->segment == INSN_SEG_X86_GS && dloc->op->imm) { pr_debug_dtp(" this-cpu var\n"); @@ -907,6 +1014,24 @@ static bool check_matching_type(struct type_state *state, dloc->type_offset = offset; return true; } + return false; + } + + /* Access to per-cpu base like "-0x7dcf0500(,%rdx,8)" */ + if (dloc->op->offset < 0 && reg != state->stack_reg) { + const char *var_name = NULL; + + addr = (s64) dloc->op->offset; + + if (get_global_var_info(dloc, addr, &var_name, &offset) && + !strcmp(var_name, "__per_cpu_offset") && offset == 0 && + get_global_var_type(cu_die, dloc, dloc->ip, addr, + &offset, type_die)) { + pr_debug_dtp(" percpu base\n"); + + dloc->type_offset = offset; + return true; + } } } @@ -1015,7 +1140,7 @@ again: ret = 0; pr_debug_dtp("found by insn track: %#x(reg%d) type-offset=%#x", dloc->op->offset, reg, dloc->type_offset); - pr_debug_type_name(type_die); + pr_debug_type_name(type_die, TSR_KIND_TYPE); break; } @@ -1147,7 +1272,7 @@ retry: loc->offset, reg, fb_offset, offset); else pr_debug_dtp("%#x(reg%d)", loc->offset, reg); - pr_debug_type_name(type_die); + pr_debug_type_name(type_die, TSR_KIND_TYPE); } dloc->type_offset = offset; goto out; |