Skip to content

Commit ba16c22

Browse files
author
Fox Snowpatch
committedJul 13, 2024·
1 parent 582b0e5 commit ba16c22

22 files changed

+1421
-505
lines changed
 

‎tools/include/linux/string.h

+2
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ extern char * __must_check skip_spaces(const char *);
4646

4747
extern char *strim(char *);
4848

49+
extern void remove_spaces(char *s);
50+
4951
extern void *memchr_inv(const void *start, int c, size_t bytes);
5052
#endif /* _TOOLS_LINUX_STRING_H_ */

‎tools/lib/string.c

+13
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,19 @@ char *strim(char *s)
153153
return skip_spaces(s);
154154
}
155155

156+
/*
157+
* remove_spaces - Removes whitespaces from @s
158+
*/
159+
void remove_spaces(char *s)
160+
{
161+
char *d = s;
162+
163+
do {
164+
while (*d == ' ')
165+
++d;
166+
} while ((*s++ = *d++));
167+
}
168+
156169
/**
157170
* strreplace - Replace all occurrences of character in string.
158171
* @s: The string to operate on.

‎tools/perf/arch/arm64/annotate/instructions.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ struct arm64_annotate {
1111

1212
static int arm64_mov__parse(struct arch *arch __maybe_unused,
1313
struct ins_operands *ops,
14-
struct map_symbol *ms __maybe_unused)
14+
struct map_symbol *ms __maybe_unused,
15+
struct disasm_line *dl __maybe_unused)
1516
{
1617
char *s = strchr(ops->raw, ','), *target, *endptr;
1718

‎tools/perf/arch/loongarch/annotate/instructions.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
66
*/
77

8-
static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
8+
static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
9+
struct disasm_line *dl __maybe_unused)
910
{
1011
char *c, *endptr, *tok, *name;
1112
struct map *map = ms->map;
@@ -51,7 +52,8 @@ static struct ins_ops loongarch_call_ops = {
5152
.scnprintf = call__scnprintf,
5253
};
5354

54-
static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
55+
static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
56+
struct disasm_line *dl __maybe_unused)
5557
{
5658
struct map *map = ms->map;
5759
struct symbol *sym = ms->sym;

‎tools/perf/arch/powerpc/annotate/instructions.c

+254
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,266 @@ static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, con
4949
return ops;
5050
}
5151

