Skip to content

Commit ca89bc0

Browse files
mhiramatrostedt
authored andcommitted
tracing/kprobe: Add multi-probe per event support
Add multi-probe per one event support to kprobe events. User can define several different probes on one trace event if those events have same "event signature", e.g. # echo p:testevent _do_fork > kprobe_events # echo p:testevent fork_idle >> kprobe_events # kprobe_events p:kprobes/testevent _do_fork p:kprobes/testevent fork_idle The event signature is defined by kprobe type (retprobe or not), the number of args, argument names, and argument types. Note that this only support appending method. Delete event operation will delete all probes on the event. Link: http://lkml.kernel.org/r/156095686913.28024.9357292202316540742.stgit@devnote2 Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 3019913 commit ca89bc0

File tree

4 files changed

+111
-18
lines changed

4 files changed

+111
-18
lines changed

kernel/trace/trace.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4815,11 +4815,11 @@ static const char readme_msg[] =
48154815
#endif
48164816
#endif /* CONFIG_STACK_TRACER */
48174817
#ifdef CONFIG_DYNAMIC_EVENTS
4818-
" dynamic_events\t\t- Add/remove/show the generic dynamic events\n"
4818+
" dynamic_events\t\t- Create/append/remove/show the generic dynamic events\n"
48194819
"\t\t\t Write into this file to define/undefine new trace events.\n"
48204820
#endif
48214821
#ifdef CONFIG_KPROBE_EVENTS
4822-
" kprobe_events\t\t- Add/remove/show the kernel dynamic events\n"
4822+
" kprobe_events\t\t- Create/append/remove/show the kernel dynamic events\n"
48234823
"\t\t\t Write into this file to define/undefine new trace events.\n"
48244824
#endif
48254825
#ifdef CONFIG_UPROBE_EVENTS

kernel/trace/trace_kprobe.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,10 @@ static void __unregister_trace_kprobe(struct trace_kprobe *tk)
492492
/* Unregister a trace_probe and probe_event */
493493
static int unregister_trace_kprobe(struct trace_kprobe *tk)
494494
{
495+
/* If other probes are on the event, just unregister kprobe */
496+
if (trace_probe_has_sibling(&tk->tp))
497+
goto unreg;
498+
495499
/* Enabled event can not be unregistered */
496500
if (trace_probe_is_enabled(&tk->tp))
497501
return -EBUSY;
@@ -500,12 +504,38 @@ static int unregister_trace_kprobe(struct trace_kprobe *tk)
500504
if (unregister_kprobe_event(tk))
501505
return -EBUSY;
502506

507+
unreg:
503508
__unregister_trace_kprobe(tk);
504509
dyn_event_remove(&tk->devent);
510+
trace_probe_unlink(&tk->tp);
505511

506512
return 0;
507513
}
508514

515+
static int append_trace_kprobe(struct trace_kprobe *tk, struct trace_kprobe *to)
516+
{
517+
int ret;
518+
519+
/* Append to existing event */
520+
ret = trace_probe_append(&tk->tp, &to->tp);
521+
if (ret)
522+
return ret;
523+
524+
/* Register k*probe */
525+
ret = __register_trace_kprobe(tk);
526+
if (ret == -ENOENT && !trace_kprobe_module_exist(tk)) {
527+
pr_warn("This probe might be able to register after target module is loaded. Continue.\n");
528+
ret = 0;
529+
}
530+
531+
if (ret)
532+
trace_probe_unlink(&tk->tp);
533+
else
534+
dyn_event_add(&tk->devent);
535+
536+
return ret;
537+
}
538+
509539
/* Register a trace_probe and probe_event */
510540
static int register_trace_kprobe(struct trace_kprobe *tk)
511541
{
@@ -514,14 +544,24 @@ static int register_trace_kprobe(struct trace_kprobe *tk)
514544

515545
mutex_lock(&event_mutex);
516546

517-
/* Delete old (same name) event if exist */
518547
old_tk = find_trace_kprobe(trace_probe_name(&tk->tp),
519548
trace_probe_group_name(&tk->tp));
520549
if (old_tk) {
521-
ret = unregister_trace_kprobe(old_tk);
522-
if (ret < 0)
523-
goto end;
524-
free_trace_kprobe(old_tk);
550+
if (trace_kprobe_is_return(tk) != trace_kprobe_is_return(old_tk)) {
551+
trace_probe_log_set_index(0);
552+
trace_probe_log_err(0, DIFF_PROBE_TYPE);
553+
ret = -EEXIST;
554+
} else {
555+
ret = trace_probe_compare_arg_type(&tk->tp, &old_tk->tp);
556+
if (ret) {
557+
/* Note that argument starts index = 2 */
558+
trace_probe_log_set_index(ret + 1);
559+
trace_probe_log_err(0, DIFF_ARG_TYPE);
560+
ret = -EEXIST;
561+
} else
562+
ret = append_trace_kprobe(tk, old_tk);
563+
}
564+
goto end;
525565
}
526566

527567
/* Register new event */
@@ -755,7 +795,7 @@ static int trace_kprobe_create(int argc, const char *argv[])
755795
trace_probe_log_err(0, BAD_INSN_BNDRY);
756796
else if (ret == -ENOENT)
757797
trace_probe_log_err(0, BAD_PROBE_ADDR);
758-
else if (ret != -ENOMEM)
798+
else if (ret != -ENOMEM && ret != -EEXIST)
759799
trace_probe_log_err(0, FAIL_REG_PROBE);
760800
goto error;
761801
}

