diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-09-10 10:31:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-09-10 10:31:24 -0400 |
commit | f2955b490b249ca56e465fd32cc355f84aedf8bd (patch) | |
tree | 401faf98c61f4a0a65703644277521a10da98eec | |
parent | 3d96406c7da1ed5811ea52a3b0905f4f0e295376 (diff) | |
parent | 9efdda310cb26bdc28429cb831c3ec5fa270feb7 (diff) |
Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
tracing: t_start: reset FTRACE_ITER_HASH in case of seek/pread
perf symbols: Fix multiple initialization of symbol system
perf: Fix CPU hotplug
perf, trace: Fix module leak
tracing/kprobe: Fix handling of C-unlike argument names
tracing/kprobes: Fix handling of argument names
perf probe: Fix handling of arguments names
perf probe: Fix return probe support
tracing/kprobe: Fix a memory leak in error case
tracing: Do not allow llseek to set_ftrace_filter
-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; |