Skip to content

Commit e2d0d7b

Browse files
committed
tracing/probes: Add tracepoint support on fprobe_events
Allow fprobe_events to trace raw tracepoints so that user can trace tracepoints which don't have traceevent wrappers. This new event is always available if the fprobe_events is enabled (thus no kconfig), because the fprobe_events depends on the trace-event and traceporint. e.g. # echo 't sched_overutilized_tp' >> dynamic_events # echo 't 9p_client_req' >> dynamic_events # cat dynamic_events t:tracepoints/sched_overutilized_tp sched_overutilized_tp t:tracepoints/_9p_client_req 9p_client_req The event name is based on the tracepoint name, but if it is started with digit character, an underscore '_' will be added. NOTE: to avoid further confusion, this renames TPARG_FL_TPOINT to TPARG_FL_TEVENT because this flag is used for eprobe (trace-event probe). And reuse TPARG_FL_TPOINT for this raw tracepoint probe. Link: https://lore.kernel.org/all/168507471874.913472.17214624519622959593.stgit@mhiramat.roam.corp.google.com/ Reported-by: kernel test robot <[email protected]> Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/ Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent 4d42a76 commit e2d0d7b

File tree

7 files changed

+157
-16
lines changed

7 files changed

+157
-16
lines changed

include/linux/tracepoint-defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct tracepoint {
3535
struct static_call_key *static_call_key;
3636
void *static_call_tramp;
3737
void *iterator;
38+
void *probestub;
3839
int (*regfunc)(void);
3940
void (*unregfunc)(void);
4041
struct tracepoint_func __rcu *funcs;

include/linux/tracepoint.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,15 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
303303
__section("__tracepoints_strings") = #_name; \
304304
extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \
305305
int __traceiter_##_name(void *__data, proto); \
306+
void __probestub_##_name(void *__data, proto); \
306307
struct tracepoint __tracepoint_##_name __used \
307308
__section("__tracepoints") = { \
308309
.name = __tpstrtab_##_name, \
309310
.key = STATIC_KEY_INIT_FALSE, \
310311
.static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \
311312
.static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \
312313
.iterator = &__traceiter_##_name, \
314+
.probestub = &__probestub_##_name, \
313315
.regfunc = _reg, \
314316
.unregfunc = _unreg, \
315317
.funcs = NULL }; \
@@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
330332
} \
331333
return 0; \
332334
} \
335+
void __probestub_##_name(void *__data, proto) \
336+
{ \
337+
} \
333338
DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name);
334339

335340
#define DEFINE_TRACE(name, proto, args) \

kernel/trace/trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5681,6 +5681,7 @@ static const char readme_msg[] =
56815681
#endif
56825682
#ifdef CONFIG_FPROBE_EVENTS
56835683
"\t f[:[<group>/][<event>]] <func-name>[%return] [<args>]\n"
5684+
"\t t[:[<group>/][<event>]] <tracepoint> [<args>]\n"
56845685
#endif
56855686
#ifdef CONFIG_HIST_TRIGGERS
56865687
"\t s:[synthetic/]<event> <field> [<field>]\n"

kernel/trace/trace_eprobe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ find_and_get_event(const char *system, const char *event_name)
817817

818818
static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[], int i)
819819
{
820-
unsigned int flags = TPARG_FL_KERNEL | TPARG_FL_TPOINT;
820+
unsigned int flags = TPARG_FL_KERNEL | TPARG_FL_TEVENT;
821821
int ret;
822822

823823
ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], flags);

kernel/trace/trace_fprobe.c

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/module.h>
1010
#include <linux/rculist.h>
1111
#include <linux/security.h>
12+
#include <linux/tracepoint.h>
1213
#include <linux/uaccess.h>
1314

1415
#include "trace_dynevent.h"
@@ -17,6 +18,7 @@
1718
#include "trace_probe_tmpl.h"
1819

1920
#define FPROBE_EVENT_SYSTEM "fprobes"
21+
#define TRACEPOINT_EVENT_SYSTEM "tracepoints"
2022
#define RETHOOK_MAXACTIVE_MAX 4096
2123

2224
static int trace_fprobe_create(const char *raw_command);
@@ -41,6 +43,8 @@ struct trace_fprobe {
4143
struct dyn_event devent;
4244
struct fprobe fp;
4345
const char *symbol;
46+
struct tracepoint *tpoint;
47+
struct module *mod;
4448
struct trace_probe tp;
4549
};
4650

@@ -68,6 +72,11 @@ static bool trace_fprobe_is_return(struct trace_fprobe *tf)
6872
return tf->fp.exit_handler != NULL;
6973
}
7074

