aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@kernel.org>2018-04-25 08:21:26 -0400
committerSteven Rostedt (VMware) <rostedt@goodmis.org>2018-10-10 22:19:11 -0400
commita1303af5d79eb13a658633a9fb0ce3aed0f7decf (patch)
tree06ab7627ff3bec7afcba894432a5798babd3e0bc
parent3c88ee194c288205733d248b51f0aca516ff4940 (diff)
tracing: probeevent: Add $argN for accessing function args
Add $argN special fetch variable for accessing function arguments. This allows user to trace the Nth argument easily at the function entry. Note that this returns most probably assignment of registers and stacks. In some case, it may not work well. If you need to access correct registers or stacks you should use perf-probe. Link: http://lkml.kernel.org/r/152465888632.26224.3412465701570253696.stgit@devbox Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r--Documentation/trace/kprobetrace.rst10
-rw-r--r--kernel/trace/trace.c4
-rw-r--r--kernel/trace/trace_kprobe.c18
-rw-r--r--kernel/trace/trace_probe.c36
-rw-r--r--kernel/trace/trace_probe.h9
-rw-r--r--kernel/trace/trace_uprobe.c2
6 files changed, 55 insertions, 24 deletions
diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst
index 2dfed7a1ea6f..47e765c2f2c3 100644
--- a/Documentation/trace/kprobetrace.rst
+++ b/Documentation/trace/kprobetrace.rst
@@ -45,16 +45,18 @@ Synopsis of kprobe_events
45 @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) 45 @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
46 $stackN : Fetch Nth entry of stack (N >= 0) 46 $stackN : Fetch Nth entry of stack (N >= 0)
47 $stack : Fetch stack address. 47 $stack : Fetch stack address.
48 $retval : Fetch return value.(*) 48 $argN : Fetch the Nth function argument. (N >= 1) (\*1)
49 $retval : Fetch return value.(\*2)
49 $comm : Fetch current task comm. 50 $comm : Fetch current task comm.
50 +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) 51 +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(\*3)
51 NAME=FETCHARG : Set NAME as the argument name of FETCHARG. 52 NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
52 FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types 53 FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
53 (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types 54 (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
54 (x8/x16/x32/x64), "string" and bitfield are supported. 55 (x8/x16/x32/x64), "string" and bitfield are supported.
55 56
56 (*) only for return probe. 57 (\*1) only for the probe on function entry (offs == 0).
57 (**) this is useful for fetching a field of data structures. 58 (\*2) only for return probe.
59 (\*3) this is useful for fetching a field of data structures.
58 60
59Types 61Types
60----- 62-----
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e7f99f513959..ec5b21778806 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4625,7 +4625,11 @@ static const char readme_msg[] =
4625#endif 4625#endif
4626 "\t args: <name>=fetcharg[:type]\n" 4626 "\t args: <name>=fetcharg[:type]\n"
4627 "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n" 4627 "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
4628#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
4629 "\t $stack<index>, $stack, $retval, $comm, $arg<N>\n"
4630#else
4628 "\t $stack<index>, $stack, $retval, $comm\n" 4631 "\t $stack<index>, $stack, $retval, $comm\n"
4632#endif
4629 "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" 4633 "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
4630 "\t b<bit-width>@<bit-offset>/<container-size>,\n" 4634 "\t b<bit-width>@<bit-offset>/<container-size>,\n"
4631 "\t <type>\\[<array-size>\\]\n" 4635 "\t <type>\\[<array-size>\\]\n"
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index fdd43f2f1fd1..3faaadbddf54 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -533,13 +533,15 @@ static int create_trace_kprobe(int argc, char **argv)
533 long offset = 0; 533 long offset = 0;
534 void *addr = NULL; 534 void *addr = NULL;
535 char buf[MAX_EVENT_NAME_LEN]; 535 char buf[MAX_EVENT_NAME_LEN];
536 unsigned int flags = TPARG_FL_KERNEL;
536 537
537 /* argc must be >= 1 */ 538 /* argc must be >= 1 */
538 if (argv[0][0] == 'p') 539 if (argv[0][0] == 'p')
539 is_return = false; 540 is_return = false;
540 else if (argv[0][0] == 'r') 541 else if (argv[0][0] == 'r') {
541 is_return = true; 542 is_return = true;
542 else if (argv[0][0] == '-') 543 flags |= TPARG_FL_RETURN;
544 } else if (argv[0][0] == '-')
543 is_delete = true; 545 is_delete = true;
544 else { 546 else {
545 pr_info("Probe definition must be started with 'p', 'r' or" 547 pr_info("Probe definition must be started with 'p', 'r' or"
@@ -625,8 +627,9 @@ static int create_trace_kprobe(int argc, char **argv)
625 pr_info("Failed to parse either an address or a symbol.\n"); 627 pr_info("Failed to parse either an address or a symbol.\n");
626 return ret; 628 return ret;
627 } 629 }
628 if (offset && is_return && 630 if (kprobe_on_func_entry(NULL, symbol, offset))
629 !kprobe_on_func_entry(NULL, symbol, offset)) { 631 flags |= TPARG_FL_FENTRY;
632 if (offset && is_return && !(flags & TPARG_FL_FENTRY)) {
630 pr_info("Given offset is not valid for return probe.\n"); 633 pr_info("Given offset is not valid for return probe.\n");
631 return -EINVAL; 634 return -EINVAL;
632 } 635 }
@@ -696,7 +699,7 @@ static int create_trace_kprobe(int argc, char **argv)
696 699
697 /* Parse fetch argument */ 700 /* Parse fetch argument */
698 ret = traceprobe_parse_probe_arg(arg, &tk->tp.size, parg, 701 ret = traceprobe_parse_probe_arg(arg, &tk->tp.size, parg,
699 is_return, true); 702 flags);
700 if (ret) { 703 if (ret) {
701 pr_info("Parse error at argument[%d]. (%d)\n", i, ret); 704 pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
702 goto error; 705 goto error;
@@ -932,6 +935,11 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
932 case FETCH_OP_COMM: 935 case FETCH_OP_COMM:
933 val = (unsigned long)current->comm; 936 val = (unsigned long)current->comm;
934 break; 937 break;
938#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
939 case FETCH_OP_ARG:
940 val = regs_get_kernel_argument(regs, code->param);
941 break;
942#endif
935 default: 943 default:
936 return -EILSEQ; 944 return -EILSEQ;
937 } 945 }
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index dfd096031305..333cda6d2633 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -157,14 +157,13 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)
157#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) 157#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
158 158
159static int parse_probe_vars(char *arg, const struct fetch_type *t, 159static int parse_probe_vars(char *arg, const struct fetch_type *t,
160 struct fetch_insn *code, bool is_return, 160 struct fetch_insn *code, unsigned int flags)
161 bool is_kprobe)
162{ 161{
163 int ret = 0; 162 int ret = 0;
164 unsigned long param; 163 unsigned long param;
165 164
166 if (strcmp(arg, "retval") == 0) { 165 if (strcmp(arg, "retval") == 0) {
167 if (is_return) 166 if (flags & TPARG_FL_RETURN)
168 code->op = FETCH_OP_RETVAL; 167 code->op = FETCH_OP_RETVAL;
169 else 168 else
170 ret = -EINVAL; 169 ret = -EINVAL;
@@ -173,7 +172,8 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
173 code->op = FETCH_OP_STACKP; 172 code->op = FETCH_OP_STACKP;
174 } else if (isdigit(arg[5])) { 173 } else if (isdigit(arg[5])) {
175 ret = kstrtoul(arg + 5, 10, &param); 174 ret = kstrtoul(arg + 5, 10, &param);
176 if (ret || (is_kprobe && param > PARAM_MAX_STACK)) 175 if (ret || ((flags & TPARG_FL_KERNEL) &&
176 param > PARAM_MAX_STACK))
177 ret = -EINVAL; 177 ret = -EINVAL;
178 else { 178 else {
179 code->op = FETCH_OP_STACK; 179 code->op = FETCH_OP_STACK;
@@ -183,6 +183,18 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
183 ret = -EINVAL; 183 ret = -EINVAL;
184 } else if (strcmp(arg, "comm") == 0) { 184 } else if (strcmp(arg, "comm") == 0) {
185 code->op = FETCH_OP_COMM; 185 code->op = FETCH_OP_COMM;
186#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
187 } else if (((flags & TPARG_FL_MASK) ==
188 (TPARG_FL_KERNEL | TPARG_FL_FENTRY)) &&
189 strncmp(arg, "arg", 3) == 0) {
190 if (!isdigit(arg[3]))
191 return -EINVAL;
192 ret = kstrtoul(arg + 3, 10, &param);
193 if (ret || !param || param > PARAM_MAX_STACK)
194 return -EINVAL;
195 code->op = FETCH_OP_ARG;
196 code->param = (unsigned int)param - 1;
197#endif
186 } else 198 } else
187 ret = -EINVAL; 199 ret = -EINVAL;
188 200
@@ -193,7 +205,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
193static int 205static int
194parse_probe_arg(char *arg, const struct fetch_type *type, 206parse_probe_arg(char *arg, const struct fetch_type *type,
195 struct fetch_insn **pcode, struct fetch_insn *end, 207 struct fetch_insn **pcode, struct fetch_insn *end,
196 bool is_return, bool is_kprobe) 208 unsigned int flags)
197{ 209{
198 struct fetch_insn *code = *pcode; 210 struct fetch_insn *code = *pcode;
199 unsigned long param; 211 unsigned long param;
@@ -203,8 +215,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
203 215
204 switch (arg[0]) { 216 switch (arg[0]) {
205 case '$': 217 case '$':
206 ret = parse_probe_vars(arg + 1, type, code, 218 ret = parse_probe_vars(arg + 1, type, code, flags);
207 is_return, is_kprobe);
208 break; 219 break;
209 220
210 case '%': /* named register */ 221 case '%': /* named register */
@@ -226,7 +237,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
226 code->immediate = param; 237 code->immediate = param;
227 } else if (arg[1] == '+') { 238 } else if (arg[1] == '+') {
228 /* kprobes don't support file offsets */ 239 /* kprobes don't support file offsets */
229 if (is_kprobe) 240 if (flags & TPARG_FL_KERNEL)
230 return -EINVAL; 241 return -EINVAL;
231 242
232 ret = kstrtol(arg + 2, 0, &offset); 243 ret = kstrtol(arg + 2, 0, &offset);
@@ -237,7 +248,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
237 code->immediate = (unsigned long)offset; // imm64? 248 code->immediate = (unsigned long)offset; // imm64?
238 } else { 249 } else {
239 /* uprobes don't support symbols */ 250 /* uprobes don't support symbols */
240 if (!is_kprobe) 251 if (!(flags & TPARG_FL_KERNEL))
241 return -EINVAL; 252 return -EINVAL;
242 253
243 ret = traceprobe_split_symbol_offset(arg + 1, &offset); 254 ret = traceprobe_split_symbol_offset(arg + 1, &offset);
@@ -278,8 +289,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
278 const struct fetch_type *t2 = find_fetch_type(NULL); 289 const struct fetch_type *t2 = find_fetch_type(NULL);
279 290
280 *tmp = '\0'; 291 *tmp = '\0';
281 ret = parse_probe_arg(arg, t2, &code, end, is_return, 292 ret = parse_probe_arg(arg, t2, &code, end, flags);
282 is_kprobe);
283 if (ret) 293 if (ret)
284 break; 294 break;
285 if (code->op == FETCH_OP_COMM) 295 if (code->op == FETCH_OP_COMM)
@@ -339,7 +349,7 @@ static int __parse_bitfield_probe_arg(const char *bf,
339 349
340/* String length checking wrapper */ 350/* String length checking wrapper */
341int traceprobe_parse_probe_arg(char *arg, ssize_t *size, 351int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
342 struct probe_arg *parg, bool is_return, bool is_kprobe) 352 struct probe_arg *parg, unsigned int flags)
343{ 353{
344 struct fetch_insn *code, *scode, *tmp = NULL; 354 struct fetch_insn *code, *scode, *tmp = NULL;
345 char *t, *t2; 355 char *t, *t2;
@@ -397,7 +407,7 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
397 code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; 407 code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
398 408
399 ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], 409 ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
400 is_return, is_kprobe); 410 flags);
401 if (ret) 411 if (ret)
402 goto fail; 412 goto fail;
403 413
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 1f456fd82483..09f62171cc23 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -23,6 +23,7 @@
23#include <linux/stringify.h> 23#include <linux/stringify.h>
24#include <linux/limits.h> 24#include <linux/limits.h>
25#include <linux/uaccess.h> 25#include <linux/uaccess.h>
26#include <linux/bitops.h>
26#include <asm/bitsperlong.h> 27#include <asm/bitsperlong.h>
27 28
28#include "trace.h" 29#include "trace.h"
@@ -86,6 +87,7 @@ enum fetch_op {
86 FETCH_OP_RETVAL, /* Return value */ 87 FETCH_OP_RETVAL, /* Return value */
87 FETCH_OP_IMM, /* Immediate : .immediate */ 88 FETCH_OP_IMM, /* Immediate : .immediate */
88 FETCH_OP_COMM, /* Current comm */ 89 FETCH_OP_COMM, /* Current comm */
90 FETCH_OP_ARG, /* Function argument : .param */
89 FETCH_OP_FOFFS, /* File offset: .immediate */ 91 FETCH_OP_FOFFS, /* File offset: .immediate */
90 // Stage 2 (dereference) op 92 // Stage 2 (dereference) op
91 FETCH_OP_DEREF, /* Dereference: .offset */ 93 FETCH_OP_DEREF, /* Dereference: .offset */
@@ -263,8 +265,13 @@ find_event_file_link(struct trace_probe *tp, struct trace_event_file *file)
263 return NULL; 265 return NULL;
264} 266}
265 267
268#define TPARG_FL_RETURN BIT(0)
269#define TPARG_FL_KERNEL BIT(1)
270#define TPARG_FL_FENTRY BIT(2)
271#define TPARG_FL_MASK GENMASK(2, 0)
272
266extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, 273extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
267 struct probe_arg *parg, bool is_return, bool is_kprobe); 274 struct probe_arg *parg, unsigned int flags);
268 275
269extern int traceprobe_conflict_field_name(const char *name, 276extern int traceprobe_conflict_field_name(const char *name,
270 struct probe_arg *args, int narg); 277 struct probe_arg *args, int narg);
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 7154473ffaa4..394b93572506 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -557,7 +557,7 @@ static int create_trace_uprobe(int argc, char **argv)
557 557
558 /* Parse fetch argument */ 558 /* Parse fetch argument */
559 ret = traceprobe_parse_probe_arg(arg, &tu->tp.size, parg, 559 ret = traceprobe_parse_probe_arg(arg, &tu->tp.size, parg,
560 is_return, false); 560 is_return ? TPARG_FL_RETURN : 0);
561 if (ret) { 561 if (ret) {
562 pr_info("Parse error at argument[%d]. (%d)\n", i, ret); 562 pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
563 goto error; 563 goto error;