Skip to content

Commit 18b1e87

Browse files
committed
tracing/probes: Add $arg* meta argument for all function args
Add the '$arg*' meta fetch argument for function-entry probe events. This will be expanded to the all arguments of the function and the tracepoint using BTF function argument information. e.g. # echo 'p vfs_read $arg*' >> dynamic_events # echo 'f vfs_write $arg*' >> dynamic_events # echo 't sched_overutilized_tp $arg*' >> dynamic_events # cat dynamic_events p:kprobes/p_vfs_read_0 vfs_read file=file buf=buf count=count pos=pos f:fprobes/vfs_write__entry vfs_write file=file buf=buf count=count pos=pos t:tracepoints/sched_overutilized_tp sched_overutilized_tp rd=rd overutilized=overutilized Also, single '$arg[0-9]*' will be converted to the BTF function argument. NOTE: This seems like a wildcard, but a fake one at this moment. This is just for telling user that this can be expanded to several arguments. And it is not like other $-vars, you can not use this $arg* as a part of fetch args, e.g. specifying name "foo=$arg*" and using it in dereferences "+0($arg*)" will lead a parse error. Link: https://lore.kernel.org/all/168507475126.913472.18329684401466211816.stgit@mhiramat.roam.corp.google.com/ Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent b576e09 commit 18b1e87

File tree

4 files changed

+212
-12
lines changed

4 files changed

+212
-12
lines changed

kernel/trace/trace_fprobe.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -925,14 +925,16 @@ static int __trace_fprobe_create(int argc, const char *argv[])
925925
* FETCHARG:TYPE : use TYPE instead of unsigned long.
926926
*/
927927
struct trace_fprobe *tf = NULL;
928-
int i, len, ret = 0;
928+
int i, len, new_argc = 0, ret = 0;
929929
bool is_return = false;
930930
char *symbol = NULL, *tmp = NULL;
931931
const char *event = NULL, *group = FPROBE_EVENT_SYSTEM;
932+
const char **new_argv = NULL;
932933
int maxactive = 0;
933934
char buf[MAX_EVENT_NAME_LEN];
934935
char gbuf[MAX_EVENT_NAME_LEN];
935936
char sbuf[KSYM_NAME_LEN];
937+
char abuf[MAX_BTF_ARGS_LEN];
936938
bool is_tracepoint = false;
937939
struct tracepoint *tpoint = NULL;
938940
struct traceprobe_parse_context ctx = {
@@ -1040,9 +1042,22 @@ static int __trace_fprobe_create(int argc, const char *argv[])
10401042
} else
10411043
ctx.funcname = symbol;
10421044

