mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Probes updates for v6.17:
- 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.
-----BEGIN PGP SIGNATURE-----
iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmiJ2DQbHG1hc2FtaS5o
aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8bSfkH/06Zn5I55rU85FKSBQll
FN4hipmef/9Nd13skDwpEuFyzLPNS4P1up/UBUuyDQUTlO74+t2zSFO2dpcNrWmu
sPTenQ+6h82H3K591WTIC23VzF54syIbFLXEj8iMBALT3wyU4Nn0bs4DCbnTo5HX
R3NVo77rk6wxNJoKYOtT6ALf/lHonuNlGF+KTUGWP8UbWsIY3fIp0RWWy572M0bt
+YBE8D8RIVrw+ZY+vNKn1LdZdWlR1ton518XDf1gV9isTCfKErcd/6HJKwuj5q2v
qMgwiaKK+Gne/ylAKmWLEg2oNDo7kpyfW+612oiECitgZkqxOXhyYYfWgRt1lFNp
Wb8=
=E+Z6
-----END PGP SIGNATURE-----
Merge tag 'probes-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
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
This commit is contained in:
commit
b7dbc2e813
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,10 +344,15 @@ get_event_field(struct fetch_insn *code, void *rec)
|
|||
val = *(unsigned int *)addr;
|
||||
break;
|
||||
default:
|
||||
if (field->is_signed)
|
||||
val = *(long *)addr;
|
||||
else
|
||||
val = *(unsigned long *)addr;
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -4,15 +4,18 @@
|
|||
* Copyright (C) 2022 Google LLC.
|
||||
*/
|
||||
#define pr_fmt(fmt) "trace_fprobe: " fmt
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#include <linux/fprobe.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#include "trace_dynevent.h"
|
||||
#include "trace_probe.h"
|
||||
#include "trace_probe_kernel.h"
|
||||
|
|
@ -21,7 +24,6 @@
|
|||
#define FPROBE_EVENT_SYSTEM "fprobes"
|
||||
#define TRACEPOINT_EVENT_SYSTEM "tracepoints"
|
||||
#define RETHOOK_MAXACTIVE_MAX 4096
|
||||
#define TRACEPOINT_STUB ERR_PTR(-ENOENT)
|
||||
|
||||
static int trace_fprobe_create(const char *raw_command);
|
||||
static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev);
|
||||
|
|
@ -38,15 +40,156 @@ static struct dyn_event_operations trace_fprobe_ops = {
|
|||
.match = trace_fprobe_match,
|
||||
};
|
||||
|
||||
/* List of tracepoint_user */
|
||||
static LIST_HEAD(tracepoint_user_list);
|
||||
static DEFINE_MUTEX(tracepoint_user_mutex);
|
||||
|
||||
/* While living tracepoint_user, @tpoint can be NULL and @refcount != 0. */
|
||||
struct tracepoint_user {
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
struct tracepoint *tpoint;
|
||||
unsigned int refcount;
|
||||
};
|
||||
|
||||
/* NOTE: you must lock tracepoint_user_mutex. */
|
||||
#define for_each_tracepoint_user(tuser) \
|
||||
list_for_each_entry(tuser, &tracepoint_user_list, list)
|
||||
|
||||
static int tracepoint_user_register(struct tracepoint_user *tuser)
|
||||
{
|
||||
struct tracepoint *tpoint = tuser->tpoint;
|
||||
|
||||
if (!tpoint)
|
||||
return 0;
|
||||
|
||||
return tracepoint_probe_register_prio_may_exist(tpoint,
|
||||
tpoint->probestub, NULL, 0);
|
||||
}
|
||||
|
||||
static void tracepoint_user_unregister(struct tracepoint_user *tuser)
|
||||
{
|
||||
if (!tuser->tpoint)
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(tracepoint_probe_unregister(tuser->tpoint, tuser->tpoint->probestub, NULL));
|
||||
tuser->tpoint = NULL;
|
||||
}
|
||||
|
||||
static unsigned long tracepoint_user_ip(struct tracepoint_user *tuser)
|
||||
{
|
||||
if (!tuser->tpoint)
|
||||
return 0UL;
|
||||
|
||||
return (unsigned long)tuser->tpoint->probestub;
|
||||
}
|
||||
|
||||
static void __tracepoint_user_free(struct tracepoint_user *tuser)
|
||||
{
|
||||
if (!tuser)
|
||||
return;
|
||||
kfree(tuser->name);
|
||||
kfree(tuser);
|
||||
}
|
||||
|
||||
DEFINE_FREE(tuser_free, struct tracepoint_user *, __tracepoint_user_free(_T))
|
||||
|
||||
static struct tracepoint_user *__tracepoint_user_init(const char *name, struct tracepoint *tpoint)
|
||||
{
|
||||
struct tracepoint_user *tuser __free(tuser_free) = NULL;
|
||||
int ret;
|
||||
|
||||
tuser = kzalloc(sizeof(*tuser), GFP_KERNEL);
|
||||
if (!tuser)
|
||||
return NULL;
|
||||
tuser->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!tuser->name)
|
||||
return NULL;
|
||||
|
||||
if (tpoint) {
|
||||
ret = tracepoint_user_register(tuser);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
tuser->tpoint = tpoint;
|
||||
tuser->refcount = 1;
|
||||
INIT_LIST_HEAD(&tuser->list);
|
||||
list_add(&tuser->list, &tracepoint_user_list);
|
||||
|
||||
return_ptr(tuser);
|
||||
}
|
||||
|
||||
static struct tracepoint *find_tracepoint(const char *tp_name,
|
||||
struct module **tp_mod);
|
||||
|
||||
/*
|
||||
* Get tracepoint_user if exist, or allocate new one and register it.
|
||||
* If tracepoint is on a module, get its refcounter too.
|
||||
* This returns errno or NULL (not loaded yet) or tracepoint_user.
|
||||
*/
|
||||
static struct tracepoint_user *tracepoint_user_find_get(const char *name, struct module **pmod)
|
||||
{
|
||||
struct module *mod __free(module_put) = NULL;
|
||||
struct tracepoint_user *tuser;
|
||||
struct tracepoint *tpoint;
|
||||
|
||||
if (!name || !pmod)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Get and lock the module which has tracepoint. */
|
||||
tpoint = find_tracepoint(name, &mod);
|
||||
|
||||
guard(mutex)(&tracepoint_user_mutex);
|
||||
/* Search existing tracepoint_user */
|
||||
for_each_tracepoint_user(tuser) {
|
||||
if (!strcmp(tuser->name, name)) {
|
||||
tuser->refcount++;
|
||||
*pmod = no_free_ptr(mod);
|
||||
return tuser;
|
||||
}
|
||||
}
|
||||
|
||||
/* The corresponding tracepoint_user is not found. */
|
||||
tuser = __tracepoint_user_init(name, tpoint);
|
||||
if (!IS_ERR_OR_NULL(tuser))
|
||||
*pmod = no_free_ptr(mod);
|
||||
|
||||
return tuser;
|
||||
}
|
||||
|
||||
static void tracepoint_user_put(struct tracepoint_user *tuser)
|
||||
{
|
||||
scoped_guard(mutex, &tracepoint_user_mutex) {
|
||||
if (--tuser->refcount > 0)
|
||||
return;
|
||||
|
||||
list_del(&tuser->list);
|
||||
tracepoint_user_unregister(tuser);
|
||||
}
|
||||
|
||||
__tracepoint_user_free(tuser);
|
||||
}
|
||||
|
||||
DEFINE_FREE(tuser_put, struct tracepoint_user *,
|
||||
if (!IS_ERR_OR_NULL(_T))
|
||||
tracepoint_user_put(_T))
|
||||
|
||||
/*
|
||||
* Fprobe event core functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* @tprobe is true for tracepoint probe.
|
||||
* @tuser can be NULL if the trace_fprobe is disabled or the tracepoint is not
|
||||
* loaded with a module. If @tuser != NULL, this trace_fprobe is enabled.
|
||||
*/
|
||||
struct trace_fprobe {
|
||||
struct dyn_event devent;
|
||||
struct fprobe fp;
|
||||
const char *symbol;
|
||||
struct tracepoint *tpoint;
|
||||
struct module *mod;
|
||||
bool tprobe;
|
||||
struct tracepoint_user *tuser;
|
||||
struct trace_probe tp;
|
||||
};
|
||||
|
||||
|
|
@ -76,7 +219,7 @@ static bool trace_fprobe_is_return(struct trace_fprobe *tf)
|
|||
|
||||
static bool trace_fprobe_is_tracepoint(struct trace_fprobe *tf)
|
||||
{
|
||||
return tf->tpoint != NULL;
|
||||
return tf->tprobe;
|
||||
}
|
||||
|
||||
static const char *trace_fprobe_symbol(struct trace_fprobe *tf)
|
||||
|
|
@ -411,6 +554,8 @@ static void free_trace_fprobe(struct trace_fprobe *tf)
|
|||
{
|
||||
if (tf) {
|
||||
trace_probe_cleanup(&tf->tp);
|
||||
if (tf->tuser)
|
||||
tracepoint_user_put(tf->tuser);
|
||||
kfree(tf->symbol);
|
||||
kfree(tf);
|
||||
}
|
||||
|
|
@ -425,9 +570,8 @@ DEFINE_FREE(free_trace_fprobe, struct trace_fprobe *, if (!IS_ERR_OR_NULL(_T)) f
|
|||
static struct trace_fprobe *alloc_trace_fprobe(const char *group,
|
||||
const char *event,
|
||||
const char *symbol,
|
||||
struct tracepoint *tpoint,
|
||||
struct module *mod,
|
||||
int nargs, bool is_return)
|
||||
int nargs, bool is_return,
|
||||
bool is_tracepoint)
|
||||
{
|
||||
struct trace_fprobe *tf __free(free_trace_fprobe) = NULL;
|
||||
int ret = -ENOMEM;
|
||||
|
|
@ -445,8 +589,7 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group,
|
|||
else
|
||||
tf->fp.entry_handler = fentry_dispatcher;
|
||||
|
||||
tf->tpoint = tpoint;
|
||||
tf->mod = mod;
|
||||
tf->tprobe = is_tracepoint;
|
||||
|
||||
ret = trace_probe_init(&tf->tp, event, group, false, nargs);
|
||||
if (ret < 0)
|
||||
|
|
@ -469,98 +612,6 @@ static struct trace_fprobe *find_trace_fprobe(const char *event,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline int __enable_trace_fprobe(struct trace_fprobe *tf)
|
||||
{
|
||||
if (trace_fprobe_is_registered(tf))
|
||||
enable_fprobe(&tf->fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __disable_trace_fprobe(struct trace_probe *tp)
|
||||
{
|
||||
struct trace_fprobe *tf;
|
||||
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
if (!trace_fprobe_is_registered(tf))
|
||||
continue;
|
||||
disable_fprobe(&tf->fp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable trace_probe
|
||||
* if the file is NULL, enable "perf" handler, or enable "trace" handler.
|
||||
*/
|
||||
static int enable_trace_fprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_probe *tp;
|
||||
struct trace_fprobe *tf;
|
||||
bool enabled;
|
||||
int ret = 0;
|
||||
|
||||
tp = trace_probe_primary_from_call(call);
|
||||
if (WARN_ON_ONCE(!tp))
|
||||
return -ENODEV;
|
||||
enabled = trace_probe_is_enabled(tp);
|
||||
|
||||
/* This also changes "enabled" state */
|
||||
if (file) {
|
||||
ret = trace_probe_add_file(tp, file);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
trace_probe_set_flag(tp, TP_FLAG_PROFILE);
|
||||
|
||||
if (!enabled) {
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
/* TODO: check the fprobe is gone */
|
||||
__enable_trace_fprobe(tf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable trace_probe
|
||||
* if the file is NULL, disable "perf" handler, or disable "trace" handler.
|
||||
*/
|
||||
static int disable_trace_fprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_probe *tp;
|
||||
|
||||
tp = trace_probe_primary_from_call(call);
|
||||
if (WARN_ON_ONCE(!tp))
|
||||
return -ENODEV;
|
||||
|
||||
if (file) {
|
||||
if (!trace_probe_get_file_link(tp, file))
|
||||
return -ENOENT;
|
||||
if (!trace_probe_has_single_file(tp))
|
||||
goto out;
|
||||
trace_probe_clear_flag(tp, TP_FLAG_TRACE);
|
||||
} else
|
||||
trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
|
||||
|
||||
if (!trace_probe_is_enabled(tp))
|
||||
__disable_trace_fprobe(tp);
|
||||
|
||||
out:
|
||||
if (file)
|
||||
/*
|
||||
* Synchronization is done in below function. For perf event,
|
||||
* file == NULL and perf_trace_event_unreg() calls
|
||||
* tracepoint_synchronize_unregister() to ensure synchronize
|
||||
* event. We don't need to care about it.
|
||||
*/
|
||||
trace_probe_remove_file(tp, file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Event entry printers */
|
||||
static enum print_line_t
|
||||
print_fentry_event(struct trace_iterator *iter, int flags,
|
||||
|
|
@ -712,20 +763,52 @@ static int unregister_fprobe_event(struct trace_fprobe *tf)
|
|||
|
||||
static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf)
|
||||
{
|
||||
struct tracepoint *tpoint = tf->tpoint;
|
||||
unsigned long ip = (unsigned long)tpoint->probestub;
|
||||
struct tracepoint_user *tuser __free(tuser_put) = NULL;
|
||||
struct module *mod __free(module_put) = NULL;
|
||||
unsigned long ip;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(tf->tuser))
|
||||
return -EINVAL;
|
||||
|
||||
/* If the tracepoint is in a module, it must be locked in this function. */
|
||||
tuser = tracepoint_user_find_get(tf->symbol, &mod);
|
||||
/* This tracepoint is not loaded yet */
|
||||
if (IS_ERR(tuser))
|
||||
return PTR_ERR(tuser);
|
||||
if (!tuser)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Register fprobe only if the tracepoint is loaded. */
|
||||
if (tuser->tpoint) {
|
||||
ip = tracepoint_user_ip(tuser);
|
||||
if (WARN_ON_ONCE(!ip))
|
||||
return -ENOENT;
|
||||
|
||||
ret = register_fprobe_ips(&tf->fp, &ip, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
tf->tuser = no_free_ptr(tuser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns an error if the target function is not available, or 0 */
|
||||
static int trace_fprobe_verify_target(struct trace_fprobe *tf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Tracepoint should have a stub function. */
|
||||
if (trace_fprobe_is_tracepoint(tf))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Here, we do 2 steps to enable fprobe on a tracepoint.
|
||||
* At first, put __probestub_##TP function on the tracepoint
|
||||
* and put a fprobe on the stub function.
|
||||
* Note: since we don't lock the module, even if this succeeded,
|
||||
* register_fprobe() later can fail.
|
||||
*/
|
||||
ret = tracepoint_probe_register_prio_may_exist(tpoint,
|
||||
tpoint->probestub, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return register_fprobe_ips(&tf->fp, &ip, 1);
|
||||
ret = fprobe_count_ips_from_filter(tf->symbol, NULL);
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
|
||||
/* Internal register function - just handle fprobe and flags */
|
||||
|
|
@ -747,20 +830,10 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Set/clear disabled flag according to tp->flag */
|
||||
if (trace_probe_is_enabled(&tf->tp))
|
||||
tf->fp.flags &= ~FPROBE_FL_DISABLED;
|
||||
else
|
||||
tf->fp.flags |= FPROBE_FL_DISABLED;
|
||||
|
||||
if (trace_fprobe_is_tracepoint(tf)) {
|
||||
|
||||
/* This tracepoint is not loaded yet */
|
||||
if (tf->tpoint == TRACEPOINT_STUB)
|
||||
return 0;
|
||||
tf->fp.flags &= ~FPROBE_FL_DISABLED;
|
||||
|
||||
if (trace_fprobe_is_tracepoint(tf))
|
||||
return __regsiter_tracepoint_fprobe(tf);
|
||||
}
|
||||
|
||||
/* TODO: handle filter, nofilter or symbol list */
|
||||
return register_fprobe(&tf->fp, tf->symbol, NULL);
|
||||
|
|
@ -769,15 +842,11 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)
|
|||
/* Internal unregister function - just handle fprobe and flags */
|
||||
static void __unregister_trace_fprobe(struct trace_fprobe *tf)
|
||||
{
|
||||
if (trace_fprobe_is_registered(tf)) {
|
||||
if (trace_fprobe_is_registered(tf))
|
||||
unregister_fprobe(&tf->fp);
|
||||
memset(&tf->fp, 0, sizeof(tf->fp));
|
||||
if (trace_fprobe_is_tracepoint(tf)) {
|
||||
tracepoint_probe_unregister(tf->tpoint,
|
||||
tf->tpoint->probestub, NULL);
|
||||
tf->tpoint = NULL;
|
||||
tf->mod = NULL;
|
||||
}
|
||||
if (tf->tuser) {
|
||||
tracepoint_user_put(tf->tuser);
|
||||
tf->tuser = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -837,7 +906,7 @@ static bool trace_fprobe_has_same_fprobe(struct trace_fprobe *orig,
|
|||
return false;
|
||||
}
|
||||
|
||||
static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)
|
||||
static int append_trace_fprobe_event(struct trace_fprobe *tf, struct trace_fprobe *to)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -865,7 +934,7 @@ static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __register_trace_fprobe(tf);
|
||||
ret = trace_fprobe_verify_target(tf);
|
||||
if (ret)
|
||||
trace_probe_unlink(&tf->tp);
|
||||
else
|
||||
|
|
@ -874,8 +943,8 @@ static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register a trace_probe and probe_event */
|
||||
static int register_trace_fprobe(struct trace_fprobe *tf)
|
||||
/* Register a trace_probe and probe_event, and check the fprobe is available. */
|
||||
static int register_trace_fprobe_event(struct trace_fprobe *tf)
|
||||
{
|
||||
struct trace_fprobe *old_tf;
|
||||
int ret;
|
||||
|
|
@ -885,7 +954,7 @@ static int register_trace_fprobe(struct trace_fprobe *tf)
|
|||
old_tf = find_trace_fprobe(trace_probe_name(&tf->tp),
|
||||
trace_probe_group_name(&tf->tp));
|
||||
if (old_tf)
|
||||
return append_trace_fprobe(tf, old_tf);
|
||||
return append_trace_fprobe_event(tf, old_tf);
|
||||
|
||||
/* Register new event */
|
||||
ret = register_fprobe_event(tf);
|
||||
|
|
@ -898,8 +967,8 @@ static int register_trace_fprobe(struct trace_fprobe *tf)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register fprobe */
|
||||
ret = __register_trace_fprobe(tf);
|
||||
/* Verify fprobe is sane. */
|
||||
ret = trace_fprobe_verify_target(tf);
|
||||
if (ret < 0)
|
||||
unregister_fprobe_event(tf);
|
||||
else
|
||||
|
|
@ -963,15 +1032,6 @@ static struct tracepoint *find_tracepoint(const char *tp_name,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
static void reenable_trace_fprobe(struct trace_fprobe *tf)
|
||||
{
|
||||
struct trace_probe *tp = &tf->tp;
|
||||
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
__enable_trace_fprobe(tf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a tracepoint from specified module. In this case, this does not get the
|
||||
* module's refcount. The caller must ensure the module is not freed.
|
||||
|
|
@ -988,36 +1048,94 @@ static struct tracepoint *find_tracepoint_in_module(struct module *mod,
|
|||
return data.tpoint;
|
||||
}
|
||||
|
||||
/* These are CONFIG_MODULES=y specific functions. */
|
||||
static bool tracepoint_user_within_module(struct tracepoint_user *tuser,
|
||||
struct module *mod)
|
||||
{
|
||||
return within_module(tracepoint_user_ip(tuser), mod);
|
||||
}
|
||||
|
||||
static int tracepoint_user_register_again(struct tracepoint_user *tuser,
|
||||
struct tracepoint *tpoint)
|
||||
{
|
||||
tuser->tpoint = tpoint;
|
||||
return tracepoint_user_register(tuser);
|
||||
}
|
||||
|
||||
static void tracepoint_user_unregister_clear(struct tracepoint_user *tuser)
|
||||
{
|
||||
tracepoint_user_unregister(tuser);
|
||||
tuser->tpoint = NULL;
|
||||
}
|
||||
|
||||
/* module callback for tracepoint_user */
|
||||
static int __tracepoint_probe_module_cb(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct tp_module *tp_mod = data;
|
||||
struct tracepoint_user *tuser;
|
||||
struct tracepoint *tpoint;
|
||||
|
||||
if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
mutex_lock(&tracepoint_user_mutex);
|
||||
for_each_tracepoint_user(tuser) {
|
||||
if (val == MODULE_STATE_COMING) {
|
||||
/* This is not a tracepoint in this module. Skip it. */
|
||||
tpoint = find_tracepoint_in_module(tp_mod->mod, tuser->name);
|
||||
if (!tpoint)
|
||||
continue;
|
||||
WARN_ON_ONCE(tracepoint_user_register_again(tuser, tpoint));
|
||||
} else if (val == MODULE_STATE_GOING &&
|
||||
tracepoint_user_within_module(tuser, tp_mod->mod)) {
|
||||
/* Unregister all tracepoint_user in this module. */
|
||||
tracepoint_user_unregister_clear(tuser);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tracepoint_user_mutex);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block tracepoint_module_nb = {
|
||||
.notifier_call = __tracepoint_probe_module_cb,
|
||||
};
|
||||
|
||||
/* module callback for tprobe events */
|
||||
static int __tprobe_event_module_cb(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct trace_fprobe *tf;
|
||||
struct dyn_event *pos;
|
||||
struct module *mod = data;
|
||||
|
||||
if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
for_each_trace_fprobe(tf, pos) {
|
||||
if (val == MODULE_STATE_COMING && tf->tpoint == TRACEPOINT_STUB) {
|
||||
tpoint = find_tracepoint_in_module(tp_mod->mod, tf->symbol);
|
||||
if (tpoint) {
|
||||
tf->tpoint = tpoint;
|
||||
tf->mod = tp_mod->mod;
|
||||
if (!WARN_ON_ONCE(__regsiter_tracepoint_fprobe(tf)) &&
|
||||
trace_probe_is_enabled(&tf->tp))
|
||||
reenable_trace_fprobe(tf);
|
||||
}
|
||||
} else if (val == MODULE_STATE_GOING && tp_mod->mod == tf->mod) {
|
||||
/* Skip fprobe and disabled tprobe events. */
|
||||
if (!trace_fprobe_is_tracepoint(tf) || !tf->tuser)
|
||||
continue;
|
||||
|
||||
/* Before this notification, tracepoint notifier has already done. */
|
||||
if (val == MODULE_STATE_COMING &&
|
||||
tracepoint_user_within_module(tf->tuser, mod)) {
|
||||
unsigned long ip = tracepoint_user_ip(tf->tuser);
|
||||
|
||||
WARN_ON_ONCE(register_fprobe_ips(&tf->fp, &ip, 1));
|
||||
} else if (val == MODULE_STATE_GOING &&
|
||||
/*
|
||||
* tracepoint_user_within_module() does not work here because
|
||||
* tracepoint_user is already unregistered and cleared tpoint.
|
||||
* Instead, checking whether the fprobe is registered but
|
||||
* tpoint is cleared(unregistered). Such unbalance probes
|
||||
* must be adjusted anyway.
|
||||
*/
|
||||
trace_fprobe_is_registered(tf) &&
|
||||
!tf->tuser->tpoint) {
|
||||
unregister_fprobe(&tf->fp);
|
||||
if (trace_fprobe_is_tracepoint(tf)) {
|
||||
tracepoint_probe_unregister(tf->tpoint,
|
||||
tf->tpoint->probestub, NULL);
|
||||
tf->tpoint = TRACEPOINT_STUB;
|
||||
tf->mod = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&event_mutex);
|
||||
|
|
@ -1025,8 +1143,11 @@ static int __tracepoint_probe_module_cb(struct notifier_block *self,
|
|||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block tracepoint_module_nb = {
|
||||
.notifier_call = __tracepoint_probe_module_cb,
|
||||
/* NOTE: this must be called after tracepoint callback */
|
||||
static struct notifier_block tprobe_event_module_nb = {
|
||||
.notifier_call = __tprobe_event_module_cb,
|
||||
/* Make sure this is later than tracepoint module notifier. */
|
||||
.priority = -10,
|
||||
};
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
|
|
@ -1086,8 +1207,6 @@ static int parse_symbol_and_return(int argc, const char *argv[],
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_FREE(module_put, struct module *, if (_T) module_put(_T))
|
||||
|
||||
static int trace_fprobe_create_internal(int argc, const char *argv[],
|
||||
struct traceprobe_parse_context *ctx)
|
||||
{
|
||||
|
|
@ -1116,19 +1235,18 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
* FETCHARG:TYPE : use TYPE instead of unsigned long.
|
||||
*/
|
||||
struct trace_fprobe *tf __free(free_trace_fprobe) = NULL;
|
||||
int i, new_argc = 0, ret = 0;
|
||||
bool is_return = false;
|
||||
char *symbol __free(kfree) = NULL;
|
||||
const char *event = NULL, *group = FPROBE_EVENT_SYSTEM;
|
||||
struct module *mod __free(module_put) = NULL;
|
||||
const char **new_argv __free(kfree) = NULL;
|
||||
char buf[MAX_EVENT_NAME_LEN];
|
||||
char gbuf[MAX_EVENT_NAME_LEN];
|
||||
char sbuf[KSYM_NAME_LEN];
|
||||
char abuf[MAX_BTF_ARGS_LEN];
|
||||
char *symbol __free(kfree) = NULL;
|
||||
char *ebuf __free(kfree) = NULL;
|
||||
char *gbuf __free(kfree) = NULL;
|
||||
char *sbuf __free(kfree) = NULL;
|
||||
char *abuf __free(kfree) = NULL;
|
||||
char *dbuf __free(kfree) = NULL;
|
||||
int i, new_argc = 0, ret = 0;
|
||||
bool is_tracepoint = false;
|
||||
struct module *tp_mod __free(module_put) = NULL;
|
||||
struct tracepoint *tpoint = NULL;
|
||||
bool is_return = false;
|
||||
|
||||
if ((argv[0][0] != 'f' && argv[0][0] != 't') || argc < 2)
|
||||
return -ECANCELED;
|
||||
|
|
@ -1156,6 +1274,9 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
|
||||
trace_probe_log_set_index(0);
|
||||
if (event) {
|
||||
gbuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!gbuf)
|
||||
return -ENOMEM;
|
||||
ret = traceprobe_parse_event_name(&event, &group, gbuf,
|
||||
event - argv[0]);
|
||||
if (ret)
|
||||
|
|
@ -1163,15 +1284,18 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
}
|
||||
|
||||
if (!event) {
|
||||
ebuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!ebuf)
|
||||
return -ENOMEM;
|
||||
/* Make a new event name */
|
||||
if (is_tracepoint)
|
||||
snprintf(buf, MAX_EVENT_NAME_LEN, "%s%s",
|
||||
snprintf(ebuf, MAX_EVENT_NAME_LEN, "%s%s",
|
||||
isdigit(*symbol) ? "_" : "", symbol);
|
||||
else
|
||||
snprintf(buf, MAX_EVENT_NAME_LEN, "%s__%s", symbol,
|
||||
snprintf(ebuf, MAX_EVENT_NAME_LEN, "%s__%s", symbol,
|
||||
is_return ? "exit" : "entry");
|
||||
sanitize_event_name(buf);
|
||||
event = buf;
|
||||
sanitize_event_name(ebuf);
|
||||
event = ebuf;
|
||||
}
|
||||
|
||||
if (is_return)
|
||||
|
|
@ -1179,25 +1303,28 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
else
|
||||
ctx->flags |= TPARG_FL_FENTRY;
|
||||
|
||||
ctx->funcname = NULL;
|
||||
if (is_tracepoint) {
|
||||
/* Get tracepoint and lock its module until the end of the registration. */
|
||||
struct tracepoint *tpoint;
|
||||
|
||||
ctx->flags |= TPARG_FL_TPOINT;
|
||||
tpoint = find_tracepoint(symbol, &tp_mod);
|
||||
mod = NULL;
|
||||
tpoint = find_tracepoint(symbol, &mod);
|
||||
if (tpoint) {
|
||||
ctx->funcname = kallsyms_lookup(
|
||||
(unsigned long)tpoint->probestub,
|
||||
NULL, NULL, NULL, sbuf);
|
||||
} else if (IS_ENABLED(CONFIG_MODULES)) {
|
||||
/* This *may* be loaded afterwards */
|
||||
tpoint = TRACEPOINT_STUB;
|
||||
ctx->funcname = symbol;
|
||||
} else {
|
||||
trace_probe_log_set_index(1);
|
||||
trace_probe_log_err(0, NO_TRACEPOINT);
|
||||
return -EINVAL;
|
||||
sbuf = kmalloc(KSYM_NAME_LEN, GFP_KERNEL);
|
||||
if (!sbuf)
|
||||
return -ENOMEM;
|
||||
ctx->funcname = kallsyms_lookup((unsigned long)tpoint->probestub,
|
||||
NULL, NULL, NULL, sbuf);
|
||||
}
|
||||
} else
|
||||
}
|
||||
if (!ctx->funcname)
|
||||
ctx->funcname = symbol;
|
||||
|
||||
abuf = kmalloc(MAX_BTF_ARGS_LEN, GFP_KERNEL);
|
||||
if (!abuf)
|
||||
return -ENOMEM;
|
||||
argc -= 2; argv += 2;
|
||||
new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,
|
||||
abuf, MAX_BTF_ARGS_LEN, ctx);
|
||||
|
|
@ -1218,8 +1345,7 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
return ret;
|
||||
|
||||
/* setup a probe */
|
||||
tf = alloc_trace_fprobe(group, event, symbol, tpoint, tp_mod,
|
||||
argc, is_return);
|
||||
tf = alloc_trace_fprobe(group, event, symbol, argc, is_return, is_tracepoint);
|
||||
if (IS_ERR(tf)) {
|
||||
ret = PTR_ERR(tf);
|
||||
/* This must return -ENOMEM, else there is a bug */
|
||||
|
|
@ -1251,7 +1377,7 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = register_trace_fprobe(tf);
|
||||
ret = register_trace_fprobe_event(tf);
|
||||
if (ret) {
|
||||
trace_probe_log_set_index(1);
|
||||
if (ret == -EILSEQ)
|
||||
|
|
@ -1271,14 +1397,17 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
|
|||
|
||||
static int trace_fprobe_create_cb(int argc, const char *argv[])
|
||||
{
|
||||
struct traceprobe_parse_context ctx = {
|
||||
.flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE,
|
||||
};
|
||||
struct traceprobe_parse_context *ctx __free(traceprobe_parse_context) = NULL;
|
||||
int ret;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE;
|
||||
|
||||
trace_probe_log_init("trace_fprobe", argc, argv);
|
||||
ret = trace_fprobe_create_internal(argc, argv, &ctx);
|
||||
traceprobe_finish_parse(&ctx);
|
||||
ret = trace_fprobe_create_internal(argc, argv, ctx);
|
||||
trace_probe_log_clear();
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1320,6 +1449,84 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable trace_probe
|
||||
* if the file is NULL, enable "perf" handler, or enable "trace" handler.
|
||||
*/
|
||||
static int enable_trace_fprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_probe *tp;
|
||||
struct trace_fprobe *tf;
|
||||
bool enabled;
|
||||
int ret = 0;
|
||||
|
||||
tp = trace_probe_primary_from_call(call);
|
||||
if (WARN_ON_ONCE(!tp))
|
||||
return -ENODEV;
|
||||
enabled = trace_probe_is_enabled(tp);
|
||||
|
||||
/* This also changes "enabled" state */
|
||||
if (file) {
|
||||
ret = trace_probe_add_file(tp, file);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
trace_probe_set_flag(tp, TP_FLAG_PROFILE);
|
||||
|
||||
if (!enabled) {
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
ret = __register_trace_fprobe(tf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable trace_probe
|
||||
* if the file is NULL, disable "perf" handler, or disable "trace" handler.
|
||||
*/
|
||||
static int disable_trace_fprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_fprobe *tf;
|
||||
struct trace_probe *tp;
|
||||
|
||||
tp = trace_probe_primary_from_call(call);
|
||||
if (WARN_ON_ONCE(!tp))
|
||||
return -ENODEV;
|
||||
|
||||
if (file) {
|
||||
if (!trace_probe_get_file_link(tp, file))
|
||||
return -ENOENT;
|
||||
if (!trace_probe_has_single_file(tp))
|
||||
goto out;
|
||||
trace_probe_clear_flag(tp, TP_FLAG_TRACE);
|
||||
} else
|
||||
trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
|
||||
|
||||
if (!trace_probe_is_enabled(tp)) {
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
unregister_fprobe(&tf->fp);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (file)
|
||||
/*
|
||||
* Synchronization is done in below function. For perf event,
|
||||
* file == NULL and perf_trace_event_unreg() calls
|
||||
* tracepoint_synchronize_unregister() to ensure synchronize
|
||||
* event. We don't need to care about it.
|
||||
*/
|
||||
trace_probe_remove_file(tp, file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex.
|
||||
*/
|
||||
|
|
@ -1365,6 +1572,9 @@ static __init int init_fprobe_trace_early(void)
|
|||
ret = register_tracepoint_module_notifier(&tracepoint_module_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = register_module_notifier(&tprobe_event_module_nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -9,19 +9,19 @@
|
|||
|
||||
#include <linux/bpf-cgroup.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/error-injection.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
|
||||
|
||||
#include "trace_dynevent.h"
|
||||
#include "trace_kprobe_selftest.h"
|
||||
#include "trace_probe.h"
|
||||
#include "trace_probe_tmpl.h"
|
||||
#include "trace_probe_kernel.h"
|
||||
#include "trace_probe_tmpl.h"
|
||||
|
||||
#define KPROBE_EVENT_SYSTEM "kprobes"
|
||||
#define KRETPROBE_MAXACTIVE_MAX 4096
|
||||
|
|
@ -861,20 +861,20 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
* FETCHARG:TYPE : use TYPE instead of unsigned long.
|
||||
*/
|
||||
struct trace_kprobe *tk __free(free_trace_kprobe) = NULL;
|
||||
int i, len, new_argc = 0, ret = 0;
|
||||
bool is_return = false;
|
||||
char *symbol __free(kfree) = NULL;
|
||||
char *tmp = NULL;
|
||||
const char **new_argv __free(kfree) = NULL;
|
||||
const char *event = NULL, *group = KPROBE_EVENT_SYSTEM;
|
||||
enum probe_print_type ptype;
|
||||
int maxactive = 0;
|
||||
long offset = 0;
|
||||
void *addr = NULL;
|
||||
char buf[MAX_EVENT_NAME_LEN];
|
||||
char gbuf[MAX_EVENT_NAME_LEN];
|
||||
char abuf[MAX_BTF_ARGS_LEN];
|
||||
const char **new_argv __free(kfree) = NULL;
|
||||
int i, len, new_argc = 0, ret = 0;
|
||||
char *symbol __free(kfree) = NULL;
|
||||
char *ebuf __free(kfree) = NULL;
|
||||
char *gbuf __free(kfree) = NULL;
|
||||
char *abuf __free(kfree) = NULL;
|
||||
char *dbuf __free(kfree) = NULL;
|
||||
enum probe_print_type ptype;
|
||||
bool is_return = false;
|
||||
int maxactive = 0;
|
||||
void *addr = NULL;
|
||||
char *tmp = NULL;
|
||||
long offset = 0;
|
||||
|
||||
switch (argv[0][0]) {
|
||||
case 'r':
|
||||
|
|
@ -893,6 +893,8 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
event++;
|
||||
|
||||
if (isdigit(argv[0][1])) {
|
||||
char *buf __free(kfree) = NULL;
|
||||
|
||||
if (!is_return) {
|
||||
trace_probe_log_err(1, BAD_MAXACT_TYPE);
|
||||
return -EINVAL;
|
||||
|
|
@ -905,7 +907,7 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
trace_probe_log_err(1, BAD_MAXACT);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(buf, &argv[0][1], len);
|
||||
buf = kmemdup(&argv[0][1], len + 1, GFP_KERNEL);
|
||||
buf[len] = '\0';
|
||||
ret = kstrtouint(buf, 0, &maxactive);
|
||||
if (ret || !maxactive) {
|
||||
|
|
@ -973,6 +975,9 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
|
||||
trace_probe_log_set_index(0);
|
||||
if (event) {
|
||||
gbuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!gbuf)
|
||||
return -ENOMEM;
|
||||
ret = traceprobe_parse_event_name(&event, &group, gbuf,
|
||||
event - argv[0]);
|
||||
if (ret)
|
||||
|
|
@ -981,16 +986,22 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
|
||||
if (!event) {
|
||||
/* Make a new event name */
|
||||
ebuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!ebuf)
|
||||
return -ENOMEM;
|
||||
if (symbol)
|
||||
snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_%ld",
|
||||
snprintf(ebuf, MAX_EVENT_NAME_LEN, "%c_%s_%ld",
|
||||
is_return ? 'r' : 'p', symbol, offset);
|
||||
else
|
||||
snprintf(buf, MAX_EVENT_NAME_LEN, "%c_0x%p",
|
||||
snprintf(ebuf, MAX_EVENT_NAME_LEN, "%c_0x%p",
|
||||
is_return ? 'r' : 'p', addr);
|
||||
sanitize_event_name(buf);
|
||||
event = buf;
|
||||
sanitize_event_name(ebuf);
|
||||
event = ebuf;
|
||||
}
|
||||
|
||||
abuf = kmalloc(MAX_BTF_ARGS_LEN, GFP_KERNEL);
|
||||
if (!abuf)
|
||||
return -ENOMEM;
|
||||
argc -= 2; argv += 2;
|
||||
ctx->funcname = symbol;
|
||||
new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,
|
||||
|
|
@ -1065,14 +1076,18 @@ static int trace_kprobe_create_internal(int argc, const char *argv[],
|
|||
|
||||
static int trace_kprobe_create_cb(int argc, const char *argv[])
|
||||
{
|
||||
struct traceprobe_parse_context ctx = { .flags = TPARG_FL_KERNEL };
|
||||
struct traceprobe_parse_context *ctx __free(traceprobe_parse_context) = NULL;
|
||||
int ret;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
ctx->flags = TPARG_FL_KERNEL;
|
||||
|
||||
trace_probe_log_init("trace_kprobe", argc, argv);
|
||||
|
||||
ret = trace_kprobe_create_internal(argc, argv, &ctx);
|
||||
ret = trace_kprobe_create_internal(argc, argv, ctx);
|
||||
|
||||
traceprobe_finish_parse(&ctx);
|
||||
trace_probe_log_clear();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/fs.h>
|
||||
#include "trace_btf.h"
|
||||
|
||||
#include "trace_btf.h"
|
||||
#include "trace_probe.h"
|
||||
|
||||
#undef C
|
||||
|
|
@ -247,7 +247,25 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* @buf must has MAX_EVENT_NAME_LEN size */
|
||||
/**
|
||||
* traceprobe_parse_event_name() - Parse a string into group and event names
|
||||
* @pevent: A pointer to the string to be parsed.
|
||||
* @pgroup: A pointer to the group name.
|
||||
* @buf: A buffer to store the parsed group name.
|
||||
* @offset: The offset of the string in the original user command, for logging.
|
||||
*
|
||||
* This parses a string with the format `[GROUP/][EVENT]` or `[GROUP.][EVENT]`
|
||||
* (either GROUP or EVENT or both must be specified).
|
||||
* Since the parsed group name is stored in @buf, the caller must ensure @buf
|
||||
* is at least MAX_EVENT_NAME_LEN bytes.
|
||||
*
|
||||
* Return: 0 on success, or -EINVAL on failure.
|
||||
*
|
||||
* If success, *@pevent is updated to point to the event name part of the
|
||||
* original string, or NULL if there is no event name.
|
||||
* Also, *@pgroup is updated to point to the parsed group which is stored
|
||||
* in @buf, or NULL if there is no group name.
|
||||
*/
|
||||
int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
|
||||
char *buf, int offset)
|
||||
{
|
||||
|
|
@ -779,6 +797,36 @@ static int check_prepare_btf_string_fetch(char *typename,
|
|||
|
||||
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
|
||||
|
||||
static void store_entry_arg_at(struct fetch_insn *code, int argnum, int offset)
|
||||
{
|
||||
code[0].op = FETCH_OP_ARG;
|
||||
code[0].param = argnum;
|
||||
code[1].op = FETCH_OP_ST_EDATA;
|
||||
code[1].offset = offset;
|
||||
}
|
||||
|
||||
static int get_entry_arg_max_offset(struct probe_entry_arg *earg)
|
||||
{
|
||||
int i, max_offset = 0;
|
||||
|
||||
/*
|
||||
* earg->code[] array has an operation sequence which is run in
|
||||
* the entry handler.
|
||||
* The sequence stopped by FETCH_OP_END and each data stored in
|
||||
* the entry data buffer by FETCH_OP_ST_EDATA. The FETCH_OP_ST_EDATA
|
||||
* stores the data at the data buffer + its offset, and all data are
|
||||
* "unsigned long" size. The offset must be increased when a data is
|
||||
* stored. Thus we need to find the last FETCH_OP_ST_EDATA in the
|
||||
* code array.
|
||||
*/
|
||||
for (i = 0; i < earg->size - 1 && earg->code[i].op != FETCH_OP_END; i++) {
|
||||
if (earg->code[i].op == FETCH_OP_ST_EDATA)
|
||||
if (earg->code[i].offset > max_offset)
|
||||
max_offset = earg->code[i].offset;
|
||||
}
|
||||
return max_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the entry code to store the 'argnum'th parameter and return the offset
|
||||
* in the entry data buffer where the data will be stored.
|
||||
|
|
@ -786,8 +834,7 @@ static int check_prepare_btf_string_fetch(char *typename,
|
|||
static int __store_entry_arg(struct trace_probe *tp, int argnum)
|
||||
{
|
||||
struct probe_entry_arg *earg = tp->entry_arg;
|
||||
bool match = false;
|
||||
int i, offset;
|
||||
int i, offset, last_offset = 0;
|
||||
|
||||
if (!earg) {
|
||||
earg = kzalloc(sizeof(*tp->entry_arg), GFP_KERNEL);
|
||||
|
|
@ -804,78 +851,59 @@ static int __store_entry_arg(struct trace_probe *tp, int argnum)
|
|||
for (i = 0; i < earg->size; i++)
|
||||
earg->code[i].op = FETCH_OP_END;
|
||||
tp->entry_arg = earg;
|
||||
store_entry_arg_at(earg->code, argnum, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The entry code array is repeating the pair of
|
||||
* [FETCH_OP_ARG(argnum)][FETCH_OP_ST_EDATA(offset of entry data buffer)]
|
||||
* and the rest of entries are filled with [FETCH_OP_END].
|
||||
* NOTE: if anyone change the following rule, please rewrite this.
|
||||
* The entry code array is filled with the pair of
|
||||
*
|
||||
* To reduce the redundant function parameter fetching, we scan the entry
|
||||
* code array to find the FETCH_OP_ARG which already fetches the 'argnum'
|
||||
* parameter. If it doesn't match, update 'offset' to find the last
|
||||
* offset.
|
||||
* If we find the FETCH_OP_END without matching FETCH_OP_ARG entry, we
|
||||
* will save the entry with FETCH_OP_ARG and FETCH_OP_ST_EDATA, and
|
||||
* return data offset so that caller can find the data offset in the entry
|
||||
* data buffer.
|
||||
* [FETCH_OP_ARG(argnum)]
|
||||
* [FETCH_OP_ST_EDATA(offset of entry data buffer)]
|
||||
*
|
||||
* and the rest of entries are filled with [FETCH_OP_END].
|
||||
* The offset should be incremented, thus the last pair should
|
||||
* have the largest offset.
|
||||
*/
|
||||
offset = 0;
|
||||
for (i = 0; i < earg->size - 1; i++) {
|
||||
switch (earg->code[i].op) {
|
||||
case FETCH_OP_END:
|
||||
earg->code[i].op = FETCH_OP_ARG;
|
||||
earg->code[i].param = argnum;
|
||||
earg->code[i + 1].op = FETCH_OP_ST_EDATA;
|
||||
earg->code[i + 1].offset = offset;
|
||||
return offset;
|
||||
case FETCH_OP_ARG:
|
||||
match = (earg->code[i].param == argnum);
|
||||
break;
|
||||
case FETCH_OP_ST_EDATA:
|
||||
offset = earg->code[i].offset;
|
||||
if (match)
|
||||
return offset;
|
||||
offset += sizeof(unsigned long);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Search the offset for the sprcified argnum. */
|
||||
for (i = 0; i < earg->size - 1 && earg->code[i].op != FETCH_OP_END; i += 2) {
|
||||
if (WARN_ON_ONCE(earg->code[i].op != FETCH_OP_ARG))
|
||||
return -EINVAL;
|
||||
|
||||
if (earg->code[i].param != argnum)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(earg->code[i + 1].op != FETCH_OP_ST_EDATA))
|
||||
return -EINVAL;
|
||||
|
||||
return earg->code[i + 1].offset;
|
||||
}
|
||||
return -ENOSPC;
|
||||
/* Not found, append new entry if possible. */
|
||||
if (i >= earg->size - 1)
|
||||
return -ENOSPC;
|
||||
|
||||
/* The last entry must have the largest offset. */
|
||||
if (i != 0) {
|
||||
if (WARN_ON_ONCE(earg->code[i - 1].op != FETCH_OP_ST_EDATA))
|
||||
return -EINVAL;
|
||||
last_offset = earg->code[i - 1].offset;
|
||||
}
|
||||
|
||||
offset = last_offset + sizeof(unsigned long);
|
||||
store_entry_arg_at(&earg->code[i], argnum, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int traceprobe_get_entry_data_size(struct trace_probe *tp)
|
||||
{
|
||||
struct probe_entry_arg *earg = tp->entry_arg;
|
||||
int i, size = 0;
|
||||
|
||||
if (!earg)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* earg->code[] array has an operation sequence which is run in
|
||||
* the entry handler.
|
||||
* The sequence stopped by FETCH_OP_END and each data stored in
|
||||
* the entry data buffer by FETCH_OP_ST_EDATA. The FETCH_OP_ST_EDATA
|
||||
* stores the data at the data buffer + its offset, and all data are
|
||||
* "unsigned long" size. The offset must be increased when a data is
|
||||
* stored. Thus we need to find the last FETCH_OP_ST_EDATA in the
|
||||
* code array.
|
||||
*/
|
||||
for (i = 0; i < earg->size; i++) {
|
||||
switch (earg->code[i].op) {
|
||||
case FETCH_OP_END:
|
||||
goto out;
|
||||
case FETCH_OP_ST_EDATA:
|
||||
size = earg->code[i].offset + sizeof(unsigned long);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return size;
|
||||
return get_entry_arg_max_offset(earg) + sizeof(unsigned long);
|
||||
}
|
||||
|
||||
void store_trace_entry_data(void *edata, struct trace_probe *tp, struct pt_regs *regs)
|
||||
|
|
|
|||
|
|
@ -10,20 +10,22 @@
|
|||
* Author: Srikar Dronamraju
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/tracefs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/btf.h>
|
||||
|
||||
#include <asm/bitsperlong.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
|
@ -438,6 +440,14 @@ extern void traceprobe_free_probe_arg(struct probe_arg *arg);
|
|||
* this MUST be called for clean up the context and return a resource.
|
||||
*/
|
||||
void traceprobe_finish_parse(struct traceprobe_parse_context *ctx);
|
||||
static inline void traceprobe_free_parse_ctx(struct traceprobe_parse_context *ctx)
|
||||
{
|
||||
traceprobe_finish_parse(ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
DEFINE_FREE(traceprobe_parse_context, struct traceprobe_parse_context *,
|
||||
if (_T) traceprobe_free_parse_ctx(_T))
|
||||
|
||||
extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
|
||||
int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
|
||||
|
|
|
|||
|
|
@ -8,17 +8,19 @@
|
|||
#define pr_fmt(fmt) "trace_uprobe: " fmt
|
||||
|
||||
#include <linux/bpf-cgroup.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_dynevent.h"
|
||||
#include "trace_probe.h"
|
||||
#include "trace_probe_tmpl.h"
|
||||
|
|
@ -537,15 +539,15 @@ static int register_trace_uprobe(struct trace_uprobe *tu)
|
|||
*/
|
||||
static int __trace_uprobe_create(int argc, const char **argv)
|
||||
{
|
||||
struct trace_uprobe *tu;
|
||||
const char *event = NULL, *group = UPROBE_EVENT_SYSTEM;
|
||||
char *arg, *filename, *rctr, *rctr_end, *tmp;
|
||||
char buf[MAX_EVENT_NAME_LEN];
|
||||
char gbuf[MAX_EVENT_NAME_LEN];
|
||||
enum probe_print_type ptype;
|
||||
struct path path;
|
||||
unsigned long offset, ref_ctr_offset;
|
||||
char *gbuf __free(kfree) = NULL;
|
||||
char *buf __free(kfree) = NULL;
|
||||
enum probe_print_type ptype;
|
||||
struct trace_uprobe *tu;
|
||||
bool is_return = false;
|
||||
struct path path;
|
||||
int i, ret;
|
||||
|
||||
ref_ctr_offset = 0;
|
||||
|
|
@ -653,6 +655,10 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
|||
/* setup a probe */
|
||||
trace_probe_log_set_index(0);
|
||||
if (event) {
|
||||
gbuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!gbuf)
|
||||
goto fail_mem;
|
||||
|
||||
ret = traceprobe_parse_event_name(&event, &group, gbuf,
|
||||
event - argv[0]);
|
||||
if (ret)
|
||||
|
|
@ -664,15 +670,16 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
|||
char *ptr;
|
||||
|
||||
tail = kstrdup(kbasename(filename), GFP_KERNEL);
|
||||
if (!tail) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_address_parse;
|
||||
}
|
||||
if (!tail)
|
||||
goto fail_mem;
|
||||
|
||||
ptr = strpbrk(tail, ".-_");
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
buf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto fail_mem;
|
||||
snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_0x%lx", 'p', tail, offset);
|
||||
event = buf;
|
||||
kfree(tail);
|
||||
|
|
@ -695,13 +702,16 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
|||
|
||||
/* parse arguments */
|
||||
for (i = 0; i < argc; i++) {
|
||||
struct traceprobe_parse_context ctx = {
|
||||
.flags = (is_return ? TPARG_FL_RETURN : 0) | TPARG_FL_USER,
|
||||
};
|
||||
struct traceprobe_parse_context *ctx __free(traceprobe_parse_context)
|
||||
= kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
|
||||
if (!ctx) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
ctx->flags = (is_return ? TPARG_FL_RETURN : 0) | TPARG_FL_USER;
|
||||
trace_probe_log_set_index(i + 2);
|
||||
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], &ctx);
|
||||
traceprobe_finish_parse(&ctx);
|
||||
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], ctx);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -721,6 +731,9 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
|||
trace_probe_log_clear();
|
||||
return ret;
|
||||
|
||||
fail_mem:
|
||||
ret = -ENOMEM;
|
||||
|
||||
fail_address_parse:
|
||||
trace_probe_log_clear();
|
||||
path_put(&path);
|
||||
|
|
|
|||
|
|
@ -16,36 +16,41 @@ ocnt=`cat enabled_functions | wc -l`
|
|||
|
||||
echo "f:myevent1 $PLACE" >> dynamic_events
|
||||
|
||||
# Make sure the event is attached and is the only one
|
||||
grep -q $PLACE enabled_functions
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 1)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo "f:myevent2 $PLACE%return" >> dynamic_events
|
||||
|
||||
# It should till be the only attached function
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 1)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
# add another event
|
||||
echo "f:myevent3 $PLACE2" >> dynamic_events
|
||||
|
||||
grep -q $PLACE2 enabled_functions
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 2)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
grep -q myevent1 dynamic_events
|
||||
grep -q myevent2 dynamic_events
|
||||
grep -q myevent3 dynamic_events
|
||||
test -d events/fprobes/myevent1
|
||||
test -d events/fprobes/myevent2
|
||||
|
||||
echo 1 > events/fprobes/myevent1/enable
|
||||
# Make sure the event is attached and is the only one
|
||||
grep -q $PLACE enabled_functions
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 1)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo 1 > events/fprobes/myevent2/enable
|
||||
# It should till be the only attached function
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 1)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo 1 > events/fprobes/myevent3/enable
|
||||
# If the function is different, the attached function should be increased
|
||||
grep -q $PLACE2 enabled_functions
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 2)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo 0 > events/fprobes/myevent2/enable
|
||||
echo "-:myevent2" >> dynamic_events
|
||||
|
||||
grep -q myevent1 dynamic_events
|
||||
|
|
@ -57,6 +62,7 @@ if [ $cnt -ne $((ocnt + 2)) ]; then
|
|||
exit_fail
|
||||
fi
|
||||
|
||||
echo 0 > events/fprobes/enable
|
||||
echo > dynamic_events
|
||||
|
||||
# Should have none left
|
||||
|
|
@ -67,12 +73,14 @@ fi
|
|||
|
||||
echo "f:myevent4 $PLACE" >> dynamic_events
|
||||
|
||||
echo 1 > events/fprobes/myevent4/enable
|
||||
# Should only have one enabled
|
||||
cnt=`cat enabled_functions | wc -l`
|
||||
if [ $cnt -ne $((ocnt + 1)) ]; then
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo 0 > events/fprobes/enable
|
||||
echo > dynamic_events
|
||||
|
||||
# Should have none left
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user