Commit b7dbc2e8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull probes updates from Masami Hiramatsu:
 "Stack usage reduction for probe events:
   - Allocate string buffers from the heap for uprobe, eprobe, kprobe,
     and fprobe events to avoid stack overflow
   - Allocate traceprobe_parse_context from the heap to prevent
     potential stack overflow
   - Fix a typo in the above commit

  New features for eprobe and tprobe events:
   - Add support for arrays in eprobes
   - Support multiple tprobes on the same tracepoint

  Improve efficiency:
   - Register fprobe-events only when it is enabled to reduce overhead
   - Register tracepoints for tprobe events only when enabled to resolve
     a lock dependency

  Code Cleanup:
   - Add kerneldoc for traceprobe_parse_event_name() and
     __get_insn_slot()
   - Sort #include alphabetically in the probes code
   - Remove the unused 'mod' field from the tprobe-event
   - Clean up the entry-arg storing code in probe-events

  Selftest update
   - Enable fprobe events before checking enable_functions in selftests"

* tag 'probes-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tracing: trace_fprobe: Fix typo of the semicolon
  tracing: Have eprobes handle arrays
  tracing: probes: Add a kerneldoc for traceprobe_parse_event_name()
  tracing: uprobe-event: Allocate string buffers from heap
  tracing: eprobe-event: Allocate string buffers from heap
  tracing: kprobe-event: Allocate string buffers from heap
  tracing: fprobe-event: Allocate string buffers from heap
  tracing: probe: Allocate traceprobe_parse_context from heap
  tracing: probes: Sort #include alphabetically
  kprobes: Add missing kerneldoc for __get_insn_slot
  tracing: tprobe-events: Register tracepoint when enable tprobe event
  selftests: tracing: Enable fprobe events before checking enable_functions
  tracing: fprobe-events: Register fprobe-events only when it is enabled
  tracing: tprobe-events: Support multiple tprobes on the same tracepoint
  tracing: tprobe-events: Remove mod field from tprobe-event
  tracing: probe-events: Cleanup entry-arg storing code
parents a03eec74 133c302a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num);
int register_fprobe_syms(struct fprobe *fp, const char **syms, int num);
int unregister_fprobe(struct fprobe *fp);
bool fprobe_is_registered(struct fprobe *fp);
int fprobe_count_ips_from_filter(const char *filter, const char *notfilter);
#else
static inline int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
{
@@ -115,6 +116,10 @@ static inline bool fprobe_is_registered(struct fprobe *fp)
{
	return false;
}
static inline int fprobe_count_ips_from_filter(const char *filter, const char *notfilter)
{
	return -EOPNOTSUPP;
}
#endif

/**
+4 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/buildid.h>
#include <linux/compiler.h>
#include <linux/cache.h>
#include <linux/cleanup.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/elf.h>
@@ -1018,4 +1019,7 @@ static inline unsigned long find_kallsyms_symbol_value(struct module *mod,

#endif  /* CONFIG_MODULES && CONFIG_KALLSYMS */

/* Define __free(module_put) macro for struct module *. */
DEFINE_FREE(module_put, struct module *, if (_T) module_put(_T))

#endif /* _LINUX_MODULE_H */
+6 −2
Original line number Diff line number Diff line
@@ -135,8 +135,12 @@ struct kprobe_insn_cache kprobe_insn_slots = {
static int collect_garbage_slots(struct kprobe_insn_cache *c);

/**
 * __get_insn_slot() - Find a slot on an executable page for an instruction.
 * We allocate an executable page if there's no room on existing ones.
 * __get_insn_slot - Find a slot on an executable page for an instruction.
 * @c: Pointer to kprobe instruction cache
 *
 * Description: Locates available slot on existing executable pages,
 *              allocates an executable page if there's no room on existing ones.
 * Return: Pointer to instruction slot on success, NULL on failure.
 */
kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
{
+5 −0
Original line number Diff line number Diff line
@@ -648,6 +648,11 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)

#define FPROBE_IPS_MAX	INT_MAX

int fprobe_count_ips_from_filter(const char *filter, const char *notfilter)
{
	return get_ips_from_filter(filter, notfilter, NULL, NULL, FPROBE_IPS_MAX);
}

/**
 * register_fprobe() - Register fprobe to ftrace by pattern.
 * @fp: A fprobe data structure to be registered.
+37 −16
Original line number Diff line number Diff line
@@ -9,14 +9,15 @@
 * Copyright (C) 2021, VMware Inc, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
 *
 */
#include <linux/cleanup.h>
#include <linux/ftrace.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ftrace.h>

#include "trace_dynevent.h"
#include "trace_probe.h"
#include "trace_probe_tmpl.h"
#include "trace_probe_kernel.h"
#include "trace_probe_tmpl.h"

#define EPROBE_EVENT_SYSTEM "eprobes"

@@ -343,12 +344,17 @@ get_event_field(struct fetch_insn *code, void *rec)
			val = *(unsigned int *)addr;
		break;
	default:
		if (field->size == sizeof(long)) {
			if (field->is_signed)
				val = *(long *)addr;
			else
				val = *(unsigned long *)addr;
			break;
		}
		/* This is an array, point to the addr itself */
		val = (unsigned long)addr;
		break;
	}
	return val;
}

