Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,9 @@
perf-util-y += header.o
perf-util-y += mem-events.o
perf-util-y += pmu.o
perf-util-y += sym-handling.o
perf-util-y += evsel.o
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
perf-util-y += auxtrace.o
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0
/*
* VPA support
*/
#include <errno.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include "../../util/evlist.h"
#include "../../util/debug.h"
#include "../../util/auxtrace.h"
#include "../../util/powerpc-vpadtl.h"
#include "../../util/record.h"
#include <internal/lib.h> // page_size
#define KiB(x) ((x) * 1024)
static int
powerpc_vpadtl_recording_options(struct auxtrace_record *ar __maybe_unused,
struct evlist *evlist __maybe_unused,
struct record_opts *opts)
{
opts->full_auxtrace = true;
/*
* Set auxtrace_mmap_pages to minimum
* two pages
*/
if (!opts->auxtrace_mmap_pages) {
opts->auxtrace_mmap_pages = KiB(128) / page_size;
if (opts->mmap_pages == UINT_MAX)
opts->mmap_pages = KiB(256) / page_size;
}
return 0;
}
static size_t powerpc_vpadtl_info_priv_size(struct auxtrace_record *itr __maybe_unused,
struct evlist *evlist __maybe_unused)
{
return VPADTL_AUXTRACE_PRIV_SIZE;
}
static int
powerpc_vpadtl_info_fill(struct auxtrace_record *itr __maybe_unused,
struct perf_session *session __maybe_unused,
struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size __maybe_unused)
{
auxtrace_info->type = PERF_AUXTRACE_VPA_DTL;
return 0;
}
static void powerpc_vpadtl_free(struct auxtrace_record *itr)
{
free(itr);
}
static u64 powerpc_vpadtl_reference(struct auxtrace_record *itr __maybe_unused)
{
return 0;
}
struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int *err)
{
struct auxtrace_record *aux;
struct evsel *pos;
int found = 0;
/*
* Set err value to zero here. Any fail later
* will set appropriate return code to err.
*/
*err = 0;
evlist__for_each_entry(evlist, pos) {
if (strstarts(pos->name, "vpa_dtl")) {
found = 1;
pos->needs_auxtrace_mmap = true;
break;
}
}
if (!found)
return NULL;
/*
* To obtain the auxtrace buffer file descriptor, the auxtrace event
* must come first.
*/
evlist__to_front(pos->evlist, pos);
aux = zalloc(sizeof(*aux));
if (aux == NULL) {
pr_debug("aux record is NULL\n");
*err = -ENOMEM;
return NULL;
}
aux->recording_options = powerpc_vpadtl_recording_options;
aux->info_priv_size = powerpc_vpadtl_info_priv_size;
aux->info_fill = powerpc_vpadtl_info_fill;
aux->free = powerpc_vpadtl_free;
aux->reference = powerpc_vpadtl_reference;
return aux;
}
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include "util/evsel.h"
void arch_evsel__set_sample_weight(struct evsel *evsel)
{
evsel__set_sample_bit(evsel, WEIGHT_STRUCT);
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/stringify.h>
#include "header.h"
#include "utils_header.h"
#include "metricgroup.h"
#include <api/fs/fs.h>
#include <sys/auxv.h>
static bool is_compat_mode(void)
{
unsigned long base_platform = getauxval(AT_BASE_PLATFORM);
unsigned long platform = getauxval(AT_PLATFORM);
if (!strcmp((char *)platform, (char *)base_platform))
return false;
return true;
}
int
get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
{
unsigned long pvr;
int nb;
pvr = mfspr(SPRN_PVR);
nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
buffer[nb-1] = '\0';
return 0;
}
return ENOBUFS;
}
char *
get_cpuid_str(struct perf_cpu cpu __maybe_unused)
{
char *bufp;
unsigned long pvr;
/*
* IBM Power System supports compatible mode. That is
* Nth generation platform can support previous generation
* OS in a mode called compatibile mode. For ex. LPAR can be
* booted in a Power9 mode when the system is a Power10.
*
* In the compatible mode, care must be taken when generating
* PVR value. When read, PVR will be of the AT_BASE_PLATFORM
* To support generic events, return 0x00ffffff as pvr when
* booted in compat mode. Based on this pvr value, json will
* pick events from pmu-events/arch/powerpc/compat
*/
if (!is_compat_mode())
pvr = mfspr(SPRN_PVR);
else
pvr = 0x00ffffff;
if (asprintf(&bufp, "0x%.8lx", pvr) < 0)
bufp = NULL;
return bufp;
}
int arch_get_runtimeparam(const struct pmu_metric *pm)
{
int count;
char path[PATH_MAX] = "/devices/hv_24x7/interface/";
strcat(path, pm->aggr_mode == PerChip ? "sockets" : "coresperchip");
return sysfs__read_int(path, &count) < 0 ? 1 : count;
}
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "util/map_symbol.h"
#include "util/mem-events.h"
#include "mem-events.h"
#define E(t, n, s, l, a) { .tag = t, .name = n, .event_name = s, .ldlat = l, .aux_event = a }
struct perf_mem_event perf_mem_events_power[PERF_MEM_EVENTS__MAX] = {
E("ldlat-loads", "%s/mem-loads/", "mem-loads", false, 0),
E("ldlat-stores", "%s/mem-stores/", "mem-stores", false, 0),
E(NULL, NULL, NULL, false, 0),
};
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _POWER_MEM_EVENTS_H
#define _POWER_MEM_EVENTS_H
extern struct perf_mem_event perf_mem_events_power[PERF_MEM_EVENTS__MAX];
#endif /* _POWER_MEM_EVENTS_H */
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "../../../util/pmu.h"
#include "mem-events.h"
void perf_pmu__arch_init(struct perf_pmu *pmu)
{
if (pmu->is_core)
pmu->mem_events = perf_mem_events_power;
}
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Use DWARF Debug information to skip unnecessary callchain entries.
*
* Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation.
* Copyright (C) 2014 Ulrich Weigand, IBM Corporation.
*/
#include <inttypes.h>
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include "util/thread.h"
#include "util/callchain.h"
#include "util/debug.h"
#include "util/dso.h"
#include "util/event.h" // struct ip_callchain
#include "util/map.h"
#include "util/symbol.h"
/*
* When saving the callchain on Power, the kernel conservatively saves
* excess entries in the callchain. A few of these entries are needed
* in some cases but not others. If the unnecessary entries are not
* ignored, we end up with duplicate arcs in the call-graphs. Use
* DWARF debug information to skip over any unnecessary callchain
* entries.
*
* See function header for arch_adjust_callchain() below for more details.
*
* The libdwfl code in this file is based on code from elfutils
* (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
*/
/*
* Use the DWARF expression for the Call-frame-address and determine
* if return address is in LR and if a new frame was allocated.
*/
static int check_return_reg(int ra_regno, Dwarf_Frame *frame)
{
Dwarf_Op ops_mem[3];
Dwarf_Op dummy;
Dwarf_Op *ops = &dummy;
size_t nops;
int result;
result = dwarf_frame_register(frame, ra_regno, ops_mem, &ops, &nops);
if (result < 0) {
pr_debug("dwarf_frame_register() %s\n", dwarf_errmsg(-1));
return -1;
}
/*
* Check if return address is on the stack. If return address
* is in a register (typically R0), it is yet to be saved on
* the stack.
*/
if ((nops != 0 || ops != NULL) &&
!(nops == 1 && ops[0].atom == DW_OP_regx &&
ops[0].number2 == 0 && ops[0].offset == 0))
return 0;
/*
* Return address is in LR. Check if a frame was allocated
* but not-yet used.
*/
result = dwarf_frame_cfa(frame, &ops, &nops);
if (result < 0) {
pr_debug("dwarf_frame_cfa() returns %d, %s\n", result,
dwarf_errmsg(-1));
return -1;
}
/*
* If call frame address is in r1, no new frame was allocated.
*/
if (nops == 1 && ops[0].atom == DW_OP_bregx && ops[0].number == 1 &&
ops[0].number2 == 0)
return 1;
/*
* A new frame was allocated but has not yet been used.
*/
return 2;
}
/*
* Get the DWARF frame from the .eh_frame section.
*/
static Dwarf_Frame *get_eh_frame(Dwfl_Module *mod, Dwarf_Addr pc)
{
int result;
Dwarf_Addr bias;
Dwarf_CFI *cfi;
Dwarf_Frame *frame;
cfi = dwfl_module_eh_cfi(mod, &bias);
if (!cfi) {
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
result = dwarf_cfi_addrframe(cfi, pc-bias, &frame);
if (result) {
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
return frame;
}
/*
* Get the DWARF frame from the .debug_frame section.
*/
static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
{
Dwarf_CFI *cfi;
Dwarf_Addr bias;
Dwarf_Frame *frame;
int result;
cfi = dwfl_module_dwarf_cfi(mod, &bias);
if (!cfi) {
pr_debug("%s(): no CFI - %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
result = dwarf_cfi_addrframe(cfi, pc-bias, &frame);
if (result) {
pr_debug("%s(): %s\n", __func__, dwfl_errmsg(-1));
return NULL;
}
return frame;
}
/*
* Return:
* 0 if return address for the program counter @pc is on stack
* 1 if return address is in LR and no new stack frame was allocated
* 2 if return address is in LR and a new frame was allocated (but not
* yet used)
* -1 in case of errors
*/
static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc)
{
int rc = -1;
Dwfl *dwfl;
Dwfl_Module *mod;
Dwarf_Frame *frame;
int ra_regno;
Dwarf_Addr start = mapped_pc;
Dwarf_Addr end = mapped_pc;
bool signalp;
dwfl = dso__libdw_dwfl(dso);
if (!dwfl)
return -1;
mod = dwfl_addrmodule(dwfl, mapped_pc);
if (!mod) {
pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
goto out;
}
/*
* To work with split debug info files (eg: glibc), check both
* .eh_frame and .debug_frame sections of the ELF header.
*/
frame = get_eh_frame(mod, mapped_pc);
if (!frame) {
frame = get_dwarf_frame(mod, mapped_pc);
if (!frame)
goto out;
}
ra_regno = dwarf_frame_info(frame, &start, &end, &signalp);
if (ra_regno < 0) {
pr_debug("Return address register unavailable: %s\n",
dwarf_errmsg(-1));
goto out;
}
rc = check_return_reg(ra_regno, frame);
out:
return rc;
}
/*
* The callchain saved by the kernel always includes the link register (LR).
*
* 0: PERF_CONTEXT_USER
* 1: Program counter (Next instruction pointer)
* 2: LR value
* 3: Caller's caller
* 4: ...
*
* The value in LR is only needed when it holds a return address. If the
* return address is on the stack, we should ignore the LR value.
*
* Further, when the return address is in the LR, if a new frame was just
* allocated but the LR was not saved into it, then the LR contains the
* caller, slot 4: contains the caller's caller and the contents of slot 3:
* (chain->ips[3]) is undefined and must be ignored.
*
* Use DWARF debug information to determine if any entries need to be skipped.
*
* Return:
* index: of callchain entry that needs to be ignored (if any)
* -1 if no entry needs to be ignored or in case of errors
*/
int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
{
struct addr_location al;
struct dso *dso = NULL;
int rc;
u64 ip;
u64 skip_slot = -1;
if (!chain || chain->nr < 3)
return skip_slot;
addr_location__init(&al);
ip = chain->ips[1];
thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
if (al.map)
dso = map__dso(al.map);
if (!dso) {
pr_debug("%" PRIx64 " dso is NULL\n", ip);
addr_location__exit(&al);
return skip_slot;
}
rc = check_return_addr(dso, map__map_ip(al.map, ip));
pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
dso__long_name(dso), al.sym->name, ip, rc);
if (rc == 0) {
/*
* Return address on stack. Ignore LR value in callchain
*/
skip_slot = 2;
} else if (rc == 2) {
/*
* New frame allocated but return address still in LR.
* Ignore the caller's caller entry in callchain.
*/
skip_slot = 3;
}
addr_location__exit(&al);
return skip_slot;
}
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Copyright (C) 2015 Naveen N. Rao, IBM Corporation
*/
#include "dso.h"
#include "symbol.h"
#include "map.h"
#include "probe-event.h"
#include "probe-file.h"
int arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
char *sym = syma->name;
#if !defined(_CALL_ELF) || _CALL_ELF != 2
/* Skip over any initial dot */
if (*sym == '.')
sym++;
#endif
/* Avoid "SyS" kernel syscall aliases */
if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3))
return SYMBOL_B;
if (strlen(sym) >= 10 && !strncmp(sym, "compat_SyS", 10))
return SYMBOL_B;
return SYMBOL_A;
}
#if !defined(_CALL_ELF) || _CALL_ELF != 2
/* Allow matching against dot variants */
int arch__compare_symbol_names(const char *namea, const char *nameb)
{
/* Skip over initial dot */
if (*namea == '.')
namea++;
if (*nameb == '.')
nameb++;
return strcmp(namea, nameb);
}
int arch__compare_symbol_names_n(const char *namea, const char *nameb,
unsigned int n)
{
/* Skip over initial dot */
if (*namea == '.')
namea++;
if (*nameb == '.')
nameb++;
return strncmp(namea, nameb, n);
}
const char *arch__normalize_symbol_name(const char *name)
{
/* Skip over initial dot */
if (name && *name == '.')
name++;
return name;
}
#endif
#if defined(_CALL_ELF) && _CALL_ELF == 2
#ifdef HAVE_LIBELF_SUPPORT
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
{
s->arch_sym = sym->st_other;
}
#endif
#define PPC64LE_LEP_OFFSET 8
void arch__fix_tev_from_maps(struct perf_probe_event *pev,
struct probe_trace_event *tev, struct map *map,
struct symbol *sym)
{
int lep_offset;
/*
* When probing at a function entry point, we normally always want the
* LEP since that catches calls to the function through both the GEP and
* the LEP. Hence, we would like to probe at an offset of 8 bytes if
* the user only specified the function entry.
*
* However, if the user specifies an offset, we fall back to using the
* GEP since all userspace applications (objdump/readelf) show function
* disassembly with offsets from the GEP.
*/
if (pev->point.offset || !map || !sym)
return;
/* For kretprobes, add an offset only if the kernel supports it */
if (!pev->uprobes && pev->point.retprobe) {
#ifdef HAVE_LIBELF_SUPPORT
if (!kretprobe_offset_is_supported())
#endif
return;
}
lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
if (map__dso(map)->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
tev->point.offset += PPC64LE_LEP_OFFSET;
else if (lep_offset) {
if (pev->uprobes)
tev->point.address += lep_offset;
else
tev->point.offset += lep_offset;
}
}
#ifdef HAVE_LIBELF_SUPPORT
void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
int ntevs)
{
struct probe_trace_event *tev;
struct map *map;
struct symbol *sym = NULL;
struct rb_node *tmp;
int i = 0;
map = get_target_map(pev->target, pev->nsi, pev->uprobes);
if (!map || map__load(map) < 0)
return;
for (i = 0; i < ntevs; i++) {
tev = &pev->tevs[i];
map__for_each_symbol(map, sym, tmp) {
if (map__unmap_ip(map, sym->start) == tev->point.address) {
arch__fix_tev_from_maps(pev, tev, map, sym);
break;
}
}
}
}
#endif /* HAVE_LIBELF_SUPPORT */
#endif
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_UTIL_HEADER_H
#define __PERF_UTIL_HEADER_H
#include <linux/stringify.h>
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define SPRN_PVR 0x11F /* Processor Version Register */
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revision field */
#endif /* __PERF_UTIL_HEADER_H */