1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

perf-tools fixes for 6.14

An early round of random fixes in perf tools for this cycle.
 
 perf trace
 ----------
 * Fix loading of BPF program on certain clang versions
 * Fix out-of-bound access in syscalls with 6 arguments
 * Skip syscall enum test if landlock syscall is not available
 
 perf annotate
 -------------
 * Fix segfaults due to invalid access in disasm arrays
 
 perf stat
 ---------
 * Fix error handling in topology parsing
 
 Signed-off-by: Namhyung Kim <namhyung@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSo2x5BnqMqsoHtzsmMstVUGiXMgwUCZ5vx+gAKCRCMstVUGiXM
 g8PpAP9fNWvkxEiylqO9GGqMJWnIwWwlz4NCqqOZWyPspcECrgD9Eu0lZlna4tOL
 3I8giYN2m7ogNt+ZXP2b0y2np7hOGQc=
 =lVVJ
 -----END PGP SIGNATURE-----

Merge tag 'perf-tools-fixes-for-v6.14-2025-01-30' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools

Pull perf tools fixes from Namhyung Kim:
 "An early round of random fixes in perf tools for this cycle.

  perf trace:
   - Fix loading of BPF program on certain clang versions
   - Fix out-of-bound access in syscalls with 6 arguments
   - Skip syscall enum test if landlock syscall is not available

  perf annotate:
   - Fix segfaults due to invalid access in disasm arrays

  perf stat:
   - Fix error handling in topology parsing"

* tag 'perf-tools-fixes-for-v6.14-2025-01-30' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools:
  perf cpumap: Fix die and cluster IDs
  perf test: Skip syscall enum test if no landlock syscall
  perf trace: Fix runtime error of index out of bounds
  perf annotate: Use an array for the disassembler preference
  perf trace: Fix BPF loading failure (-E2BIG)
This commit is contained in:
Linus Torvalds 2025-01-30 17:38:20 -08:00
commit c06310fd6b
7 changed files with 113 additions and 90 deletions

View file

@ -2107,8 +2107,12 @@ static int trace__read_syscall_info(struct trace *trace, int id)
return PTR_ERR(sc->tp_format);
}
/*
* The tracepoint format contains __syscall_nr field, so it's one more
* than the actual number of syscall arguments.
*/
if (syscall__alloc_arg_fmts(sc, IS_ERR(sc->tp_format) ?
RAW_SYSCALL_ARGS_NUM : sc->tp_format->format.nr_fields))
RAW_SYSCALL_ARGS_NUM : sc->tp_format->format.nr_fields - 1))
return -ENOMEM;
sc->args = sc->tp_format->format.fields;

View file