kernel/trace/trace_probe.c

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,35 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
886886
return 0;
887887
}
888888

889+
static void trace_probe_event_free(struct trace_probe_event *tpe)
890+
{
891+
kfree(tpe->class.system);
892+
kfree(tpe->call.name);
893+
kfree(tpe->call.print_fmt);
894+
kfree(tpe);
895+
}
896+
897+
int trace_probe_append(struct trace_probe *tp, struct trace_probe *to)
898+
{
899+
if (trace_probe_has_sibling(tp))
900+
return -EBUSY;
901+
902+
list_del_init(&tp->list);
903+
trace_probe_event_free(tp->event);
904+
905+
tp->event = to->event;
906+
list_add_tail(&tp->list, trace_probe_probe_list(to));
907+
908+
return 0;
909+
}
910+
911+
void trace_probe_unlink(struct trace_probe *tp)
912+
{
913+
list_del_init(&tp->list);
914+
if (list_empty(trace_probe_probe_list(tp)))
915+
trace_probe_event_free(tp->event);
916+
tp->event = NULL;
917+
}
889918

890919
void trace_probe_cleanup(struct trace_probe *tp)
891920
{
@@ -894,15 +923,8 @@ void trace_probe_cleanup(struct trace_probe *tp)
894923
for (i = 0; i < tp->nr_args; i++)
895924
traceprobe_free_probe_arg(&tp->args[i]);
896925

897-
if (tp->event) {
898-
struct trace_event_call *call = trace_probe_event_call(tp);
899-
900-
kfree(tp->event->class.system);
901-
kfree(call->name);
902-
kfree(call->print_fmt);
903-
kfree(tp->event);
904-
tp->event = NULL;
905-
}
926+
if (tp->event)
927+
trace_probe_unlink(tp);
906928
}
907929

908930
int trace_probe_init(struct trace_probe *tp, const char *event,
@@ -1006,3 +1028,22 @@ int trace_probe_remove_file(struct trace_probe *tp,
10061028

10071029
return 0;
10081030
}
1031+
1032+
/*
1033+
* Return the smallest index of different type argument (start from 1).
1034+
* If all argument types and name are same, return 0.
1035+
*/
1036+
int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b)
1037+
{
1038+
int i;
1039+
1040+
for (i = 0; i < a->nr_args; i++) {
1041+
if ((b->nr_args <= i) ||
1042+
((a->args[i].type != b->args[i].type) ||
1043+
(a->args[i].count != b->args[i].count) ||
1044+
strcmp(a->args[i].name, b->args[i].name)))
1045+
return i + 1;
1046+
}
1047+
1048+
return 0;
1049+
}

kernel/trace/trace_probe.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ static inline struct list_head *trace_probe_probe_list(struct trace_probe *tp)
302302
return &tp->event->probes;
303303
}
304304

305+
static inline bool trace_probe_has_sibling(struct trace_probe *tp)
306+
{
307+
struct list_head *list = trace_probe_probe_list(tp);
308+
309+
return !list_empty(list) && !list_is_singular(list);
310+
}
311+
305312
static inline int trace_probe_unregister_event_call(struct trace_probe *tp)
306313
{
307314
/* tp->event is unregistered in trace_remove_event_call() */
@@ -316,12 +323,15 @@ static inline bool trace_probe_has_single_file(struct trace_probe *tp)
316323
int trace_probe_init(struct trace_probe *tp, const char *event,
317324
const char *group);
318325
void trace_probe_cleanup(struct trace_probe *tp);
326+
int trace_probe_append(struct trace_probe *tp, struct trace_probe *to);
327+
void trace_probe_unlink(struct trace_probe *tp);
319328
int trace_probe_register_event_call(struct trace_probe *tp);
320329
int trace_probe_add_file(struct trace_probe *tp, struct trace_event_file *file);
321330
int trace_probe_remove_file(struct trace_probe *tp,
322331
struct trace_event_file *file);
323332
struct event_file_link *trace_probe_get_file_link(struct trace_probe *tp,
324333
struct trace_event_file *file);
334+
int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b);
325335

326336
#define trace_probe_for_each_link(pos, tp) \
327337
list_for_each_entry(pos, &(tp)->event->files, list)
@@ -419,7 +429,9 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
419429
C(ARG_TOO_LONG, "Argument expression is too long"), \
420430
C(NO_ARG_BODY, "No argument expression"), \
421431
C(BAD_INSN_BNDRY, "Probe point is not an instruction boundary"),\
422-
C(FAIL_REG_PROBE, "Failed to register probe event"),
432+
C(FAIL_REG_PROBE, "Failed to register probe event"),\
433+
C(DIFF_PROBE_TYPE, "Probe type is different from existing probe"),\
434+
C(DIFF_ARG_TYPE, "Argument type or name is different from existing probe"),
423435

424436
#undef C
425437
#define C(a, b) TP_ERR_##a

0 commit comments

Comments
 (0)