52+
#define PPC_OP(op) (((op) >> 26) & 0x3F)
53+
#define PPC_21_30(R) (((R) >> 1) & 0x3ff)
54+
#define PPC_22_30(R) (((R) >> 1) & 0x1ff)
55+
56+
struct insn_offset {
57+
const char *name;
58+
int value;
59+
};
60+
61+
/*
62+
* There are memory instructions with opcode 31 which are
63+
* of X Form, Example:
64+
* ldx RT,RA,RB
65+
* ______________________________________
66+
* | 31 | RT | RA | RB | 21 |/|
67+
* --------------------------------------
68+
* 0 6 11 16 21 30 31
69+
*
70+
* But all instructions with opcode 31 are not memory.
71+
* Example: add RT,RA,RB
72+
*
73+
* Use bits 21 to 30 to check memory insns with 31 as opcode.
74+
* In ins_array below, for ldx instruction:
75+
* name => OP_31_XOP_LDX
76+
* value => 21
77+
*/
78+
79+
static struct insn_offset ins_array[] = {
80+
{ .name = "OP_31_XOP_LXSIWZX", .value = 12, },
81+
{ .name = "OP_31_XOP_LWARX", .value = 20, },
82+
{ .name = "OP_31_XOP_LDX", .value = 21, },
83+
{ .name = "OP_31_XOP_LWZX", .value = 23, },
84+
{ .name = "OP_31_XOP_LDUX", .value = 53, },
85+
{ .name = "OP_31_XOP_LWZUX", .value = 55, },
86+
{ .name = "OP_31_XOP_LXSIWAX", .value = 76, },
87+
{ .name = "OP_31_XOP_LDARX", .value = 84, },
88+
{ .name = "OP_31_XOP_LBZX", .value = 87, },
89+
{ .name = "OP_31_XOP_LVX", .value = 103, },
90+
{ .name = "OP_31_XOP_LBZUX", .value = 119, },
91+
{ .name = "OP_31_XOP_STXSIWX", .value = 140, },
92+
{ .name = "OP_31_XOP_STDX", .value = 149, },
93+
{ .name = "OP_31_XOP_STWX", .value = 151, },
94+
{ .name = "OP_31_XOP_STDUX", .value = 181, },
95+
{ .name = "OP_31_XOP_STWUX", .value = 183, },
96+
{ .name = "OP_31_XOP_STBX", .value = 215, },
97+
{ .name = "OP_31_XOP_STVX", .value = 231, },
98+
{ .name = "OP_31_XOP_STBUX", .value = 247, },
99+
{ .name = "OP_31_XOP_LHZX", .value = 279, },
100+
{ .name = "OP_31_XOP_LHZUX", .value = 311, },
101+
{ .name = "OP_31_XOP_LXVDSX", .value = 332, },
102+
{ .name = "OP_31_XOP_LWAX", .value = 341, },
103+
{ .name = "OP_31_XOP_LHAX", .value = 343, },
104+
{ .name = "OP_31_XOP_LWAUX", .value = 373, },
105+
{ .name = "OP_31_XOP_LHAUX", .value = 375, },
106+
{ .name = "OP_31_XOP_STHX", .value = 407, },
107+
{ .name = "OP_31_XOP_STHUX", .value = 439, },
108+
{ .name = "OP_31_XOP_LXSSPX", .value = 524, },
109+
{ .name = "OP_31_XOP_LDBRX", .value = 532, },
110+
{ .name = "OP_31_XOP_LSWX", .value = 533, },
111+
{ .name = "OP_31_XOP_LWBRX", .value = 534, },
112+
{ .name = "OP_31_XOP_LFSUX", .value = 567, },
113+
{ .name = "OP_31_XOP_LXSDX", .value = 588, },
114+
{ .name = "OP_31_XOP_LSWI", .value = 597, },
115+
{ .name = "OP_31_XOP_LFDX", .value = 599, },
116+
{ .name = "OP_31_XOP_LFDUX", .value = 631, },
117+
{ .name = "OP_31_XOP_STXSSPX", .value = 652, },
118+
{ .name = "OP_31_XOP_STDBRX", .value = 660, },
119+
{ .name = "OP_31_XOP_STXWX", .value = 661, },
120+
{ .name = "OP_31_XOP_STWBRX", .value = 662, },
121+
{ .name = "OP_31_XOP_STFSX", .value = 663, },
122+
{ .name = "OP_31_XOP_STFSUX", .value = 695, },
123+
{ .name = "OP_31_XOP_STXSDX", .value = 716, },
124+
{ .name = "OP_31_XOP_STSWI", .value = 725, },
125+
{ .name = "OP_31_XOP_STFDX", .value = 727, },
126+
{ .name = "OP_31_XOP_STFDUX", .value = 759, },
127+
{ .name = "OP_31_XOP_LXVW4X", .value = 780, },
128+
{ .name = "OP_31_XOP_LHBRX", .value = 790, },
129+
{ .name = "OP_31_XOP_LXVD2X", .value = 844, },
130+
{ .name = "OP_31_XOP_LFIWAX", .value = 855, },
131+
{ .name = "OP_31_XOP_LFIWZX", .value = 887, },
132+
{ .name = "OP_31_XOP_STXVW4X", .value = 908, },
133+
{ .name = "OP_31_XOP_STHBRX", .value = 918, },
134+
{ .name = "OP_31_XOP_STXVD2X", .value = 972, },
135+
{ .name = "OP_31_XOP_STFIWX", .value = 983, },
136+
};
137+
138+
/*
139+
* Arithmetic instructions which are having opcode as 31.
140+
* These instructions are tracked to save the register state
141+
* changes. Example:
142+
*
143+
* lwz r10,264(r3)
144+
* add r31, r3, r3
145+
* lwz r9, 0(r31)
146+
*
147+
* Here instruction tracking needs to identify the "add"
148+
* instruction and save data type of r3 to r31. If a sample
149+
* is hit at next "lwz r9, 0(r31)", by this instruction tracking,
150+
* data type of r31 can be resolved.
151+
*/
152+
static struct insn_offset arithmetic_ins_op_31[] = {
153+
{ .name = "SUB_CARRY_XO_FORM", .value = 8, },
154+
{ .name = "MUL_HDW_XO_FORM1", .value = 9, },
155+
{ .name = "ADD_CARRY_XO_FORM", .value = 10, },
156+
{ .name = "MUL_HW_XO_FORM1", .value = 11, },
157+
{ .name = "SUB_XO_FORM", .value = 40, },
158+
{ .name = "MUL_HDW_XO_FORM", .value = 73, },
159+
{ .name = "MUL_HW_XO_FORM", .value = 75, },
160+
{ .name = "SUB_EXT_XO_FORM", .value = 136, },
161+
{ .name = "ADD_EXT_XO_FORM", .value = 138, },
162+
{ .name = "SUB_ZERO_EXT_XO_FORM", .value = 200, },
163+
{ .name = "ADD_ZERO_EXT_XO_FORM", .value = 202, },
164+
{ .name = "SUB_EXT_XO_FORM2", .value = 232, },
165+
{ .name = "MUL_DW_XO_FORM", .value = 233, },
166+
{ .name = "ADD_EXT_XO_FORM2", .value = 234, },
167+
{ .name = "MUL_W_XO_FORM", .value = 235, },
168+
{ .name = "ADD_XO_FORM", .value = 266, },
169+
{ .name = "DIV_DW_XO_FORM1", .value = 457, },
170+
{ .name = "DIV_W_XO_FORM1", .value = 459, },
171+
{ .name = "DIV_DW_XO_FORM", .value = 489, },
172+
{ .name = "DIV_W_XO_FORM", .value = 491, },
173+
};
174+
175+
static struct insn_offset arithmetic_two_ops[] = {
176+
{ .name = "mulli", .value = 7, },
177+
{ .name = "subfic", .value = 8, },
178+
{ .name = "addic", .value = 12, },
179+
{ .name = "addic.", .value = 13, },
180+
{ .name = "addi", .value = 14, },
181+
{ .name = "addis", .value = 15, },
182+
};
183+
184+
static int cmp_offset(const void *a, const void *b)
185+
{
186+
const struct insn_offset *val1 = a;
187+
const struct insn_offset *val2 = b;
188+
189+
return (val1->value - val2->value);
190+
}
191+
192+
static struct ins_ops *check_ppc_insn(struct disasm_line *dl)
193+
{
194+
int raw_insn = dl->raw.raw_insn;
195+
int opcode = PPC_OP(raw_insn);
196+
int mem_insn_31 = PPC_21_30(raw_insn);
197+
struct insn_offset *ret;
198+
struct insn_offset mem_insns_31_opcode = {
199+
"OP_31_INSN",
200+
mem_insn_31
201+
};
202+
char name_insn[32];
203+
204+
/*
205+
* Instructions with opcode 32 to 63 are memory
206+
* instructions in powerpc
207+
*/
208+
if ((opcode & 0x20)) {
209+
/*
210+
* Set name in case of raw instruction to
211+
* opcode to be used in insn-stat
212+
*/
213+
if (!strlen(dl->ins.name)) {
214+
sprintf(name_insn, "%d", opcode);
215+
dl->ins.name = strdup(name_insn);
216+
}
217+
return &load_store_ops;
218+
} else if (opcode == 31) {
219+
/* Check for memory instructions with opcode 31 */
220+
ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset);
221+
if (ret) {
222+
if (!strlen(dl->ins.name))
223+
dl->ins.name = strdup(ret->name);
224+
return &load_store_ops;
225+
} else {
226+
mem_insns_31_opcode.value = PPC_22_30(raw_insn);
227+
ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31),
228+
sizeof(arithmetic_ins_op_31[0]), cmp_offset);
229+
if (ret != NULL)
230+
return &arithmetic_ops;
231+
/* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */
232+
if (PPC_21_30(raw_insn) == 444)
233+
return &arithmetic_ops;
234+
}
235+
} else {
236+
mem_insns_31_opcode.value = opcode;
237+
ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops),
238+
sizeof(arithmetic_two_ops[0]), cmp_offset);
239+
if (ret != NULL)
240+
return &arithmetic_ops;
241+
}
242+
243+
return NULL;
244+
}
245+
246+
/*
247+
* Instruction tracking function to track register state moves.
248+
* Example sequence:
249+
* ld r10,264(r3)
250+
* mr r31,r3
251+
* <<after some sequence>
252+
* ld r9,312(r31)
253+
*
254+
* Previous instruction sequence shows that register state of r3
255+
* is moved to r31. update_insn_state_powerpc tracks these state
256+
* changes
257+
*/
258+
#ifdef HAVE_DWARF_SUPPORT
259+
static void update_insn_state_powerpc(struct type_state *state,
260+
struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,
261+
struct disasm_line *dl)
262+
{
263+
struct annotated_insn_loc loc;
264+
struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
265+
struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
266+
struct type_state_reg *tsr;
267+
u32 insn_offset = dl->al.offset;
268+
269+
if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
270+
return;
271+
272+
/*
273+
* Value 444 for bits 21:30 is for "mr"
274+
* instruction. "mr" is extended OR. So set the
275+
* source and destination reg correctly
276+
*/
277+
if (PPC_21_30(dl->raw.raw_insn) == 444) {
278+
int src_reg = src->reg1;
279+
280+
src->reg1 = dst->reg1;
281+
dst->reg1 = src_reg;
282+
}
283+
284+
if (!has_reg_type(state, dst->reg1))
285+
return;
286+
287+
tsr = &state->regs[dst->reg1];
288+
289+
if (!has_reg_type(state, src->reg1) ||
290+
!state->regs[src->reg1].ok) {
291+
tsr->ok = false;
292+
return;
293+
}
294+
295+
tsr->type = state->regs[src->reg1].type;
296+
tsr->kind = state->regs[src->reg1].kind;
297+
tsr->ok = true;
298+
299+
pr_debug_dtp("mov [%x] reg%d -> reg%d",
300+
insn_offset, src->reg1, dst->reg1);
301+
pr_debug_type_name(&tsr->type, tsr->kind);
302+
}
303+
#endif /* HAVE_DWARF_SUPPORT */
304+
52305
static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
53306
{
54307
if (!arch->initialized) {
55308
arch->initialized = true;
56309
arch->associate_instruction_ops = powerpc__associate_instruction_ops;
57310
arch->objdump.comment_char = '#';
311+
annotate_opts.show_asm_raw = true;
58312
}
59313

60314
return 0;

‎tools/perf/arch/powerpc/util/dwarf-regs.c

+53
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,56 @@ int regs_query_register_offset(const char *name)
9898
return roff->ptregs_offset;
9999
return -EINVAL;
100100
}
101+
102+
#define PPC_OP(op) (((op) >> 26) & 0x3F)
103+
#define PPC_RA(a) (((a) >> 16) & 0x1f)
104+
#define PPC_RT(t) (((t) >> 21) & 0x1f)
105+
#define PPC_RB(b) (((b) >> 11) & 0x1f)
106+
#define PPC_D(D) ((D) & 0xfffe)
107+
#define PPC_DS(DS) ((DS) & 0xfffc)
108+
#define OP_LD 58
109+
#define OP_STD 62
110+
111+
static int get_source_reg(u32 raw_insn)
112+
{
113+
return PPC_RA(raw_insn);
114+
}
115+
116+
static int get_target_reg(u32 raw_insn)
117+
{
118+
return PPC_RT(raw_insn);
119+
}
120+
121+
static int get_offset_opcode(u32 raw_insn)
122+
{
123+
int opcode = PPC_OP(raw_insn);
124+
125+
/* DS- form */
126+
if ((opcode == OP_LD) || (opcode == OP_STD))
127+
return PPC_DS(raw_insn);
128+
else
129+
return PPC_D(raw_insn);
130+
}
131+
132+
/*
133+
* Fills the required fields for op_loc depending on if it
134+
* is a source or target.
135+
* D form: ins RT,D(RA) -> src_reg1 = RA, offset = D, dst_reg1 = RT
136+
* DS form: ins RT,DS(RA) -> src_reg1 = RA, offset = DS, dst_reg1 = RT
137+
* X form: ins RT,RA,RB -> src_reg1 = RA, src_reg2 = RB, dst_reg1 = RT
138+
*/
139+
void get_powerpc_regs(u32 raw_insn, int is_source,
140+
struct annotated_op_loc *op_loc)
141+
{
142+
if (is_source)
143+
op_loc->reg1 = get_source_reg(raw_insn);
144+
else
145+
op_loc->reg1 = get_target_reg(raw_insn);
146+
147+
if (op_loc->multi_regs)
148+
op_loc->reg2 = PPC_RB(raw_insn);
149+
150+
/* TODO: Implement offset handling for X Form */
151+
if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31))
152+
op_loc->offset = get_offset_opcode(raw_insn);
153+
}

