Skip to content

Commit 25f00e4

Browse files
committed
tracing/probes: Support $argN in return probe (kprobe and fprobe)
Support accessing $argN in the return probe events. This will help users to record entry data in function return (exit) event for simplfing the function entry/exit information in one event, and record the result values (e.g. allocated object/initialized object) at function exit. For example, if we have a function `int init_foo(struct foo *obj, int param)` sometimes we want to check how `obj` is initialized. In such case, we can define a new return event like below; # echo 'r init_foo retval=$retval param=$arg2 field1=+0($arg1)' >> kprobe_events Thus it records the function parameter `param` and its result `obj->field1` (the dereference will be done in the function exit timing) value at once. This also support fprobe, BTF args and'$arg*'. So if CONFIG_DEBUG_INFO_BTF is enabled, we can trace both function parameters and the return value by following command. # echo 'f target_function%return $arg* $retval' >> dynamic_events Link: https://lore.kernel.org/all/170952365552.229804.224112990211602895.stgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent c18f9ea commit 25f00e4

File tree

10 files changed

+289
-62
lines changed

10 files changed

+289
-62
lines changed

kernel/trace/trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5755,6 +5755,7 @@ static const char readme_msg[] =
57555755
"\t $stack<index>, $stack, $retval, $comm,\n"
57565756
#endif
57575757
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
5758+
"\t kernel return probes support: $retval, $arg<N>, $comm\n"
57585759
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n"
57595760
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
57605761
"\t symstr, <type>\\[<array-size>\\]\n"

kernel/trace/trace_eprobe.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec)
390390

391391
/* Note that we don't verify it, since the code does not come from user space */
392392
static int
393-
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
394-
void *base)
393+
process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
394+
void *dest, void *base)
395395
{
396396
unsigned long val;
397397
int ret;
@@ -438,7 +438,7 @@ __eprobe_trace_func(struct eprobe_data *edata, void *rec)
438438
return;
439439

440440
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
441-
store_trace_args(&entry[1], &edata->ep->tp, rec, sizeof(*entry), dsize);
441+
store_trace_args(&entry[1], &edata->ep->tp, rec, NULL, sizeof(*entry), dsize);
442442

443443
trace_event_buffer_commit(&fbuffer);
444444
}

kernel/trace/trace_fprobe.c

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Copyright (C) 2022 Google LLC.
55
*/
66
#define pr_fmt(fmt) "trace_fprobe: " fmt
7+
#include <asm/ptrace.h>
78

89
#include <linux/fprobe.h>
910
#include <linux/module.h>
@@ -129,8 +130,8 @@ static bool trace_fprobe_is_registered(struct trace_fprobe *tf)
129130
* from user space.
130131
*/
131132
static int
132-
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
133-
void *base)
133+
process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
134+
void *dest, void *base)
134135
{
135136
struct pt_regs *regs = rec;
136137
unsigned long val;
@@ -152,6 +153,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
152153
case FETCH_OP_ARG:
153154
val = regs_get_kernel_argument(regs, code->param);
154155
break;
156+
case FETCH_OP_EDATA:
157+
val = *(unsigned long *)((unsigned long)edata + code->offset);
158+
break;
155159
#endif
156160
case FETCH_NOP_SYMBOL: /* Ignore a place holder */
157161
code++;
@@ -184,7 +188,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
184188
if (trace_trigger_soft_disabled(trace_file))
185189
return;
186190

187-
dsize = __get_data_size(&tf->tp, regs);
191+
dsize = __get_data_size(&tf->tp, regs, NULL);
188192

189193
entry = trace_event_buffer_reserve(&fbuffer, trace_file,
190194
sizeof(*entry) + tf->tp.size + dsize);
@@ -194,7 +198,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
194198
fbuffer.regs = regs;
195199
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
196200
entry->ip = entry_ip;
197-
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize);
201+
store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
198202

199203
trace_event_buffer_commit(&fbuffer);
200204
}
@@ -211,10 +215,23 @@ fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
211215
NOKPROBE_SYMBOL(fentry_trace_func);
212216

213217
/* function exit handler */
218+
static int trace_fprobe_entry_handler(struct fprobe *fp, unsigned long entry_ip,
219+
unsigned long ret_ip, struct pt_regs *regs,
220+
void *entry_data)
221+
{
222+
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
223+
224+
if (tf->tp.entry_arg)
225+
store_trace_entry_data(entry_data, &tf->tp, regs);
226+
227+
return 0;
228+
}
229+
NOKPROBE_SYMBOL(trace_fprobe_entry_handler)
230+
214231
static nokprobe_inline void
215232
__fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
216233
unsigned long ret_ip, struct pt_regs *regs,
217-
struct trace_event_file *trace_file)
234+
void *entry_data, struct trace_event_file *trace_file)
218235
{
219236
struct fexit_trace_entry_head *entry;
220237
struct trace_event_buffer fbuffer;
@@ -227,7 +244,7 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
227244
if (trace_trigger_soft_disabled(trace_file))
228245
return;
229246

230-
dsize = __get_data_size(&tf->tp, regs);
247+
dsize = __get_data_size(&tf->tp, regs, entry_data);
231248

232249
entry = trace_event_buffer_reserve(&fbuffer, trace_file,
233250
sizeof(*entry) + tf->tp.size + dsize);
@@ -238,19 +255,19 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
238255
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
239256
entry->func = entry_ip;
240257
entry->ret_ip = ret_ip;
241-
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize);
258+
store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
242259