@@ -797,18 +803,20 @@ find_and_get_event(const char *system, const char *event_name)

static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[], int i)
{
	struct traceprobe_parse_context ctx = {
		.event = ep->event,
		.flags = TPARG_FL_KERNEL | TPARG_FL_TEVENT,
	};
	struct traceprobe_parse_context *ctx __free(traceprobe_parse_context) = NULL;
	int ret;

	ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], &ctx);
	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;
	ctx->event = ep->event;
	ctx->flags = TPARG_FL_KERNEL | TPARG_FL_TEVENT;

	ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], ctx);
	/* Handle symbols "@" */
	if (!ret)
		ret = traceprobe_update_arg(&ep->tp.args[i]);

	traceprobe_finish_parse(&ctx);
	return ret;
}

@@ -869,10 +877,10 @@ static int __trace_eprobe_create(int argc, const char *argv[])
	const char *event = NULL, *group = EPROBE_EVENT_SYSTEM;
	const char *sys_event = NULL, *sys_name = NULL;
	struct trace_event_call *event_call;
	char *buf1 __free(kfree) = NULL;
	char *buf2 __free(kfree) = NULL;
	char *gbuf __free(kfree) = NULL;
	struct trace_eprobe *ep = NULL;
	char buf1[MAX_EVENT_NAME_LEN];
	char buf2[MAX_EVENT_NAME_LEN];
	char gbuf[MAX_EVENT_NAME_LEN];
	int ret = 0, filter_idx = 0;
	int i, filter_cnt;

@@ -883,6 +891,9 @@ static int __trace_eprobe_create(int argc, const char *argv[])

	event = strchr(&argv[0][1], ':');
	if (event) {
		gbuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
		if (!gbuf)
			goto mem_error;
		event++;
		ret = traceprobe_parse_event_name(&event, &group, gbuf,
						  event - argv[0]);
@@ -892,6 +903,11 @@ static int __trace_eprobe_create(int argc, const char *argv[])

	trace_probe_log_set_index(1);
	sys_event = argv[1];

	buf2 = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
	if (!buf2)
		goto mem_error;

	ret = traceprobe_parse_event_name(&sys_event, &sys_name, buf2, 0);
	if (ret || !sys_event || !sys_name) {
		trace_probe_log_err(0, NO_EVENT_INFO);
@@ -899,7 +915,9 @@ static int __trace_eprobe_create(int argc, const char *argv[])
	}

	if (!event) {
		strscpy(buf1, sys_event, MAX_EVENT_NAME_LEN);
		buf1 = kstrdup(sys_event, GFP_KERNEL);
		if (!buf1)
			goto mem_error;
		event = buf1;
	}

@@ -972,6 +990,9 @@ static int __trace_eprobe_create(int argc, const char *argv[])
	trace_probe_log_clear();
	return ret;

mem_error:
	ret = -ENOMEM;
	goto error;
parse_error:
	ret = -EINVAL;
error:
Loading