‎tools/perf/arch/s390/annotate/instructions.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <linux/compiler.h>
33

44
static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
5-
struct map_symbol *ms)
5+
struct map_symbol *ms, struct disasm_line *dl __maybe_unused)
66
{
77
char *endptr, *tok, *name;
88
struct map *map = ms->map;
@@ -52,7 +52,8 @@ static struct ins_ops s390_call_ops = {
5252

5353
static int s390_mov__parse(struct arch *arch __maybe_unused,
5454
struct ins_operands *ops,
55-
struct map_symbol *ms __maybe_unused)
55+
struct map_symbol *ms __maybe_unused,
56+
struct disasm_line *dl __maybe_unused)
5657
{
5758
char *s = strchr(ops->raw, ','), *target, *endptr;
5859

‎tools/perf/arch/x86/annotate/instructions.c

+377
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,380 @@ static int x86__annotate_init(struct arch *arch, char *cpuid)
206206
arch->initialized = true;
207207
return err;
208208
}
209+
210+
#ifdef HAVE_DWARF_SUPPORT
211+
static void update_insn_state_x86(struct type_state *state,
212+
struct data_loc_info *dloc, Dwarf_Die *cu_die,
213+
struct disasm_line *dl)
214+
{
215+
struct annotated_insn_loc loc;
216+
struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
217+
struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
218+
struct type_state_reg *tsr;
219+
Dwarf_Die type_die;
220+
u32 insn_offset = dl->al.offset;
221+
int fbreg = dloc->fbreg;
222+
int fboff = 0;
223+
224+
if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
225+
return;
226+
227+
if (ins__is_call(&dl->ins)) {
228+
struct symbol *func = dl->ops.target.sym;
229+
230+
if (func == NULL)
231+
return;
232+
233+
/* __fentry__ will preserve all registers */
234+
if (!strcmp(func->name, "__fentry__"))
235+
return;
236+
237+
pr_debug_dtp("call [%x] %s\n", insn_offset, func->name);
238+
239+
/* Otherwise invalidate caller-saved registers after call */
240+
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
241+
if (state->regs[i].caller_saved)
242+
state->regs[i].ok = false;
243+
}
244+
245+
/* Update register with the return type (if any) */
246+
if (die_find_func_rettype(cu_die, func->name, &type_die)) {
247+
tsr = &state->regs[state->ret_reg];
248+
tsr->type = type_die;
249+
tsr->kind = TSR_KIND_TYPE;
250+
tsr->ok = true;
251+
252+
pr_debug_dtp("call [%x] return -> reg%d",
253+
insn_offset, state->ret_reg);
254+
pr_debug_type_name(&type_die, tsr->kind);
255+
}
256+
return;
257+
}
258+
259+
if (!strncmp(dl->ins.name, "add", 3)) {
260+
u64 imm_value = -1ULL;
261+
int offset;
262+
const char *var_name = NULL;
263+
struct map_symbol *ms = dloc->ms;
264+
u64 ip = ms->sym->start + dl->al.offset;
265+
266+
if (!has_reg_type(state, dst->reg1))
267+
return;
268+
269+
tsr = &state->regs[dst->reg1];
270+
271+
if (src->imm)
272+
imm_value = src->offset;
273+
else if (has_reg_type(state, src->reg1) &&
274+
state->regs[src->reg1].kind == TSR_KIND_CONST)
275+
imm_value = state->regs[src->reg1].imm_value;
276+
else if (src->reg1 == DWARF_REG_PC) {
277+
u64 var_addr = annotate_calc_pcrel(dloc->ms, ip,
278+
src->offset, dl);
279+
280+
if (get_global_var_info(dloc, var_addr,
281+
&var_name, &offset) &&
282+
!strcmp(var_name, "this_cpu_off") &&
283+
tsr->kind == TSR_KIND_CONST) {
284+
tsr->kind = TSR_KIND_PERCPU_BASE;
285+
imm_value = tsr->imm_value;
286+
}
287+
}
288+
else
289+
return;
290+
291+
if (tsr->kind != TSR_KIND_PERCPU_BASE)
292+
return;
293+
294+
if (get_global_var_type(cu_die, dloc, ip, imm_value, &offset,
295+
&type_die) && offset == 0) {
296+
/*
297+
* This is not a pointer type, but it should be treated
298+
* as a pointer.
299+
*/
300+
tsr->type = type_die;
301+
tsr->kind = TSR_KIND_POINTER;
302+
tsr->ok = true;
303+
304+
pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d",
305+
insn_offset, imm_value, dst->reg1);
306+
pr_debug_type_name(&tsr->type, tsr->kind);
307+
}
308+
return;
309+
}
310+
311+
if (strncmp(dl->ins.name, "mov", 3))
312+
return;
313+
314+
if (dloc->fb_cfa) {
315+
u64 ip = dloc->ms->sym->start + dl->al.offset;
316+
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
317+
318+
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
319+
fbreg = -1;
320+
}
321+
322+
/* Case 1. register to register or segment:offset to register transfers */
323+
if (!src->mem_ref && !dst->mem_ref) {
324+
if (!has_reg_type(state, dst->reg1))
325+
return;
326+
327+
tsr = &state->regs[dst->reg1];
328+
if (dso__kernel(map__dso(dloc->ms->map)) &&
329+
src->segment == INSN_SEG_X86_GS && src->imm) {
330+
u64 ip = dloc->ms->sym->start + dl->al.offset;
331+
u64 var_addr;
332+
int offset;
333+
334+
/*
335+
* In kernel, %gs points to a per-cpu region for the
336+
* current CPU. Access with a constant offset should
337+
* be treated as a global variable access.
338+
*/
339+
var_addr = src->offset;
340+
341+
if (var_addr == 40) {
342+
tsr->kind = TSR_KIND_CANARY;
343+
tsr->ok = true;
344+
345+
pr_debug_dtp("mov [%x] stack canary -> reg%d\n",
346+
insn_offset, dst->reg1);
347+
return;
348+
}
349+
350+
if (!get_global_var_type(cu_die, dloc, ip, var_addr,
351+
&offset, &type_die) ||
352+
!die_get_member_type(&type_die, offset, &type_die)) {
353+
tsr->ok = false;
354+
return;
355+
}
356+
357+
tsr->type = type_die;
358+
tsr->kind = TSR_KIND_TYPE;
359+
tsr->ok = true;
360+
361+
pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d",
362+
insn_offset, var_addr, dst->reg1);
363+
pr_debug_type_name(&tsr->type, tsr->kind);
364+
return;
365+
}
366+
367+
if (src->imm) {
368+
tsr->kind = TSR_KIND_CONST;
369+
tsr->imm_value = src->offset;
370+
tsr->ok = true;
371+
372+
pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n",
373+
insn_offset, tsr->imm_value, dst->reg1);
374+
return;
375+
}
376+
377+
if (!has_reg_type(state, src->reg1) ||
378+
!state->regs[src->reg1].ok) {
379+
tsr->ok = false;
380+
return;
381+
}
382+
383+
tsr->type = state->regs[src->reg1].type;
384+
tsr->kind = state->regs[src->reg1].kind;
385+
tsr->ok = true;
386+
387+
pr_debug_dtp("mov [%x] reg%d -> reg%d",
388+
insn_offset, src->reg1, dst->reg1);
389+
pr_debug_type_name(&tsr->type, tsr->kind);
390+
}
391+
/* Case 2. memory to register transers */
392+
if (src->mem_ref && !dst->mem_ref) {
393+
int sreg = src->reg1;
394+
395+
if (!has_reg_type(state, dst->reg1))
396+
return;
397+
398+
tsr = &state->regs[dst->reg1];
399+
400+
retry:
401+
/* Check stack variables with offset */
402+
if (sreg == fbreg) {
403+
struct type_state_stack *stack;
404+
int offset = src->offset - fboff;
405+
406+
stack = find_stack_state(state, offset);
407+
if (stack == NULL) {
408+
tsr->ok = false;
409+
return;
410+
} else if (!stack->compound) {
411+
tsr->type = stack->type;
412+
tsr->kind = stack->kind;
413+
tsr->ok = true;
414+
} else if (die_get_member_type(&stack->type,
415+
offset - stack->offset,
416+
&type_die)) {
417+
tsr->type = type_die;
418+
tsr->kind = TSR_KIND_TYPE;
419+
tsr->ok = true;
420+
} else {
421+
tsr->ok = false;
422+
return;
423+
}
424+
425+
pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d",
426+
insn_offset, -offset, dst->reg1);
427+
pr_debug_type_name(&tsr->type, tsr->kind);
428+
}
429+
/* And then dereference the pointer if it has one */
430+
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
431+
state->regs[sreg].kind == TSR_KIND_TYPE &&
432+
die_deref_ptr_type(&state->regs[sreg].type,
433+
src->offset, &type_die)) {
434+
tsr->type = type_die;
435+
tsr->kind = TSR_KIND_TYPE;
436+
tsr->ok = true;
437+
438+
pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",
439+
insn_offset, src->offset, sreg, dst->reg1);
440+
pr_debug_type_name(&tsr->type, tsr->kind);
441+
}
442+
/* Or check if it's a global variable */
443+
else if (sreg == DWARF_REG_PC) {
444+
struct map_symbol *ms = dloc->ms;
445+
u64 ip = ms->sym->start + dl->al.offset;
446+
u64 addr;
447+
int offset;
448+
449+
addr = annotate_calc_pcrel(ms, ip, src->offset, dl);
450+
451+
if (!get_global_var_type(cu_die, dloc, ip, addr, &offset,
452+
&type_die) ||
453+
!die_get_member_type(&type_die, offset, &type_die)) {
454+
tsr->ok = false;
455+
return;
456+
}
457+
458+
tsr->type = type_die;
459+
tsr->kind = TSR_KIND_TYPE;
460+
tsr->ok = true;
461+
462+
pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d",
463+
insn_offset, addr, dst->reg1);
464+
pr_debug_type_name(&type_die, tsr->kind);
465+
}
466+
/* And check percpu access with base register */
467+
else if (has_reg_type(state, sreg) &&
468+
state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {
469+
u64 ip = dloc->ms->sym->start + dl->al.offset;
470+
u64 var_addr = src->offset;
471+
int offset;
472+
473+
if (src->multi_regs) {
474+
int reg2 = (sreg == src->reg1) ? src->reg2 : src->reg1;
475+
476+
if (has_reg_type(state, reg2) && state->regs[reg2].ok &&
477+
state->regs[reg2].kind == TSR_KIND_CONST)
478+
var_addr += state->regs[reg2].imm_value;
479+
}
480+
481+
/*
482+
* In kernel, %gs points to a per-cpu region for the
483+
* current CPU. Access with a constant offset should
484+
* be treated as a global variable access.
485+
*/
486+
if (get_global_var_type(cu_die, dloc, ip, var_addr,
487+
&offset, &type_die) &&
488+
die_get_member_type(&type_die, offset, &type_die)) {
489+
tsr->type = type_die;
490+
tsr->kind = TSR_KIND_TYPE;
491+
tsr->ok = true;
492+
493+
if (src->multi_regs) {
494+
pr_debug_dtp("mov [%x] percpu %#x(reg%d,reg%d) -> reg%d",
495+
insn_offset, src->offset, src->reg1,
496+
src->reg2, dst->reg1);
497+
} else {
498+
pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d",
499+
insn_offset, src->offset, sreg, dst->reg1);
500+
}
501+
pr_debug_type_name(&tsr->type, tsr->kind);
502+
} else {
503+
tsr->ok = false;
504+
}
505+
}
506+
/* And then dereference the calculated pointer if it has one */
507+
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
508+
state->regs[sreg].kind == TSR_KIND_POINTER &&
509+
die_get_member_type(&state->regs[sreg].type,
510+
src->offset, &type_die)) {
511+
tsr->type = type_die;
512+
tsr->kind = TSR_KIND_TYPE;
513+
tsr->ok = true;
514+
515+
pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d",
516+
insn_offset, src->offset, sreg, dst->reg1);
517+
pr_debug_type_name(&tsr->type, tsr->kind);
518+
}
519+
/* Or try another register if any */
520+
else if (src->multi_regs && sreg == src->reg1 &&
521+
src->reg1 != src->reg2) {
522+
sreg = src->reg2;
523+
goto retry;
524+
}
525+
else {
526+
int offset;
527+
const char *var_name = NULL;
528+
529+
/* it might be per-cpu variable (in kernel) access */
530+
if (src->offset < 0) {
531+
if (get_global_var_info(dloc, (s64)src->offset,
532+
&var_name, &offset) &&
533+
!strcmp(var_name, "__per_cpu_offset")) {
534+
tsr->kind = TSR_KIND_PERCPU_BASE;
535+
536+
pr_debug_dtp("mov [%x] percpu base reg%d\n",
537+
insn_offset, dst->reg1);
538+
}
539+
}
540+
541+
tsr->ok = false;
542+
}
543+
}
544+
/* Case 3. register to memory transfers */
545+
if (!src->mem_ref && dst->mem_ref) {
546+
if (!has_reg_type(state, src->reg1) ||
547+
!state->regs[src->reg1].ok)
548+
return;
549+
550+
/* Check stack variables with offset */
551+
if (dst->reg1 == fbreg) {
552+
struct type_state_stack *stack;
553+
int offset = dst->offset - fboff;
554+
555+
tsr = &state->regs[src->reg1];
556+
557+
stack = find_stack_state(state, offset);
558+
if (stack) {
559+
/*
560+
* The source register is likely to hold a type
561+
* of member if it's a compound type. Do not
562+
* update the stack variable type since we can
563+
* get the member type later by using the
564+
* die_get_member_type().
565+
*/
566+
if (!stack->compound)
567+
set_stack_state(stack, offset, tsr->kind,
568+
&tsr->type);
569+
} else {
570+
findnew_stack_state(state, offset, tsr->kind,
571+
&tsr->type);
572+
}
573+
574+
pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)",
575+
insn_offset, src->reg1, -offset);
576+
pr_debug_type_name(&tsr->type, tsr->kind);
577+
}
578+
/*
579+
* Ignore other transfers since it'd set a value in a struct
580+
* and won't change the type.
581+
*/
582+
}
583+
/* Case 4. memory to memory transfers (not handled for now) */
584+
}
585+
#endif