243260
trace_event_buffer_commit(&fbuffer);
244261
}
245262

246263
static void
247264
fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
248-
unsigned long ret_ip, struct pt_regs *regs)
265+
unsigned long ret_ip, struct pt_regs *regs, void *entry_data)
249266
{
250267
struct event_file_link *link;
251268

252269
trace_probe_for_each_link_rcu(link, &tf->tp)
253-
__fexit_trace_func(tf, entry_ip, ret_ip, regs, link->file);
270+
__fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data, link->file);
254271
}
255272
NOKPROBE_SYMBOL(fexit_trace_func);
256273

@@ -269,7 +286,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
269286
if (hlist_empty(head))
270287
return 0;
271288

272-
dsize = __get_data_size(&tf->tp, regs);
289+
dsize = __get_data_size(&tf->tp, regs, NULL);
273290
__size = sizeof(*entry) + tf->tp.size + dsize;
274291
size = ALIGN(__size + sizeof(u32), sizeof(u64));
275292
size -= sizeof(u32);
@@ -280,7 +297,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
280297

281298
entry->ip = entry_ip;
282299
memset(&entry[1], 0, dsize);
283-
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize);
300+
store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
284301
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
285302
head, NULL);
286303
return 0;
@@ -289,7 +306,8 @@ NOKPROBE_SYMBOL(fentry_perf_func);
289306

290307
static void
291308
fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
292-
unsigned long ret_ip, struct pt_regs *regs)
309+
unsigned long ret_ip, struct pt_regs *regs,
310+
void *entry_data)
293311
{
294312
struct trace_event_call *call = trace_probe_event_call(&tf->tp);
295313
struct fexit_trace_entry_head *entry;
@@ -301,7 +319,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
301319
if (hlist_empty(head))
302320
return;
303321

304-
dsize = __get_data_size(&tf->tp, regs);
322+
dsize = __get_data_size(&tf->tp, regs, entry_data);
305323
__size = sizeof(*entry) + tf->tp.size + dsize;
306324
size = ALIGN(__size + sizeof(u32), sizeof(u64));
307325
size -= sizeof(u32);
@@ -312,7 +330,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
312330

313331
entry->func = entry_ip;
314332
entry->ret_ip = ret_ip;
315-
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize);
333+
store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
316334
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
317335
head, NULL);
318336
}
@@ -343,10 +361,10 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
343361
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
344362

345363
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
346-
fexit_trace_func(tf, entry_ip, ret_ip, regs);
364+
fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data);
347365
#ifdef CONFIG_PERF_EVENTS
348366
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
349-
fexit_perf_func(tf, entry_ip, ret_ip, regs);
367+
fexit_perf_func(tf, entry_ip, ret_ip, regs, entry_data);
350368
#endif
351369
}
352370
NOKPROBE_SYMBOL(fexit_dispatcher);
@@ -1109,6 +1127,11 @@ static int __trace_fprobe_create(int argc, const char *argv[])
11091127
goto error; /* This can be -ENOMEM */
11101128
}
11111129

1130+
if (is_return && tf->tp.entry_arg) {
1131+
tf->fp.entry_handler = trace_fprobe_entry_handler;
1132+
tf->fp.entry_data_size = traceprobe_get_entry_data_size(&tf->tp);
1133+
}
1134+
11121135
ret = traceprobe_set_print_fmt(&tf->tp,
11131136
is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL);
11141137
if (ret < 0)

kernel/trace/trace_kprobe.c

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ static unsigned int number_of_same_symbols(char *func_name)
740740
return ctx.count;
741741
}
742742

743+
static int trace_kprobe_entry_handler(struct kretprobe_instance *ri,
744+
struct pt_regs *regs);
745+
743746
static int __trace_kprobe_create(int argc, const char *argv[])
744747
{
745748
/*
@@ -948,6 +951,11 @@ static int __trace_kprobe_create(int argc, const char *argv[])
948951
if (ret)
949952
goto error; /* This can be -ENOMEM */
950953
}
954+
/* entry handler for kretprobe */
955+
if (is_return && tk->tp.entry_arg) {
956+
tk->rp.entry_handler = trace_kprobe_entry_handler;
957+
tk->rp.data_size = traceprobe_get_entry_data_size(&tk->tp);
958+
}
951959

