diff options
| -rw-r--r-- | kernel/trace/trace_kprobe.c | 12 | ||||
| -rw-r--r-- | kernel/trace/trace_probe.c | 62 | ||||
| -rw-r--r-- | kernel/trace/trace_probe.h | 4 |
3 files changed, 68 insertions, 10 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 4727a13824f0..fec67188c4d2 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
| @@ -366,7 +366,7 @@ static bool within_notrace_func(struct trace_kprobe *tk) | |||
| 366 | /* Internal register function - just handle k*probes and flags */ | 366 | /* Internal register function - just handle k*probes and flags */ |
| 367 | static int __register_trace_kprobe(struct trace_kprobe *tk) | 367 | static int __register_trace_kprobe(struct trace_kprobe *tk) |
| 368 | { | 368 | { |
| 369 | int ret; | 369 | int i, ret; |
| 370 | 370 | ||
| 371 | if (trace_probe_is_registered(&tk->tp)) | 371 | if (trace_probe_is_registered(&tk->tp)) |
| 372 | return -EINVAL; | 372 | return -EINVAL; |
| @@ -377,6 +377,12 @@ static int __register_trace_kprobe(struct trace_kprobe *tk) | |||
| 377 | return -EINVAL; | 377 | return -EINVAL; |
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | for (i = 0; i < tk->tp.nr_args; i++) { | ||
| 381 | ret = traceprobe_update_arg(&tk->tp.args[i]); | ||
| 382 | if (ret) | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 380 | /* Set/clear disabled flag according to tp->flag */ | 386 | /* Set/clear disabled flag according to tp->flag */ |
| 381 | if (trace_probe_is_enabled(&tk->tp)) | 387 | if (trace_probe_is_enabled(&tk->tp)) |
| 382 | tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; | 388 | tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; |
| @@ -928,6 +934,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, | |||
| 928 | { | 934 | { |
| 929 | unsigned long val; | 935 | unsigned long val; |
| 930 | 936 | ||
| 937 | retry: | ||
| 931 | /* 1st stage: get value from context */ | 938 | /* 1st stage: get value from context */ |
| 932 | switch (code->op) { | 939 | switch (code->op) { |
| 933 | case FETCH_OP_REG: | 940 | case FETCH_OP_REG: |
| @@ -953,6 +960,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, | |||
| 953 | val = regs_get_kernel_argument(regs, code->param); | 960 | val = regs_get_kernel_argument(regs, code->param); |
| 954 | break; | 961 | break; |
| 955 | #endif | 962 | #endif |
| 963 | case FETCH_NOP_SYMBOL: /* Ignore a place holder */ | ||
| 964 | code++; | ||
| 965 | goto retry; | ||
| 956 | default: | 966 | default: |
| 957 | return -EILSEQ; | 967 | return -EILSEQ; |
| 958 | } | 968 | } |
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 333cda6d2633..5b3d573b3dcf 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c | |||
| @@ -251,16 +251,16 @@ parse_probe_arg(char *arg, const struct fetch_type *type, | |||
| 251 | if (!(flags & TPARG_FL_KERNEL)) | 251 | if (!(flags & TPARG_FL_KERNEL)) |
| 252 | return -EINVAL; | 252 | return -EINVAL; |
| 253 | 253 | ||
| 254 | ret = traceprobe_split_symbol_offset(arg + 1, &offset); | 254 | /* Preserve symbol for updating */ |
| 255 | if (ret) | 255 | code->op = FETCH_NOP_SYMBOL; |
| 256 | break; | 256 | code->data = kstrdup(arg + 1, GFP_KERNEL); |
| 257 | if (!code->data) | ||
| 258 | return -ENOMEM; | ||
| 259 | if (++code == end) | ||
| 260 | return -E2BIG; | ||
| 257 | 261 | ||
| 258 | code->op = FETCH_OP_IMM; | 262 | code->op = FETCH_OP_IMM; |
| 259 | code->immediate = | 263 | code->immediate = 0; |
| 260 | (unsigned long)kallsyms_lookup_name(arg + 1); | ||
| 261 | if (!code->immediate) | ||
| 262 | return -ENOENT; | ||
| 263 | code->immediate += offset; | ||
| 264 | } | 264 | } |
| 265 | /* These are fetching from memory */ | 265 | /* These are fetching from memory */ |
| 266 | if (++code == end) | 266 | if (++code == end) |
| @@ -480,6 +480,11 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, | |||
| 480 | memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1)); | 480 | memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1)); |
| 481 | 481 | ||
| 482 | fail: | 482 | fail: |
| 483 | if (ret) { | ||
| 484 | for (code = tmp; code < tmp + FETCH_INSN_MAX; code++) | ||
| 485 | if (code->op == FETCH_NOP_SYMBOL) | ||
| 486 | kfree(code->data); | ||
| 487 | } | ||
| 483 | kfree(tmp); | 488 | kfree(tmp); |
| 484 | 489 | ||
| 485 | return ret; | 490 | return ret; |
| @@ -504,12 +509,53 @@ int traceprobe_conflict_field_name(const char *name, | |||
| 504 | 509 | ||
| 505 | void traceprobe_free_probe_arg(struct probe_arg *arg) | 510 | void traceprobe_free_probe_arg(struct probe_arg *arg) |
| 506 | { | 511 | { |
| 512 | struct fetch_insn *code = arg->code; | ||
| 513 | |||
| 514 | while (code && code->op != FETCH_OP_END) { | ||
| 515 | if (code->op == FETCH_NOP_SYMBOL) | ||
| 516 | kfree(code->data); | ||
| 517 | code++; | ||
| 518 | } | ||
| 507 | kfree(arg->code); | 519 | kfree(arg->code); |
| 508 | kfree(arg->name); | 520 | kfree(arg->name); |
| 509 | kfree(arg->comm); | 521 | kfree(arg->comm); |
| 510 | kfree(arg->fmt); | 522 | kfree(arg->fmt); |
| 511 | } | 523 | } |
| 512 | 524 | ||
| 525 | int traceprobe_update_arg(struct probe_arg *arg) | ||
| 526 | { | ||
| 527 | struct fetch_insn *code = arg->code; | ||
| 528 | long offset; | ||
| 529 | char *tmp; | ||
| 530 | char c; | ||
| 531 | int ret = 0; | ||
| 532 | |||
| 533 | while (code && code->op != FETCH_OP_END) { | ||
| 534 | if (code->op == FETCH_NOP_SYMBOL) { | ||
| 535 | if (code[1].op != FETCH_OP_IMM) | ||
| 536 | return -EINVAL; | ||
| 537 | |||
| 538 | tmp = strpbrk("+-", code->data); | ||
| 539 | if (tmp) | ||
| 540 | c = *tmp; | ||
| 541 | ret = traceprobe_split_symbol_offset(code->data, | ||
| 542 | &offset); | ||
| 543 | if (ret) | ||
| 544 | return ret; | ||
| 545 | |||
| 546 | code[1].immediate = | ||
| 547 | (unsigned long)kallsyms_lookup_name(code->data); | ||
| 548 | if (tmp) | ||
| 549 | *tmp = c; | ||
| 550 | if (!code[1].immediate) | ||
| 551 | return -ENOENT; | ||
| 552 | code[1].immediate += offset; | ||
| 553 | } | ||
| 554 | code++; | ||
| 555 | } | ||
| 556 | return 0; | ||
| 557 | } | ||
| 558 | |||
| 513 | /* When len=0, we just calculate the needed length */ | 559 | /* When len=0, we just calculate the needed length */ |
| 514 | #define LEN_OR_ZERO (len ? len - pos : 0) | 560 | #define LEN_OR_ZERO (len ? len - pos : 0) |
| 515 | static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, | 561 | static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, |
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 09f62171cc23..974afc1a3e73 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h | |||
| @@ -100,6 +100,7 @@ enum fetch_op { | |||
| 100 | // Stage 5 (loop) op | 100 | // Stage 5 (loop) op |
| 101 | FETCH_OP_LP_ARRAY, /* Array: .param = loop count */ | 101 | FETCH_OP_LP_ARRAY, /* Array: .param = loop count */ |
| 102 | FETCH_OP_END, | 102 | FETCH_OP_END, |
| 103 | FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */ | ||
| 103 | }; | 104 | }; |
| 104 | 105 | ||
| 105 | struct fetch_insn { | 106 | struct fetch_insn { |
| @@ -116,6 +117,7 @@ struct fetch_insn { | |||
| 116 | unsigned char rshift; | 117 | unsigned char rshift; |
| 117 | }; | 118 | }; |
| 118 | unsigned long immediate; | 119 | unsigned long immediate; |
| 120 | void *data; | ||
| 119 | }; | 121 | }; |
| 120 | }; | 122 | }; |
| 121 | 123 | ||
| @@ -276,7 +278,7 @@ extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, | |||
| 276 | extern int traceprobe_conflict_field_name(const char *name, | 278 | extern int traceprobe_conflict_field_name(const char *name, |
| 277 | struct probe_arg *args, int narg); | 279 | struct probe_arg *args, int narg); |
| 278 | 280 | ||
| 279 | extern void traceprobe_update_arg(struct probe_arg *arg); | 281 | extern int traceprobe_update_arg(struct probe_arg *arg); |
| 280 | extern void traceprobe_free_probe_arg(struct probe_arg *arg); | 282 | extern void traceprobe_free_probe_arg(struct probe_arg *arg); |
| 281 | 283 | ||
| 282 | extern int traceprobe_split_symbol_offset(char *symbol, long *offset); | 284 | extern int traceprobe_split_symbol_offset(char *symbol, long *offset); |