‎tools/perf/builtin-annotate.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,10 @@ static void print_annotate_item_stat(struct list_head *head, const char *title)
396396
printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n\n", total,
397397
total_good, 100.0 * total_good / (total ?: 1),
398398
total_bad, 100.0 * total_bad / (total ?: 1));
399-
printf(" %-10s: %5s %5s\n", "Name", "Good", "Bad");
399+
printf(" %-20s: %5s %5s\n", "Name/opcode", "Good", "Bad");
400400
printf("-----------------------------------------------------------\n");
401401
list_for_each_entry(istat, head, list)
402-
printf(" %-10s: %5d %5d\n", istat->name, istat->good, istat->bad);
402+
printf(" %-20s: %5d %5d\n", istat->name, istat->good, istat->bad);
403403
printf("\n");
404404
}
405405

‎tools/perf/util/annotate-data.c

+102-442
Large diffs are not rendered by default.

‎tools/perf/util/annotate-data.h

+83
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
#include <linux/compiler.h>
77
#include <linux/rbtree.h>
88
#include <linux/types.h>
9+
#include "dwarf-regs.h"
10+
#include "annotate.h"
11+
12+
#ifdef HAVE_DWARF_SUPPORT
13+
#include "debuginfo.h"
14+
#endif
915

1016
struct annotated_op_loc;
1117
struct debuginfo;
@@ -15,6 +21,23 @@ struct hist_entry;
1521
struct map_symbol;
1622
struct thread;
1723

