Skip to content

Commit 33ea4b2

Browse files
liu-song-6Ingo Molnar
authored andcommitted
perf/core: Implement the 'perf_uprobe' PMU
This patch adds perf_uprobe support with similar pattern as previous patch (for kprobe). Two functions, create_local_trace_uprobe() and destroy_local_trace_uprobe(), are created so a uprobe can be created and attached to the file descriptor created by perf_event_open(). Signed-off-by: Song Liu <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Yonghong Song <[email protected]> Reviewed-by: Josef Bacik <[email protected]> Cc: <[email protected]> Cc: <[email protected]> Cc: <[email protected]> Cc: <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent e12f03d commit 33ea4b2

File tree

5 files changed

+186
-9
lines changed

5 files changed

+186
-9
lines changed

include/linux/trace_events.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@ extern void perf_trace_del(struct perf_event *event, int flags);
537537
extern int perf_kprobe_init(struct perf_event *event, bool is_retprobe);
538538
extern void perf_kprobe_destroy(struct perf_event *event);
539539
#endif
540+
#ifdef CONFIG_UPROBE_EVENTS
541+
extern int perf_uprobe_init(struct perf_event *event, bool is_retprobe);
542+
extern void perf_uprobe_destroy(struct perf_event *event);
543+
#endif
540544
extern int ftrace_profile_set_filter(struct perf_event *event, int event_id,
541545
char *filter_str);
542546
extern void ftrace_profile_free_filter(struct perf_event *event);

kernel/events/core.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7992,7 +7992,7 @@ static struct pmu perf_tracepoint = {
79927992
.read = perf_swevent_read,
79937993
};
79947994

7995-
#ifdef CONFIG_KPROBE_EVENTS
7995+
#if defined(CONFIG_KPROBE_EVENTS) || defined(CONFIG_UPROBE_EVENTS)
79967996
/*
79977997
* Flags in config, used by dynamic PMU kprobe and uprobe
79987998
* The flags should match following PMU_FORMAT_ATTR().
@@ -8020,7 +8020,9 @@ static const struct attribute_group *probe_attr_groups[] = {
80208020
&probe_format_group,
80218021
NULL,
80228022
};
8023+
#endif
80238024

8025+
#ifdef CONFIG_KPROBE_EVENTS
80248026
static int perf_kprobe_event_init(struct perf_event *event);
80258027
static struct pmu perf_kprobe = {
80268028
.task_ctx_nr = perf_sw_context,
@@ -8057,12 +8059,52 @@ static int perf_kprobe_event_init(struct perf_event *event)
80578059
}
80588060
#endif /* CONFIG_KPROBE_EVENTS */
80598061

8062+
#ifdef CONFIG_UPROBE_EVENTS
8063+
static int perf_uprobe_event_init(struct perf_event *event);
8064+
static struct pmu perf_uprobe = {
8065+
.task_ctx_nr = perf_sw_context,
8066+
.event_init = perf_uprobe_event_init,
8067+
.add = perf_trace_add,
8068+
.del = perf_trace_del,
8069+
.start = perf_swevent_start,
8070+
.stop = perf_swevent_stop,
8071+
.read = perf_swevent_read,
8072+
.attr_groups = probe_attr_groups,
8073+
};
8074+
8075+
static int perf_uprobe_event_init(struct perf_event *event)
8076+
{
8077+
int err;
8078+
bool is_retprobe;
8079+
8080+
if (event->attr.type != perf_uprobe.type)
8081+
return -ENOENT;
8082+
/*
8083+
* no branch sampling for probe events
8084+
*/
8085+
if (has_branch_stack(event))
8086+
return -EOPNOTSUPP;
8087+
8088+
is_retprobe = event->attr.config & PERF_PROBE_CONFIG_IS_RETPROBE;
8089+
err = perf_uprobe_init(event, is_retprobe);
8090+
if (err)
8091+
return err;
8092+
8093+
event->destroy = perf_uprobe_destroy;
8094+
8095+
return 0;
8096+
}
8097+
#endif /* CONFIG_UPROBE_EVENTS */
8098+
80608099
static inline void perf_tp_register(void)
80618100
{
80628101
perf_pmu_register(&perf_tracepoint, "tracepoint", PERF_TYPE_TRACEPOINT);
80638102
#ifdef CONFIG_KPROBE_EVENTS
80648103
perf_pmu_register(&perf_kprobe, "kprobe", -1);
80658104
#endif
8105+
#ifdef CONFIG_UPROBE_EVENTS
8106+
perf_pmu_register(&perf_uprobe, "uprobe", -1);
8107+
#endif
80668108
}
80678109