@ -26,8 +26,12 @@ check_vmlinux() {
trace_landlock() {
echo "Tracing syscall ${syscall}"
# test flight just to see if landlock_add_rule and libbpf are available
$TESTPROG
# test flight just to see if landlock_add_rule is available
if ! perf trace $TESTPROG 2>&1 | grep -q landlock
then
echo "No landlock system call found, skipping to non-syscall tracing."
return
fi
if perf trace -e $syscall $TESTPROG 2>&1 | \
grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -1.*"

View file

@ -2100,6 +2100,57 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
return 0;
}
const char * const perf_disassembler__strs[] = {
[PERF_DISASM_UNKNOWN] = "unknown",
[PERF_DISASM_LLVM] = "llvm",
[PERF_DISASM_CAPSTONE] = "capstone",
[PERF_DISASM_OBJDUMP] = "objdump",
};
static void annotation_options__add_disassembler(struct annotation_options *options,
enum perf_disassembler dis)
{
for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers); i++) {
if (options->disassemblers[i] == dis) {
/* Disassembler is already present then don't add again. */
return;
}
if (options->disassemblers[i] == PERF_DISASM_UNKNOWN) {
/* Found a free slot. */
options->disassemblers[i] = dis;
return;
}
}
pr_err("Failed to add disassembler %d\n", dis);
}
static int annotation_options__add_disassemblers_str(struct annotation_options *options,
const char *str)
{
while (str && *str != '\0') {
const char *comma = strchr(str, ',');
int len = comma ? comma - str : (int)strlen(str);
bool match = false;
for (u8 i = 0; i < ARRAY_SIZE(perf_disassembler__strs); i++) {
const char *dis_str = perf_disassembler__strs[i];
if (len == (int)strlen(dis_str) && !strncmp(str, dis_str, len)) {
annotation_options__add_disassembler(options, i);
match = true;
break;
}
}
if (!match) {
pr_err("Invalid disassembler '%.*s'\n", len, str);
return -1;
}
str = comma ? comma + 1 : NULL;
}
return 0;
}
static int annotation__config(const char *var, const char *value, void *data)
{
struct annotation_options *opt = data;
@ -2115,11 +2166,10 @@ static int annotation__config(const char *var, const char *value, void *data)
else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL)
opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
} else if (!strcmp(var, "annotate.disassemblers")) {
opt->disassemblers_str = strdup(value);
if (!opt->disassemblers_str) {
pr_err("Not enough memory for annotate.disassemblers\n");
return -1;
}
int err = annotation_options__add_disassemblers_str(opt, value);
if (err)
return err;
} else if (!strcmp(var, "annotate.hide_src_code")) {
opt->hide_src_code = perf_config_bool("hide_src_code", value);
} else if (!strcmp(var, "annotate.jump_arrows")) {
@ -2185,9 +2235,25 @@ void annotation_options__exit(void)
zfree(&annotate_opts.objdump_path);
}
static void annotation_options__default_init_disassemblers(struct annotation_options *options)
{
if (options->disassemblers[0] != PERF_DISASM_UNKNOWN) {
/* Already initialized. */
return;
}
#ifdef HAVE_LIBLLVM_SUPPORT
annotation_options__add_disassembler(options, PERF_DISASM_LLVM);
#endif
#ifdef HAVE_LIBCAPSTONE_SUPPORT
annotation_options__add_disassembler(options, PERF_DISASM_CAPSTONE);
#endif
annotation_options__add_disassembler(options, PERF_DISASM_OBJDUMP);
}
void annotation_config__init(void)
{
perf_config(annotation__config, &annotate_opts);
annotation_options__default_init_disassemblers(&annotate_opts);
}
static unsigned int parse_percent_type(char *str1, char *str2)

View file

@ -34,8 +34,13 @@ struct annotated_data_type;
#define ANNOTATION__BR_CNTR_WIDTH 30
#define ANNOTATION_DUMMY_LEN 256
// llvm, capstone, objdump
#define MAX_DISASSEMBLERS 3
enum perf_disassembler {
PERF_DISASM_UNKNOWN = 0,
PERF_DISASM_LLVM,
PERF_DISASM_CAPSTONE,
PERF_DISASM_OBJDUMP,
};
#define MAX_DISASSEMBLERS (PERF_DISASM_OBJDUMP + 1)
struct annotation_options {
bool hide_src_code,
@ -52,14 +57,12 @@ struct annotation_options {
annotate_src,
full_addr;
u8 offset_level;
u8 nr_disassemblers;
u8 disassemblers[MAX_DISASSEMBLERS];
int min_pcnt;
int max_lines;
int context;
char *objdump_path;
char *disassembler_style;
const char *disassemblers_str;
const char *disassemblers[MAX_DISASSEMBLERS];
const char *prefix;
const char *prefix_strip;
unsigned int percent_type;
@ -134,6 +137,8 @@ struct disasm_line {
struct annotation_line al;
};
extern const char * const perf_disassembler__strs[];
void annotation_line__add(struct annotation_line *al, struct list_head *head);
static inline double annotation_data__percent(struct annotation_data *data,

View file

@ -431,9 +431,9 @@ static bool pid_filter__has(struct pids_filtered *pids, pid_t pid)
static int augment_sys_enter(void *ctx, struct syscall_enter_args *args)
{
bool augmented, do_output = false;
int zero = 0, size, aug_size, index,
value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value);
int zero = 0, index, value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value);
u64 output = 0; /* has to be u64, otherwise it won't pass the verifier */
s64 aug_size, size;
unsigned int nr, *beauty_map;
struct beauty_payload_enter *payload;
void *arg, *payload_offset;
@ -484,14 +484,11 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args)
} else if (size > 0 && size <= value_size) { /* struct */
if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, size, arg))
augmented = true;
} else if (size < 0 && size >= -6) { /* buffer */
} else if ((int)size < 0 && size >= -6) { /* buffer */
index = -(size + 1);
barrier_var(index); // Prevent clang (noticed with v18) from removing the &= 7 trick.
index &= 7; // Satisfy the bounds checking with the verifier in some kernels.
aug_size = args->args[index];
if (aug_size > TRACE_AUG_MAX_BUF)
aug_size = TRACE_AUG_MAX_BUF;
aug_size = args->args[index] > TRACE_AUG_MAX_BUF ? TRACE_AUG_MAX_BUF : args->args[index];
if (aug_size > 0) {
if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, aug_size, arg))

