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

perf dwarf-aux: Add die_get_cfa()

The die_get_cfa() is to get frame base register and offset at the given
instruction address (pc). This info will be used to locate stack
variables which have location expression using DW_OP_fbreg.

Reviewed-by: Ian Rogers <irogers@google.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Link: https://lore.kernel.org/r/20240117062657.985479-8-namhyung@kernel.org
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

+82 -1
+67 -1
tools/perf/util/dwarf-aux.c
··· 1407 1407 *offset = data.offset; 1408 1408 return result; 1409 1409 } 1410 - #endif 1410 + #endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */ 1411 + 1412 + #ifdef HAVE_DWARF_CFI_SUPPORT 1413 + static int reg_from_dwarf_op(Dwarf_Op *op) 1414 + { 1415 + switch (op->atom) { 1416 + case DW_OP_reg0 ... DW_OP_reg31: 1417 + return op->atom - DW_OP_reg0; 1418 + case DW_OP_breg0 ... DW_OP_breg31: 1419 + return op->atom - DW_OP_breg0; 1420 + case DW_OP_regx: 1421 + case DW_OP_bregx: 1422 + return op->number; 1423 + default: 1424 + break; 1425 + } 1426 + return -1; 1427 + } 1428 + 1429 + static int offset_from_dwarf_op(Dwarf_Op *op) 1430 + { 1431 + switch (op->atom) { 1432 + case DW_OP_reg0 ... DW_OP_reg31: 1433 + case DW_OP_regx: 1434 + return 0; 1435 + case DW_OP_breg0 ... DW_OP_breg31: 1436 + return op->number; 1437 + case DW_OP_bregx: 1438 + return op->number2; 1439 + default: 1440 + break; 1441 + } 1442 + return -1; 1443 + } 1444 + 1445 + /** 1446 + * die_get_cfa - Get frame base information 1447 + * @dwarf: a Dwarf info 1448 + * @pc: program address 1449 + * @preg: pointer for saved register 1450 + * @poffset: pointer for saved offset 1451 + * 1452 + * This function gets register and offset for CFA (Canonical Frame Address) 1453 + * by searching the CIE/FDE info. The CFA usually points to the start address 1454 + * of the current stack frame and local variables can be located using an offset 1455 + * from the CFA. The @preg and @poffset will be updated if it returns 0. 1456 + */ 1457 + int die_get_cfa(Dwarf *dwarf, u64 pc, int *preg, int *poffset) 1458 + { 1459 + Dwarf_CFI *cfi; 1460 + Dwarf_Frame *frame = NULL; 1461 + Dwarf_Op *ops = NULL; 1462 + size_t nops; 1463 + 1464 + cfi = dwarf_getcfi(dwarf); 1465 + if (cfi == NULL) 1466 + return -1; 1467 + 1468 + if (!dwarf_cfi_addrframe(cfi, pc, &frame) && 1469 + !dwarf_frame_cfa(frame, &ops, &nops) && nops == 1) { 1470 + *preg = reg_from_dwarf_op(ops); 1471 + *poffset = offset_from_dwarf_op(ops); 1472 + return 0; 1473 + } 1474 + return -1; 1475 + } 1476 + #endif /* HAVE_DWARF_CFI_SUPPORT */ 1411 1477 1412 1478 /* 1413 1479 * die_has_loclist - Check if DW_AT_location of @vr_die is a location list
+15
tools/perf/util/dwarf-aux.h
··· 177 177 178 178 #endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */ 179 179 180 + #ifdef HAVE_DWARF_CFI_SUPPORT 181 + 182 + /* Get the frame base information from CFA */ 183 + int die_get_cfa(Dwarf *dwarf, u64 pc, int *preg, int *poffset); 184 + 185 + #else /* HAVE_DWARF_CFI_SUPPORT */ 186 + 187 + static inline int die_get_cfa(Dwarf *dwarf __maybe_unused, u64 pc __maybe_unused, 188 + int *preg __maybe_unused, int *poffset __maybe_unused) 189 + { 190 + return -1; 191 + } 192 + 193 + #endif /* HAVE_DWARF_CFI_SUPPORT */ 194 + 180 195 #endif /* _DWARF_AUX_H */