Commit c06310fd authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'perf-tools-fixes-for-v6.14-2025-01-30' of...

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)
parents e20f2bb8 9fae5884
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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;
+6 −2
Original line number Diff line number Diff line
@@ -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.*"
+71 −5
Original line number Diff line number Diff line
@@ -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)
+10 −5
Original line number Diff line number Diff line
@@ -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,
+4 −7
Original line number Diff line number Diff line
@@ -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))
Loading