24+
#define pr_debug_dtp(fmt, ...) \
25+
do { \
26+
if (debug_type_profile) \
27+
pr_info(fmt, ##__VA_ARGS__); \
28+
else \
29+
pr_debug3(fmt, ##__VA_ARGS__); \
30+
} while (0)
31+
32+
enum type_state_kind {
33+
TSR_KIND_INVALID = 0,
34+
TSR_KIND_TYPE,
35+
TSR_KIND_PERCPU_BASE,
36+
TSR_KIND_CONST,
37+
TSR_KIND_POINTER,
38+
TSR_KIND_CANARY,
39+
};
40+
1841
/**
1942
* struct annotated_member - Type of member field
2043
* @node: List entry in the parent list
@@ -143,6 +166,51 @@ struct annotated_data_stat {
143166
extern struct annotated_data_stat ann_data_stat;
144167

145168
#ifdef HAVE_DWARF_SUPPORT
169+
/*
170+
* Type information in a register, valid when @ok is true.
171+
* The @caller_saved registers are invalidated after a function call.
172+
*/
173+
struct type_state_reg {
174+
Dwarf_Die type;
175+
u32 imm_value;
176+
bool ok;
177+
bool caller_saved;
178+
u8 kind;
179+
};
180+
181+
/* Type information in a stack location, dynamically allocated */
182+
struct type_state_stack {
183+
struct list_head list;
184+
Dwarf_Die type;
185+
int offset;
186+
int size;
187+
bool compound;
188+
u8 kind;
189+
};
190+
191+
/* FIXME: This should be arch-dependent */
192+
#ifdef __powerpc__
193+
#define TYPE_STATE_MAX_REGS 32
194+
#else
195+
#define TYPE_STATE_MAX_REGS 16
196+
#endif
197+
198+
/*
199+
* State table to maintain type info in each register and stack location.
200+
* It'll be updated when new variable is allocated or type info is moved
201+
* to a new location (register or stack). As it'd be used with the
202+
* shortest path of basic blocks, it only maintains a single table.
203+
*/
204+
struct type_state {
205+
/* state of general purpose registers */
206+
struct type_state_reg regs[TYPE_STATE_MAX_REGS];
207+
/* state of stack location */
208+
struct list_head stack_vars;
209+
/* return value register */
210+
int ret_reg;
211+
/* stack pointer register */
212+
int stack_reg;
213+
};
146214

147215
/* Returns data type at the location (ip, reg, offset) */
148216
struct annotated_data_type *find_data_type(struct data_loc_info *dloc);
@@ -160,6 +228,21 @@ void global_var_type__tree_delete(struct rb_root *root);
160228

161229
int hist_entry__annotate_data_tty(struct hist_entry *he, struct evsel *evsel);
162230

231+
bool has_reg_type(struct type_state *state, int reg);
232+
struct type_state_stack *findnew_stack_state(struct type_state *state,
233+
int offset, u8 kind,
234+
Dwarf_Die *type_die);
235+
void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
236+
Dwarf_Die *type_die);
237+
struct type_state_stack *find_stack_state(struct type_state *state,
238+
int offset);
239+
bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
240+
u64 ip, u64 var_addr, int *var_offset,
241+
Dwarf_Die *type_die);
242+
bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
243+
const char **var_name, int *var_offset);
244+
void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind);
245+
163246
#else /* HAVE_DWARF_SUPPORT */
164247

