| 1 |
|
| 2 | |
| 3 | * Convert sample address to data type using DWARF debug info.
|
| 4 | *
|
| 5 | * Written by Namhyung Kim <[email protected]>
|
| 6 |
|
| 7 | #include <errno.h>
|
| 8 | #include <stdio.h>
|
| 9 | #include <stdlib.h>
|
| 10 | #include <inttypes.h>
|
| 11 | #include <linux/zalloc.h>
|
| 12 |
|
| 13 | #include "annotate.h"
|
| 14 | #include "annotate-data.h"
|
| 15 | #include "debuginfo.h"
|
| 16 | #include "debug.h"
|
| 17 | #include "dso.h"
|
| 18 | #include "dwarf-regs.h"
|
| 19 | #include "evsel.h"
|
| 20 | #include "evlist.h"
|
| 21 | #include "map.h"
|
| 22 | #include "map_symbol.h"
|
| 23 | #include "sort.h"
|
| 24 | #include "strbuf.h"
|
| 25 | #include "symbol.h"
|
| 26 | #include "symbol_conf.h"
|
| 27 | #include "thread.h"
|
| 28 |
|
| 29 |
|
| 30 | #define X86_REG_SP 7
|
| 31 |
|
| 32 | static void delete_var_types(struct die_var_type *var_types);
|
| 33 |
|
| 34 | #define pr_debug_dtp(fmt, ...) \
|
| 35 | do { \
|
| 36 | if (debug_type_profile) \
|
| 37 | pr_info(fmt, ##__VA_ARGS__); \
|
| 38 | else \
|
| 39 | pr_debug3(fmt, ##__VA_ARGS__); \
|
| 40 | } while (0)
|
| 41 |
|
| 42 | void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind)
|
| 43 | {
|
| 44 | struct strbuf sb;
|
| 45 | char *str;
|
| 46 | Dwarf_Word size = 0;
|
| 47 |
|
| 48 | if (!debug_type_profile && verbose < 3)
|
| 49 | return;
|
| 50 |
|
| 51 | switch (kind) {
|
| 52 | case TSR_KIND_INVALID:
|
| 53 | pr_info("\n");
|
| 54 | return;
|
| 55 | case TSR_KIND_PERCPU_BASE:
|
| 56 | pr_info(" percpu base\n");
|
| 57 | return;
|
| 58 | case TSR_KIND_CONST:
|
| 59 | pr_info(" constant\n");
|
| 60 | return;
|
| 61 | case TSR_KIND_PERCPU_POINTER:
|
| 62 | pr_info(" percpu pointer");
|
| 63 |
|
| 64 | break;
|
| 65 | case TSR_KIND_POINTER:
|
| 66 | pr_info(" pointer");
|
| 67 |
|
| 68 | break;
|
| 69 | case TSR_KIND_CANARY:
|
| 70 | pr_info(" stack canary\n");
|
| 71 | return;
|
| 72 | case TSR_KIND_TYPE:
|
| 73 | default:
|
| 74 | break;
|
| 75 | }
|
| 76 |
|
| 77 | if (dwarf_aggregate_size(die, &size) != 0)
|
| 78 | size = 0;
|
| 79 |
|
| 80 | strbuf_init(&sb, 32);
|
| 81 | die_get_typename_from_type(die, &sb);
|
| 82 | str = strbuf_detach(&sb, NULL);
|
| 83 | pr_info(" type='%s' size=%#lx (die:%#lx)\n",
|
| 84 | str, (long)size, (long)dwarf_dieoffset(die));
|
| 85 | free(str);
|
| 86 | }
|
| 87 |
|
| 88 | static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg)
|
| 89 | {
|
| 90 | ptrdiff_t off = 0;
|
| 91 | Dwarf_Attribute attr;
|
| 92 | Dwarf_Addr base, start, end;
|
| 93 | Dwarf_Op *ops;
|
| 94 | size_t nops;
|
| 95 |
|
| 96 | if (!debug_type_profile && verbose < 3)
|
| 97 | return;
|
| 98 |
|
| 99 | if (dwarf_attr(die, DW_AT_location, &attr) == NULL)
|
| 100 | return;
|
| 101 |
|
| 102 | while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) {
|
| 103 | if (reg != DWARF_REG_PC && end <= pc)
|
| 104 | continue;
|
| 105 | if (reg != DWARF_REG_PC && start > pc)
|
| 106 | break;
|
| 107 |
|
| 108 | pr_info(" variable location: ");
|
| 109 | switch (ops->atom) {
|
| 110 | case DW_OP_reg0 ...DW_OP_reg31:
|
| 111 | pr_info("reg%d\n", ops->atom - DW_OP_reg0);
|
| 112 | break;
|
| 113 | case DW_OP_breg0 ...DW_OP_breg31:
|
| 114 | pr_info("base=reg%d, offset=%#lx\n",
|
| 115 | ops->atom - DW_OP_breg0, (long)ops->number);
|
| 116 | break;
|
| 117 | case DW_OP_regx:
|
| 118 | pr_info("reg%ld\n", (long)ops->number);
|
| 119 | break;
|
| 120 | case DW_OP_bregx:
|
| 121 | pr_info("base=reg%ld, offset=%#lx\n",
|
| 122 | (long)ops->number, (long)ops->number2);
|
| 123 | break;
|
| 124 | case DW_OP_fbreg:
|
| 125 | pr_info("use frame base, offset=%#lx\n", (long)ops->number);
|
| 126 | break;
|
| 127 | case DW_OP_addr:
|
| 128 | pr_info("address=%#lx\n", (long)ops->number);
|
| 129 | break;
|
| 130 | default:
|
| 131 | pr_info("unknown: code=%#x, number=%#lx\n",
|
| 132 | ops->atom, (long)ops->number);
|
| 133 | break;
|
| 134 | }
|
| 135 | break;
|
| 136 | }
|
| 137 | }
|
| 138 |
|
| 139 | static void pr_debug_scope(Dwarf_Die *scope_die)
|
| 140 | {
|
| 141 | int tag;
|
| 142 |
|
| 143 | if (!debug_type_profile && verbose < 3)
|
| 144 | return;
|
| 145 |
|
| 146 | pr_info("(die:%lx) ", (long)dwarf_dieoffset(scope_die));
|
| 147 |
|
| 148 | tag = dwarf_tag(scope_die);
|
| 149 | if (tag == DW_TAG_subprogram)
|
| 150 | pr_info("[function] %s\n", die_name(scope_die));
|
| 151 | else if (tag == DW_TAG_inlined_subroutine)
|
| 152 | pr_info("[inlined] %s\n", die_name(scope_die));
|
| 153 | else if (tag == DW_TAG_lexical_block)
|
| 154 | pr_info("[block]\n");
|
| 155 | else
|
| 156 | pr_info("[unknown] tag=%x\n", tag);
|
| 157 | }
|
| 158 |
|
| 159 | bool has_reg_type(struct type_state *state, int reg)
|
| 160 | {
|
| 161 | return (unsigned)reg < ARRAY_SIZE(state->regs);
|
| 162 | }
|
| 163 |
|
| 164 | static void init_type_state(struct type_state *state, const struct arch *arch)
|
| 165 | {
|
| 166 | memset(state, 0, sizeof(*state));
|
| 167 | INIT_LIST_HEAD(&state->stack_vars);
|
| 168 |
|
| 169 | if (arch__is_x86(arch)) {
|
| 170 | state->regs[0].caller_saved = true;
|
| 171 | state->regs[1].caller_saved = true;
|
| 172 | state->regs[2].caller_saved = true;
|
| 173 | state->regs[4].caller_saved = true;
|
| 174 | state->regs[5].caller_saved = true;
|
| 175 | state->regs[8].caller_saved = true;
|
| 176 | state->regs[9].caller_saved = true;
|
| 177 | state->regs[10].caller_saved = true;
|
| 178 | state->regs[11].caller_saved = true;
|
| 179 | state->ret_reg = 0;
|
| 180 | state->stack_reg = X86_REG_SP;
|
| 181 | }
|
| 182 | }
|
| 183 |
|
| 184 | static void exit_type_state(struct type_state *state)
|
| 185 | {
|
| 186 | struct type_state_stack *stack, *tmp;
|
| 187 |
|
| 188 | list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) {
|
| 189 | list_del(&stack->list);
|
| 190 | free(stack);
|
| 191 | }
|
| 192 | }
|
| 193 |
|
| 194 | |
| 195 | * Compare type name and size to maintain them in a tree.
|
| 196 | * I'm not sure if DWARF would have information of a single type in many
|
| 197 | * different places (compilation units). If not, it could compare the
|
| 198 | * offset of the type entry in the .debug_info section.
|
| 199 |
|
| 200 | static int data_type_cmp(const void *_key, const struct rb_node *node)
|
| 201 | {
|
| 202 | const struct annotated_data_type *key = _key;
|
| 203 | struct annotated_data_type *type;
|
| 204 |
|
| 205 | type = rb_entry(node, struct annotated_data_type, node);
|
| 206 |
|
| 207 | if (key->self.size != type->self.size)
|
| 208 | return key->self.size - type->self.size;
|
| 209 | return strcmp(key->self.type_name, type->self.type_name);
|
| 210 | }
|
| 211 |
|
| 212 | static bool data_type_less(struct rb_node *node_a, const struct rb_node *node_b)
|
| 213 | {
|
| 214 | struct annotated_data_type *a, *b;
|
| 215 |
|
| 216 | a = rb_entry(node_a, struct annotated_data_type, node);
|
| 217 | b = rb_entry(node_b, struct annotated_data_type, node);
|
| 218 |
|
| 219 | if (a->self.size != b->self.size)
|
| 220 | return a->self.size < b->self.size;
|
| 221 | return strcmp(a->self.type_name, b->self.type_name) < 0;
|
| 222 | }
|
| 223 |
|
| 224 |
|
| 225 | static int __add_member_cb(Dwarf_Die *die, void *arg)
|
| 226 | {
|
| 227 | struct annotated_member *parent = arg;
|
| 228 | struct annotated_member *member;
|
| 229 | Dwarf_Die member_type, die_mem;
|
| 230 | Dwarf_Word size, loc, bit_size = 0;
|
| 231 | Dwarf_Attribute attr;
|
| 232 | struct strbuf sb;
|
| 233 | int tag;
|
| 234 |
|
| 235 | if (dwarf_tag(die) != DW_TAG_member)
|
| 236 | return DIE_FIND_CB_SIBLING;
|
| 237 |
|
| 238 | member = zalloc(sizeof(*member));
|
| 239 | if (member == NULL)
|
| 240 | return DIE_FIND_CB_END;
|
| 241 |
|
| 242 | strbuf_init(&sb, 32);
|
| 243 | die_get_typename(die, &sb);
|
| 244 |
|
| 245 | __die_get_real_type(die, &member_type);
|
| 246 | if (dwarf_tag(&member_type) == DW_TAG_typedef)
|
| 247 | die_get_real_type(&member_type, &die_mem);
|
| 248 | else
|
| 249 | die_mem = member_type;
|
| 250 |
|
| 251 | if (dwarf_aggregate_size(&die_mem, &size) < 0)
|
| 252 | size = 0;
|
| 253 |
|
| 254 | if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) {
|
| 255 | if (dwarf_formudata(&attr, &loc) != 0) {
|
| 256 | if (die_get_data_member_location(die, &loc) != 0)
|
| 257 | loc = 0;
|
| 258 | }
|
| 259 | } else {
|
| 260 |
|
| 261 | if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) &&
|
| 262 | dwarf_formudata(&attr, &loc) == 0)
|
| 263 | loc /= 8;
|
| 264 | else
|
| 265 | loc = 0;
|
| 266 |
|
| 267 | if (dwarf_attr_integrate(die, DW_AT_bit_size, &attr) &&
|
| 268 | dwarf_formudata(&attr, &bit_size) == 0)
|
| 269 | size = (bit_size + 7) / 8;
|
| 270 | }
|
| 271 |
|
| 272 | member->type_name = strbuf_detach(&sb, NULL);
|
| 273 |
|
| 274 | if (dwarf_diename(die)) {
|
| 275 | if (bit_size) {
|
| 276 | if (asprintf(&member->var_name, "%s:%ld",
|
| 277 | dwarf_diename(die), (long)bit_size) < 0)
|
| 278 | member->var_name = NULL;
|
| 279 | } else {
|
| 280 | const char *name = dwarf_diename(die);
|
| 281 |
|
| 282 | member->var_name = name ? strdup(name) : NULL;
|
| 283 | }
|
| 284 |
|
| 285 | if (member->var_name == NULL) {
|
| 286 | free(member);
|
| 287 | return DIE_FIND_CB_END;
|
| 288 | }
|
| 289 | }
|
| 290 | member->size = size;
|
| 291 | member->offset = loc + parent->offset;
|
| 292 | INIT_LIST_HEAD(&member->children);
|
| 293 | list_add_tail(&member->node, &parent->children);
|
| 294 |
|
| 295 | tag = dwarf_tag(&die_mem);
|
| 296 | switch (tag) {
|
| 297 | case DW_TAG_structure_type:
|
| 298 | case DW_TAG_union_type:
|
| 299 | die_find_child(&die_mem, __add_member_cb, member, &die_mem);
|
| 300 | break;
|
| 301 | default:
|
| 302 | break;
|
| 303 | }
|
| 304 | return DIE_FIND_CB_SIBLING;
|
| 305 | }
|
| 306 |
|
| 307 | static void add_member_types(struct annotated_data_type *parent, Dwarf_Die *type)
|
| 308 | {
|
| 309 | Dwarf_Die die_mem;
|
| 310 |
|
| 311 | die_find_child(type, __add_member_cb, &parent->self, &die_mem);
|
| 312 | }
|
| 313 |
|
| 314 | static void delete_members(struct annotated_member *member)
|
| 315 | {
|
| 316 | struct annotated_member *child, *tmp;
|
| 317 |
|
| 318 | list_for_each_entry_safe(child, tmp, &member->children, node) {
|
| 319 | list_del(&child->node);
|
| 320 | delete_members(child);
|
| 321 | zfree(&child->type_name);
|
| 322 | zfree(&child->var_name);
|
| 323 | free(child);
|
| 324 | }
|
| 325 | }
|
| 326 |
|
| 327 | static int fill_member_name(char *buf, size_t sz, struct annotated_member *m,
|
| 328 | int offset, bool first)
|
| 329 | {
|
| 330 | struct annotated_member *child;
|
| 331 |
|
| 332 | if (list_empty(&m->children))
|
| 333 | return 0;
|
| 334 |
|
| 335 | list_for_each_entry(child, &m->children, node) {
|
| 336 | int len;
|
| 337 |
|
| 338 | if (offset < child->offset || offset >= child->offset + child->size)
|
| 339 | continue;
|
| 340 |
|
| 341 |
|
| 342 | if (child->var_name) {
|
| 343 | len = scnprintf(buf, sz, "%s%s",
|
| 344 | first ? "" : ".", child->var_name);
|
| 345 | first = false;
|
| 346 | } else {
|
| 347 | len = 0;
|
| 348 | }
|
| 349 |
|
| 350 | return fill_member_name(buf + len, sz - len, child, offset, first) + len;
|
| 351 | }
|
| 352 | return 0;
|
| 353 | }
|
| 354 |
|
| 355 | int annotated_data_type__get_member_name(struct annotated_data_type *adt,
|
| 356 | char *buf, size_t sz, int member_offset)
|
| 357 | {
|
| 358 | return fill_member_name(buf, sz, &adt->self, member_offset, true);
|
| 359 | }
|
| 360 |
|
| 361 | static struct annotated_data_type *dso__findnew_data_type(struct dso *dso,
|
| 362 | Dwarf_Die *type_die)
|
| 363 | {
|
| 364 | struct annotated_data_type *result = NULL;
|
| 365 | struct annotated_data_type key;
|
| 366 | struct rb_node *node;
|
| 367 | struct strbuf sb;
|
| 368 | char *type_name;
|
| 369 | Dwarf_Word size;
|
| 370 |
|
| 371 | strbuf_init(&sb, 32);
|
| 372 | if (die_get_typename_from_type(type_die, &sb) < 0)
|
| 373 | strbuf_add(&sb, "(unknown type)", 14);
|
| 374 | type_name = strbuf_detach(&sb, NULL);
|
| 375 |
|
| 376 | if (dwarf_tag(type_die) == DW_TAG_typedef)
|
| 377 | die_get_real_type(type_die, type_die);
|
| 378 |
|
| 379 | if (dwarf_aggregate_size(type_die, &size) != 0)
|
| 380 | size = 0;
|
| 381 |
|
| 382 |
|
| 383 | key.self.type_name = type_name;
|
| 384 | key.self.size = size;
|
| 385 | node = rb_find(&key, dso__data_types(dso), data_type_cmp);
|
| 386 | if (node) {
|
| 387 | result = rb_entry(node, struct annotated_data_type, node);
|
| 388 | free(type_name);
|
| 389 | return result;
|
| 390 | }
|
| 391 |
|
| 392 |
|
| 393 | result = zalloc(sizeof(*result));
|
| 394 | if (result == NULL) {
|
| 395 | free(type_name);
|
| 396 | return NULL;
|
| 397 | }
|
| 398 |
|
| 399 | result->self.type_name = type_name;
|
| 400 | result->self.size = size;
|
| 401 | INIT_LIST_HEAD(&result->self.children);
|
| 402 |
|
| 403 | if (symbol_conf.annotate_data_member)
|
| 404 | add_member_types(result, type_die);
|
| 405 |
|
| 406 | rb_add(&result->node, dso__data_types(dso), data_type_less);
|
| 407 | return result;
|
| 408 | }
|
| 409 |
|
| 410 | static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die)
|
| 411 | {
|
| 412 | Dwarf_Off off, next_off;
|
| 413 | size_t header_size;
|
| 414 |
|
| 415 | if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL)
|
| 416 | return cu_die;
|
| 417 |
|
| 418 | |
| 419 | * There are some kernels don't have full aranges and contain only a few
|
| 420 | * aranges entries. Fallback to iterate all CU entries in .debug_info
|
| 421 | * in case it's missing.
|
| 422 |
|
| 423 | off = 0;
|
| 424 | while (dwarf_nextcu(di->dbg, off, &next_off, &header_size,
|
| 425 | NULL, NULL, NULL) == 0) {
|
| 426 | if (dwarf_offdie(di->dbg, off + header_size, cu_die) &&
|
| 427 | dwarf_haspc(cu_die, pc))
|
| 428 | return true;
|
| 429 |
|
| 430 | off = next_off;
|
| 431 | }
|
| 432 | return false;
|
| 433 | }
|
| 434 |
|
| 435 | enum type_match_result {
|
| 436 | PERF_TMR_UNKNOWN = 0,
|
| 437 | PERF_TMR_OK,
|
| 438 | PERF_TMR_NO_TYPE,
|
| 439 | PERF_TMR_NO_POINTER,
|
| 440 | PERF_TMR_NO_SIZE,
|
| 441 | PERF_TMR_BAD_OFFSET,
|
| 442 | PERF_TMR_BAIL_OUT,
|
| 443 | };
|
| 444 |
|
| 445 | static const char *match_result_str(enum type_match_result tmr)
|
| 446 | {
|
| 447 | switch (tmr) {
|
| 448 | case PERF_TMR_OK:
|
| 449 | return "Good!";
|
| 450 | case PERF_TMR_NO_TYPE:
|
| 451 | return "no type information";
|
| 452 | case PERF_TMR_NO_POINTER:
|
| 453 | return "no/void pointer";
|
| 454 | case PERF_TMR_NO_SIZE:
|
| 455 | return "type size is unknown";
|
| 456 | case PERF_TMR_BAD_OFFSET:
|
| 457 | return "offset bigger than size";
|
| 458 | case PERF_TMR_UNKNOWN:
|
| 459 | case PERF_TMR_BAIL_OUT:
|
| 460 | default:
|
| 461 | return "invalid state";
|
| 462 | }
|
| 463 | }
|
| 464 |
|
| 465 | static bool is_compound_type(Dwarf_Die *type_die)
|
| 466 | {
|
| 467 | int tag = dwarf_tag(type_die);
|
| 468 |
|
| 469 | return tag == DW_TAG_structure_type || tag == DW_TAG_union_type;
|
| 470 | }
|
| 471 |
|
| 472 |
|
| 473 | static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b)
|
| 474 | {
|
| 475 | Dwarf_Word size_a, size_b;
|
| 476 | Dwarf_Die die_a, die_b;
|
| 477 | Dwarf_Die ptr_a, ptr_b;
|
| 478 | Dwarf_Die *ptr_type_a, *ptr_type_b;
|
| 479 |
|
| 480 | ptr_type_a = die_get_pointer_type(type_a, &ptr_a);
|
| 481 | ptr_type_b = die_get_pointer_type(type_b, &ptr_b);
|
| 482 |
|
| 483 |
|
| 484 | if ((ptr_type_a != NULL) != (ptr_type_b != NULL))
|
| 485 | return ptr_type_b != NULL;
|
| 486 |
|
| 487 | if (ptr_type_b) {
|
| 488 | |
| 489 | * We want to compare the target type, but 'void *' can fail to
|
| 490 | * get the target type.
|
| 491 |
|
| 492 | if (die_get_real_type(ptr_type_a, &die_a) == NULL)
|
| 493 | return true;
|
| 494 | if (die_get_real_type(ptr_type_b, &die_b) == NULL)
|
| 495 | return false;
|
| 496 |
|
| 497 | type_a = &die_a;
|
| 498 | type_b = &die_b;
|
| 499 | }
|
| 500 |
|
| 501 |
|
| 502 | if (dwarf_aggregate_size(type_a, &size_a) < 0 ||
|
| 503 | dwarf_aggregate_size(type_b, &size_b) < 0)
|
| 504 | return false;
|
| 505 |
|
| 506 | if (size_a != size_b)
|
| 507 | return size_a < size_b;
|
| 508 |
|
| 509 |
|
| 510 | if (is_compound_type(type_a) != is_compound_type(type_b))
|
| 511 | return is_compound_type(type_b);
|
| 512 |
|
| 513 |
|
| 514 | if (dwarf_tag(type_b) == DW_TAG_typedef)
|
| 515 | return true;
|
| 516 |
|
| 517 | return false;
|
| 518 | }
|
| 519 |
|
| 520 |
|
| 521 | static enum type_match_result check_variable(struct data_loc_info *dloc,
|
| 522 | Dwarf_Die *var_die,
|
| 523 | Dwarf_Die *type_die, int reg,
|
| 524 | int offset, bool is_fbreg)
|
| 525 | {
|
| 526 | Dwarf_Word size;
|
| 527 | bool needs_pointer = true;
|
| 528 | Dwarf_Die sized_type;
|
| 529 |
|
| 530 | if (reg == DWARF_REG_PC)
|
| 531 | needs_pointer = false;
|
| 532 | else if (reg == dloc->fbreg || is_fbreg)
|
| 533 | needs_pointer = false;
|
| 534 | else if (arch__is_x86(dloc->arch) && reg == X86_REG_SP)
|
| 535 | needs_pointer = false;
|
| 536 |
|
| 537 |
|
| 538 | if (__die_get_real_type(var_die, type_die) == NULL)
|
| 539 | return PERF_TMR_NO_TYPE;
|
| 540 |
|
| 541 | |
| 542 | * Usually it expects a pointer type for a memory access.
|
| 543 | * Convert to a real type it points to. But global variables
|
| 544 | * and local variables are accessed directly without a pointer.
|
| 545 |
|
| 546 | if (needs_pointer) {
|
| 547 | if (die_get_pointer_type(type_die, type_die) == NULL ||
|
| 548 | __die_get_real_type(type_die, type_die) == NULL)
|
| 549 | return PERF_TMR_NO_POINTER;
|
| 550 | }
|
| 551 |
|
| 552 | if (dwarf_tag(type_die) == DW_TAG_typedef)
|
| 553 | die_get_real_type(type_die, &sized_type);
|
| 554 | else
|
| 555 | sized_type = *type_die;
|
| 556 |
|
| 557 |
|
| 558 | if (dwarf_aggregate_size(&sized_type, &size) < 0)
|
| 559 | return PERF_TMR_NO_SIZE;
|
| 560 |
|
| 561 |
|
| 562 | if ((unsigned)offset >= size)
|
| 563 | return PERF_TMR_BAD_OFFSET;
|
| 564 |
|
| 565 | return PERF_TMR_OK;
|
| 566 | }
|
| 567 |
|
| 568 | struct type_state_stack *find_stack_state(struct type_state *state,
|
| 569 | int offset)
|
| 570 | {
|
| 571 | struct type_state_stack *stack;
|
| 572 |
|
| 573 | list_for_each_entry(stack, &state->stack_vars, list) {
|
| 574 | if (offset == stack->offset)
|
| 575 | return stack;
|
| 576 |
|
| 577 | if (stack->compound && stack->offset < offset &&
|
| 578 | offset < stack->offset + stack->size)
|
| 579 | return stack;
|
| 580 | }
|
| 581 | return NULL;
|
| 582 | }
|
| 583 |
|
| 584 | void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
|
| 585 | Dwarf_Die *type_die, int ptr_offset)
|
| 586 | {
|
| 587 | int tag;
|
| 588 | Dwarf_Word size;
|
| 589 |
|
| 590 | if (kind == TSR_KIND_POINTER) {
|
| 591 |
|
| 592 | size = sizeof(void *);
|
| 593 | }
|
| 594 | else if (dwarf_aggregate_size(type_die, &size) < 0)
|
| 595 | size = 0;
|
| 596 |
|
| 597 | stack->type = *type_die;
|
| 598 | stack->size = size;
|
| 599 | stack->offset = offset;
|
| 600 | stack->ptr_offset = ptr_offset;
|
| 601 | stack->kind = kind;
|
| 602 |
|
| 603 | if (kind == TSR_KIND_POINTER) {
|
| 604 | stack->compound = false;
|
| 605 | return;
|
| 606 | }
|
| 607 |
|
| 608 | tag = dwarf_tag(type_die);
|
| 609 |
|
| 610 | switch (tag) {
|
| 611 | case DW_TAG_structure_type:
|
| 612 | case DW_TAG_union_type:
|
| 613 | stack->compound = (kind != TSR_KIND_PERCPU_POINTER);
|
| 614 | break;
|
| 615 | default:
|
| 616 | stack->compound = false;
|
| 617 | break;
|
| 618 | }
|
| 619 | }
|
| 620 |
|
| 621 | struct type_state_stack *findnew_stack_state(struct type_state *state,
|
| 622 | int offset, u8 kind,
|
| 623 | Dwarf_Die *type_die,
|
| 624 | int ptr_offset)
|
| 625 | {
|
| 626 | struct type_state_stack *stack = find_stack_state(state, offset);
|
| 627 |
|
| 628 | if (stack) {
|
| 629 | set_stack_state(stack, offset, kind, type_die, ptr_offset);
|
| 630 | return stack;
|
| 631 | }
|
| 632 |
|
| 633 | stack = malloc(sizeof(*stack));
|
| 634 | if (stack) {
|
| 635 | set_stack_state(stack, offset, kind, type_die, ptr_offset);
|
| 636 | list_add(&stack->list, &state->stack_vars);
|
| 637 | }
|
| 638 | return stack;
|
| 639 | }
|
| 640 |
|
| 641 |
|
| 642 | struct global_var_entry {
|
| 643 | struct rb_node node;
|
| 644 | char *name;
|
| 645 | u64 start;
|
| 646 | u64 end;
|
| 647 | u64 die_offset;
|
| 648 | };
|
| 649 |
|
| 650 | static int global_var_cmp(const void *_key, const struct rb_node *node)
|
| 651 | {
|
| 652 | const u64 addr = (uintptr_t)_key;
|
| 653 | struct global_var_entry *gvar;
|
| 654 |
|
| 655 | gvar = rb_entry(node, struct global_var_entry, node);
|
| 656 |
|
| 657 | if (gvar->start <= addr && addr < gvar->end)
|
| 658 | return 0;
|
| 659 | return gvar->start > addr ? -1 : 1;
|
| 660 | }
|
| 661 |
|
| 662 | static bool global_var_less(struct rb_node *node_a, const struct rb_node *node_b)
|
| 663 | {
|
| 664 | struct global_var_entry *gvar_a, *gvar_b;
|
| 665 |
|
| 666 | gvar_a = rb_entry(node_a, struct global_var_entry, node);
|
| 667 | gvar_b = rb_entry(node_b, struct global_var_entry, node);
|
| 668 |
|
| 669 | return gvar_a->start < gvar_b->start;
|
| 670 | }
|
| 671 |
|
| 672 | static struct global_var_entry *global_var__find(struct data_loc_info *dloc, u64 addr)
|
| 673 | {
|
| 674 | struct dso *dso = map__dso(dloc->ms->map);
|
| 675 | struct rb_node *node;
|
| 676 |
|
| 677 | node = rb_find((void *)(uintptr_t)addr, dso__global_vars(dso), global_var_cmp);
|
| 678 | if (node == NULL)
|
| 679 | return NULL;
|
| 680 |
|
| 681 | return rb_entry(node, struct global_var_entry, node);
|
| 682 | }
|
| 683 |
|
| 684 | static bool global_var__add(struct data_loc_info *dloc, u64 addr,
|
| 685 | const char *name, Dwarf_Die *type_die)
|
| 686 | {
|
| 687 | struct dso *dso = map__dso(dloc->ms->map);
|
| 688 | struct global_var_entry *gvar;
|
| 689 | Dwarf_Word size;
|
| 690 |
|
| 691 | if (dwarf_aggregate_size(type_die, &size) < 0)
|
| 692 | return false;
|
| 693 |
|
| 694 | gvar = malloc(sizeof(*gvar));
|
| 695 | if (gvar == NULL)
|
| 696 | return false;
|
| 697 |
|
| 698 | gvar->name = name ? strdup(name) : NULL;
|
| 699 | if (name && gvar->name == NULL) {
|
| 700 | free(gvar);
|
| 701 | return false;
|
| 702 | }
|
| 703 |
|
| 704 | gvar->start = addr;
|
| 705 | gvar->end = addr + size;
|
| 706 | gvar->die_offset = dwarf_dieoffset(type_die);
|
| 707 |
|
| 708 | rb_add(&gvar->node, dso__global_vars(dso), global_var_less);
|
| 709 | return true;
|
| 710 | }
|
| 711 |
|
| 712 | void global_var_type__tree_delete(struct rb_root *root)
|
| 713 | {
|
| 714 | struct global_var_entry *gvar;
|
| 715 |
|
| 716 | while (!RB_EMPTY_ROOT(root)) {
|
| 717 | struct rb_node *node = rb_first(root);
|
| 718 |
|
| 719 | rb_erase(node, root);
|
| 720 | gvar = rb_entry(node, struct global_var_entry, node);
|
| 721 | zfree(&gvar->name);
|
| 722 | free(gvar);
|
| 723 | }
|
| 724 | }
|
| 725 |
|
| 726 | bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
|
| 727 | const char **var_name, int *var_offset)
|
| 728 | {
|
| 729 | struct addr_location al;
|
| 730 | struct symbol *sym;
|
| 731 | u64 mem_addr;
|
| 732 |
|
| 733 |
|
| 734 | mem_addr = addr + map__reloc(dloc->ms->map);
|
| 735 |
|
| 736 | addr_location__init(&al);
|
| 737 | sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode,
|
| 738 | mem_addr, &al);
|
| 739 | if (sym) {
|
| 740 | *var_name = sym->name;
|
| 741 |
|
| 742 | *var_offset = mem_addr - map__unmap_ip(al.map, sym->start);
|
| 743 | } else {
|
| 744 | *var_name = NULL;
|
| 745 | }
|
| 746 | addr_location__exit(&al);
|
| 747 | if (*var_name == NULL)
|
| 748 | return false;
|
| 749 |
|
| 750 | return true;
|
| 751 | }
|
| 752 |
|
| 753 | static void global_var__collect(struct data_loc_info *dloc)
|
| 754 | {
|
| 755 | Dwarf *dwarf = dloc->di->dbg;
|
| 756 | Dwarf_Off off, next_off;
|
| 757 | Dwarf_Die cu_die, type_die;
|
| 758 | size_t header_size;
|
| 759 |
|
| 760 |
|
| 761 | off = 0;
|
| 762 | while (dwarf_nextcu(dwarf, off, &next_off, &header_size,
|
| 763 | NULL, NULL, NULL) == 0) {
|
| 764 | struct die_var_type *var_types = NULL;
|
| 765 | struct die_var_type *pos;
|
| 766 |
|
| 767 | if (dwarf_offdie(dwarf, off + header_size, &cu_die) == NULL) {
|
| 768 | off = next_off;
|
| 769 | continue;
|
| 770 | }
|
| 771 |
|
| 772 | die_collect_global_vars(&cu_die, &var_types);
|
| 773 |
|
| 774 | for (pos = var_types; pos; pos = pos->next) {
|
| 775 | const char *var_name = NULL;
|
| 776 | int var_offset = 0;
|
| 777 |
|
| 778 | if (pos->reg != -1)
|
| 779 | continue;
|
| 780 |
|
| 781 | if (!dwarf_offdie(dwarf, pos->die_off, &type_die))
|
| 782 | continue;
|
| 783 |
|
| 784 | get_global_var_info(dloc, pos->addr, &var_name, &var_offset);
|
| 785 |
|
| 786 | global_var__add(dloc, pos->addr, var_name, &type_die);
|
| 787 | }
|
| 788 |
|
| 789 | delete_var_types(var_types);
|
| 790 |
|
| 791 | off = next_off;
|
| 792 | }
|
| 793 | }
|
| 794 |
|
| 795 | bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
|
| 796 | u64 ip, u64 var_addr, int *var_offset,
|
| 797 | Dwarf_Die *type_die)
|
| 798 | {
|
| 799 | u64 pc;
|
| 800 | int offset;
|
| 801 | const char *var_name = NULL;
|
| 802 | struct global_var_entry *gvar;
|
| 803 | struct dso *dso = map__dso(dloc->ms->map);
|
| 804 | Dwarf_Die var_die;
|
| 805 |
|
| 806 | if (RB_EMPTY_ROOT(dso__global_vars(dso)))
|
| 807 | global_var__collect(dloc);
|
| 808 |
|
| 809 | gvar = global_var__find(dloc, var_addr);
|
| 810 | if (gvar) {
|
| 811 | if (!dwarf_offdie(dloc->di->dbg, gvar->die_offset, type_die))
|
| 812 | return false;
|
| 813 |
|
| 814 | *var_offset = var_addr - gvar->start;
|
| 815 | return true;
|
| 816 | }
|
| 817 |
|
| 818 |
|
| 819 | if (die_find_variable_by_addr(cu_die, var_addr, &var_die, type_die,
|
| 820 | &offset)) {
|
| 821 | var_name = dwarf_diename(&var_die);
|
| 822 | *var_offset = offset;
|
| 823 | goto ok;
|
| 824 | }
|
| 825 |
|
| 826 | if (!get_global_var_info(dloc, var_addr, &var_name, var_offset))
|
| 827 | return false;
|
| 828 |
|
| 829 | pc = map__rip_2objdump(dloc->ms->map, ip);
|
| 830 |
|
| 831 |
|
| 832 | if (die_find_variable_at(cu_die, var_name, pc, &var_die) &&
|
| 833 | check_variable(dloc, &var_die, type_die, DWARF_REG_PC, *var_offset,
|
| 834 | false) == PERF_TMR_OK)
|
| 835 | goto ok;
|
| 836 |
|
| 837 | return false;
|
| 838 |
|
| 839 | ok:
|
| 840 |
|
| 841 | global_var__add(dloc, var_addr - *var_offset, var_name, type_die);
|
| 842 | return true;
|
| 843 | }
|
| 844 |
|
| 845 | static bool die_is_same(Dwarf_Die *die_a, Dwarf_Die *die_b)
|
| 846 | {
|
| 847 | return (die_a->cu == die_b->cu) && (die_a->addr == die_b->addr);
|
| 848 | }
|
| 849 |
|
| 850 | static void tsr_set_lifetime(struct type_state_reg *tsr,
|
| 851 | const struct die_var_type *var)
|
| 852 | {
|
| 853 | if (var && var->has_range && var->end > var->addr) {
|
| 854 | tsr->lifetime_active = true;
|
| 855 | tsr->lifetime_end = var->end;
|
| 856 | } else {
|
| 857 | tsr->lifetime_active = false;
|
| 858 | tsr->lifetime_end = 0;
|
| 859 | }
|
| 860 | }
|
| 861 |
|
| 862 | |
| 863 | * update_var_state - Update type state using given variables
|
| 864 | * @state: type state table
|
| 865 | * @dloc: data location info
|
| 866 | * @addr: instruction address to match with variable
|
| 867 | * @insn_offset: instruction offset (for debug)
|
| 868 | * @var_types: list of variables with type info
|
| 869 | *
|
| 870 | * This function fills the @state table using @var_types info. Each variable
|
| 871 | * is used only at the given location and updates an entry in the table.
|
| 872 |
|
| 873 | static void update_var_state(struct type_state *state, struct data_loc_info *dloc,
|
| 874 | u64 addr, u64 insn_offset, struct die_var_type *var_types)
|
| 875 | {
|
| 876 | Dwarf_Die mem_die;
|
| 877 | struct die_var_type *var;
|
| 878 | int fbreg = dloc->fbreg;
|
| 879 | int fb_offset = 0;
|
| 880 |
|
| 881 | if (dloc->fb_cfa) {
|
| 882 | if (die_get_cfa(dloc->di->dbg, addr, &fbreg, &fb_offset) < 0)
|
| 883 | fbreg = -1;
|
| 884 | }
|
| 885 |
|
| 886 | for (var = var_types; var != NULL; var = var->next) {
|
| 887 |
|
| 888 | if (var->has_range) {
|
| 889 | if (addr < var->addr || (var->end && addr >= var->end))
|
| 890 | continue;
|
| 891 | } else {
|
| 892 | if (addr != var->addr)
|
| 893 | continue;
|
| 894 | }
|
| 895 |
|
| 896 | if (!dwarf_offdie(dloc->di->dbg, var->die_off, &mem_die))
|
| 897 | continue;
|
| 898 |
|
| 899 | if (var->reg == DWARF_REG_FB || var->reg == fbreg || var->reg == state->stack_reg) {
|
| 900 | Dwarf_Die ptr_die;
|
| 901 | Dwarf_Die *ptr_type;
|
| 902 | int offset = var->offset;
|
| 903 | struct type_state_stack *stack;
|
| 904 |
|
| 905 | ptr_type = die_get_pointer_type(&mem_die, &ptr_die);
|
| 906 |
|
| 907 |
|
| 908 | if (!var->is_reg_var_addr && ptr_type &&
|
| 909 | __die_get_real_type(ptr_type, &mem_die) == NULL)
|
| 910 | continue;
|
| 911 |
|
| 912 | if (var->reg != DWARF_REG_FB)
|
| 913 | offset -= fb_offset;
|
| 914 |
|
| 915 | stack = find_stack_state(state, offset);
|
| 916 | if (stack && stack->kind == TSR_KIND_TYPE &&
|
| 917 | !is_better_type(&stack->type, &mem_die))
|
| 918 | continue;
|
| 919 |
|
| 920 | findnew_stack_state(state, offset, TSR_KIND_TYPE,
|
| 921 | &mem_die, 0);
|
| 922 |
|
| 923 | if (var->reg == state->stack_reg) {
|
| 924 | pr_debug_dtp("var [%"PRIx64"] %#x(reg%d)",
|
| 925 | insn_offset, offset, state->stack_reg);
|
| 926 | } else {
|
| 927 | pr_debug_dtp("var [%"PRIx64"] -%#x(stack)",
|
| 928 | insn_offset, -offset);
|
| 929 | }
|
| 930 | pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
|
| 931 | } else if (has_reg_type(state, var->reg)) {
|
| 932 | struct type_state_reg *reg;
|
| 933 | Dwarf_Die orig_type;
|
| 934 |
|
| 935 | reg = &state->regs[var->reg];
|
| 936 |
|
| 937 | if (reg->ok && reg->kind == TSR_KIND_TYPE &&
|
| 938 | (!is_better_type(®->type, &mem_die) || var->is_reg_var_addr))
|
| 939 | continue;
|
| 940 |
|
| 941 |
|
| 942 | if (var->is_reg_var_addr) {
|
| 943 | if (reg->ok && reg->kind == TSR_KIND_POINTER &&
|
| 944 | !is_better_type(®->type, &mem_die))
|
| 945 | continue;
|
| 946 |
|
| 947 | reg->offset = -var->offset;
|
| 948 | reg->type = mem_die;
|
| 949 | reg->kind = TSR_KIND_POINTER;
|
| 950 | reg->ok = true;
|
| 951 | tsr_set_lifetime(reg, var);
|
| 952 |
|
| 953 | pr_debug_dtp("var [%"PRIx64"] reg%d addr offset %x",
|
| 954 | insn_offset, var->reg, var->offset);
|
| 955 | pr_debug_type_name(&mem_die, TSR_KIND_POINTER);
|
| 956 | continue;
|
| 957 | }
|
| 958 |
|
| 959 | orig_type = reg->type;
|
| 960 | |
| 961 | * var->offset + reg value is the beginning of the struct
|
| 962 | * reg->offset is the offset the reg points
|
| 963 |
|
| 964 | reg->offset = -var->offset;
|
| 965 | reg->type = mem_die;
|
| 966 | reg->kind = TSR_KIND_TYPE;
|
| 967 | reg->ok = true;
|
| 968 | tsr_set_lifetime(reg, var);
|
| 969 |
|
| 970 | pr_debug_dtp("var [%"PRIx64"] reg%d offset %x",
|
| 971 | insn_offset, var->reg, var->offset);
|
| 972 | pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
|
| 973 |
|
| 974 | |
| 975 | * If this register is directly copied from another and it gets a
|
| 976 | * better type, also update the type of the source register. This
|
| 977 | * is usually the case of container_of() macro with offset of 0.
|
| 978 |
|
| 979 | if (has_reg_type(state, reg->copied_from)) {
|
| 980 | struct type_state_reg *copy_reg;
|
| 981 |
|
| 982 | copy_reg = &state->regs[reg->copied_from];
|
| 983 |
|
| 984 |
|
| 985 | if (!copy_reg->ok || (copy_reg->kind != TSR_KIND_TYPE) ||
|
| 986 | !die_is_same(©_reg->type, &orig_type) ||
|
| 987 | !is_better_type(©_reg->type, &mem_die))
|
| 988 | continue;
|
| 989 |
|
| 990 | copy_reg->type = mem_die;
|
| 991 |
|
| 992 | pr_debug_dtp("var [%"PRIx64"] copyback reg%d",
|
| 993 | insn_offset, reg->copied_from);
|
| 994 | pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
|
| 995 | }
|
| 996 | }
|
| 997 | }
|
| 998 | }
|
| 999 |
|
| 1000 | |
| 1001 | * update_insn_state - Update type state for an instruction
|
| 1002 | * @state: type state table
|
| 1003 | * @dloc: data location info
|
| 1004 | * @cu_die: compile unit debug entry
|
| 1005 | * @dl: disasm line for the instruction
|
| 1006 | *
|
| 1007 | * This function updates the @state table for the target operand of the
|
| 1008 | * instruction at @dl if it transfers the type like MOV on x86. Since it
|
| 1009 | * tracks the type, it won't care about the values like in arithmetic
|
| 1010 | * instructions like ADD/SUB/MUL/DIV and INC/DEC.
|
| 1011 | *
|
| 1012 | * Note that ops->reg2 is only available when both mem_ref and multi_regs
|
| 1013 | * are true.
|
| 1014 |
|
| 1015 | static void update_insn_state(struct type_state *state, struct data_loc_info *dloc,
|
| 1016 | Dwarf_Die *cu_die, struct disasm_line *dl)
|
| 1017 | {
|
| 1018 | if (dloc->arch->update_insn_state)
|
| 1019 | dloc->arch->update_insn_state(state, dloc, cu_die, dl);
|
| 1020 | }
|
| 1021 |
|
| 1022 | |
| 1023 | * Prepend this_blocks (from the outer scope) to full_blocks, removing
|
| 1024 | * duplicate disasm line.
|
| 1025 |
|
| 1026 | static void prepend_basic_blocks(struct list_head *this_blocks,
|
| 1027 | struct list_head *full_blocks)
|
| 1028 | {
|
| 1029 | struct annotated_basic_block *first_bb, *last_bb;
|
| 1030 |
|
| 1031 | last_bb = list_last_entry(this_blocks, typeof(*last_bb), list);
|
| 1032 | first_bb = list_first_entry(full_blocks, typeof(*first_bb), list);
|
| 1033 |
|
| 1034 | if (list_empty(full_blocks))
|
| 1035 | goto out;
|
| 1036 |
|
| 1037 |
|
| 1038 | if (last_bb->end != first_bb->begin) {
|
| 1039 | pr_debug("prepend basic blocks: mismatched disasm line %"PRIx64" -> %"PRIx64"\n",
|
| 1040 | last_bb->end->al.offset, first_bb->begin->al.offset);
|
| 1041 | goto out;
|
| 1042 | }
|
| 1043 |
|
| 1044 |
|
| 1045 | if (last_bb->begin == last_bb->end) {
|
| 1046 | list_del(&last_bb->list);
|
| 1047 | free(last_bb);
|
| 1048 | goto out;
|
| 1049 | }
|
| 1050 |
|
| 1051 |
|
| 1052 | last_bb->end = list_prev_entry(last_bb->end, al.node);
|
| 1053 |
|
| 1054 | out:
|
| 1055 | list_splice(this_blocks, full_blocks);
|
| 1056 | }
|
| 1057 |
|
| 1058 | static void delete_basic_blocks(struct list_head *basic_blocks)
|
| 1059 | {
|
| 1060 | struct annotated_basic_block *bb, *tmp;
|
| 1061 |
|
| 1062 | list_for_each_entry_safe(bb, tmp, basic_blocks, list) {
|
| 1063 | list_del(&bb->list);
|
| 1064 | free(bb);
|
| 1065 | }
|
| 1066 | }
|
| 1067 |
|
| 1068 |
|
| 1069 | static void fixup_var_address(struct die_var_type *var_types, u64 addr)
|
| 1070 | {
|
| 1071 | while (var_types) {
|
| 1072 | |
| 1073 | * Some variables have no address range meaning it's always
|
| 1074 | * available in the whole scope. Let's adjust the start
|
| 1075 | * address to the start of the scope.
|
| 1076 |
|
| 1077 | if (var_types->addr == 0)
|
| 1078 | var_types->addr = addr;
|
| 1079 |
|
| 1080 | var_types = var_types->next;
|
| 1081 | }
|
| 1082 | }
|
| 1083 |
|
| 1084 | static void delete_var_types(struct die_var_type *var_types)
|
| 1085 | {
|
| 1086 | while (var_types) {
|
| 1087 | struct die_var_type *next = var_types->next;
|
| 1088 |
|
| 1089 | free(var_types);
|
| 1090 | var_types = next;
|
| 1091 | }
|
| 1092 | }
|
| 1093 |
|
| 1094 |
|
| 1095 | static void setup_stack_canary(struct data_loc_info *dloc)
|
| 1096 | {
|
| 1097 | if (arch__is_x86(dloc->arch)) {
|
| 1098 | dloc->op->segment = INSN_SEG_X86_GS;
|
| 1099 | dloc->op->imm = true;
|
| 1100 | dloc->op->offset = 40;
|
| 1101 | }
|
| 1102 | }
|
| 1103 |
|
| 1104 | |
| 1105 | * It's at the target address, check if it has a matching type.
|
| 1106 | * It returns PERF_TMR_BAIL_OUT when it looks up per-cpu variables which
|
| 1107 | * are similar to global variables and no additional info is needed.
|
| 1108 |
|
| 1109 | static enum type_match_result check_matching_type(struct type_state *state,
|
| 1110 | struct data_loc_info *dloc,
|
| 1111 | Dwarf_Die *cu_die,
|
| 1112 | struct disasm_line *dl,
|
| 1113 | Dwarf_Die *type_die)
|
| 1114 | {
|
| 1115 | Dwarf_Word size;
|
| 1116 | u32 insn_offset = dl->al.offset;
|
| 1117 | int reg = dloc->op->reg1;
|
| 1118 | int offset = dloc->op->offset;
|
| 1119 | const char *offset_sign = "";
|
| 1120 | bool retry = true;
|
| 1121 |
|
| 1122 | if (offset < 0) {
|
| 1123 | offset = -offset;
|
| 1124 | offset_sign = "-";
|
| 1125 | }
|
| 1126 |
|
| 1127 | again:
|
| 1128 | pr_debug_dtp("chk [%x] reg%d offset=%s%#x ok=%d kind=%d ",
|
| 1129 | insn_offset, reg, offset_sign, offset,
|
| 1130 | state->regs[reg].ok, state->regs[reg].kind);
|
| 1131 |
|
| 1132 | if (!state->regs[reg].ok)
|
| 1133 | goto check_non_register;
|
| 1134 |
|
| 1135 | if (state->regs[reg].kind == TSR_KIND_TYPE) {
|
| 1136 | Dwarf_Die ptr_die;
|
| 1137 | Dwarf_Die sized_type;
|
| 1138 | Dwarf_Die *ptr_type;
|
| 1139 | struct strbuf sb;
|
| 1140 |
|
| 1141 | strbuf_init(&sb, 32);
|
| 1142 | die_get_typename_from_type(&state->regs[reg].type, &sb);
|
| 1143 | pr_debug_dtp("(%s)", sb.buf);
|
| 1144 | strbuf_release(&sb);
|
| 1145 |
|
| 1146 | |
| 1147 | * Normal registers should hold a pointer (or array) to
|
| 1148 | * dereference a memory location.
|
| 1149 |
|
| 1150 | ptr_type = die_get_pointer_type(&state->regs[reg].type, &ptr_die);
|
| 1151 | if (!ptr_type) {
|
| 1152 | if (dloc->op->offset < 0 && reg != state->stack_reg)
|
| 1153 | goto check_kernel;
|
| 1154 |
|
| 1155 | return PERF_TMR_NO_POINTER;
|
| 1156 | }
|
| 1157 |
|
| 1158 |
|
| 1159 | if (__die_get_real_type(ptr_type, type_die) == NULL)
|
| 1160 | return PERF_TMR_NO_POINTER;
|
| 1161 |
|
| 1162 | dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
|
| 1163 |
|
| 1164 | if (dwarf_tag(type_die) == DW_TAG_typedef)
|
| 1165 | die_get_real_type(type_die, &sized_type);
|
| 1166 | else
|
| 1167 | sized_type = *type_die;
|
| 1168 |
|
| 1169 |
|
| 1170 | if (dwarf_aggregate_size(&sized_type, &size) < 0 ||
|
| 1171 | (unsigned)dloc->type_offset >= size)
|
| 1172 | return PERF_TMR_BAD_OFFSET;
|
| 1173 |
|
| 1174 | return PERF_TMR_OK;
|
| 1175 | }
|
| 1176 |
|
| 1177 | if (state->regs[reg].kind == TSR_KIND_POINTER) {
|
| 1178 | struct strbuf sb;
|
| 1179 |
|
| 1180 | strbuf_init(&sb, 32);
|
| 1181 | die_get_typename_from_type(&state->regs[reg].type, &sb);
|
| 1182 | pr_debug_dtp("(ptr->%s)", sb.buf);
|
| 1183 | strbuf_release(&sb);
|
| 1184 |
|
| 1185 | |
| 1186 | * Register holds a pointer (address) to the target variable.
|
| 1187 | * The type is the type of the variable it points to.
|
| 1188 |
|
| 1189 | *type_die = state->regs[reg].type;
|
| 1190 |
|
| 1191 | dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
|
| 1192 |
|
| 1193 |
|
| 1194 | if (dwarf_aggregate_size(type_die, &size) < 0 ||
|
| 1195 | (unsigned)dloc->type_offset >= size)
|
| 1196 | return PERF_TMR_BAD_OFFSET;
|
| 1197 |
|
| 1198 | return PERF_TMR_OK;
|
| 1199 | }
|
| 1200 |
|
| 1201 | if (state->regs[reg].kind == TSR_KIND_PERCPU_POINTER) {
|
| 1202 | pr_debug_dtp("percpu ptr");
|
| 1203 |
|
| 1204 | |
| 1205 | * It's actaully pointer but the address was calculated using
|
| 1206 | * some arithmetic. So it points to the actual type already.
|
| 1207 |
|
| 1208 | *type_die = state->regs[reg].type;
|
| 1209 |
|
| 1210 | dloc->type_offset = dloc->op->offset;
|
| 1211 |
|
| 1212 |
|
| 1213 | if (dwarf_aggregate_size(type_die, &size) < 0 ||
|
| 1214 | (unsigned)dloc->type_offset >= size)
|
| 1215 | return PERF_TMR_BAIL_OUT;
|
| 1216 |
|
| 1217 | return PERF_TMR_OK;
|
| 1218 | }
|
| 1219 |
|
| 1220 | if (state->regs[reg].kind == TSR_KIND_CANARY) {
|
| 1221 | pr_debug_dtp("stack canary");
|
| 1222 |
|
| 1223 | |
| 1224 | * This is a saved value of the stack canary which will be handled
|
| 1225 | * in the outer logic when it returns failure here. Pretend it's
|
| 1226 | * from the stack canary directly.
|
| 1227 |
|
| 1228 | setup_stack_canary(dloc);
|
| 1229 |
|
| 1230 | return PERF_TMR_BAIL_OUT;
|
| 1231 | }
|
| 1232 |
|
| 1233 | if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) {
|
| 1234 | u64 var_addr = dloc->op->offset;
|
| 1235 | int var_offset;
|
| 1236 |
|
| 1237 | pr_debug_dtp("percpu var");
|
| 1238 |
|
| 1239 | if (dloc->op->multi_regs) {
|
| 1240 | int reg2 = dloc->op->reg2;
|
| 1241 |
|
| 1242 | if (dloc->op->reg2 == reg)
|
| 1243 | reg2 = dloc->op->reg1;
|
| 1244 |
|
| 1245 | if (has_reg_type(state, reg2) && state->regs[reg2].ok &&
|
| 1246 | state->regs[reg2].kind == TSR_KIND_CONST)
|
| 1247 | var_addr += state->regs[reg2].imm_value;
|
| 1248 | }
|
| 1249 |
|
| 1250 | if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr,
|
| 1251 | &var_offset, type_die)) {
|
| 1252 | dloc->type_offset = var_offset;
|
| 1253 | return PERF_TMR_OK;
|
| 1254 | }
|
| 1255 |
|
| 1256 | return PERF_TMR_BAIL_OUT;
|
| 1257 | }
|
| 1258 |
|
| 1259 | if (state->regs[reg].kind == TSR_KIND_CONST &&
|
| 1260 | dso__kernel(map__dso(dloc->ms->map))) {
|
| 1261 | if (dloc->op->offset < 0 && reg != state->stack_reg && reg != dloc->fbreg)
|
| 1262 | goto check_kernel;
|
| 1263 | }
|
| 1264 | check_non_register:
|
| 1265 | if (reg == dloc->fbreg || reg == state->stack_reg) {
|
| 1266 | struct type_state_stack *stack;
|
| 1267 |
|
| 1268 | pr_debug_dtp("%s", reg == dloc->fbreg ? "fbreg" : "stack");
|
| 1269 |
|
| 1270 | stack = find_stack_state(state, dloc->type_offset);
|
| 1271 | if (stack == NULL) {
|
| 1272 | if (retry) {
|
| 1273 | pr_debug_dtp(" : retry\n");
|
| 1274 | retry = false;
|
| 1275 |
|
| 1276 |
|
| 1277 | update_insn_state(state, dloc, cu_die, dl);
|
| 1278 | goto again;
|
| 1279 | }
|
| 1280 | return PERF_TMR_NO_TYPE;
|
| 1281 | }
|
| 1282 |
|
| 1283 | if (stack->kind == TSR_KIND_CANARY) {
|
| 1284 | setup_stack_canary(dloc);
|
| 1285 | return PERF_TMR_BAIL_OUT;
|
| 1286 | }
|
| 1287 |
|
| 1288 | if (stack->kind != TSR_KIND_TYPE)
|
| 1289 | return PERF_TMR_NO_TYPE;
|
| 1290 |
|
| 1291 | *type_die = stack->type;
|
| 1292 |
|
| 1293 | dloc->type_offset -= stack->offset;
|
| 1294 |
|
| 1295 | return PERF_TMR_OK;
|
| 1296 | }
|
| 1297 |
|
| 1298 | if (dloc->fb_cfa) {
|
| 1299 | struct type_state_stack *stack;
|
| 1300 | u64 pc = map__rip_2objdump(dloc->ms->map, dloc->ip);
|
| 1301 | int fbreg, fboff;
|
| 1302 |
|
| 1303 | pr_debug_dtp("cfa");
|
| 1304 |
|
| 1305 | if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
|
| 1306 | fbreg = -1;
|
| 1307 |
|
| 1308 | if (reg != fbreg)
|
| 1309 | return PERF_TMR_NO_TYPE;
|
| 1310 |
|
| 1311 | stack = find_stack_state(state, dloc->type_offset - fboff);
|
| 1312 | if (stack == NULL) {
|
| 1313 | if (retry) {
|
| 1314 | pr_debug_dtp(" : retry\n");
|
| 1315 | retry = false;
|
| 1316 |
|
| 1317 |
|
| 1318 | update_insn_state(state, dloc, cu_die, dl);
|
| 1319 | goto again;
|
| 1320 | }
|
| 1321 | return PERF_TMR_NO_TYPE;
|
| 1322 | }
|
| 1323 |
|
| 1324 | if (stack->kind == TSR_KIND_CANARY) {
|
| 1325 | setup_stack_canary(dloc);
|
| 1326 | return PERF_TMR_BAIL_OUT;
|
| 1327 | }
|
| 1328 |
|
| 1329 | if (stack->kind != TSR_KIND_TYPE)
|
| 1330 | return PERF_TMR_NO_TYPE;
|
| 1331 |
|
| 1332 | *type_die = stack->type;
|
| 1333 |
|
| 1334 | dloc->type_offset -= fboff + stack->offset;
|
| 1335 |
|
| 1336 | return PERF_TMR_OK;
|
| 1337 | }
|
| 1338 |
|
| 1339 | check_kernel:
|
| 1340 | if (dso__kernel(map__dso(dloc->ms->map))) {
|
| 1341 | u64 addr;
|
| 1342 |
|
| 1343 |
|
| 1344 | if (dloc->op->segment == INSN_SEG_X86_GS && dloc->op->imm &&
|
| 1345 | arch__is_x86(dloc->arch)) {
|
| 1346 | pr_debug_dtp("this-cpu var");
|
| 1347 |
|
| 1348 | addr = dloc->op->offset;
|
| 1349 |
|
| 1350 | if (get_global_var_type(cu_die, dloc, dloc->ip, addr,
|
| 1351 | &offset, type_die)) {
|
| 1352 | dloc->type_offset = offset;
|
| 1353 | return PERF_TMR_OK;
|
| 1354 | }
|
| 1355 | return PERF_TMR_BAIL_OUT;
|
| 1356 | }
|
| 1357 |
|
| 1358 |
|
| 1359 | if (dloc->op->offset < 0 && reg != state->stack_reg) {
|
| 1360 | addr = (s64) dloc->op->offset;
|
| 1361 |
|
| 1362 | if (get_global_var_type(cu_die, dloc, dloc->ip, addr,
|
| 1363 | &offset, type_die)) {
|
| 1364 | pr_debug_dtp("global var");
|
| 1365 |
|
| 1366 | dloc->type_offset = offset;
|
| 1367 | return PERF_TMR_OK;
|
| 1368 | }
|
| 1369 | return PERF_TMR_BAIL_OUT;
|
| 1370 | }
|
| 1371 | }
|
| 1372 |
|
| 1373 | return PERF_TMR_UNKNOWN;
|
| 1374 | }
|
| 1375 |
|
| 1376 |
|
| 1377 | static enum type_match_result find_data_type_insn(struct data_loc_info *dloc,
|
| 1378 | struct list_head *basic_blocks,
|
| 1379 | struct die_var_type *var_types,
|
| 1380 | Dwarf_Die *cu_die,
|
| 1381 | Dwarf_Die *type_die)
|
| 1382 | {
|
| 1383 | struct type_state state;
|
| 1384 | struct symbol *sym = dloc->ms->sym;
|
| 1385 | struct annotation *notes = symbol__annotation(sym);
|
| 1386 | struct annotated_basic_block *bb;
|
| 1387 | enum type_match_result ret = PERF_TMR_UNKNOWN;
|
| 1388 |
|
| 1389 | init_type_state(&state, dloc->arch);
|
| 1390 |
|
| 1391 | list_for_each_entry(bb, basic_blocks, list) {
|
| 1392 | struct disasm_line *dl = bb->begin;
|
| 1393 |
|
| 1394 | BUG_ON(bb->begin->al.offset == -1 || bb->end->al.offset == -1);
|
| 1395 |
|
| 1396 | pr_debug_dtp("bb: [%"PRIx64" - %"PRIx64"]\n",
|
| 1397 | bb->begin->al.offset, bb->end->al.offset);
|
| 1398 |
|
| 1399 | list_for_each_entry_from(dl, ¬es->src->source, al.node) {
|
| 1400 | u64 this_ip = sym->start + dl->al.offset;
|
| 1401 | u64 addr = map__rip_2objdump(dloc->ms->map, this_ip);
|
| 1402 |
|
| 1403 |
|
| 1404 | if (dl->al.offset == -1)
|
| 1405 | continue;
|
| 1406 |
|
| 1407 |
|
| 1408 | update_var_state(&state, dloc, addr, dl->al.offset, var_types);
|
| 1409 |
|
| 1410 | if (this_ip == dloc->ip) {
|
| 1411 | ret = check_matching_type(&state, dloc,
|
| 1412 | cu_die, dl, type_die);
|
| 1413 | pr_debug_dtp(" : %s\n", match_result_str(ret));
|
| 1414 | goto out;
|
| 1415 | }
|
| 1416 |
|
| 1417 |
|
| 1418 | update_insn_state(&state, dloc, cu_die, dl);
|
| 1419 | if (dl == bb->end)
|
| 1420 | break;
|
| 1421 | }
|
| 1422 | }
|
| 1423 |
|
| 1424 | out:
|
| 1425 | exit_type_state(&state);
|
| 1426 | return ret;
|
| 1427 | }
|
| 1428 |
|
| 1429 | static int arch_supports_insn_tracking(struct data_loc_info *dloc)
|
| 1430 | {
|
| 1431 | if ((arch__is_x86(dloc->arch)) || (arch__is_powerpc(dloc->arch)))
|
| 1432 | return 1;
|
| 1433 | return 0;
|
| 1434 | }
|
| 1435 |
|
| 1436 | |
| 1437 | * Construct a list of basic blocks for each scope with variables and try to find
|
| 1438 | * the data type by updating a type state table through instructions.
|
| 1439 |
|
| 1440 | static enum type_match_result find_data_type_block(struct data_loc_info *dloc,
|
| 1441 | Dwarf_Die *cu_die,
|
| 1442 | Dwarf_Die *scopes,
|
| 1443 | int nr_scopes,
|
| 1444 | Dwarf_Die *type_die)
|
| 1445 | {
|
| 1446 | LIST_HEAD(basic_blocks);
|
| 1447 | struct die_var_type *var_types = NULL;
|
| 1448 | u64 src_ip, dst_ip, prev_dst_ip;
|
| 1449 | enum type_match_result ret = PERF_TMR_UNKNOWN;
|
| 1450 |
|
| 1451 |
|
| 1452 | if (!arch_supports_insn_tracking(dloc))
|
| 1453 | return PERF_TMR_BAIL_OUT;
|
| 1454 |
|
| 1455 | prev_dst_ip = dst_ip = dloc->ip;
|
| 1456 | for (int i = nr_scopes - 1; i >= 0; i--) {
|
| 1457 | Dwarf_Addr base, start, end;
|
| 1458 | LIST_HEAD(this_blocks);
|
| 1459 |
|
| 1460 | if (dwarf_ranges(&scopes[i], 0, &base, &start, &end) < 0)
|
| 1461 | break;
|
| 1462 |
|
| 1463 | pr_debug_dtp("scope: [%d/%d] ", i + 1, nr_scopes);
|
| 1464 | pr_debug_scope(&scopes[i]);
|
| 1465 |
|
| 1466 | src_ip = map__objdump_2rip(dloc->ms->map, start);
|
| 1467 |
|
| 1468 | again:
|
| 1469 |
|
| 1470 | if (annotate_get_basic_blocks(dloc->ms->sym, src_ip, dst_ip,
|
| 1471 | &this_blocks) < 0) {
|
| 1472 |
|
| 1473 | if (prev_dst_ip != dst_ip) {
|
| 1474 | dst_ip = prev_dst_ip;
|
| 1475 | goto again;
|
| 1476 | }
|
| 1477 |
|
| 1478 | pr_debug_dtp("cannot find a basic block from %"PRIx64" to %"PRIx64"\n",
|
| 1479 | src_ip - dloc->ms->sym->start,
|
| 1480 | dst_ip - dloc->ms->sym->start);
|
| 1481 | continue;
|
| 1482 | }
|
| 1483 | prepend_basic_blocks(&this_blocks, &basic_blocks);
|
| 1484 |
|
| 1485 |
|
| 1486 | die_collect_vars(&scopes[i], &var_types);
|
| 1487 | fixup_var_address(var_types, start);
|
| 1488 |
|
| 1489 |
|
| 1490 | ret = find_data_type_insn(dloc, &basic_blocks, var_types,
|
| 1491 | cu_die, type_die);
|
| 1492 | if (ret == PERF_TMR_OK) {
|
| 1493 | char buf[64];
|
| 1494 | int offset = dloc->op->offset;
|
| 1495 | const char *offset_sign = "";
|
| 1496 |
|
| 1497 | if (offset < 0) {
|
| 1498 | offset = -offset;
|
| 1499 | offset_sign = "-";
|
| 1500 | }
|
| 1501 |
|
| 1502 | if (dloc->op->multi_regs)
|
| 1503 | snprintf(buf, sizeof(buf), "reg%d, reg%d",
|
| 1504 | dloc->op->reg1, dloc->op->reg2);
|
| 1505 | else
|
| 1506 | snprintf(buf, sizeof(buf), "reg%d", dloc->op->reg1);
|
| 1507 |
|
| 1508 | pr_debug_dtp("found by insn track: %s%#x(%s) type-offset=%#x\n",
|
| 1509 | offset_sign, offset, buf, dloc->type_offset);
|
| 1510 | break;
|
| 1511 | }
|
| 1512 |
|
| 1513 | if (ret == PERF_TMR_BAIL_OUT)
|
| 1514 | break;
|
| 1515 |
|
| 1516 |
|
| 1517 | prev_dst_ip = dst_ip;
|
| 1518 | dst_ip = src_ip;
|
| 1519 | }
|
| 1520 |
|
| 1521 | delete_basic_blocks(&basic_blocks);
|
| 1522 | delete_var_types(var_types);
|
| 1523 | return ret;
|
| 1524 | }
|
| 1525 |
|
| 1526 |
|
| 1527 | static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
|
| 1528 | {
|
| 1529 | struct annotated_op_loc *loc = dloc->op;
|
| 1530 | Dwarf_Die cu_die, var_die;
|
| 1531 | Dwarf_Die *scopes = NULL;
|
| 1532 | int reg, offset = loc->offset;
|
| 1533 | int ret = -1;
|
| 1534 | int i, nr_scopes;
|
| 1535 | int fbreg = -1;
|
| 1536 | int fb_offset = 0;
|
| 1537 | bool is_fbreg = false;
|
| 1538 | bool found = false;
|
| 1539 | u64 pc;
|
| 1540 | char buf[64];
|
| 1541 | enum type_match_result result = PERF_TMR_UNKNOWN;
|
| 1542 | const char *offset_sign = "";
|
| 1543 |
|
| 1544 | if (dloc->op->multi_regs)
|
| 1545 | snprintf(buf, sizeof(buf), "reg%d, reg%d", dloc->op->reg1, dloc->op->reg2);
|
| 1546 | else if (dloc->op->reg1 == DWARF_REG_PC)
|
| 1547 | snprintf(buf, sizeof(buf), "PC");
|
| 1548 | else
|
| 1549 | snprintf(buf, sizeof(buf), "reg%d", dloc->op->reg1);
|
| 1550 |
|
| 1551 | if (offset < 0) {
|
| 1552 | offset = -offset;
|
| 1553 | offset_sign = "-";
|
| 1554 | }
|
| 1555 |
|
| 1556 | pr_debug_dtp("-----------------------------------------------------------\n");
|
| 1557 | pr_debug_dtp("find data type for %s%#x(%s) at %s+%#"PRIx64"\n",
|
| 1558 | offset_sign, offset, buf,
|
| 1559 | dloc->ms->sym->name, dloc->ip - dloc->ms->sym->start);
|
| 1560 |
|
| 1561 | |
| 1562 | * IP is a relative instruction address from the start of the map, as
|
| 1563 | * it can be randomized/relocated, it needs to translate to PC which is
|
| 1564 | * a file address for DWARF processing.
|
| 1565 |
|
| 1566 | pc = map__rip_2objdump(dloc->ms->map, dloc->ip);
|
| 1567 |
|
| 1568 |
|
| 1569 | if (!find_cu_die(dloc->di, pc, &cu_die)) {
|
| 1570 | pr_debug_dtp("cannot find CU for address %"PRIx64"\n", pc);
|
| 1571 | ann_data_stat.no_cuinfo++;
|
| 1572 | return -1;
|
| 1573 | }
|
| 1574 |
|
| 1575 | reg = loc->reg1;
|
| 1576 | offset = loc->offset;
|
| 1577 |
|
| 1578 | pr_debug_dtp("CU for %s (die:%#lx)\n",
|
| 1579 | die_name(&cu_die), (long)dwarf_dieoffset(&cu_die));
|
| 1580 |
|
| 1581 | if (reg == DWARF_REG_PC) {
|
| 1582 | if (get_global_var_type(&cu_die, dloc, dloc->ip, dloc->var_addr,
|
| 1583 | &offset, type_die)) {
|
| 1584 | dloc->type_offset = offset;
|
| 1585 |
|
| 1586 | pr_debug_dtp("found by addr=%#"PRIx64" type_offset=%#x\n",
|
| 1587 | dloc->var_addr, offset);
|
| 1588 | pr_debug_type_name(type_die, TSR_KIND_TYPE);
|
| 1589 | found = true;
|
| 1590 | goto out;
|
| 1591 | }
|
| 1592 | }
|
| 1593 |
|
| 1594 |
|
| 1595 | nr_scopes = die_get_scopes(&cu_die, pc, &scopes);
|
| 1596 |
|
| 1597 | if (reg != DWARF_REG_PC && dwarf_hasattr(&scopes[0], DW_AT_frame_base)) {
|
| 1598 | Dwarf_Attribute attr;
|
| 1599 | Dwarf_Block block;
|
| 1600 |
|
| 1601 |
|
| 1602 | if (dwarf_attr(&scopes[0], DW_AT_frame_base, &attr) != NULL &&
|
| 1603 | dwarf_formblock(&attr, &block) == 0 && block.length == 1) {
|
| 1604 | switch (*block.data) {
|
| 1605 | case DW_OP_reg0 ... DW_OP_reg31:
|
| 1606 | fbreg = dloc->fbreg = *block.data - DW_OP_reg0;
|
| 1607 | break;
|
| 1608 | case DW_OP_call_frame_cfa:
|
| 1609 | dloc->fb_cfa = true;
|
| 1610 | if (die_get_cfa(dloc->di->dbg, pc, &fbreg,
|
| 1611 | &fb_offset) < 0)
|
| 1612 | fbreg = -1;
|
| 1613 | break;
|
| 1614 | default:
|
| 1615 | break;
|
| 1616 | }
|
| 1617 |
|
| 1618 | pr_debug_dtp("frame base: cfa=%d fbreg=%d\n",
|
| 1619 | dloc->fb_cfa, fbreg);
|
| 1620 | }
|
| 1621 | }
|
| 1622 |
|
| 1623 | retry:
|
| 1624 | is_fbreg = (reg == fbreg);
|
| 1625 | if (is_fbreg)
|
| 1626 | offset = loc->offset - fb_offset;
|
| 1627 |
|
| 1628 |
|
| 1629 | for (i = nr_scopes - 1; i >= 0; i--) {
|
| 1630 | Dwarf_Die mem_die;
|
| 1631 | int type_offset = offset;
|
| 1632 |
|
| 1633 | if (reg == DWARF_REG_PC) {
|
| 1634 | if (!die_find_variable_by_addr(&scopes[i], dloc->var_addr,
|
| 1635 | &var_die, &mem_die,
|
| 1636 | &type_offset))
|
| 1637 | continue;
|
| 1638 | } else {
|
| 1639 |
|
| 1640 | if (!die_find_variable_by_reg(&scopes[i], pc, reg,
|
| 1641 | &mem_die, &type_offset, is_fbreg, &var_die))
|
| 1642 | continue;
|
| 1643 | }
|
| 1644 |
|
| 1645 | pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ",
|
| 1646 | die_name(&var_die), (long)dwarf_dieoffset(&var_die),
|
| 1647 | i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i]));
|
| 1648 |
|
| 1649 | if (reg == DWARF_REG_PC) {
|
| 1650 | pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n",
|
| 1651 | dloc->var_addr, type_offset);
|
| 1652 | } else if (reg == DWARF_REG_FB || is_fbreg) {
|
| 1653 | pr_debug_dtp("stack_offset=%#x type_offset=%#x\n",
|
| 1654 | fb_offset, type_offset);
|
| 1655 | } else {
|
| 1656 | pr_debug_dtp("type_offset=%#x\n", type_offset);
|
| 1657 | }
|
| 1658 |
|
| 1659 | if (!found || dloc->type_offset < type_offset ||
|
| 1660 | (dloc->type_offset == type_offset &&
|
| 1661 | !is_better_type(&mem_die, type_die))) {
|
| 1662 | *type_die = mem_die;
|
| 1663 | dloc->type_offset = type_offset;
|
| 1664 | found = true;
|
| 1665 | }
|
| 1666 |
|
| 1667 | pr_debug_location(&var_die, pc, reg);
|
| 1668 | pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
|
| 1669 | }
|
| 1670 |
|
| 1671 | if (!found && loc->multi_regs && reg == loc->reg1 && loc->reg1 != loc->reg2) {
|
| 1672 | reg = loc->reg2;
|
| 1673 | goto retry;
|
| 1674 | }
|
| 1675 |
|
| 1676 | if (!found && reg != DWARF_REG_PC) {
|
| 1677 | result = find_data_type_block(dloc, &cu_die, scopes,
|
| 1678 | nr_scopes, type_die);
|
| 1679 | if (result == PERF_TMR_OK) {
|
| 1680 | ann_data_stat.insn_track++;
|
| 1681 | found = true;
|
| 1682 | }
|
| 1683 | }
|
| 1684 |
|
| 1685 | out:
|
| 1686 | pr_debug_dtp("final result: ");
|
| 1687 | if (found) {
|
| 1688 | pr_debug_type_name(type_die, TSR_KIND_TYPE);
|
| 1689 | ret = 0;
|
| 1690 | } else {
|
| 1691 | switch (result) {
|
| 1692 | case PERF_TMR_NO_TYPE:
|
| 1693 | case PERF_TMR_NO_POINTER:
|
| 1694 | pr_debug_dtp("%s\n", match_result_str(result));
|
| 1695 | ann_data_stat.no_typeinfo++;
|
| 1696 | break;
|
| 1697 | case PERF_TMR_NO_SIZE:
|
| 1698 | pr_debug_dtp("%s\n", match_result_str(result));
|
| 1699 | ann_data_stat.invalid_size++;
|
| 1700 | break;
|
| 1701 | case PERF_TMR_BAD_OFFSET:
|
| 1702 | pr_debug_dtp("%s\n", match_result_str(result));
|
| 1703 | ann_data_stat.bad_offset++;
|
| 1704 | break;
|
| 1705 | case PERF_TMR_UNKNOWN:
|
| 1706 | case PERF_TMR_BAIL_OUT:
|
| 1707 | case PERF_TMR_OK:
|
| 1708 | default:
|
| 1709 | pr_debug_dtp("no variable found\n");
|
| 1710 | ann_data_stat.no_var++;
|
| 1711 | break;
|
| 1712 | }
|
| 1713 | ret = -1;
|
| 1714 | }
|
| 1715 |
|
| 1716 | free(scopes);
|
| 1717 | return ret;
|
| 1718 | }
|
| 1719 |
|
| 1720 | |
| 1721 | * find_data_type - Return a data type at the location
|
| 1722 | * @dloc: data location
|
| 1723 | *
|
| 1724 | * This functions searches the debug information of the binary to get the data
|
| 1725 | * type it accesses. The exact location is expressed by (ip, reg, offset)
|
| 1726 | * for pointer variables or (ip, addr) for global variables. Note that global
|
| 1727 | * variables might update the @dloc->type_offset after finding the start of the
|
| 1728 | * variable. If it cannot find a global variable by address, it tried to find
|
| 1729 | * a declaration of the variable using var_name. In that case, @dloc->offset
|
| 1730 | * won't be updated.
|
| 1731 | *
|
| 1732 | * It return %NULL if not found.
|
| 1733 |
|
| 1734 | struct annotated_data_type *find_data_type(struct data_loc_info *dloc)
|
| 1735 | {
|
| 1736 | struct dso *dso = map__dso(dloc->ms->map);
|
| 1737 | Dwarf_Die type_die;
|
| 1738 |
|
| 1739 | |
| 1740 | * The type offset is the same as instruction offset by default.
|
| 1741 | * But when finding a global variable, the offset won't be valid.
|
| 1742 |
|
| 1743 | dloc->type_offset = dloc->op->offset;
|
| 1744 |
|
| 1745 | dloc->fbreg = -1;
|
| 1746 |
|
| 1747 | if (find_data_type_die(dloc, &type_die) < 0)
|
| 1748 | return NULL;
|
| 1749 |
|
| 1750 | return dso__findnew_data_type(dso, &type_die);
|
| 1751 | }
|
| 1752 |
|
| 1753 | static int alloc_data_type_histograms(struct annotated_data_type *adt, int nr_entries)
|
| 1754 | {
|
| 1755 | int i;
|
| 1756 | size_t sz = sizeof(struct type_hist);
|
| 1757 |
|
| 1758 | sz += sizeof(struct type_hist_entry) * adt->self.size;
|
| 1759 |
|
| 1760 |
|
| 1761 | adt->histograms = calloc(nr_entries, sizeof(*adt->histograms));
|
| 1762 | if (adt->histograms == NULL)
|
| 1763 | return -ENOMEM;
|
| 1764 |
|
| 1765 | |
| 1766 | * Each histogram is allocated for the whole size of the type.
|
| 1767 | * TODO: Probably we can move the histogram to members.
|
| 1768 |
|
| 1769 | for (i = 0; i < nr_entries; i++) {
|
| 1770 | adt->histograms[i] = zalloc(sz);
|
| 1771 | if (adt->histograms[i] == NULL)
|
| 1772 | goto err;
|
| 1773 | }
|
| 1774 |
|
| 1775 | adt->nr_histograms = nr_entries;
|
| 1776 | return 0;
|
| 1777 |
|
| 1778 | err:
|
| 1779 | while (--i >= 0)
|
| 1780 | zfree(&(adt->histograms[i]));
|
| 1781 | zfree(&adt->histograms);
|
| 1782 | return -ENOMEM;
|
| 1783 | }
|
| 1784 |
|
| 1785 | static void delete_data_type_histograms(struct annotated_data_type *adt)
|
| 1786 | {
|
| 1787 | for (int i = 0; i < adt->nr_histograms; i++)
|
| 1788 | zfree(&(adt->histograms[i]));
|
| 1789 |
|
| 1790 | zfree(&adt->histograms);
|
| 1791 | adt->nr_histograms = 0;
|
| 1792 | }
|
| 1793 |
|
| 1794 | void annotated_data_type__tree_delete(struct rb_root *root)
|
| 1795 | {
|
| 1796 | struct annotated_data_type *pos;
|
| 1797 |
|
| 1798 | while (!RB_EMPTY_ROOT(root)) {
|
| 1799 | struct rb_node *node = rb_first(root);
|
| 1800 |
|
| 1801 | rb_erase(node, root);
|
| 1802 | pos = rb_entry(node, struct annotated_data_type, node);
|
| 1803 | delete_members(&pos->self);
|
| 1804 | delete_data_type_histograms(pos);
|
| 1805 | zfree(&pos->self.type_name);
|
| 1806 | free(pos);
|
| 1807 | }
|
| 1808 | }
|
| 1809 |
|
| 1810 | |
| 1811 | * annotated_data_type__update_samples - Update histogram
|
| 1812 | * @adt: Data type to update
|
| 1813 | * @evsel: Event to update
|
| 1814 | * @offset: Offset in the type
|
| 1815 | * @nr_samples: Number of samples at this offset
|
| 1816 | * @period: Event count at this offset
|
| 1817 | *
|
| 1818 | * This function updates type histogram at @ofs for @evsel. Samples are
|
| 1819 | * aggregated before calling this function so it can be called with more
|
| 1820 | * than one samples at a certain offset.
|
| 1821 |
|
| 1822 | int annotated_data_type__update_samples(struct annotated_data_type *adt,
|
| 1823 | struct evsel *evsel, int offset,
|
| 1824 | int nr_samples, u64 period)
|
| 1825 | {
|
| 1826 | struct type_hist *h;
|
| 1827 |
|
| 1828 | if (adt == NULL)
|
| 1829 | return 0;
|
| 1830 |
|
| 1831 | if (adt->histograms == NULL) {
|
| 1832 | int nr = evlist__nr_entries(evsel->evlist);
|
| 1833 |
|
| 1834 | if (alloc_data_type_histograms(adt, nr) < 0)
|
| 1835 | return -1;
|
| 1836 | }
|
| 1837 |
|
| 1838 | if (offset < 0 || offset >= adt->self.size)
|
| 1839 | return -1;
|
| 1840 |
|
| 1841 | h = adt->histograms[evsel->core.idx];
|
| 1842 |
|
| 1843 | h->nr_samples += nr_samples;
|
| 1844 | h->addr[offset].nr_samples += nr_samples;
|
| 1845 | h->period += period;
|
| 1846 | h->addr[offset].period += period;
|
| 1847 | return 0;
|
| 1848 | }
|
| 1849 |
|
| 1850 | static void print_annotated_data_header(struct hist_entry *he, struct evsel *evsel)
|
| 1851 | {
|
| 1852 | struct dso *dso = map__dso(he->ms.map);
|
| 1853 | int nr_members = 1;
|
| 1854 | int nr_samples = he->stat.nr_events;
|
| 1855 | int width = 7;
|
| 1856 | const char *val_hdr = "Percent";
|
| 1857 |
|
| 1858 | if (evsel__is_group_event(evsel)) {
|
| 1859 | struct hist_entry *pair;
|
| 1860 |
|
| 1861 | list_for_each_entry(pair, &he->pairs.head, pairs.node)
|
| 1862 | nr_samples += pair->stat.nr_events;
|
| 1863 | }
|
| 1864 |
|
| 1865 | printf("Annotate type: '%s' in %s (%d samples):\n",
|
| 1866 | he->mem_type->self.type_name, dso__name(dso), nr_samples);
|
| 1867 |
|
| 1868 | if (evsel__is_group_event(evsel)) {
|
| 1869 | struct evsel *pos;
|
| 1870 | int i = 0;
|
| 1871 |
|
| 1872 | nr_members = 0;
|
| 1873 | for_each_group_evsel(pos, evsel) {
|
| 1874 | if (symbol_conf.skip_empty &&
|
| 1875 | evsel__hists(pos)->stats.nr_samples == 0)
|
| 1876 | continue;
|
| 1877 |
|
| 1878 | printf(" event[%d] = %s\n", i++, pos->name);
|
| 1879 | nr_members++;
|
| 1880 | }
|
| 1881 | }
|
| 1882 |
|
| 1883 | if (symbol_conf.show_total_period) {
|
| 1884 | width = 11;
|
| 1885 | val_hdr = "Period";
|
| 1886 | } else if (symbol_conf.show_nr_samples) {
|
| 1887 | width = 7;
|
| 1888 | val_hdr = "Samples";
|
| 1889 | }
|
| 1890 |
|
| 1891 | printf("============================================================================\n");
|
| 1892 | printf("%*s %10s %10s %s\n", (width + 1) * nr_members, val_hdr,
|
| 1893 | "offset", "size", "field");
|
| 1894 | }
|
| 1895 |
|
| 1896 | static void print_annotated_data_value(struct type_hist *h, u64 period, int nr_samples)
|
| 1897 | {
|
| 1898 | double percent = h->period ? (100.0 * period / h->period) : 0;
|
| 1899 | const char *color = get_percent_color(percent);
|
| 1900 |
|
| 1901 | if (symbol_conf.show_total_period)
|
| 1902 | color_fprintf(stdout, color, " %11" PRIu64, period);
|
| 1903 | else if (symbol_conf.show_nr_samples)
|
| 1904 | color_fprintf(stdout, color, " %7d", nr_samples);
|
| 1905 | else
|
| 1906 | color_fprintf(stdout, color, " %7.2f", percent);
|
| 1907 | }
|
| 1908 |
|
| 1909 | static void print_annotated_data_type(struct annotated_data_type *mem_type,
|
| 1910 | struct annotated_member *member,
|
| 1911 | struct evsel *evsel, int indent)
|
| 1912 | {
|
| 1913 | struct annotated_member *child;
|
| 1914 | struct type_hist *h = mem_type->histograms[evsel->core.idx];
|
| 1915 | int i, nr_events = 0, samples = 0;
|
| 1916 | u64 period = 0;
|
| 1917 | int width = symbol_conf.show_total_period ? 11 : 7;
|
| 1918 | struct evsel *pos;
|
| 1919 |
|
| 1920 | for_each_group_evsel(pos, evsel) {
|
| 1921 | h = mem_type->histograms[pos->core.idx];
|
| 1922 |
|
| 1923 | if (symbol_conf.skip_empty &&
|
| 1924 | evsel__hists(pos)->stats.nr_samples == 0)
|
| 1925 | continue;
|
| 1926 |
|
| 1927 | samples = 0;
|
| 1928 | period = 0;
|
| 1929 | for (i = 0; i < member->size; i++) {
|
| 1930 | samples += h->addr[member->offset + i].nr_samples;
|
| 1931 | period += h->addr[member->offset + i].period;
|
| 1932 | }
|
| 1933 | print_annotated_data_value(h, period, samples);
|
| 1934 | nr_events++;
|
| 1935 | }
|
| 1936 |
|
| 1937 | printf(" %#10x %#10x %*s%s\t%s",
|
| 1938 | member->offset, member->size, indent, "", member->type_name,
|
| 1939 | member->var_name ?: "");
|
| 1940 |
|
| 1941 | if (!list_empty(&member->children))
|
| 1942 | printf(" {\n");
|
| 1943 |
|
| 1944 | list_for_each_entry(child, &member->children, node)
|
| 1945 | print_annotated_data_type(mem_type, child, evsel, indent + 4);
|
| 1946 |
|
| 1947 | if (!list_empty(&member->children))
|
| 1948 | printf("%*s}", (width + 1) * nr_events + 24 + indent, "");
|
| 1949 | printf(";\n");
|
| 1950 | }
|
| 1951 |
|
| 1952 | int hist_entry__annotate_data_tty(struct hist_entry *he, struct evsel *evsel)
|
| 1953 | {
|
| 1954 | print_annotated_data_header(he, evsel);
|
| 1955 | print_annotated_data_type(he->mem_type, &he->mem_type->self, evsel, 0);
|
| 1956 | printf("\n");
|
| 1957 |
|
| 1958 |
|
| 1959 | return '>';
|
| 1960 | }
|