tracing/uprobes: Use dyn_event framework for uprobe events
Use dyn_event framework for uprobe events. This shows uprobe events on "dynamic_events" file. User can also define new uprobe events via dynamic_events. Link: http://lkml.kernel.org/r/154140858481.17322.9091293846515154065.stgit@devbox Reviewed-by: Tom Zanussi <tom.zanussi@linux.intel.com> Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
Родитель
6212dd2968
Коммит
0597c49c69
|
@ -18,6 +18,10 @@ current_tracer. Instead of that, add probe points via
|
||||||
However unlike kprobe-event tracer, the uprobe event interface expects the
|
However unlike kprobe-event tracer, the uprobe event interface expects the
|
||||||
user to calculate the offset of the probepoint in the object.
|
user to calculate the offset of the probepoint in the object.
|
||||||
|
|
||||||
|
You can also use /sys/kernel/debug/tracing/dynamic_events instead of
|
||||||
|
uprobe_events. That interface will provide unified access to other
|
||||||
|
dynamic events too.
|
||||||
|
|
||||||
Synopsis of uprobe_tracer
|
Synopsis of uprobe_tracer
|
||||||
-------------------------
|
-------------------------
|
||||||
::
|
::
|
||||||
|
|
|
@ -501,6 +501,7 @@ config UPROBE_EVENTS
|
||||||
depends on PERF_EVENTS
|
depends on PERF_EVENTS
|
||||||
select UPROBES
|
select UPROBES
|
||||||
select PROBE_EVENTS
|
select PROBE_EVENTS
|
||||||
|
select DYNAMIC_EVENTS
|
||||||
select TRACING
|
select TRACING
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
#define pr_fmt(fmt) "trace_kprobe: " fmt
|
#define pr_fmt(fmt) "trace_kprobe: " fmt
|
||||||
|
|
||||||
|
#include <linux/ctype.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/uprobes.h>
|
#include <linux/uprobes.h>
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/rculist.h>
|
#include <linux/rculist.h>
|
||||||
|
|
||||||
|
#include "trace_dynevent.h"
|
||||||
#include "trace_probe.h"
|
#include "trace_probe.h"
|
||||||
#include "trace_probe_tmpl.h"
|
#include "trace_probe_tmpl.h"
|
||||||
|
|
||||||
|
@ -37,11 +39,26 @@ struct trace_uprobe_filter {
|
||||||
struct list_head perf_events;
|
struct list_head perf_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int trace_uprobe_create(int argc, const char **argv);
|
||||||
|
static int trace_uprobe_show(struct seq_file *m, struct dyn_event *ev);
|
||||||
|
static int trace_uprobe_release(struct dyn_event *ev);
|
||||||
|
static bool trace_uprobe_is_busy(struct dyn_event *ev);
|
||||||
|
static bool trace_uprobe_match(const char *system, const char *event,
|
||||||
|
struct dyn_event *ev);
|
||||||
|
|
||||||
|
static struct dyn_event_operations trace_uprobe_ops = {
|
||||||
|
.create = trace_uprobe_create,
|
||||||
|
.show = trace_uprobe_show,
|
||||||
|
.is_busy = trace_uprobe_is_busy,
|
||||||
|
.free = trace_uprobe_release,
|
||||||
|
.match = trace_uprobe_match,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* uprobe event core functions
|
* uprobe event core functions
|
||||||
*/
|
*/
|
||||||
struct trace_uprobe {
|
struct trace_uprobe {
|
||||||
struct list_head list;
|
struct dyn_event devent;
|
||||||
struct trace_uprobe_filter filter;
|
struct trace_uprobe_filter filter;
|
||||||
struct uprobe_consumer consumer;
|
struct uprobe_consumer consumer;
|
||||||
struct path path;
|
struct path path;
|
||||||
|
@ -53,6 +70,25 @@ struct trace_uprobe {
|
||||||
struct trace_probe tp;
|
struct trace_probe tp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool is_trace_uprobe(struct dyn_event *ev)
|
||||||
|
{
|
||||||
|
return ev->ops == &trace_uprobe_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trace_uprobe *to_trace_uprobe(struct dyn_event *ev)
|
||||||
|
{
|
||||||
|
return container_of(ev, struct trace_uprobe, devent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for_each_trace_uprobe - iterate over the trace_uprobe list
|
||||||
|
* @pos: the struct trace_uprobe * for each entry
|
||||||
|
* @dpos: the struct dyn_event * to use as a loop cursor
|
||||||
|
*/
|
||||||
|
#define for_each_trace_uprobe(pos, dpos) \
|
||||||
|
for_each_dyn_event(dpos) \
|
||||||
|
if (is_trace_uprobe(dpos) && (pos = to_trace_uprobe(dpos)))
|
||||||
|
|
||||||
#define SIZEOF_TRACE_UPROBE(n) \
|
#define SIZEOF_TRACE_UPROBE(n) \
|
||||||
(offsetof(struct trace_uprobe, tp.args) + \
|
(offsetof(struct trace_uprobe, tp.args) + \
|
||||||
(sizeof(struct probe_arg) * (n)))
|
(sizeof(struct probe_arg) * (n)))
|
||||||
|
@ -60,9 +96,6 @@ struct trace_uprobe {
|
||||||
static int register_uprobe_event(struct trace_uprobe *tu);
|
static int register_uprobe_event(struct trace_uprobe *tu);
|
||||||
static int unregister_uprobe_event(struct trace_uprobe *tu);
|
static int unregister_uprobe_event(struct trace_uprobe *tu);
|
||||||
|
|
||||||
static DEFINE_MUTEX(uprobe_lock);
|
|
||||||
static LIST_HEAD(uprobe_list);
|
|
||||||
|
|
||||||
struct uprobe_dispatch_data {
|
struct uprobe_dispatch_data {
|
||||||
struct trace_uprobe *tu;
|
struct trace_uprobe *tu;
|
||||||
unsigned long bp_addr;
|
unsigned long bp_addr;
|
||||||
|
@ -209,6 +242,22 @@ static inline bool is_ret_probe(struct trace_uprobe *tu)
|
||||||
return tu->consumer.ret_handler != NULL;
|
return tu->consumer.ret_handler != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool trace_uprobe_is_busy(struct dyn_event *ev)
|
||||||
|
{
|
||||||
|
struct trace_uprobe *tu = to_trace_uprobe(ev);
|
||||||
|
|
||||||
|
return trace_probe_is_enabled(&tu->tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool trace_uprobe_match(const char *system, const char *event,
|
||||||
|
struct dyn_event *ev)
|
||||||
|
{
|
||||||
|
struct trace_uprobe *tu = to_trace_uprobe(ev);
|
||||||
|
|
||||||
|
return strcmp(trace_event_name(&tu->tp.call), event) == 0 &&
|
||||||
|
(!system || strcmp(tu->tp.call.class->system, system) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate new trace_uprobe and initialize it (including uprobes).
|
* Allocate new trace_uprobe and initialize it (including uprobes).
|
||||||
*/
|
*/
|
||||||
|
@ -236,7 +285,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
|
||||||
if (!tu->tp.class.system)
|
if (!tu->tp.class.system)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&tu->list);
|
dyn_event_init(&tu->devent, &trace_uprobe_ops);
|
||||||
INIT_LIST_HEAD(&tu->tp.files);
|
INIT_LIST_HEAD(&tu->tp.files);
|
||||||
tu->consumer.handler = uprobe_dispatcher;
|
tu->consumer.handler = uprobe_dispatcher;
|
||||||
if (is_ret)
|
if (is_ret)
|
||||||
|
@ -255,6 +304,9 @@ static void free_trace_uprobe(struct trace_uprobe *tu)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (!tu)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; i < tu->tp.nr_args; i++)
|
for (i = 0; i < tu->tp.nr_args; i++)
|
||||||
traceprobe_free_probe_arg(&tu->tp.args[i]);
|
traceprobe_free_probe_arg(&tu->tp.args[i]);
|
||||||
|
|
||||||
|
@ -267,9 +319,10 @@ static void free_trace_uprobe(struct trace_uprobe *tu)
|
||||||
|
|
||||||
static struct trace_uprobe *find_probe_event(const char *event, const char *group)
|
static struct trace_uprobe *find_probe_event(const char *event, const char *group)
|
||||||
{
|
{
|
||||||
|
struct dyn_event *pos;
|
||||||
struct trace_uprobe *tu;
|
struct trace_uprobe *tu;
|
||||||
|
|
||||||
list_for_each_entry(tu, &uprobe_list, list)
|
for_each_trace_uprobe(tu, pos)
|
||||||
if (strcmp(trace_event_name(&tu->tp.call), event) == 0 &&
|
if (strcmp(trace_event_name(&tu->tp.call), event) == 0 &&
|
||||||
strcmp(tu->tp.call.class->system, group) == 0)
|
strcmp(tu->tp.call.class->system, group) == 0)
|
||||||
return tu;
|
return tu;
|
||||||
|
@ -277,7 +330,7 @@ static struct trace_uprobe *find_probe_event(const char *event, const char *grou
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */
|
/* Unregister a trace_uprobe and probe_event */
|
||||||
static int unregister_trace_uprobe(struct trace_uprobe *tu)
|
static int unregister_trace_uprobe(struct trace_uprobe *tu)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -286,7 +339,7 @@ static int unregister_trace_uprobe(struct trace_uprobe *tu)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
list_del(&tu->list);
|
dyn_event_remove(&tu->devent);
|
||||||
free_trace_uprobe(tu);
|
free_trace_uprobe(tu);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -302,13 +355,14 @@ static int unregister_trace_uprobe(struct trace_uprobe *tu)
|
||||||
*/
|
*/
|
||||||
static struct trace_uprobe *find_old_trace_uprobe(struct trace_uprobe *new)
|
static struct trace_uprobe *find_old_trace_uprobe(struct trace_uprobe *new)
|
||||||
{
|
{
|
||||||
|
struct dyn_event *pos;
|
||||||
struct trace_uprobe *tmp, *old = NULL;
|
struct trace_uprobe *tmp, *old = NULL;
|
||||||
struct inode *new_inode = d_real_inode(new->path.dentry);
|
struct inode *new_inode = d_real_inode(new->path.dentry);
|
||||||
|
|
||||||
old = find_probe_event(trace_event_name(&new->tp.call),
|
old = find_probe_event(trace_event_name(&new->tp.call),
|
||||||
new->tp.call.class->system);
|
new->tp.call.class->system);
|
||||||
|
|
||||||
list_for_each_entry(tmp, &uprobe_list, list) {
|
for_each_trace_uprobe(tmp, pos) {
|
||||||
if ((old ? old != tmp : true) &&
|
if ((old ? old != tmp : true) &&
|
||||||
new_inode == d_real_inode(tmp->path.dentry) &&
|
new_inode == d_real_inode(tmp->path.dentry) &&
|
||||||
new->offset == tmp->offset &&
|
new->offset == tmp->offset &&
|
||||||
|
@ -326,7 +380,7 @@ static int register_trace_uprobe(struct trace_uprobe *tu)
|
||||||
struct trace_uprobe *old_tu;
|
struct trace_uprobe *old_tu;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&uprobe_lock);
|
mutex_lock(&event_mutex);
|
||||||
|
|
||||||
/* register as an event */
|
/* register as an event */
|
||||||
old_tu = find_old_trace_uprobe(tu);
|
old_tu = find_old_trace_uprobe(tu);
|
||||||
|
@ -348,10 +402,10 @@ static int register_trace_uprobe(struct trace_uprobe *tu)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_add_tail(&tu->list, &uprobe_list);
|
dyn_event_add(&tu->devent);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
mutex_unlock(&uprobe_lock);
|
mutex_unlock(&event_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -362,91 +416,49 @@ end:
|
||||||
*
|
*
|
||||||
* - Remove uprobe: -:[GRP/]EVENT
|
* - Remove uprobe: -:[GRP/]EVENT
|
||||||
*/
|
*/
|
||||||
static int create_trace_uprobe(int argc, char **argv)
|
static int trace_uprobe_create(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
struct trace_uprobe *tu;
|
struct trace_uprobe *tu;
|
||||||
char *arg, *event, *group, *filename, *rctr, *rctr_end;
|
const char *event = NULL, *group = UPROBE_EVENT_SYSTEM;
|
||||||
|
char *arg, *filename, *rctr, *rctr_end, *tmp;
|
||||||
char buf[MAX_EVENT_NAME_LEN];
|
char buf[MAX_EVENT_NAME_LEN];
|
||||||
struct path path;
|
struct path path;
|
||||||
unsigned long offset, ref_ctr_offset;
|
unsigned long offset, ref_ctr_offset;
|
||||||
bool is_delete, is_return;
|
bool is_return = false;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
is_delete = false;
|
|
||||||
is_return = false;
|
|
||||||
event = NULL;
|
|
||||||
group = NULL;
|
|
||||||
ref_ctr_offset = 0;
|
ref_ctr_offset = 0;
|
||||||
|
|
||||||
/* argc must be >= 1 */
|
/* argc must be >= 1 */
|
||||||
if (argv[0][0] == '-')
|
if (argv[0][0] == 'r')
|
||||||
is_delete = true;
|
|
||||||
else if (argv[0][0] == 'r')
|
|
||||||
is_return = true;
|
is_return = true;
|
||||||
else if (argv[0][0] != 'p') {
|
else if (argv[0][0] != 'p' || argc < 2)
|
||||||
pr_info("Probe definition must be started with 'p', 'r' or '-'.\n");
|
return -ECANCELED;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argv[0][1] == ':') {
|
if (argv[0][1] == ':')
|
||||||
event = &argv[0][2];
|
event = &argv[0][2];
|
||||||
arg = strchr(event, '/');
|
|
||||||
|
|
||||||
if (arg) {
|
if (!strchr(argv[1], '/'))
|
||||||
group = event;
|
return -ECANCELED;
|
||||||
event = arg + 1;
|
|
||||||
event[-1] = '\0';
|
|
||||||
|
|
||||||
if (strlen(group) == 0) {
|
filename = kstrdup(argv[1], GFP_KERNEL);
|
||||||
pr_info("Group name is not specified\n");
|
if (!filename)
|
||||||
return -EINVAL;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strlen(event) == 0) {
|
|
||||||
pr_info("Event name is not specified\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!group)
|
|
||||||
group = UPROBE_EVENT_SYSTEM;
|
|
||||||
|
|
||||||
if (is_delete) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!event) {
|
|
||||||
pr_info("Delete command needs an event name.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&uprobe_lock);
|
|
||||||
tu = find_probe_event(event, group);
|
|
||||||
|
|
||||||
if (!tu) {
|
|
||||||
mutex_unlock(&uprobe_lock);
|
|
||||||
pr_info("Event %s/%s doesn't exist.\n", group, event);
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
/* delete an event */
|
|
||||||
ret = unregister_trace_uprobe(tu);
|
|
||||||
mutex_unlock(&uprobe_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
pr_info("Probe point is not specified.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
/* Find the last occurrence, in case the path contains ':' too. */
|
/* Find the last occurrence, in case the path contains ':' too. */
|
||||||
arg = strrchr(argv[1], ':');
|
arg = strrchr(filename, ':');
|
||||||
if (!arg)
|
if (!arg || !isdigit(arg[1])) {
|
||||||
return -EINVAL;
|
kfree(filename);
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
*arg++ = '\0';
|
*arg++ = '\0';
|
||||||
filename = argv[1];
|
|
||||||
ret = kern_path(filename, LOOKUP_FOLLOW, &path);
|
ret = kern_path(filename, LOOKUP_FOLLOW, &path);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
kfree(filename);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
if (!d_is_reg(path.dentry)) {
|
if (!d_is_reg(path.dentry)) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail_address_parse;
|
goto fail_address_parse;
|
||||||
|
@ -480,7 +492,11 @@ static int create_trace_uprobe(int argc, char **argv)
|
||||||
argv += 2;
|
argv += 2;
|
||||||
|
|
||||||
/* setup a probe */
|
/* setup a probe */
|
||||||
if (!event) {
|
if (event) {
|
||||||
|
ret = traceprobe_parse_event_name(&event, &group, buf);
|
||||||
|
if (ret)
|
||||||
|
goto fail_address_parse;
|
||||||
|
} else {
|
||||||
char *tail;
|
char *tail;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
|
@ -508,18 +524,19 @@ static int create_trace_uprobe(int argc, char **argv)
|
||||||
tu->offset = offset;
|
tu->offset = offset;
|
||||||
tu->ref_ctr_offset = ref_ctr_offset;
|
tu->ref_ctr_offset = ref_ctr_offset;
|
||||||
tu->path = path;
|
tu->path = path;
|
||||||
tu->filename = kstrdup(filename, GFP_KERNEL);
|
tu->filename = filename;
|
||||||
|
|
||||||
if (!tu->filename) {
|
|
||||||
pr_info("Failed to allocate filename.\n");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse arguments */
|
/* parse arguments */
|
||||||
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
|
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
|
||||||
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i],
|
tmp = kstrdup(argv[i], GFP_KERNEL);
|
||||||
|
if (!tmp) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = traceprobe_parse_probe_arg(&tu->tp, i, tmp,
|
||||||
is_return ? TPARG_FL_RETURN : 0);
|
is_return ? TPARG_FL_RETURN : 0);
|
||||||
|
kfree(tmp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -535,55 +552,35 @@ error:
|
||||||
|
|
||||||
fail_address_parse:
|
fail_address_parse:
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
|
kfree(filename);
|
||||||
|
|
||||||
pr_info("Failed to parse address or file.\n");
|
pr_info("Failed to parse address or file.\n");
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cleanup_all_probes(void)
|
static int create_or_delete_trace_uprobe(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct trace_uprobe *tu;
|
int ret;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
mutex_lock(&uprobe_lock);
|
if (argv[0][0] == '-')
|
||||||
/* Ensure no probe is in use. */
|
return dyn_event_release(argc, argv, &trace_uprobe_ops);
|
||||||
list_for_each_entry(tu, &uprobe_list, list)
|
|
||||||
if (trace_probe_is_enabled(&tu->tp)) {
|
ret = trace_uprobe_create(argc, (const char **)argv);
|
||||||
ret = -EBUSY;
|
return ret == -ECANCELED ? -EINVAL : ret;
|
||||||
goto end;
|
}
|
||||||
}
|
|
||||||
while (!list_empty(&uprobe_list)) {
|
static int trace_uprobe_release(struct dyn_event *ev)
|
||||||
tu = list_entry(uprobe_list.next, struct trace_uprobe, list);
|
{
|
||||||
ret = unregister_trace_uprobe(tu);
|
struct trace_uprobe *tu = to_trace_uprobe(ev);
|
||||||
if (ret)
|
|
||||||
break;
|
return unregister_trace_uprobe(tu);
|
||||||
}
|
|
||||||
end:
|
|
||||||
mutex_unlock(&uprobe_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Probes listing interfaces */
|
/* Probes listing interfaces */
|
||||||
static void *probes_seq_start(struct seq_file *m, loff_t *pos)
|
static int trace_uprobe_show(struct seq_file *m, struct dyn_event *ev)
|
||||||
{
|
{
|
||||||
mutex_lock(&uprobe_lock);
|
struct trace_uprobe *tu = to_trace_uprobe(ev);
|
||||||
return seq_list_start(&uprobe_list, *pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *probes_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
|
||||||
{
|
|
||||||
return seq_list_next(v, &uprobe_list, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void probes_seq_stop(struct seq_file *m, void *v)
|
|
||||||
{
|
|
||||||
mutex_unlock(&uprobe_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int probes_seq_show(struct seq_file *m, void *v)
|
|
||||||
{
|
|
||||||
struct trace_uprobe *tu = v;
|
|
||||||
char c = is_ret_probe(tu) ? 'r' : 'p';
|
char c = is_ret_probe(tu) ? 'r' : 'p';
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -601,11 +598,21 @@ static int probes_seq_show(struct seq_file *m, void *v)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int probes_seq_show(struct seq_file *m, void *v)
|
||||||
|
{
|
||||||
|
struct dyn_event *ev = v;
|
||||||
|
|
||||||
|
if (!is_trace_uprobe(ev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return trace_uprobe_show(m, ev);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct seq_operations probes_seq_op = {
|
static const struct seq_operations probes_seq_op = {
|
||||||
.start = probes_seq_start,
|
.start = dyn_event_seq_start,
|
||||||
.next = probes_seq_next,
|
.next = dyn_event_seq_next,
|
||||||
.stop = probes_seq_stop,
|
.stop = dyn_event_seq_stop,
|
||||||
.show = probes_seq_show
|
.show = probes_seq_show
|
||||||
};
|
};
|
||||||
|
|
||||||
static int probes_open(struct inode *inode, struct file *file)
|
static int probes_open(struct inode *inode, struct file *file)
|
||||||
|
@ -613,7 +620,7 @@ static int probes_open(struct inode *inode, struct file *file)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
|
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
|
||||||
ret = cleanup_all_probes();
|
ret = dyn_events_release_all(&trace_uprobe_ops);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -624,7 +631,8 @@ static int probes_open(struct inode *inode, struct file *file)
|
||||||
static ssize_t probes_write(struct file *file, const char __user *buffer,
|
static ssize_t probes_write(struct file *file, const char __user *buffer,
|
||||||
size_t count, loff_t *ppos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
return trace_parse_run_command(file, buffer, count, ppos, create_trace_uprobe);
|
return trace_parse_run_command(file, buffer, count, ppos,
|
||||||
|
create_or_delete_trace_uprobe);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations uprobe_events_ops = {
|
static const struct file_operations uprobe_events_ops = {
|
||||||
|
@ -639,17 +647,22 @@ static const struct file_operations uprobe_events_ops = {
|
||||||
/* Probes profiling interfaces */
|
/* Probes profiling interfaces */
|
||||||
static int probes_profile_seq_show(struct seq_file *m, void *v)
|
static int probes_profile_seq_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
struct trace_uprobe *tu = v;
|
struct dyn_event *ev = v;
|
||||||
|
struct trace_uprobe *tu;
|
||||||
|
|
||||||
|
if (!is_trace_uprobe(ev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tu = to_trace_uprobe(ev);
|
||||||
seq_printf(m, " %s %-44s %15lu\n", tu->filename,
|
seq_printf(m, " %s %-44s %15lu\n", tu->filename,
|
||||||
trace_event_name(&tu->tp.call), tu->nhit);
|
trace_event_name(&tu->tp.call), tu->nhit);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct seq_operations profile_seq_op = {
|
static const struct seq_operations profile_seq_op = {
|
||||||
.start = probes_seq_start,
|
.start = dyn_event_seq_start,
|
||||||
.next = probes_seq_next,
|
.next = dyn_event_seq_next,
|
||||||
.stop = probes_seq_stop,
|
.stop = dyn_event_seq_stop,
|
||||||
.show = probes_profile_seq_show
|
.show = probes_profile_seq_show
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1307,7 +1320,7 @@ static int register_uprobe_event(struct trace_uprobe *tu)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = trace_add_event_call(call);
|
ret = trace_add_event_call_nolock(call);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_info("Failed to register uprobe event: %s\n",
|
pr_info("Failed to register uprobe event: %s\n",
|
||||||
|
@ -1324,7 +1337,7 @@ static int unregister_uprobe_event(struct trace_uprobe *tu)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* tu->event is unregistered in trace_remove_event_call() */
|
/* tu->event is unregistered in trace_remove_event_call() */
|
||||||
ret = trace_remove_event_call(&tu->tp.call);
|
ret = trace_remove_event_call_nolock(&tu->tp.call);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
kfree(tu->tp.call.print_fmt);
|
kfree(tu->tp.call.print_fmt);
|
||||||
|
@ -1351,7 +1364,7 @@ create_local_trace_uprobe(char *name, unsigned long offs,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* local trace_kprobes are not added to probe_list, so they are never
|
* local trace_kprobes are not added to dyn_event, so they are never
|
||||||
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
||||||
* duplicated name "DUMMY_EVENT" here.
|
* duplicated name "DUMMY_EVENT" here.
|
||||||
*/
|
*/
|
||||||
|
@ -1399,6 +1412,11 @@ void destroy_local_trace_uprobe(struct trace_event_call *event_call)
|
||||||
static __init int init_uprobe_trace(void)
|
static __init int init_uprobe_trace(void)
|
||||||
{
|
{
|
||||||
struct dentry *d_tracer;
|
struct dentry *d_tracer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dyn_event_register(&trace_uprobe_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
d_tracer = tracing_init_dentry();
|
d_tracer = tracing_init_dentry();
|
||||||
if (IS_ERR(d_tracer))
|
if (IS_ERR(d_tracer))
|
||||||
|
|
Загрузка…
Ссылка в новой задаче