Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate: Add "update_insn_state" callback function to handle arch specific instruction tracking

Add "update_insn_state" callback to "struct arch" to handle instruction
tracking. Currently updating instruction state is handled by static
function "update_insn_state_x86" which is defined in "annotate-data.c".

Make this as a callback for specific arch and move to archs specific
file "arch/x86/annotate/instructions.c" . This will help to add helper
function for other platforms in file:
"arch/<platform>/annotate/instructions.c" and make changes/updates
easier.

Define callback "update_insn_state" as part of "struct arch", also make
some of the debug functions non-static so that it can be referenced from
other places.

Reviewed-by: Kajol Jain <kjain@linux.ibm.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Tested-by: Kajol Jain <kjain@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Akanksha J N <akanksha@linux.ibm.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Disha Goel <disgoel@linux.vnet.ibm.com>
Cc: Hari Bathini <hbathini@linux.ibm.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Segher Boessenkool <segher@kernel.crashing.org>
Link: https://lore.kernel.org/lkml/20240718084358.72242-3-atrajeev@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Athira Rajeev and committed by
Arnaldo Carvalho de Melo
782959ac 1d303dee

+424 -383
+377
tools/perf/arch/x86/annotate/instructions.c
··· 206 206 arch->initialized = true; 207 207 return err; 208 208 } 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
+8 -383
tools/perf/util/annotate-data.c
··· 39 39 pr_debug3(fmt, ##__VA_ARGS__); \ 40 40 } while (0) 41 41 42 - static void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) 42 + void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) 43 43 { 44 44 struct strbuf sb; 45 45 char *str; ··· 390 390 return 0; 391 391 } 392 392 393 - static struct type_state_stack *find_stack_state(struct type_state *state, 393 + struct type_state_stack *find_stack_state(struct type_state *state, 394 394 int offset) 395 395 { 396 396 struct type_state_stack *stack; ··· 406 406 return NULL; 407 407 } 408 408 409 - static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, 409 + void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, 410 410 Dwarf_Die *type_die) 411 411 { 412 412 int tag; ··· 433 433 } 434 434 } 435 435 436 - static struct type_state_stack *findnew_stack_state(struct type_state *state, 436 + struct type_state_stack *findnew_stack_state(struct type_state *state, 437 437 int offset, u8 kind, 438 438 Dwarf_Die *type_die) 439 439 { ··· 537 537 } 538 538 } 539 539 540 - static bool get_global_var_info(struct data_loc_info *dloc, u64 addr, 540 + bool get_global_var_info(struct data_loc_info *dloc, u64 addr, 541 541 const char **var_name, int *var_offset) 542 542 { 543 543 struct addr_location al; ··· 611 611 } 612 612 } 613 613 614 - static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, 614 + bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, 615 615 u64 ip, u64 var_addr, int *var_offset, 616 616 Dwarf_Die *type_die) 617 617 { ··· 722 722 } 723 723 } 724 724 725 - static void update_insn_state_x86(struct type_state *state, 726 - struct data_loc_info *dloc, Dwarf_Die *cu_die, 727 - struct disasm_line *dl) 728 - { 729 - struct annotated_insn_loc loc; 730 - struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE]; 731 - struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET]; 732 - struct type_state_reg *tsr; 733 - Dwarf_Die type_die; 734 - u32 insn_offset = dl->al.offset; 735 - int fbreg = dloc->fbreg; 736 - int fboff = 0; 737 - 738 - if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0) 739 - return; 740 - 741 - if (ins__is_call(&dl->ins)) { 742 - struct symbol *func = dl->ops.target.sym; 743 - 744 - if (func == NULL) 745 - return; 746 - 747 - /* __fentry__ will preserve all registers */ 748 - if (!strcmp(func->name, "__fentry__")) 749 - return; 750 - 751 - pr_debug_dtp("call [%x] %s\n", insn_offset, func->name); 752 - 753 - /* Otherwise invalidate caller-saved registers after call */ 754 - for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) { 755 - if (state->regs[i].caller_saved) 756 - state->regs[i].ok = false; 757 - } 758 - 759 - /* Update register with the return type (if any) */ 760 - if (die_find_func_rettype(cu_die, func->name, &type_die)) { 761 - tsr = &state->regs[state->ret_reg]; 762 - tsr->type = type_die; 763 - tsr->kind = TSR_KIND_TYPE; 764 - tsr->ok = true; 765 - 766 - pr_debug_dtp("call [%x] return -> reg%d", 767 - insn_offset, state->ret_reg); 768 - pr_debug_type_name(&type_die, tsr->kind); 769 - } 770 - return; 771 - } 772 - 773 - if (!strncmp(dl->ins.name, "add", 3)) { 774 - u64 imm_value = -1ULL; 775 - int offset; 776 - const char *var_name = NULL; 777 - struct map_symbol *ms = dloc->ms; 778 - u64 ip = ms->sym->start + dl->al.offset; 779 - 780 - if (!has_reg_type(state, dst->reg1)) 781 - return; 782 - 783 - tsr = &state->regs[dst->reg1]; 784 - 785 - if (src->imm) 786 - imm_value = src->offset; 787 - else if (has_reg_type(state, src->reg1) && 788 - state->regs[src->reg1].kind == TSR_KIND_CONST) 789 - imm_value = state->regs[src->reg1].imm_value; 790 - else if (src->reg1 == DWARF_REG_PC) { 791 - u64 var_addr = annotate_calc_pcrel(dloc->ms, ip, 792 - src->offset, dl); 793 - 794 - if (get_global_var_info(dloc, var_addr, 795 - &var_name, &offset) && 796 - !strcmp(var_name, "this_cpu_off") && 797 - tsr->kind == TSR_KIND_CONST) { 798 - tsr->kind = TSR_KIND_PERCPU_BASE; 799 - imm_value = tsr->imm_value; 800 - } 801 - } 802 - else 803 - return; 804 - 805 - if (tsr->kind != TSR_KIND_PERCPU_BASE) 806 - return; 807 - 808 - if (get_global_var_type(cu_die, dloc, ip, imm_value, &offset, 809 - &type_die) && offset == 0) { 810 - /* 811 - * This is not a pointer type, but it should be treated 812 - * as a pointer. 813 - */ 814 - tsr->type = type_die; 815 - tsr->kind = TSR_KIND_POINTER; 816 - tsr->ok = true; 817 - 818 - pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d", 819 - insn_offset, imm_value, dst->reg1); 820 - pr_debug_type_name(&tsr->type, tsr->kind); 821 - } 822 - return; 823 - } 824 - 825 - if (strncmp(dl->ins.name, "mov", 3)) 826 - return; 827 - 828 - if (dloc->fb_cfa) { 829 - u64 ip = dloc->ms->sym->start + dl->al.offset; 830 - u64 pc = map__rip_2objdump(dloc->ms->map, ip); 831 - 832 - if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0) 833 - fbreg = -1; 834 - } 835 - 836 - /* Case 1. register to register or segment:offset to register transfers */ 837 - if (!src->mem_ref && !dst->mem_ref) { 838 - if (!has_reg_type(state, dst->reg1)) 839 - return; 840 - 841 - tsr = &state->regs[dst->reg1]; 842 - if (dso__kernel(map__dso(dloc->ms->map)) && 843 - src->segment == INSN_SEG_X86_GS && src->imm) { 844 - u64 ip = dloc->ms->sym->start + dl->al.offset; 845 - u64 var_addr; 846 - int offset; 847 - 848 - /* 849 - * In kernel, %gs points to a per-cpu region for the 850 - * current CPU. Access with a constant offset should 851 - * be treated as a global variable access. 852 - */ 853 - var_addr = src->offset; 854 - 855 - if (var_addr == 40) { 856 - tsr->kind = TSR_KIND_CANARY; 857 - tsr->ok = true; 858 - 859 - pr_debug_dtp("mov [%x] stack canary -> reg%d\n", 860 - insn_offset, dst->reg1); 861 - return; 862 - } 863 - 864 - if (!get_global_var_type(cu_die, dloc, ip, var_addr, 865 - &offset, &type_die) || 866 - !die_get_member_type(&type_die, offset, &type_die)) { 867 - tsr->ok = false; 868 - return; 869 - } 870 - 871 - tsr->type = type_die; 872 - tsr->kind = TSR_KIND_TYPE; 873 - tsr->ok = true; 874 - 875 - pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d", 876 - insn_offset, var_addr, dst->reg1); 877 - pr_debug_type_name(&tsr->type, tsr->kind); 878 - return; 879 - } 880 - 881 - if (src->imm) { 882 - tsr->kind = TSR_KIND_CONST; 883 - tsr->imm_value = src->offset; 884 - tsr->ok = true; 885 - 886 - pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n", 887 - insn_offset, tsr->imm_value, dst->reg1); 888 - return; 889 - } 890 - 891 - if (!has_reg_type(state, src->reg1) || 892 - !state->regs[src->reg1].ok) { 893 - tsr->ok = false; 894 - return; 895 - } 896 - 897 - tsr->type = state->regs[src->reg1].type; 898 - tsr->kind = state->regs[src->reg1].kind; 899 - tsr->ok = true; 900 - 901 - pr_debug_dtp("mov [%x] reg%d -> reg%d", 902 - insn_offset, src->reg1, dst->reg1); 903 - pr_debug_type_name(&tsr->type, tsr->kind); 904 - } 905 - /* Case 2. memory to register transers */ 906 - if (src->mem_ref && !dst->mem_ref) { 907 - int sreg = src->reg1; 908 - 909 - if (!has_reg_type(state, dst->reg1)) 910 - return; 911 - 912 - tsr = &state->regs[dst->reg1]; 913 - 914 - retry: 915 - /* Check stack variables with offset */ 916 - if (sreg == fbreg) { 917 - struct type_state_stack *stack; 918 - int offset = src->offset - fboff; 919 - 920 - stack = find_stack_state(state, offset); 921 - if (stack == NULL) { 922 - tsr->ok = false; 923 - return; 924 - } else if (!stack->compound) { 925 - tsr->type = stack->type; 926 - tsr->kind = stack->kind; 927 - tsr->ok = true; 928 - } else if (die_get_member_type(&stack->type, 929 - offset - stack->offset, 930 - &type_die)) { 931 - tsr->type = type_die; 932 - tsr->kind = TSR_KIND_TYPE; 933 - tsr->ok = true; 934 - } else { 935 - tsr->ok = false; 936 - return; 937 - } 938 - 939 - pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d", 940 - insn_offset, -offset, dst->reg1); 941 - pr_debug_type_name(&tsr->type, tsr->kind); 942 - } 943 - /* And then dereference the pointer if it has one */ 944 - else if (has_reg_type(state, sreg) && state->regs[sreg].ok && 945 - state->regs[sreg].kind == TSR_KIND_TYPE && 946 - die_deref_ptr_type(&state->regs[sreg].type, 947 - src->offset, &type_die)) { 948 - tsr->type = type_die; 949 - tsr->kind = TSR_KIND_TYPE; 950 - tsr->ok = true; 951 - 952 - pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d", 953 - insn_offset, src->offset, sreg, dst->reg1); 954 - pr_debug_type_name(&tsr->type, tsr->kind); 955 - } 956 - /* Or check if it's a global variable */ 957 - else if (sreg == DWARF_REG_PC) { 958 - struct map_symbol *ms = dloc->ms; 959 - u64 ip = ms->sym->start + dl->al.offset; 960 - u64 addr; 961 - int offset; 962 - 963 - addr = annotate_calc_pcrel(ms, ip, src->offset, dl); 964 - 965 - if (!get_global_var_type(cu_die, dloc, ip, addr, &offset, 966 - &type_die) || 967 - !die_get_member_type(&type_die, offset, &type_die)) { 968 - tsr->ok = false; 969 - return; 970 - } 971 - 972 - tsr->type = type_die; 973 - tsr->kind = TSR_KIND_TYPE; 974 - tsr->ok = true; 975 - 976 - pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d", 977 - insn_offset, addr, dst->reg1); 978 - pr_debug_type_name(&type_die, tsr->kind); 979 - } 980 - /* And check percpu access with base register */ 981 - else if (has_reg_type(state, sreg) && 982 - state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) { 983 - u64 ip = dloc->ms->sym->start + dl->al.offset; 984 - u64 var_addr = src->offset; 985 - int offset; 986 - 987 - if (src->multi_regs) { 988 - int reg2 = (sreg == src->reg1) ? src->reg2 : src->reg1; 989 - 990 - if (has_reg_type(state, reg2) && state->regs[reg2].ok && 991 - state->regs[reg2].kind == TSR_KIND_CONST) 992 - var_addr += state->regs[reg2].imm_value; 993 - } 994 - 995 - /* 996 - * In kernel, %gs points to a per-cpu region for the 997 - * current CPU. Access with a constant offset should 998 - * be treated as a global variable access. 999 - */ 1000 - if (get_global_var_type(cu_die, dloc, ip, var_addr, 1001 - &offset, &type_die) && 1002 - die_get_member_type(&type_die, offset, &type_die)) { 1003 - tsr->type = type_die; 1004 - tsr->kind = TSR_KIND_TYPE; 1005 - tsr->ok = true; 1006 - 1007 - if (src->multi_regs) { 1008 - pr_debug_dtp("mov [%x] percpu %#x(reg%d,reg%d) -> reg%d", 1009 - insn_offset, src->offset, src->reg1, 1010 - src->reg2, dst->reg1); 1011 - } else { 1012 - pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d", 1013 - insn_offset, src->offset, sreg, dst->reg1); 1014 - } 1015 - pr_debug_type_name(&tsr->type, tsr->kind); 1016 - } else { 1017 - tsr->ok = false; 1018 - } 1019 - } 1020 - /* And then dereference the calculated pointer if it has one */ 1021 - else if (has_reg_type(state, sreg) && state->regs[sreg].ok && 1022 - state->regs[sreg].kind == TSR_KIND_POINTER && 1023 - die_get_member_type(&state->regs[sreg].type, 1024 - src->offset, &type_die)) { 1025 - tsr->type = type_die; 1026 - tsr->kind = TSR_KIND_TYPE; 1027 - tsr->ok = true; 1028 - 1029 - pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d", 1030 - insn_offset, src->offset, sreg, dst->reg1); 1031 - pr_debug_type_name(&tsr->type, tsr->kind); 1032 - } 1033 - /* Or try another register if any */ 1034 - else if (src->multi_regs && sreg == src->reg1 && 1035 - src->reg1 != src->reg2) { 1036 - sreg = src->reg2; 1037 - goto retry; 1038 - } 1039 - else { 1040 - int offset; 1041 - const char *var_name = NULL; 1042 - 1043 - /* it might be per-cpu variable (in kernel) access */ 1044 - if (src->offset < 0) { 1045 - if (get_global_var_info(dloc, (s64)src->offset, 1046 - &var_name, &offset) && 1047 - !strcmp(var_name, "__per_cpu_offset")) { 1048 - tsr->kind = TSR_KIND_PERCPU_BASE; 1049 - 1050 - pr_debug_dtp("mov [%x] percpu base reg%d\n", 1051 - insn_offset, dst->reg1); 1052 - } 1053 - } 1054 - 1055 - tsr->ok = false; 1056 - } 1057 - } 1058 - /* Case 3. register to memory transfers */ 1059 - if (!src->mem_ref && dst->mem_ref) { 1060 - if (!has_reg_type(state, src->reg1) || 1061 - !state->regs[src->reg1].ok) 1062 - return; 1063 - 1064 - /* Check stack variables with offset */ 1065 - if (dst->reg1 == fbreg) { 1066 - struct type_state_stack *stack; 1067 - int offset = dst->offset - fboff; 1068 - 1069 - tsr = &state->regs[src->reg1]; 1070 - 1071 - stack = find_stack_state(state, offset); 1072 - if (stack) { 1073 - /* 1074 - * The source register is likely to hold a type 1075 - * of member if it's a compound type. Do not 1076 - * update the stack variable type since we can 1077 - * get the member type later by using the 1078 - * die_get_member_type(). 1079 - */ 1080 - if (!stack->compound) 1081 - set_stack_state(stack, offset, tsr->kind, 1082 - &tsr->type); 1083 - } else { 1084 - findnew_stack_state(state, offset, tsr->kind, 1085 - &tsr->type); 1086 - } 1087 - 1088 - pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)", 1089 - insn_offset, src->reg1, -offset); 1090 - pr_debug_type_name(&tsr->type, tsr->kind); 1091 - } 1092 - /* 1093 - * Ignore other transfers since it'd set a value in a struct 1094 - * and won't change the type. 1095 - */ 1096 - } 1097 - /* Case 4. memory to memory transfers (not handled for now) */ 1098 - } 1099 - 1100 725 /** 1101 726 * update_insn_state - Update type state for an instruction 1102 727 * @state: type state table ··· 740 1115 static void update_insn_state(struct type_state *state, struct data_loc_info *dloc, 741 1116 Dwarf_Die *cu_die, struct disasm_line *dl) 742 1117 { 743 - if (arch__is(dloc->arch, "x86")) 744 - update_insn_state_x86(state, dloc, cu_die, dl); 1118 + if (dloc->arch->update_insn_state) 1119 + dloc->arch->update_insn_state(state, dloc, cu_die, dl); 745 1120 } 746 1121 747 1122 /*
+23
tools/perf/util/annotate-data.h
··· 6 6 #include <linux/compiler.h> 7 7 #include <linux/rbtree.h> 8 8 #include <linux/types.h> 9 + #include "dwarf-regs.h" 9 10 #include "annotate.h" 10 11 11 12 #ifdef HAVE_DWARF_SUPPORT ··· 20 19 struct hist_entry; 21 20 struct map_symbol; 22 21 struct thread; 22 + 23 + #define pr_debug_dtp(fmt, ...) \ 24 + do { \ 25 + if (debug_type_profile) \ 26 + pr_info(fmt, ##__VA_ARGS__); \ 27 + else \ 28 + pr_debug3(fmt, ##__VA_ARGS__); \ 29 + } while (0) 23 30 24 31 enum type_state_kind { 25 32 TSR_KIND_INVALID = 0, ··· 225 216 int hist_entry__annotate_data_tty(struct hist_entry *he, struct evsel *evsel); 226 217 227 218 bool has_reg_type(struct type_state *state, int reg); 219 + struct type_state_stack *findnew_stack_state(struct type_state *state, 220 + int offset, u8 kind, 221 + Dwarf_Die *type_die); 222 + void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, 223 + Dwarf_Die *type_die); 224 + struct type_state_stack *find_stack_state(struct type_state *state, 225 + int offset); 226 + bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, 227 + u64 ip, u64 var_addr, int *var_offset, 228 + Dwarf_Die *type_die); 229 + bool get_global_var_info(struct data_loc_info *dloc, u64 addr, 230 + const char **var_name, int *var_offset); 231 + void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind); 232 + 228 233 #else /* HAVE_DWARF_SUPPORT */ 229 234 230 235 static inline struct annotated_data_type *
+4
tools/perf/util/disasm.c
··· 12 12 #include <subcmd/run-command.h> 13 13 14 14 #include "annotate.h" 15 + #include "annotate-data.h" 15 16 #include "build-id.h" 16 17 #include "debug.h" 17 18 #include "disasm.h" ··· 146 145 .memory_ref_char = '(', 147 146 .imm_char = '$', 148 147 }, 148 + #ifdef HAVE_DWARF_SUPPORT 149 + .update_insn_state = update_insn_state_x86, 150 + #endif 149 151 }, 150 152 { 151 153 .name = "powerpc",
+12
tools/perf/util/disasm.h
··· 4 4 5 5 #include "map_symbol.h" 6 6 7 + #ifdef HAVE_DWARF_SUPPORT 8 + #include "dwarf-aux.h" 9 + #endif 10 + 7 11 struct annotation_options; 8 12 struct disasm_line; 9 13 struct ins; 10 14 struct evsel; 11 15 struct symbol; 16 + struct data_loc_info; 17 + struct type_state; 18 + struct disasm_line; 12 19 13 20 struct arch { 14 21 const char *name; ··· 39 32 char memory_ref_char; 40 33 char imm_char; 41 34 } objdump; 35 + #ifdef HAVE_DWARF_SUPPORT 36 + void (*update_insn_state)(struct type_state *state, 37 + struct data_loc_info *dloc, Dwarf_Die *cu_die, 38 + struct disasm_line *dl); 39 + #endif 42 40 }; 43 41 44 42 struct ins {