75+
static bool trace_fprobe_is_tracepoint(struct trace_fprobe *tf)
76+
{
77+
return tf->tpoint != NULL;
78+
}
79+
7180
static const char *trace_fprobe_symbol(struct trace_fprobe *tf)
7281
{
7382
return tf->symbol ? tf->symbol : "unknown";
@@ -668,6 +677,21 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)
668677
else
669678
tf->fp.flags |= FPROBE_FL_DISABLED;
670679

680+
if (trace_fprobe_is_tracepoint(tf)) {
681+
struct tracepoint *tpoint = tf->tpoint;
682+
unsigned long ip = (unsigned long)tpoint->probestub;
683+
/*
684+
* Here, we do 2 steps to enable fprobe on a tracepoint.
685+
* At first, put __probestub_##TP function on the tracepoint
686+
* and put a fprobe on the stub function.
687+
*/
688+
ret = tracepoint_probe_register_prio_may_exist(tpoint,
689+
tpoint->probestub, NULL, 0);
690+
if (ret < 0)
691+
return ret;
692+
return register_fprobe_ips(&tf->fp, &ip, 1);
693+
}
694+
671695
/* TODO: handle filter, nofilter or symbol list */
672696
return register_fprobe(&tf->fp, tf->symbol, NULL);
673697
}
@@ -678,6 +702,12 @@ static void __unregister_trace_fprobe(struct trace_fprobe *tf)
678702
if (trace_fprobe_is_registered(tf)) {
679703
unregister_fprobe(&tf->fp);
680704
memset(&tf->fp, 0, sizeof(tf->fp));
705+
if (trace_fprobe_is_tracepoint(tf)) {
706+
tracepoint_probe_unregister(tf->tpoint,
707+
tf->tpoint->probestub, NULL);
708+
tf->tpoint = NULL;
709+
tf->mod = NULL;
710+
}
681711
}
682712
}
683713