View file

@ -293,7 +293,7 @@ struct aggr_cpu_id aggr_cpu_id__die(struct perf_cpu cpu, void *data)
die = cpu__get_die_id(cpu);
/* There is no die_id on legacy system. */
if (die == -1)
if (die < 0)
die = 0;
/*
@ -322,7 +322,7 @@ struct aggr_cpu_id aggr_cpu_id__cluster(struct perf_cpu cpu, void *data)
struct aggr_cpu_id id;
/* There is no cluster_id on legacy system. */
if (cluster == -1)
if (cluster < 0)
cluster = 0;
id = aggr_cpu_id__die(cpu, data);

View file

@ -2216,56 +2216,6 @@ out_free_command:
return err;
}
static int annotation_options__init_disassemblers(struct annotation_options *options)
{
char *disassembler;
if (options->disassemblers_str == NULL) {
const char *default_disassemblers_str =
#ifdef HAVE_LIBLLVM_SUPPORT
"llvm,"
#endif
#ifdef HAVE_LIBCAPSTONE_SUPPORT
"capstone,"
#endif
"objdump";
options->disassemblers_str = strdup(default_disassemblers_str);
if (!options->disassemblers_str)
goto out_enomem;
}
disassembler = strdup(options->disassemblers_str);
if (disassembler == NULL)
goto out_enomem;
while (1) {
char *comma = strchr(disassembler, ',');
if (comma != NULL)
*comma = '\0';
options->disassemblers[options->nr_disassemblers++] = strim(disassembler);
if (comma == NULL)
break;
disassembler = comma + 1;
if (options->nr_disassemblers >= MAX_DISASSEMBLERS) {
pr_debug("annotate.disassemblers can have at most %d entries, ignoring \"%s\"\n",
MAX_DISASSEMBLERS, disassembler);
break;
}
}
return 0;
out_enomem:
pr_err("Not enough memory for annotate.disassemblers\n");
return -1;
}
int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
struct annotation_options *options = args->options;
@ -2274,7 +2224,6 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
char symfs_filename[PATH_MAX];
bool delete_extract = false;
struct kcore_extract kce;
const char *disassembler;
bool decomp = false;
int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
@ -2334,28 +2283,26 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
}
}
err = annotation_options__init_disassemblers(options);
if (err)
goto out_remove_tmp;
err = -1;
for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
enum perf_disassembler dis = options->disassemblers[i];
for (int i = 0; i < options->nr_disassemblers && err != 0; ++i) {
disassembler = options->disassemblers[i];
if (!strcmp(disassembler, "llvm"))
switch (dis) {
case PERF_DISASM_LLVM:
err = symbol__disassemble_llvm(symfs_filename, sym, args);
else if (!strcmp(disassembler, "capstone"))
break;
case PERF_DISASM_CAPSTONE:
err = symbol__disassemble_capstone(symfs_filename, sym, args);
else if (!strcmp(disassembler, "objdump"))
break;
case PERF_DISASM_OBJDUMP:
err = symbol__disassemble_objdump(symfs_filename, sym, args);
else
pr_debug("Unknown disassembler %s, skipping...\n", disassembler);
}
if (err == 0) {
pr_debug("Disassembled with %s\nannotate.disassemblers=%s\n",
disassembler, options->disassemblers_str);
break;
case PERF_DISASM_UNKNOWN: /* End of disassemblers. */
default:
goto out_remove_tmp;
}
if (err == 0)
pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
}
out_remove_tmp:
if (decomp)