1045+
argc -= 2; argv += 2;
1046+
new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,
1047+
abuf, MAX_BTF_ARGS_LEN, &ctx);
1048+
if (IS_ERR(new_argv)) {
1049+
ret = PTR_ERR(new_argv);
1050+
new_argv = NULL;
1051+
goto out;
1052+
}
1053+
if (new_argv) {
1054+
argc = new_argc;
1055+
argv = new_argv;
1056+
}
1057+
10431058
/* setup a probe */
10441059
tf = alloc_trace_fprobe(group, event, symbol, tpoint, maxactive,
1045-
argc - 2, is_return);
1060+
argc, is_return);
10461061
if (IS_ERR(tf)) {
10471062
ret = PTR_ERR(tf);
10481063
/* This must return -ENOMEM, else there is a bug */
@@ -1054,7 +1069,6 @@ static int __trace_fprobe_create(int argc, const char *argv[])
10541069
tf->mod = __module_text_address(
10551070
(unsigned long)tf->tpoint->probestub);
10561071

1057-
argc -= 2; argv += 2;
10581072
/* parse arguments */
10591073
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
10601074
trace_probe_log_set_index(i + 2);
@@ -1083,6 +1097,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
10831097

10841098
out:
10851099
trace_probe_log_clear();
1100+
kfree(new_argv);
10861101
kfree(symbol);
10871102
return ret;
10881103

kernel/trace/trace_kprobe.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -732,16 +732,18 @@ static int __trace_kprobe_create(int argc, const char *argv[])
732732
* FETCHARG:TYPE : use TYPE instead of unsigned long.
733733
*/
734734
struct trace_kprobe *tk = NULL;
735-
int i, len, ret = 0;
735+
int i, len, new_argc = 0, ret = 0;
736736
bool is_return = false;
737737
char *symbol = NULL, *tmp = NULL;
738+
const char **new_argv = NULL;
738739
const char *event = NULL, *group = KPROBE_EVENT_SYSTEM;
739740
enum probe_print_type ptype;
740741
int maxactive = 0;
741742
long offset = 0;
742743
void *addr = NULL;
743744
char buf[MAX_EVENT_NAME_LEN];
744745
char gbuf[MAX_EVENT_NAME_LEN];
746+
char abuf[MAX_BTF_ARGS_LEN];
745747
struct traceprobe_parse_context ctx = { .flags = TPARG_FL_KERNEL };
746748

747749
switch (argv[0][0]) {
@@ -854,19 +856,31 @@ static int __trace_kprobe_create(int argc, const char *argv[])
854856
event = buf;
855857
}
856858

859+
argc -= 2; argv += 2;
860+
ctx.funcname = symbol;
861+
new_argv = traceprobe_expand_meta_args(argc, argv, &new_argc,
862+
abuf, MAX_BTF_ARGS_LEN, &ctx);
863+
if (IS_ERR(new_argv)) {
864+
ret = PTR_ERR(new_argv);
865+
new_argv = NULL;
866+
goto out;
867+
}
868+
if (new_argv) {
869+
argc = new_argc;
870+
argv = new_argv;
871+
}
872+
857873
/* setup a probe */
858874
tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive,
859-
argc - 2, is_return);
875+
argc, is_return);
860876
if (IS_ERR(tk)) {
861877
ret = PTR_ERR(tk);
862878
/* This must return -ENOMEM, else there is a bug */
863879
WARN_ON_ONCE(ret != -ENOMEM);
864880
goto out; /* We know tk is not allocated */
865881
}
866-
argc -= 2; argv += 2;
867882

868883
/* parse arguments */
869-
ctx.funcname = symbol;
870884
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
871885
trace_probe_log_set_index(i + 2);
872886
ctx.offset = 0;
@@ -894,6 +908,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
894908

895909
out:
896910
trace_probe_log_clear();
911+
kfree(new_argv);
897912
kfree(symbol);
898913
return ret;
899914

kernel/trace/trace_probe.c

Lines changed: 165 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,11 @@ static const char *type_from_btf_id(struct btf *btf, s32 id)
371371
return NULL;
372372
}
373373