@@ -741,7 +771,8 @@ static int append_trace_fprobe(struct trace_fprobe *tf, struct trace_fprobe *to)
741771
{
742772
int ret;
743773

744-
if (trace_fprobe_is_return(tf) != trace_fprobe_is_return(to)) {
774+
if (trace_fprobe_is_return(tf) != trace_fprobe_is_return(to) ||
775+
trace_fprobe_is_tracepoint(tf) != trace_fprobe_is_tracepoint(to)) {
745776
trace_probe_log_set_index(0);
746777
trace_probe_log_err(0, DIFF_PROBE_TYPE);
747778
return -EEXIST;
@@ -811,6 +842,60 @@ static int register_trace_fprobe(struct trace_fprobe *tf)
811842
return ret;
812843
}
813844

845+
#ifdef CONFIG_MODULES
846+
static int __tracepoint_probe_module_cb(struct notifier_block *self,
847+
unsigned long val, void *data)
848+
{
849+
struct tp_module *tp_mod = data;
850+
struct trace_fprobe *tf;
851+
struct dyn_event *pos;
852+
853+
if (val != MODULE_STATE_GOING)
854+
return NOTIFY_DONE;
855+
856+
mutex_lock(&event_mutex);
857+
for_each_trace_fprobe(tf, pos) {
858+
if (tp_mod->mod == tf->mod) {
859+
tracepoint_probe_unregister(tf->tpoint,
860+
tf->tpoint->probestub, NULL);
861+
tf->tpoint = NULL;
862+
tf->mod = NULL;
863+
}
864+
}
865+
mutex_unlock(&event_mutex);
866+
867+
return NOTIFY_DONE;
868+
}
869+
870+
static struct notifier_block tracepoint_module_nb = {
871+
.notifier_call = __tracepoint_probe_module_cb,
872+
};
873+
#endif /* CONFIG_MODULES */
874+
875+
struct __find_tracepoint_cb_data {
876+
const char *tp_name;
877+
struct tracepoint *tpoint;
878+
};
879+
880+
static void __find_tracepoint_cb(struct tracepoint *tp, void *priv)
881+
{
882+
struct __find_tracepoint_cb_data *data = priv;
883+
884+
if (!data->tpoint && !strcmp(data->tp_name, tp->name))
885+
data->tpoint = tp;
886+
}
887+
888+
static struct tracepoint *find_tracepoint(const char *tp_name)
889+
{
890+
struct __find_tracepoint_cb_data data = {
891+
.tp_name = tp_name,
892+
};
893+
894+
for_each_kernel_tracepoint(__find_tracepoint_cb, &data);
895+
896+
return data.tpoint;
897+
}
898+
814899
static int __trace_fprobe_create(int argc, const char *argv[])
815900
{
816901
/*
@@ -819,6 +904,8 @@ static int __trace_fprobe_create(int argc, const char *argv[])
819904
* f[:[GRP/][EVENT]] [MOD:]KSYM [FETCHARGS]
820905
* - Add fexit probe:
821906
* f[N][:[GRP/][EVENT]] [MOD:]KSYM%return [FETCHARGS]
907+
* - Add tracepoint probe:
908+
* t[:[GRP/][EVENT]] TRACEPOINT [FETCHARGS]
822909
*
823910
* Fetch args:
824911
* $retval : fetch return value
@@ -844,10 +931,16 @@ static int __trace_fprobe_create(int argc, const char *argv[])
844931
char buf[MAX_EVENT_NAME_LEN];
845932
char gbuf[MAX_EVENT_NAME_LEN];
846933
unsigned int flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE;
934+
bool is_tracepoint = false;
847935

848-
if (argv[0][0] != 'f' || argc < 2)
936+
if ((argv[0][0] != 'f' && argv[0][0] != 't') || argc < 2)
849937
return -ECANCELED;
850938

939+
if (argv[0][0] == 't') {
940+
is_tracepoint = true;
941+
group = TRACEPOINT_EVENT_SYSTEM;
942+
}
943+
851944
trace_probe_log_init("trace_fprobe", argc, argv);
852945

853946
event = strchr(&argv[0][1], ':');
@@ -881,14 +974,14 @@ static int __trace_fprobe_create(int argc, const char *argv[])
881974

882975
trace_probe_log_set_index(1);
883976

884-
/* a symbol specified */
977+
/* a symbol(or tracepoint) must be specified */
885978
symbol = kstrdup(argv[1], GFP_KERNEL);
886979
if (!symbol)
887980
return -ENOMEM;
888981

889982
tmp = strchr(symbol, '%');
890983
if (tmp) {
891-
if (!strcmp(tmp, "%return")) {
984+
if (!is_tracepoint && !strcmp(tmp, "%return")) {
892985
*tmp = '\0';
893986
is_return = true;
894987
} else {
@@ -907,6 +1000,9 @@ static int __trace_fprobe_create(int argc, const char *argv[])
9071000
else
9081001
flags |= TPARG_FL_FENTRY;
9091002

1003+
if (is_tracepoint)
1004+
flags |= TPARG_FL_TPOINT;
1005+
9101006
trace_probe_log_set_index(0);
9111007
if (event) {
9121008
ret = traceprobe_parse_event_name(&event, &group, gbuf,
@@ -917,8 +1013,11 @@ static int __trace_fprobe_create(int argc, const char *argv[])
9171013

9181014
if (!event) {
9191015
/* Make a new event name */
920-
snprintf(buf, MAX_EVENT_NAME_LEN, "%s__%s", symbol,
921-
is_return ? "exit" : "entry");
1016+
if (is_tracepoint)
1017+
strscpy(buf, symbol, MAX_EVENT_NAME_LEN);
1018+
else
1019+
snprintf(buf, MAX_EVENT_NAME_LEN, "%s__%s", symbol,
1020+
is_return ? "exit" : "entry");
9221021
sanitize_event_name(buf);
9231022
event = buf;
9241023
}
@@ -932,6 +1031,18 @@ static int __trace_fprobe_create(int argc, const char *argv[])
9321031
WARN_ON_ONCE(ret != -ENOMEM);
9331032
goto out; /* We know tf is not allocated */
9341033
}
1034+
1035+
if (is_tracepoint) {
1036+
tf->tpoint = find_tracepoint(tf->symbol);
1037+
if (!tf->tpoint) {
1038+
trace_probe_log_set_index(1);
1039+
trace_probe_log_err(0, NO_TRACEPOINT);
1040+
goto parse_error;
1041+
}
1042+
tf->mod = __module_text_address(
1043+
(unsigned long)tf->tpoint->probestub);
1044+
}
1045+
9351046
argc -= 2; argv += 2;
9361047

9371048
/* parse arguments */
@@ -991,7 +1102,10 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev)
9911102
struct trace_fprobe *tf = to_trace_fprobe(ev);
9921103
int i;
9931104

994-
seq_putc(m, 'f');
1105+
if (trace_fprobe_is_tracepoint(tf))
1106+
seq_putc(m, 't');
1107+
else
1108+
seq_putc(m, 'f');
9951109
if (trace_fprobe_is_return(tf) && tf->fp.nr_maxactive)
9961110
seq_printf(m, "%d", tf->fp.nr_maxactive);
9971111
seq_printf(m, ":%s/%s", trace_probe_group_name(&tf->tp),
@@ -1048,6 +1162,12 @@ static __init int init_fprobe_trace_early(void)
10481162
if (ret)
10491163
return ret;
10501164

1165+
#ifdef CONFIG_MODULES
1166+
ret = register_tracepoint_module_notifier(&tracepoint_module_nb);
1167+
if (ret)
1168+
return ret;
1169+
#endif
1170+
10511171
return 0;
10521172
}
10531173
core_initcall(init_fprobe_trace_early);

kernel/trace/trace_probe.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
292292
int ret = 0;
293293
int len;
294294

295-
if (flags & TPARG_FL_TPOINT) {
295+
if (flags & TPARG_FL_TEVENT) {
296296
if (code->data)
297297
return -EFAULT;
298298
code->data = kstrdup(arg, GFP_KERNEL);
@@ -326,8 +326,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
326326
} else if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
327327
code->op = FETCH_OP_COMM;
328328
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
329-
} else if (((flags & TPARG_FL_MASK) ==
330-
(TPARG_FL_KERNEL | TPARG_FL_FENTRY)) &&
329+
} else if (tparg_is_function_entry(flags) &&
331330
(len = str_has_prefix(arg, "arg"))) {
332331
ret = kstrtoul(arg + len, 10, &param);
333332
if (ret) {
@@ -338,6 +337,12 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
338337
}
339338
code->op = FETCH_OP_ARG;
340339
code->param = (unsigned int)param - 1;
340+
/*
341+
* The tracepoint probe will probe a stub function, and the
342+
* first parameter of the stub is a dummy and should be ignored.
343+
*/
344+
if (flags & TPARG_FL_TPOINT)
345+
code->param++;
341346
#endif
342347
} else
343348
goto inval_var;
@@ -393,7 +398,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
393398
break;
394399

395400
case '%': /* named register */
396-
if (flags & (TPARG_FL_TPOINT | TPARG_FL_FPROBE)) {
401+
if (flags & (TPARG_FL_TEVENT | TPARG_FL_FPROBE)) {
397402
/* eprobe and fprobe do not handle registers */
398403
trace_probe_log_err(offs, BAD_VAR);
399404
break;
@@ -633,7 +638,7 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
633638
* Since $comm and immediate string can not be dereferenced,
634639
* we can find those by strcmp. But ignore for eprobes.
635640
*/
636-
if (!(flags & TPARG_FL_TPOINT) &&
641+
if (!(flags & TPARG_FL_TEVENT) &&
637642
(strcmp(arg, "$comm") == 0 || strcmp(arg, "$COMM") == 0 ||
638643
strncmp(arg, "\\\"", 2) == 0)) {
639644
/* The type of $comm must be "string", and not an array. */

kernel/trace/trace_probe.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,16 +359,24 @@ int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_a
359359

360360
/*
361361
* The flags used for parsing trace_probe arguments.
362-
* TPARG_FL_RETURN, TPARG_FL_FENTRY and TPARG_FL_TPOINT are mutually exclusive.
362+
* TPARG_FL_RETURN, TPARG_FL_FENTRY and TPARG_FL_TEVENT are mutually exclusive.
363363
* TPARG_FL_KERNEL and TPARG_FL_USER are also mutually exclusive.
364+
* TPARG_FL_FPROBE and TPARG_FL_TPOINT are optional but it should be with
365+
* TPARG_FL_KERNEL.
364366
*/
365367
#define TPARG_FL_RETURN BIT(0)
366368
#define TPARG_FL_KERNEL BIT(1)
367369
#define TPARG_FL_FENTRY BIT(2)
368-
#define TPARG_FL_TPOINT BIT(3)
370+
#define TPARG_FL_TEVENT BIT(3)
369371
#define TPARG_FL_USER BIT(4)
370372
#define TPARG_FL_FPROBE BIT(5)
371-
#define TPARG_FL_MASK GENMASK(4, 0)
373+
#define TPARG_FL_TPOINT BIT(6)
374+
#define TPARG_FL_LOC_MASK GENMASK(4, 0)
375+
376+
static inline bool tparg_is_function_entry(unsigned int flags)
377+
{
378+
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_FENTRY);
379+
}
372380

373381
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
374382
const char *argv, unsigned int flags);
@@ -415,6 +423,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
415423
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
416424
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
417425
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
426+
C(NO_TRACEPOINT, "Tracepoint is not found"), \
418427
C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \
419428
C(NO_GROUP_NAME, "Group name is not specified"), \
420429
C(GROUP_TOO_LONG, "Group name is too long"), \

0 commit comments

Comments
 (0)