80688110
static void perf_event_free_filter(struct perf_event *event)
@@ -8150,6 +8192,10 @@ static inline bool perf_event_is_tracing(struct perf_event *event)
81508192
#ifdef CONFIG_KPROBE_EVENTS
81518193
if (event->pmu == &perf_kprobe)
81528194
return true;
8195+
#endif
8196+
#ifdef CONFIG_UPROBE_EVENTS
8197+
if (event->pmu == &perf_uprobe)
8198+
return true;
81538199
#endif
81548200
return false;
81558201
}

kernel/trace/trace_event_perf.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,59 @@ void perf_kprobe_destroy(struct perf_event *p_event)
286286
}
287287
#endif /* CONFIG_KPROBE_EVENTS */
288288

289+
#ifdef CONFIG_UPROBE_EVENTS
290+
int perf_uprobe_init(struct perf_event *p_event, bool is_retprobe)
291+
{
292+
int ret;
293+
char *path = NULL;
294+
struct trace_event_call *tp_event;
295+
296+
if (!p_event->attr.uprobe_path)
297+
return -EINVAL;
298+
path = kzalloc(PATH_MAX, GFP_KERNEL);
299+
if (!path)
300+
return -ENOMEM;
301+
ret = strncpy_from_user(
302+
path, u64_to_user_ptr(p_event->attr.uprobe_path), PATH_MAX);
303+
if (ret < 0)
304+
goto out;
305+
if (path[0] == '\0') {
306+
ret = -EINVAL;
307+
goto out;
308+
}
309+
310+
tp_event = create_local_trace_uprobe(
311+
path, p_event->attr.probe_offset, is_retprobe);
312+
if (IS_ERR(tp_event)) {
313+
ret = PTR_ERR(tp_event);
314+
goto out;
315+
}
316+
317+
/*
318+
* local trace_uprobe need to hold event_mutex to call
319+
* uprobe_buffer_enable() and uprobe_buffer_disable().
320+
* event_mutex is not required for local trace_kprobes.
321+
*/
322+
mutex_lock(&event_mutex);
323+
ret = perf_trace_event_init(tp_event, p_event);
324+
if (ret)
325+
destroy_local_trace_uprobe(tp_event);
326+
mutex_unlock(&event_mutex);
327+
out:
328+
kfree(path);
329+
return ret;
330+
}
331+
332+
void perf_uprobe_destroy(struct perf_event *p_event)
333+
{
334+
mutex_lock(&event_mutex);
335+
perf_trace_event_close(p_event);
336+
perf_trace_event_unreg(p_event);
337+
mutex_unlock(&event_mutex);
338+
destroy_local_trace_uprobe(p_event->tp_event);
339+
}
340+
#endif /* CONFIG_UPROBE_EVENTS */
341+
289342
int perf_trace_add(struct perf_event *p_event, int flags)
290343
{
291344
struct trace_event_call *tp_event = p_event->tp_event;

kernel/trace/trace_probe.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,8 @@ extern struct trace_event_call *
410410
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
411411
bool is_return);
412412
extern void destroy_local_trace_kprobe(struct trace_event_call *event_call);
413+
414+
extern struct trace_event_call *
415+
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return);
416+
extern void destroy_local_trace_uprobe(struct trace_event_call *event_call);
413417
#endif

kernel/trace/trace_uprobe.c

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,16 +1292,25 @@ static struct trace_event_functions uprobe_funcs = {
12921292
.trace = print_uprobe_event
12931293
};
12941294