952960
ptype = is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL;
953961
ret = traceprobe_set_print_fmt(&tk->tp, ptype);
@@ -1303,8 +1311,8 @@ static const struct file_operations kprobe_profile_ops = {
13031311

13041312
/* Note that we don't verify it, since the code does not come from user space */
13051313
static int
1306-
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
1307-
void *base)
1314+
process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
1315+
void *dest, void *base)
13081316
{
13091317
struct pt_regs *regs = rec;
13101318
unsigned long val;
@@ -1329,6 +1337,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
13291337
case FETCH_OP_ARG:
13301338
val = regs_get_kernel_argument(regs, code->param);
13311339
break;
1340+
case FETCH_OP_EDATA:
1341+
val = *(unsigned long *)((unsigned long)edata + code->offset);
1342+
break;
13321343
#endif
13331344
case FETCH_NOP_SYMBOL: /* Ignore a place holder */
13341345
code++;
@@ -1359,7 +1370,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
13591370
if (trace_trigger_soft_disabled(trace_file))
13601371
return;
13611372

1362-
dsize = __get_data_size(&tk->tp, regs);
1373+
dsize = __get_data_size(&tk->tp, regs, NULL);
13631374

13641375
entry = trace_event_buffer_reserve(&fbuffer, trace_file,
13651376
sizeof(*entry) + tk->tp.size + dsize);
@@ -1368,7 +1379,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
13681379

13691380
fbuffer.regs = regs;
13701381
entry->ip = (unsigned long)tk->rp.kp.addr;
1371-
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
1382+
store_trace_args(&entry[1], &tk->tp, regs, NULL, sizeof(*entry), dsize);
13721383

13731384
trace_event_buffer_commit(&fbuffer);
13741385
}
@@ -1384,6 +1395,31 @@ kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
13841395
NOKPROBE_SYMBOL(kprobe_trace_func);
13851396

13861397
/* Kretprobe handler */
1398+
1399+
static int trace_kprobe_entry_handler(struct kretprobe_instance *ri,
1400+
struct pt_regs *regs)
1401+
{
1402+
struct kretprobe *rp = get_kretprobe(ri);
1403+
struct trace_kprobe *tk;
1404+
1405+
/*
1406+
* There is a small chance that get_kretprobe(ri) returns NULL when
1407+
* the kretprobe is unregister on another CPU between kretprobe's
1408+
* trampoline_handler and this function.
1409+
*/
1410+
if (unlikely(!rp))
1411+
return -ENOENT;
1412+
1413+
tk = container_of(rp, struct trace_kprobe, rp);
1414+
1415+
/* store argument values into ri->data as entry data */
1416+
if (tk->tp.entry_arg)
1417+
store_trace_entry_data(ri->data, &tk->tp, regs);
1418+
1419+
return 0;
1420+
}
1421+
1422+
13871423
static nokprobe_inline void
13881424
__kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
13891425
struct pt_regs *regs,
@@ -1399,7 +1435,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
13991435
if (trace_trigger_soft_disabled(trace_file))
14001436
return;
14011437

1402-
dsize = __get_data_size(&tk->tp, regs);
1438+
dsize = __get_data_size(&tk->tp, regs, ri->data);
14031439

14041440
entry = trace_event_buffer_reserve(&fbuffer, trace_file,
14051441
sizeof(*entry) + tk->tp.size + dsize);
@@ -1409,7 +1445,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
14091445
fbuffer.regs = regs;
14101446
entry->func = (unsigned long)tk->rp.kp.addr;
14111447
entry->ret_ip = get_kretprobe_retaddr(ri);
1412-
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
1448+
store_trace_args(&entry[1], &tk->tp, regs, ri->data, sizeof(*entry), dsize);
14131449

14141450
trace_event_buffer_commit(&fbuffer);
14151451
}
@@ -1557,7 +1593,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
15571593
if (hlist_empty(head))
15581594
return 0;
15591595

1560-
dsize = __get_data_size(&tk->tp, regs);
1596+
dsize = __get_data_size(&tk->tp, regs, NULL);
15611597
__size = sizeof(*entry) + tk->tp.size + dsize;
15621598
size = ALIGN(__size + sizeof(u32), sizeof(u64));
15631599
size -= sizeof(u32);
@@ -1568,7 +1604,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
15681604

15691605
entry->ip = (unsigned long)tk->rp.kp.addr;
15701606
memset(&entry[1], 0, dsize);
1571-
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
1607+
store_trace_args(&entry[1], &tk->tp, regs, NULL, sizeof(*entry), dsize);
15721608
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
15731609
head, NULL);
15741610
return 0;
@@ -1593,7 +1629,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
15931629
if (hlist_empty(head))
15941630
return;
15951631

1596-
dsize = __get_data_size(&tk->tp, regs);
1632+
dsize = __get_data_size(&tk->tp, regs, ri->data);
15971633
__size = sizeof(*entry) + tk->tp.size + dsize;
15981634
size = ALIGN(__size + sizeof(u32), sizeof(u64));
15991635
size -= sizeof(u32);
@@ -1604,7 +1640,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
16041640

16051641
entry->func = (unsigned long)tk->rp.kp.addr;
16061642
entry->ret_ip = get_kretprobe_retaddr(ri);
1607-
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
1643+
store_trace_args(&entry[1], &tk->tp, regs, ri->data, sizeof(*entry), dsize);
16081644
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
16091645
head, NULL);
16101646
}

0 commit comments

Comments
 (0)