374-
static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr)
374+
static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr,
375+
bool tracepoint)
375376
{
376377
struct btf *btf = traceprobe_get_btf();
378+
const struct btf_param *param;
377379
const struct btf_type *t;
378380
s32 id;
379381

@@ -395,9 +397,16 @@ static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr
395397
return ERR_PTR(-ENOENT);
396398

397399
*nr = btf_type_vlen(t);
400+
param = (const struct btf_param *)(t + 1);
398401

399-
if (*nr)
400-
return (const struct btf_param *)(t + 1);
402+
/* Hide the first 'data' argument of tracepoint */
403+
if (tracepoint) {
404+
(*nr)--;
405+
param++;
406+
}
407+
408+
if (*nr > 0)
409+
return param;
401410
else
402411
return NULL;
403412
}
@@ -418,7 +427,8 @@ static int parse_btf_arg(const char *varname, struct fetch_insn *code,
418427
return -EINVAL;
419428

420429
if (!ctx->params) {
421-
params = find_btf_func_param(ctx->funcname, &ctx->nr_params);
430+
params = find_btf_func_param(ctx->funcname, &ctx->nr_params,
431+
ctx->flags & TPARG_FL_TPOINT);
422432
if (IS_ERR(params)) {
423433
trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
424434
return PTR_ERR(params);
@@ -451,12 +461,19 @@ static const struct fetch_type *parse_btf_arg_type(int arg_idx,
451461

452462
return find_fetch_type(typestr, ctx->flags);
453463
}
464+
454465
#else
455466
static struct btf *traceprobe_get_btf(void)
456467
{
457468
return NULL;
458469
}
459470

471+
static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr,
472+
bool tracepoint)
473+
{
474+
return ERR_PTR(-EOPNOTSUPP);
475+
}
476+
460477
static int parse_btf_arg(const char *varname, struct fetch_insn *code,
461478
struct traceprobe_parse_context *ctx)
462479
{
@@ -1114,6 +1131,150 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
11141131
kfree(arg->fmt);
11151132
}
11161133

1134+
static int argv_has_var_arg(int argc, const char *argv[], int *args_idx,
1135+
struct traceprobe_parse_context *ctx)
1136+
{
1137+
int i, found = 0;
1138+
1139+
for (i = 0; i < argc; i++)
1140+
if (str_has_prefix(argv[i], "$arg")) {
1141+
trace_probe_log_set_index(i + 2);
1142+
1143+
if (!tparg_is_function_entry(ctx->flags)) {
1144+
trace_probe_log_err(0, NOFENTRY_ARGS);
1145+
return -EINVAL;
1146+
}
1147+
1148+
if (isdigit(argv[i][4])) {
1149+
found = 1;
1150+
continue;
1151+
}
1152+
1153+
if (argv[i][4] != '*') {
1154+
trace_probe_log_err(0, BAD_VAR);
1155+
return -EINVAL;
1156+
}
1157+
1158+
if (*args_idx >= 0 && *args_idx < argc) {
1159+
trace_probe_log_err(0, DOUBLE_ARGS);
1160+
return -EINVAL;
1161+
}
1162+
found = 1;
1163+
*args_idx = i;
1164+
}
1165+
1166+
return found;
1167+
}
1168+
1169+
static int sprint_nth_btf_arg(int idx, const char *type,
1170+
char *buf, int bufsize,
1171+
struct traceprobe_parse_context *ctx)
1172+
{
1173+
struct btf *btf = traceprobe_get_btf();
1174+
const char *name;
1175+
int ret;
1176+
1177+
if (idx >= ctx->nr_params) {
1178+
trace_probe_log_err(0, NO_BTFARG);
1179+
return -ENOENT;
1180+
}
1181+
name = btf_name_by_offset(btf, ctx->params[idx].name_off);
1182+
if (!name) {
1183+
trace_probe_log_err(0, NO_BTF_ENTRY);
1184+
return -ENOENT;
1185+
}
1186+
ret = snprintf(buf, bufsize, "%s%s", name, type);
1187+
if (ret >= bufsize) {
1188+
trace_probe_log_err(0, ARGS_2LONG);
1189+
return -E2BIG;
1190+
}
1191+
return ret;
1192+
}
1193+
1194+
/* Return new_argv which must be freed after use */
1195+
const char **traceprobe_expand_meta_args(int argc, const char *argv[],
1196+
int *new_argc, char *buf, int bufsize,
1197+
struct traceprobe_parse_context *ctx)
1198+
{
1199+
const struct btf_param *params = NULL;
1200+
int i, j, n, used, ret, args_idx = -1;
1201+
const char **new_argv = NULL;
1202+
int nr_params;
1203+
1204+
ret = argv_has_var_arg(argc, argv, &args_idx, ctx);
1205+
if (ret < 0)
1206+
return ERR_PTR(ret);
1207+
1208+
if (!ret) {
1209+
*new_argc = argc;
1210+
return NULL;
1211+
}
1212+
1213+
params = find_btf_func_param(ctx->funcname, &nr_params,
1214+
ctx->flags & TPARG_FL_TPOINT);
1215+
if (IS_ERR(params)) {
1216+
if (args_idx != -1) {
1217+
/* $arg* requires BTF info */
1218+
trace_probe_log_err(0, NOSUP_BTFARG);
1219+
return (const char **)params;
1220+
}
1221+
return 0;
1222+
}
1223+
ctx->params = params;
1224+
ctx->nr_params = nr_params;
1225+
1226+
if (args_idx >= 0)
1227+
*new_argc = argc + ctx->nr_params - 1;
1228+
else
1229+
*new_argc = argc;
1230+
1231+
new_argv = kcalloc(*new_argc, sizeof(char *), GFP_KERNEL);
1232+
if (!new_argv)
1233+
return ERR_PTR(-ENOMEM);
1234+
1235+
used = 0;
1236+
for (i = 0, j = 0; i < argc; i++) {
1237+
trace_probe_log_set_index(i + 2);
1238+
if (i == args_idx) {
1239+
for (n = 0; n < nr_params; n++) {
1240+
ret = sprint_nth_btf_arg(n, "", buf + used,
1241+
bufsize - used, ctx);
1242+
if (ret < 0)
1243+
goto error;
1244+
1245+
new_argv[j++] = buf + used;
1246+
used += ret + 1;
1247+
}
1248+
continue;
1249+
}
1250+
1251+
if (str_has_prefix(argv[i], "$arg")) {
1252+
char *type = NULL;
1253+
1254+
n = simple_strtoul(argv[i] + 4, &type, 10);
1255+
if (type && !(*type == ':' || *type == '\0')) {
1256+
trace_probe_log_err(0, BAD_VAR);
1257+
ret = -ENOENT;
1258+
goto error;
1259+
}
1260+
/* Note: $argN starts from $arg1 */
1261+
ret = sprint_nth_btf_arg(n - 1, type, buf + used,
1262+
bufsize - used, ctx);
1263+
if (ret < 0)
1264+
goto error;
1265+
new_argv[j++] = buf + used;
1266+
used += ret + 1;
1267+
} else
1268+
new_argv[j++] = argv[i];
1269+
}
1270+
1271+
return new_argv;
1272+
1273+
error:
1274+
kfree(new_argv);
1275+
return ERR_PTR(ret);
1276+
}
1277+
11171278
int traceprobe_update_arg(struct probe_arg *arg)
11181279
{
11191280
struct fetch_insn *code = arg->code;

kernel/trace/trace_probe.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
#define MAX_ARGSTR_LEN 63
3434
#define MAX_ARRAY_LEN 64
3535
#define MAX_ARG_NAME_LEN 32
36+
#define MAX_BTF_ARGS_LEN 128
3637
#define MAX_STRING_SIZE PATH_MAX
38+
#define MAX_ARG_BUF_LEN (MAX_TRACE_ARGS * MAX_ARG_NAME_LEN)
3739

3840
/* Reserved field names */
3941
#define FIELD_STRING_IP "__probe_ip"
@@ -391,6 +393,9 @@ struct traceprobe_parse_context {
391393
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
392394
const char *argv,
393395
struct traceprobe_parse_context *ctx);
396+
const char **traceprobe_expand_meta_args(int argc, const char *argv[],
397+
int *new_argc, char *buf, int bufsize,
398+
struct traceprobe_parse_context *ctx);
394399

395400
extern int traceprobe_update_arg(struct probe_arg *arg);
396401
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
@@ -485,7 +490,11 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
485490
C(NO_EP_FILTER, "No filter rule after 'if'"), \
486491
C(NOSUP_BTFARG, "BTF is not available or not supported"), \
487492
C(NO_BTFARG, "This variable is not found at this probe point"),\
488-
C(NO_BTF_ENTRY, "No BTF entry for this probe point"),
493+
C(NO_BTF_ENTRY, "No BTF entry for this probe point"), \
494+
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\
495+
C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \
496+
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
497+
C(ARGS_2LONG, "$arg* failed because the argument list is too long"),
489498

490499
#undef C
491500
#define C(a, b) TP_ERR_##a

0 commit comments

Comments
 (0)