1295-
static int register_uprobe_event(struct trace_uprobe *tu)
1295+
static inline void init_trace_event_call(struct trace_uprobe *tu,
1296+
struct trace_event_call *call)
12961297
{
1297-
struct trace_event_call *call = &tu->tp.call;
1298-
int ret;
1299-
1300-
/* Initialize trace_event_call */
13011298
INIT_LIST_HEAD(&call->class->fields);
13021299
call->event.funcs = &uprobe_funcs;
13031300
call->class->define_fields = uprobe_event_define_fields;
13041301

1302+
call->flags = TRACE_EVENT_FL_UPROBE;
1303+
call->class->reg = trace_uprobe_register;
1304+
call->data = tu;
1305+
}
1306+
1307+
static int register_uprobe_event(struct trace_uprobe *tu)
1308+
{
1309+
struct trace_event_call *call = &tu->tp.call;
1310+
int ret = 0;
1311+
1312+
init_trace_event_call(tu, call);
1313+
13051314
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0)
13061315
return -ENOMEM;
13071316

@@ -1311,9 +1320,6 @@ static int register_uprobe_event(struct trace_uprobe *tu)
13111320
return -ENODEV;
13121321
}
13131322

1314-
call->flags = TRACE_EVENT_FL_UPROBE;
1315-
call->class->reg = trace_uprobe_register;
1316-
call->data = tu;
13171323
ret = trace_add_event_call(call);
13181324

13191325
if (ret) {
@@ -1339,6 +1345,70 @@ static int unregister_uprobe_event(struct trace_uprobe *tu)
13391345
return 0;
13401346
}
13411347

1348+
#ifdef CONFIG_PERF_EVENTS
1349+
struct trace_event_call *
1350+
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return)
1351+
{
1352+
struct trace_uprobe *tu;
1353+
struct inode *inode;
1354+
struct path path;
1355+
int ret;
1356+
1357+
ret = kern_path(name, LOOKUP_FOLLOW, &path);
1358+
if (ret)
1359+
return ERR_PTR(ret);
1360+
1361+
inode = igrab(d_inode(path.dentry));
1362+
path_put(&path);
1363+
1364+
if (!inode || !S_ISREG(inode->i_mode)) {
1365+
iput(inode);
1366+
return ERR_PTR(-EINVAL);
1367+
}
1368+
1369+
/*
1370+
* local trace_kprobes are not added to probe_list, so they are never
1371+
* searched in find_trace_kprobe(). Therefore, there is no concern of
1372+
* duplicated name "DUMMY_EVENT" here.
1373+
*/
1374+
tu = alloc_trace_uprobe(UPROBE_EVENT_SYSTEM, "DUMMY_EVENT", 0,
1375+
is_return);
1376+
1377+
if (IS_ERR(tu)) {
1378+
pr_info("Failed to allocate trace_uprobe.(%d)\n",
1379+
(int)PTR_ERR(tu));
1380+
return ERR_CAST(tu);
1381+
}
1382+
1383+
tu->offset = offs;
1384+
tu->inode = inode;
1385+
tu->filename = kstrdup(name, GFP_KERNEL);
1386+
init_trace_event_call(tu, &tu->tp.call);
1387+
1388+
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0) {
1389+
ret = -ENOMEM;
1390+
goto error;
1391+
}
1392+
1393+
return &tu->tp.call;
1394+
error:
1395+
free_trace_uprobe(tu);
1396+
return ERR_PTR(ret);
1397+
}
1398+
1399+
void destroy_local_trace_uprobe(struct trace_event_call *event_call)
1400+
{
1401+
struct trace_uprobe *tu;
1402+
1403+
tu = container_of(event_call, struct trace_uprobe, tp.call);
1404+
1405+
kfree(tu->tp.call.print_fmt);
1406+
tu->tp.call.print_fmt = NULL;
1407+
1408+
free_trace_uprobe(tu);
1409+
}
1410+
#endif /* CONFIG_PERF_EVENTS */
1411+
13421412
/* Make a trace interface for controling probe points */
13431413
static __init int init_uprobe_trace(void)
13441414
{

0 commit comments

Comments
 (0)