165248
static inline struct annotated_data_type *

‎tools/perf/util/annotate.c

+25-4
Original file line numberDiff line numberDiff line change
@@ -2123,20 +2123,33 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
21232123
for_each_insn_op_loc(loc, i, op_loc) {
21242124
const char *insn_str = ops->source.raw;
21252125
bool multi_regs = ops->source.multi_regs;
2126+
bool mem_ref = ops->source.mem_ref;
21262127

21272128
if (i == INSN_OP_TARGET) {
21282129
insn_str = ops->target.raw;
21292130
multi_regs = ops->target.multi_regs;
2131+
mem_ref = ops->target.mem_ref;
21302132
}
21312133

21322134
/* Invalidate the register by default */
21332135
op_loc->reg1 = -1;
21342136
op_loc->reg2 = -1;
21352137

2136-
if (insn_str == NULL)
2137-
continue;
2138+
if (insn_str == NULL) {
2139+
if (!arch__is(arch, "powerpc"))
2140+
continue;
2141+
}
21382142

2139-
if (strchr(insn_str, arch->objdump.memory_ref_char)) {
2143+
/*
2144+
* For powerpc, call get_powerpc_regs function which extracts the
2145+
* required fields for op_loc, ie reg1, reg2, offset from the
2146+
* raw instruction.
2147+
*/
2148+
if (arch__is(arch, "powerpc")) {
2149+
op_loc->mem_ref = mem_ref;
2150+
op_loc->multi_regs = multi_regs;
2151+
get_powerpc_regs(dl->raw.raw_insn, !i, op_loc);
2152+
} else if (strchr(insn_str, arch->objdump.memory_ref_char)) {
21402153
op_loc->mem_ref = true;
21412154
op_loc->multi_regs = multi_regs;
21422155
extract_reg_offset(arch, insn_str, op_loc);
@@ -2216,7 +2229,7 @@ static struct annotated_item_stat *annotate_data_stat(struct list_head *head,
22162229
return NULL;
22172230

22182231
istat->name = strdup(name);
2219-
if (istat->name == NULL) {
2232+
if ((istat->name == NULL) || (!strlen(istat->name))) {
22202233
free(istat);
22212234
return NULL;
22222235
}
@@ -2412,6 +2425,14 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
24122425
op_loc->reg1 = DWARF_REG_PC;
24132426
}
24142427

2428+
/* Global reg variable 13 and 1
2429+
* assign to DWARF_REG_GLOBAL
2430+
*/
2431+
if (arch__is(arch, "powerpc")) {
2432+
if ((op_loc->reg1 == 13) || (op_loc->reg1 == 1))
2433+
op_loc->reg_type = DWARF_REG_GLOBAL;
2434+
}
2435+
24152436
mem_type = find_data_type(&dloc);
24162437

24172438
if (mem_type == NULL && is_stack_canary(arch, op_loc)) {

‎tools/perf/util/annotate.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,10 @@ struct annotation_line {
113113
struct disasm_line {
114114
struct ins ins;
115115
struct ins_operands ops;
116-
116+
union {
117+
u8 bytes[4];
118+
u32 raw_insn;
119+
} raw;
117120
/* This needs to be at the end. */
118121
struct annotation_line al;
119122
};
@@ -472,6 +475,7 @@ struct annotated_op_loc {
472475
bool mem_ref;
473476
bool multi_regs;
474477
bool imm;
478+
int reg_type;
475479
};
476480

477481
enum annotated_insn_ops {

‎tools/perf/util/disasm.c

+425-43
Large diffs are not rendered by default.

‎tools/perf/util/disasm.h

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44

55
#include "map_symbol.h"
66

7+
#ifdef HAVE_DWARF_SUPPORT
8+
#include "dwarf-aux.h"
9+
#endif
10+
711
struct annotation_options;
812
struct disasm_line;
913
struct ins;
1014
struct evsel;
1115
struct symbol;
16+
struct data_loc_info;
17+
struct type_state;
18+
struct disasm_line;
1219

1320
struct arch {
1421
const char *name;
@@ -32,6 +39,11 @@ struct arch {
3239
char memory_ref_char;
3340
char imm_char;
3441
} objdump;
42+
#ifdef HAVE_DWARF_SUPPORT
43+
void (*update_insn_state)(struct type_state *state,
44+
struct data_loc_info *dloc, Dwarf_Die *cu_die,
45+
struct disasm_line *dl);
46+
#endif
3547
};
3648

3749
struct ins {
@@ -50,13 +62,15 @@ struct ins_operands {
5062
bool offset_avail;
5163
bool outside;
5264
bool multi_regs;
65+
bool mem_ref;
5366
} target;
5467
union {
5568
struct {
5669
char *raw;
5770
char *name;
5871
u64 addr;
5972
bool multi_regs;
73+
bool mem_ref;
6074
} source;
6175
struct {
6276
struct ins ins;
@@ -71,7 +85,8 @@ struct ins_operands {
7185

7286
struct ins_ops {
7387
void (*free)(struct ins_operands *ops);
74-
int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms);
88+
int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
89+
struct disasm_line *dl);
7590
int (*scnprintf)(struct ins *ins, char *bf, size_t size,
7691
struct ins_operands *ops, int max_ins_name);
7792
};
@@ -90,7 +105,7 @@ struct annotate_args {
90105
struct arch *arch__find(const char *name);
91106
bool arch__is(struct arch *arch, const char *name);
92107

93-
struct ins_ops *ins__find(struct arch *arch, const char *name);
108+
struct ins_ops *ins__find(struct arch *arch, const char *name, struct disasm_line *dl);
94109
int ins__scnprintf(struct ins *ins, char *bf, size_t size,
95110
struct ins_operands *ops, int max_ins_name);
96111

‎tools/perf/util/dwarf-aux.c

+1
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,7 @@ static int __die_collect_vars_cb(Dwarf_Die *die_mem, void *arg)
16101610
vt->reg = reg_from_dwarf_op(ops);
16111611
vt->offset = offset_from_dwarf_op(ops);
16121612
vt->next = *var_types;
1613+
vt->name = dwarf_diename(die_mem);
16131614
*var_types = vt;
16141615

16151616
return DIE_FIND_CB_SIBLING;

‎tools/perf/util/dwarf-aux.h

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ struct die_var_type {
146146
u64 addr;
147147
int reg;
148148
int offset;
149+
const char *name;
149150
};
150151

151152
/* Return type info of a member at offset */

‎tools/perf/util/include/dwarf-regs.h

+12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
#ifndef _PERF_DWARF_REGS_H_
33
#define _PERF_DWARF_REGS_H_
4+
#include "annotate.h"
45

56
#define DWARF_REG_PC 0xd3af9c /* random number */
67
#define DWARF_REG_FB 0xd3affb /* random number */
8+
#define DWARF_REG_GLOBAL 0xd3affc /* random number */
79

810
#ifdef HAVE_DWARF_SUPPORT
911
const char *get_arch_regstr(unsigned int n);
@@ -31,6 +33,16 @@ static inline int get_dwarf_regnum(const char *name __maybe_unused,
3133
}
3234
#endif
3335

36+
#if !defined(__powerpc__) || !defined(HAVE_DWARF_SUPPORT)
37+
static inline void get_powerpc_regs(u32 raw_insn __maybe_unused, int is_source __maybe_unused,
38+
struct annotated_op_loc *op_loc __maybe_unused)
39+
{
40+
return;
41+
}
42+
#else
43+
void get_powerpc_regs(u32 raw_insn, int is_source, struct annotated_op_loc *op_loc);
44+
#endif
45+
3446
#ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
3547
/*
3648
* Arch should support fetching the offset of a register in pt_regs

‎tools/perf/util/print_insn.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp)
3232
#ifdef HAVE_LIBCAPSTONE_SUPPORT
3333
#include <capstone/capstone.h>
3434

35-
static int capstone_init(struct machine *machine, csh *cs_handle, bool is64)
35+
int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style)
3636
{
3737
cs_arch arch;
3838
cs_mode mode;
@@ -52,6 +52,9 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64)
5252
} else if (machine__normalized_is(machine, "s390")) {
5353
arch = CS_ARCH_SYSZ;
5454
mode = CS_MODE_BIG_ENDIAN;
55+
} else if (machine__normalized_is(machine, "powerpc")) {
56+
arch = CS_ARCH_PPC;
57+
mode = CS_MODE_64;
5558
} else {
5659
return -1;
5760
}
@@ -62,7 +65,13 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64)
6265
}
6366

6467
if (machine__normalized_is(machine, "x86")) {
65-
cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
68+
/*
69+
* In case of using capstone_init while symbol__disassemble
70+
* setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts
71+
* is set via annotation args
72+
*/
73+
if (disassembler_style)
74+
cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
6675
/*
6776
* Resolving address operands to symbols is implemented
6877
* on x86 by investigating instruction details.
@@ -122,7 +131,7 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum
122131
int ret;
123132

124133
/* TODO: Try to initiate capstone only once but need a proper place. */
125-
ret = capstone_init(machine, &cs_handle, is64bit);
134+
ret = capstone_init(machine, &cs_handle, is64bit, true);
126135
if (ret < 0)
127136
return ret;
128137

‎tools/perf/util/print_insn.h

+5
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum
1919
bool is64bit, const uint8_t *code, size_t code_size,
2020
uint64_t ip, int *lenp, int print_opts, FILE *fp);
2121

22+
#ifdef HAVE_LIBCAPSTONE_SUPPORT
23+
#include <capstone/capstone.h>
24+
int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style);
25+
#endif
26+
2227
#endif /* PERF_PRINT_INSN_H */

‎tools/perf/util/sort.c

+22-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
9595
return n;
9696
}
9797

98-
static int64_t cmp_null(const void *l, const void *r)
98+
int64_t cmp_null(const void *l, const void *r)
9999
{
100100
if (!l && !r)
101101
return 0;
@@ -2267,9 +2267,25 @@ sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
22672267
right_type = right->mem_type;
22682268
}
22692269

2270+
/*
2271+
* Compare type_name first. Next, ompare var_name if it exists
2272+
* for left and right hist_entry. If both entries doesn't have
2273+
* var_name, but one of them has, return non-zero. This is to
2274+
* indicate entries are not the same if one has var_name, but the
2275+
* other doesn't.
2276+
* If type_name and var_name is same, use mem_type_off field.
2277+
*/
22702278
ret = strcmp(left_type->self.type_name, right_type->self.type_name);
22712279
if (ret)
22722280
return ret;
2281+
2282+
if (left_type->self.var_name && right_type->self.var_name) {
2283+
ret = strcmp(left_type->self.var_name, right_type->self.var_name);
2284+
if (ret)
2285+
return ret;
2286+
} else if (!left_type->self.var_name != !right_type->self.var_name)
2287+
return cmp_null(left_type->self.var_name, right_type->self.var_name);
2288+
22732289
return left->mem_type_off - right->mem_type_off;
22742290
}
22752291

@@ -2305,9 +2321,12 @@ static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
23052321
char buf[4096];
23062322

23072323
buf[0] = '\0';
2308-
if (list_empty(&he_type->self.children))
2324+
if (list_empty(&he_type->self.children)) {
23092325
snprintf(buf, sizeof(buf), "no field");
2310-
else
2326+
if (he_type->self.var_name)
2327+
strcpy(buf, he_type->self.var_name);
2328+
2329+
} else
23112330
fill_member_name(buf, sizeof(buf), &he_type->self,
23122331
he->mem_type_off, true);
23132332
buf[4095] = '\0';

‎tools/perf/util/sort.h

+1
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,5 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right);
151151
int64_t
152152
_sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r);
153153
char *hist_entry__srcline(struct hist_entry *he);
154+
int64_t cmp_null(const void *l, const void *r);
154155
#endif /* __PERF_SORT_H */

0 commit comments

Comments
 (0)
Please sign in to comment.