diff options
| -rw-r--r-- | kernel/perf_event.c | 6 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 4 | ||||
| -rw-r--r-- | kernel/trace/trace_event_perf.c | 3 | ||||
| -rw-r--r-- | kernel/trace/trace_kprobe.c | 43 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.c | 1 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 42 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 7 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 3 |
8 files changed, 73 insertions, 36 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 657555a5f30f..db5b56064687 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
| @@ -5761,15 +5761,15 @@ perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) | |||
| 5761 | { | 5761 | { |
| 5762 | unsigned int cpu = (long)hcpu; | 5762 | unsigned int cpu = (long)hcpu; |
| 5763 | 5763 | ||
| 5764 | switch (action) { | 5764 | switch (action & ~CPU_TASKS_FROZEN) { |
| 5765 | 5765 | ||
| 5766 | case CPU_UP_PREPARE: | 5766 | case CPU_UP_PREPARE: |
| 5767 | case CPU_UP_PREPARE_FROZEN: | 5767 | case CPU_DOWN_FAILED: |
| 5768 | perf_event_init_cpu(cpu); | 5768 | perf_event_init_cpu(cpu); |
| 5769 | break; | 5769 | break; |
| 5770 | 5770 | ||
| 5771 | case CPU_UP_CANCELED: | ||
| 5771 | case CPU_DOWN_PREPARE: | 5772 | case CPU_DOWN_PREPARE: |
| 5772 | case CPU_DOWN_PREPARE_FROZEN: | ||
| 5773 | perf_event_exit_cpu(cpu); | 5773 | perf_event_exit_cpu(cpu); |
| 5774 | break; | 5774 | break; |
| 5775 | 5775 | ||
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 7cb1f45a1de1..fa7ece649fe1 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -1510,6 +1510,8 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
| 1510 | if (*pos > 0) | 1510 | if (*pos > 0) |
| 1511 | return t_hash_start(m, pos); | 1511 | return t_hash_start(m, pos); |
| 1512 | iter->flags |= FTRACE_ITER_PRINTALL; | 1512 | iter->flags |= FTRACE_ITER_PRINTALL; |
| 1513 | /* reset in case of seek/pread */ | ||
| 1514 | iter->flags &= ~FTRACE_ITER_HASH; | ||
| 1513 | return iter; | 1515 | return iter; |
| 1514 | } | 1516 | } |
| 1515 | 1517 | ||
| @@ -2416,7 +2418,7 @@ static const struct file_operations ftrace_filter_fops = { | |||
| 2416 | .open = ftrace_filter_open, | 2418 | .open = ftrace_filter_open, |
| 2417 | .read = seq_read, | 2419 | .read = seq_read, |
| 2418 | .write = ftrace_filter_write, | 2420 | .write = ftrace_filter_write, |
| 2419 | .llseek = ftrace_regex_lseek, | 2421 | .llseek = no_llseek, |
| 2420 | .release = ftrace_filter_release, | 2422 | .release = ftrace_filter_release, |
| 2421 | }; | 2423 | }; |
| 2422 | 2424 | ||
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 000e6e85b445..31cc4cb0dbf2 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c | |||
| @@ -91,6 +91,8 @@ int perf_trace_init(struct perf_event *p_event) | |||
| 91 | tp_event->class && tp_event->class->reg && | 91 | tp_event->class && tp_event->class->reg && |
| 92 | try_module_get(tp_event->mod)) { | 92 | try_module_get(tp_event->mod)) { |
| 93 | ret = perf_trace_event_init(tp_event, p_event); | 93 | ret = perf_trace_event_init(tp_event, p_event); |
| 94 | if (ret) | ||
| 95 | module_put(tp_event->mod); | ||
| 94 | break; | 96 | break; |
| 95 | } | 97 | } |
| 96 | } | 98 | } |
| @@ -146,6 +148,7 @@ void perf_trace_destroy(struct perf_event *p_event) | |||
| 146 | } | 148 | } |
| 147 | } | 149 | } |
| 148 | out: | 150 | out: |
| 151 | module_put(tp_event->mod); | ||
| 149 | mutex_unlock(&event_mutex); | 152 | mutex_unlock(&event_mutex); |
| 150 | } | 153 | } |
| 151 | 154 | ||
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 8b27c9849b42..544301d29dee 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
| @@ -514,8 +514,8 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); | |||
| 514 | static int kretprobe_dispatcher(struct kretprobe_instance *ri, | 514 | static int kretprobe_dispatcher(struct kretprobe_instance *ri, |
| 515 | struct pt_regs *regs); | 515 | struct pt_regs *regs); |
| 516 | 516 | ||
| 517 | /* Check the name is good for event/group */ | 517 | /* Check the name is good for event/group/fields */ |
| 518 | static int check_event_name(const char *name) | 518 | static int is_good_name(const char *name) |
| 519 | { | 519 | { |
| 520 | if (!isalpha(*name) && *name != '_') | 520 | if (!isalpha(*name) && *name != '_') |
| 521 | return 0; | 521 | return 0; |
| @@ -557,7 +557,7 @@ static struct trace_probe *alloc_trace_probe(const char *group, | |||
| 557 | else | 557 | else |
| 558 | tp->rp.kp.pre_handler = kprobe_dispatcher; | 558 | tp->rp.kp.pre_handler = kprobe_dispatcher; |
| 559 | 559 | ||
| 560 | if (!event || !check_event_name(event)) { | 560 | if (!event || !is_good_name(event)) { |
| 561 | ret = -EINVAL; | 561 | ret = -EINVAL; |
| 562 | goto error; | 562 | goto error; |
| 563 | } | 563 | } |
| @@ -567,7 +567,7 @@ static struct trace_probe *alloc_trace_probe(const char *group, | |||
| 567 | if (!tp->call.name) | 567 | if (!tp->call.name) |
| 568 | goto error; | 568 | goto error; |
| 569 | 569 | ||
| 570 | if (!group || !check_event_name(group)) { | 570 | if (!group || !is_good_name(group)) { |
| 571 | ret = -EINVAL; | 571 | ret = -EINVAL; |
| 572 | goto error; | 572 | goto error; |
| 573 | } | 573 | } |
| @@ -883,7 +883,7 @@ static int create_trace_probe(int argc, char **argv) | |||
| 883 | int i, ret = 0; | 883 | int i, ret = 0; |
| 884 | int is_return = 0, is_delete = 0; | 884 | int is_return = 0, is_delete = 0; |
| 885 | char *symbol = NULL, *event = NULL, *group = NULL; | 885 | char *symbol = NULL, *event = NULL, *group = NULL; |
| 886 | char *arg, *tmp; | 886 | char *arg; |
| 887 | unsigned long offset = 0; | 887 | unsigned long offset = 0; |
| 888 | void *addr = NULL; | 888 | void *addr = NULL; |
| 889 | char buf[MAX_EVENT_NAME_LEN]; | 889 | char buf[MAX_EVENT_NAME_LEN]; |
| @@ -992,26 +992,36 @@ static int create_trace_probe(int argc, char **argv) | |||
| 992 | /* parse arguments */ | 992 | /* parse arguments */ |
| 993 | ret = 0; | 993 | ret = 0; |
| 994 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { | 994 | for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { |
| 995 | /* Increment count for freeing args in error case */ | ||
| 996 | tp->nr_args++; | ||
| 997 | |||
| 995 | /* Parse argument name */ | 998 | /* Parse argument name */ |
| 996 | arg = strchr(argv[i], '='); | 999 | arg = strchr(argv[i], '='); |
| 997 | if (arg) | 1000 | if (arg) { |
| 998 | *arg++ = '\0'; | 1001 | *arg++ = '\0'; |
| 999 | else | 1002 | tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); |
| 1003 | } else { | ||
| 1000 | arg = argv[i]; | 1004 | arg = argv[i]; |
| 1005 | /* If argument name is omitted, set "argN" */ | ||
| 1006 | snprintf(buf, MAX_EVENT_NAME_LEN, "arg%d", i + 1); | ||
| 1007 | tp->args[i].name = kstrdup(buf, GFP_KERNEL); | ||
| 1008 | } | ||
| 1001 | 1009 | ||
| 1002 | tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); | ||
| 1003 | if (!tp->args[i].name) { | 1010 | if (!tp->args[i].name) { |
| 1004 | pr_info("Failed to allocate argument%d name '%s'.\n", | 1011 | pr_info("Failed to allocate argument[%d] name.\n", i); |
| 1005 | i, argv[i]); | ||
| 1006 | ret = -ENOMEM; | 1012 | ret = -ENOMEM; |
| 1007 | goto error; | 1013 | goto error; |
| 1008 | } | 1014 | } |
| 1009 | tmp = strchr(tp->args[i].name, ':'); | 1015 | |
| 1010 | if (tmp) | 1016 | if (!is_good_name(tp->args[i].name)) { |
| 1011 | *tmp = '_'; /* convert : to _ */ | 1017 | pr_info("Invalid argument[%d] name: %s\n", |
| 1018 | i, tp->args[i].name); | ||
| 1019 | ret = -EINVAL; | ||
| 1020 | goto error; | ||
| 1021 | } | ||
| 1012 | 1022 | ||
| 1013 | if (conflict_field_name(tp->args[i].name, tp->args, i)) { | 1023 | if (conflict_field_name(tp->args[i].name, tp->args, i)) { |
| 1014 | pr_info("Argument%d name '%s' conflicts with " | 1024 | pr_info("Argument[%d] name '%s' conflicts with " |
| 1015 | "another field.\n", i, argv[i]); | 1025 | "another field.\n", i, argv[i]); |
| 1016 | ret = -EINVAL; | 1026 | ret = -EINVAL; |
| 1017 | goto error; | 1027 | goto error; |
| @@ -1020,12 +1030,9 @@ static int create_trace_probe(int argc, char **argv) | |||
| 1020 | /* Parse fetch argument */ | 1030 | /* Parse fetch argument */ |
| 1021 | ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); | 1031 | ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); |
| 1022 | if (ret) { | 1032 | if (ret) { |
| 1023 | pr_info("Parse error at argument%d. (%d)\n", i, ret); | 1033 | pr_info("Parse error at argument[%d]. (%d)\n", i, ret); |
| 1024 | kfree(tp->args[i].name); | ||
| 1025 | goto error; | 1034 | goto error; |
| 1026 | } | 1035 | } |
| 1027 | |||
| 1028 | tp->nr_args++; | ||
| 1029 | } | 1036 | } |
| 1030 | 1037 | ||
| 1031 | ret = register_trace_probe(tp); | 1038 | ret = register_trace_probe(tp); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e72f05c3bef0..fcc16e4349df 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -1539,6 +1539,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
| 1539 | goto error; | 1539 | goto error; |
| 1540 | } | 1540 | } |
| 1541 | tev->point.offset = pev->point.offset; | 1541 | tev->point.offset = pev->point.offset; |
| 1542 | tev->point.retprobe = pev->point.retprobe; | ||
| 1542 | tev->nargs = pev->nargs; | 1543 | tev->nargs = pev->nargs; |
| 1543 | if (tev->nargs) { | 1544 | if (tev->nargs) { |
| 1544 | tev->args = zalloc(sizeof(struct probe_trace_arg) | 1545 | tev->args = zalloc(sizeof(struct probe_trace_arg) |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 525136684d4e..32b81f707ff5 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -686,6 +686,25 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 686 | char buf[32], *ptr; | 686 | char buf[32], *ptr; |
| 687 | int ret, nscopes; | 687 | int ret, nscopes; |
| 688 | 688 | ||
| 689 | if (!is_c_varname(pf->pvar->var)) { | ||
| 690 | /* Copy raw parameters */ | ||
| 691 | pf->tvar->value = strdup(pf->pvar->var); | ||
| 692 | if (pf->tvar->value == NULL) | ||
| 693 | return -ENOMEM; | ||
| 694 | if (pf->pvar->type) { | ||
| 695 | pf->tvar->type = strdup(pf->pvar->type); | ||
| 696 | if (pf->tvar->type == NULL) | ||
| 697 | return -ENOMEM; | ||
| 698 | } | ||
| 699 | if (pf->pvar->name) { | ||
| 700 | pf->tvar->name = strdup(pf->pvar->name); | ||
| 701 | if (pf->tvar->name == NULL) | ||
| 702 | return -ENOMEM; | ||
| 703 | } else | ||
| 704 | pf->tvar->name = NULL; | ||
| 705 | return 0; | ||
| 706 | } | ||
| 707 | |||
| 689 | if (pf->pvar->name) | 708 | if (pf->pvar->name) |
| 690 | pf->tvar->name = strdup(pf->pvar->name); | 709 | pf->tvar->name = strdup(pf->pvar->name); |
| 691 | else { | 710 | else { |
| @@ -700,19 +719,6 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 700 | if (pf->tvar->name == NULL) | 719 | if (pf->tvar->name == NULL) |
| 701 | return -ENOMEM; | 720 | return -ENOMEM; |
| 702 | 721 | ||
| 703 | if (!is_c_varname(pf->pvar->var)) { | ||
| 704 | /* Copy raw parameters */ | ||
| 705 | pf->tvar->value = strdup(pf->pvar->var); | ||
| 706 | if (pf->tvar->value == NULL) | ||
| 707 | return -ENOMEM; | ||
| 708 | if (pf->pvar->type) { | ||
| 709 | pf->tvar->type = strdup(pf->pvar->type); | ||
| 710 | if (pf->tvar->type == NULL) | ||
| 711 | return -ENOMEM; | ||
| 712 | } | ||
| 713 | return 0; | ||
| 714 | } | ||
| 715 | |||
| 716 | pr_debug("Searching '%s' variable in context.\n", | 722 | pr_debug("Searching '%s' variable in context.\n", |
| 717 | pf->pvar->var); | 723 | pf->pvar->var); |
| 718 | /* Search child die for local variables and parameters. */ | 724 | /* Search child die for local variables and parameters. */ |
| @@ -783,6 +789,16 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 783 | /* This function has no name. */ | 789 | /* This function has no name. */ |
| 784 | tev->point.offset = (unsigned long)pf->addr; | 790 | tev->point.offset = (unsigned long)pf->addr; |
| 785 | 791 | ||
| 792 | /* Return probe must be on the head of a subprogram */ | ||
| 793 | if (pf->pev->point.retprobe) { | ||
| 794 | if (tev->point.offset != 0) { | ||
| 795 | pr_warning("Return probe must be on the head of" | ||
| 796 | " a real function\n"); | ||
| 797 | return -EINVAL; | ||
| 798 | } | ||
| 799 | tev->point.retprobe = true; | ||
| 800 | } | ||
| 801 | |||
| 786 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, | 802 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, |
| 787 | tev->point.offset); | 803 | tev->point.offset); |
| 788 | 804 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 1a367734e016..b2f5ae97f33d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -2268,6 +2268,9 @@ static int setup_list(struct strlist **list, const char *list_str, | |||
| 2268 | 2268 | ||
| 2269 | int symbol__init(void) | 2269 | int symbol__init(void) |
| 2270 | { | 2270 | { |
| 2271 | if (symbol_conf.initialized) | ||
| 2272 | return 0; | ||
| 2273 | |||
| 2271 | elf_version(EV_CURRENT); | 2274 | elf_version(EV_CURRENT); |
| 2272 | if (symbol_conf.sort_by_name) | 2275 | if (symbol_conf.sort_by_name) |
| 2273 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 2276 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
| @@ -2293,6 +2296,7 @@ int symbol__init(void) | |||
| 2293 | symbol_conf.sym_list_str, "symbol") < 0) | 2296 | symbol_conf.sym_list_str, "symbol") < 0) |
| 2294 | goto out_free_comm_list; | 2297 | goto out_free_comm_list; |
| 2295 | 2298 | ||
| 2299 | symbol_conf.initialized = true; | ||
| 2296 | return 0; | 2300 | return 0; |
| 2297 | 2301 | ||
| 2298 | out_free_dso_list: | 2302 | out_free_dso_list: |
| @@ -2304,11 +2308,14 @@ out_free_comm_list: | |||
| 2304 | 2308 | ||
| 2305 | void symbol__exit(void) | 2309 | void symbol__exit(void) |
| 2306 | { | 2310 | { |
| 2311 | if (!symbol_conf.initialized) | ||
| 2312 | return; | ||
| 2307 | strlist__delete(symbol_conf.sym_list); | 2313 | strlist__delete(symbol_conf.sym_list); |
| 2308 | strlist__delete(symbol_conf.dso_list); | 2314 | strlist__delete(symbol_conf.dso_list); |
| 2309 | strlist__delete(symbol_conf.comm_list); | 2315 | strlist__delete(symbol_conf.comm_list); |
| 2310 | vmlinux_path__exit(); | 2316 | vmlinux_path__exit(); |
| 2311 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 2317 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
| 2318 | symbol_conf.initialized = false; | ||
| 2312 | } | 2319 | } |
| 2313 | 2320 | ||
| 2314 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) | 2321 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b7a8da4af5a0..ea95c2756f05 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -69,7 +69,8 @@ struct symbol_conf { | |||
| 69 | show_nr_samples, | 69 | show_nr_samples, |
| 70 | use_callchain, | 70 | use_callchain, |
| 71 | exclude_other, | 71 | exclude_other, |
| 72 | show_cpu_utilization; | 72 | show_cpu_utilization, |
| 73 | initialized; | ||
| 73 | const char *vmlinux_name, | 74 | const char *vmlinux_name, |
| 74 | *source_prefix, | 75 | *source_prefix, |
| 75 | *field_sep; | 76 | *field_sep; |
