diff options
Diffstat (limited to 'tools/perf/builtin-kwork.c')
| -rw-r--r-- | tools/perf/builtin-kwork.c | 345 |
1 files changed, 226 insertions, 119 deletions
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index c41a68d073de..7b61168e01e9 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -9,6 +9,7 @@ #include "perf.h" #include "util/data.h" +#include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/header.h" @@ -323,8 +324,8 @@ static struct kwork_work *work_search(struct rb_root_cached *root, else if (cmp < 0) node = node->rb_right; else { - if (work->name == NULL) - work->name = key->name; + if (work->name == NULL && key->name != NULL) + work->name = strdup(key->name); return work; } } @@ -371,11 +372,54 @@ static struct kwork_work *work_new(struct kwork_work *key) work->id = key->id; work->cpu = key->cpu; - work->name = key->name; + work->name = key->name ? strdup(key->name) : NULL; work->class = key->class; return work; } + +static void work_delete(struct kwork_work *work) +{ + if (work) { + work_exit(work); + free(work); + } +} + +static void kwork_work__free_root(struct rb_root_cached *root) +{ + struct rb_node *next; + struct kwork_work *work; + + while ((next = rb_first_cached(root))) { + work = rb_entry(next, struct kwork_work, node); + rb_erase_cached(next, root); + work_delete(work); + } +} + +static void perf_kwork__exit(struct perf_kwork *kwork) +{ + struct kwork_class *class; + struct kwork_atom_page *page, *tmp_page; + + list_for_each_entry(class, &kwork->class_list, list) { + kwork_work__free_root(&class->work_root); + } + + kwork_work__free_root(&kwork->sorted_work_root); + + list_for_each_entry_safe(page, tmp_page, &kwork->atom_page_list, list) { + list_del_init(&page->list); + free(page); + } + + INIT_LIST_HEAD(&kwork->class_list); + INIT_LIST_HEAD(&kwork->atom_page_list); + INIT_LIST_HEAD(&kwork->sort_list); + INIT_LIST_HEAD(&kwork->cmp_id); +} + static struct kwork_work *work_findnew(struct rb_root_cached *root, struct kwork_work *key, struct list_head *sort_list) @@ -424,7 +468,9 @@ static bool profile_event_match(struct perf_kwork *kwork, u64 time = sample->time; struct perf_time_interval *ptime = &kwork->ptime; - if ((kwork->cpu_list != NULL) && !test_bit(cpu, kwork->cpu_bitmap)) + /* Guard test_bit: cpu == -1 (absent PERF_SAMPLE_CPU) would index past the bitmap */ + if ((kwork->cpu_list != NULL) && + ((unsigned int)cpu >= MAX_NR_CPUS || !test_bit(cpu, kwork->cpu_bitmap))) return false; if (((ptime->start != 0) && (ptime->start > time)) || @@ -448,31 +494,34 @@ static int work_push_atom(struct perf_kwork *kwork, struct kwork_class *class, enum kwork_trace_type src_type, enum kwork_trace_type dst_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct kwork_work **ret_work, bool overwrite) { - struct kwork_atom *atom, *dst_atom, *last_atom; + struct kwork_atom *atom = NULL, *dst_atom, *last_atom; struct kwork_work *work, key; + int ret = 0; BUG_ON(class->work_init == NULL); - class->work_init(kwork, class, &key, src_type, evsel, sample, machine); + class->work_init(kwork, class, &key, src_type, sample, machine); atom = atom_new(kwork, sample); - if (atom == NULL) - return -1; + if (atom == NULL) { + ret = -1; + goto out; + } work = work_findnew(&class->work_root, &key, &kwork->cmp_id); if (work == NULL) { atom_free(atom); - return -1; + ret = -1; + goto out; } if (!profile_event_match(kwork, work, sample)) { atom_free(atom); - return 0; + goto out; } if (dst_type < KWORK_TRACE_MAX) { @@ -499,39 +548,39 @@ static int work_push_atom(struct perf_kwork *kwork, } list_add_tail(&atom->list, &work->atom_list[src_type]); - - return 0; +out: + work_exit(&key); + return ret; } static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, struct kwork_class *class, enum kwork_trace_type src_type, enum kwork_trace_type dst_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine, struct kwork_work **ret_work) { - struct kwork_atom *atom, *src_atom; + struct kwork_atom *atom = NULL, *src_atom; struct kwork_work *work, key; BUG_ON(class->work_init == NULL); - class->work_init(kwork, class, &key, src_type, evsel, sample, machine); + class->work_init(kwork, class, &key, src_type, sample, machine); work = work_findnew(&class->work_root, &key, &kwork->cmp_id); if (ret_work != NULL) *ret_work = work; if (work == NULL) - return NULL; + goto out; if (!profile_event_match(kwork, work, sample)) - return NULL; + goto out; atom = list_last_entry_or_null(&work->atom_list[dst_type], struct kwork_atom, list); if (atom != NULL) - return atom; + goto out; src_atom = atom_new(kwork, sample); if (src_atom != NULL) @@ -540,8 +589,9 @@ static struct kwork_atom *work_pop_atom(struct perf_kwork *kwork, if (ret_work != NULL) *ret_work = NULL; } - - return NULL; +out: + work_exit(&key); + return atom; } static struct kwork_work *find_work_by_id(struct rb_root_cached *root, @@ -599,18 +649,16 @@ static void report_update_exit_event(struct kwork_work *work, static int report_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int report_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -618,7 +666,7 @@ static int report_exit_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (work == NULL) return -1; @@ -654,18 +702,16 @@ static void latency_update_entry_event(struct kwork_work *work, static int latency_raise_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_RAISE, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int latency_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -673,7 +719,7 @@ static int latency_entry_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; atom = work_pop_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_RAISE, evsel, sample, + KWORK_TRACE_RAISE, sample, machine, &work); if (work == NULL) return -1; @@ -688,7 +734,6 @@ static int latency_entry_event(struct perf_kwork *kwork, static void timehist_save_callchain(struct perf_kwork *kwork, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { struct symbol *sym; @@ -708,7 +753,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork, cursor = get_tls_callchain_cursor(); - if (thread__resolve_callchain(thread, cursor, evsel, sample, + if (thread__resolve_callchain(thread, cursor, sample, NULL, NULL, kwork->max_stack + 2) != 0) { pr_debug("Failed to resolve callchain, skipping\n"); goto out_put; @@ -725,7 +770,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork, if (sym) { if (!strcmp(sym->name, "__softirqentry_text_start") || !strcmp(sym->name, "__do_softirq")) - sym->ignore = 1; + symbol__set_ignore(sym, true); } callchain_cursor_advance(cursor); @@ -813,18 +858,16 @@ static void timehist_print_event(struct perf_kwork *kwork, static int timehist_raise_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_RAISE, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int timehist_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -832,20 +875,19 @@ static int timehist_entry_event(struct perf_kwork *kwork, struct kwork_work *work = NULL; ret = work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_RAISE, evsel, sample, + KWORK_TRACE_RAISE, sample, machine, &work, true); if (ret) return ret; if (work != NULL) - timehist_save_callchain(kwork, sample, evsel, machine); + timehist_save_callchain(kwork, sample, machine); return 0; } static int timehist_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -856,13 +898,14 @@ static int timehist_exit_event(struct perf_kwork *kwork, addr_location__init(&al); if (machine__resolve(machine, &al, sample) < 0) { - pr_debug("Problem processing event, skipping it\n"); + pr_debug("problem processing event at offset %#" PRIx64 ", skipping it\n", + sample->file_offset); ret = -1; goto out; } atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (work == NULL) { ret = -1; @@ -896,18 +939,16 @@ static void top_update_runtime(struct kwork_work *work, static int top_entry_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { return work_push_atom(kwork, class, KWORK_TRACE_ENTRY, - KWORK_TRACE_MAX, evsel, sample, + KWORK_TRACE_MAX, sample, machine, NULL, true); } static int top_exit_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -916,7 +957,7 @@ static int top_exit_event(struct perf_kwork *kwork, struct kwork_atom *atom; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (!work) return -1; @@ -937,7 +978,6 @@ static int top_exit_event(struct perf_kwork *kwork, static int top_sched_switch_event(struct perf_kwork *kwork, struct kwork_class *class, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -945,7 +985,7 @@ static int top_sched_switch_event(struct perf_kwork *kwork, struct kwork_work *work; atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, - KWORK_TRACE_ENTRY, evsel, sample, + KWORK_TRACE_ENTRY, sample, machine, &work); if (!work) return -1; @@ -955,12 +995,11 @@ static int top_sched_switch_event(struct perf_kwork *kwork, atom_del(atom); } - return top_entry_event(kwork, class, evsel, sample, machine); + return top_entry_event(kwork, class, sample, machine); } static struct kwork_class kwork_irq; static int process_irq_handler_entry_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -968,12 +1007,11 @@ static int process_irq_handler_entry_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_irq, - evsel, sample, machine); + sample, machine); return 0; } static int process_irq_handler_exit_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -981,11 +1019,11 @@ static int process_irq_handler_exit_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_irq, - evsel, sample, machine); + sample, machine); return 0; } -const struct evsel_str_handler irq_tp_handlers[] = { +static const struct evsel_str_handler irq_tp_handlers[] = { { "irq:irq_handler_entry", process_irq_handler_entry_event, }, { "irq:irq_handler_exit", process_irq_handler_exit_event, }, }; @@ -1006,7 +1044,6 @@ static void irq_work_init(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1014,17 +1051,20 @@ static void irq_work_init(struct perf_kwork *kwork, work->cpu = sample->cpu; if (kwork->report == KWORK_REPORT_TOP) { - work->id = evsel__intval_common(evsel, sample, "common_pid"); + work->id = perf_sample__intval_common(sample, "common_pid"); work->name = NULL; } else { - work->id = evsel__intval(evsel, sample, "irq"); - work->name = evsel__strval(evsel, sample, "name"); + work->id = perf_sample__intval(sample, "irq"); + work->name = strdup(perf_sample__strval(sample, "name") ?: "<unknown>"); } } static void irq_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "%s:%" PRIu64 "", work->name, work->id); + if (work->name != NULL) + snprintf(buf, len, "%s:%" PRIu64 "", work->name, work->id); + else + snprintf(buf, len, "%" PRIu64 "", work->id); } static struct kwork_class kwork_irq = { @@ -1039,7 +1079,6 @@ static struct kwork_class kwork_irq = { static struct kwork_class kwork_softirq; static int process_softirq_raise_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1047,13 +1086,12 @@ static int process_softirq_raise_event(const struct perf_tool *tool, if (kwork->tp_handler->raise_event) return kwork->tp_handler->raise_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } static int process_softirq_entry_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1061,13 +1099,12 @@ static int process_softirq_entry_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } static int process_softirq_exit_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1075,12 +1112,12 @@ static int process_softirq_exit_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_softirq, - evsel, sample, machine); + sample, machine); return 0; } -const struct evsel_str_handler softirq_tp_handlers[] = { +static const struct evsel_str_handler softirq_tp_handlers[] = { { "irq:softirq_raise", process_softirq_raise_event, }, { "irq:softirq_entry", process_softirq_entry_event, }, { "irq:softirq_exit", process_softirq_exit_event, }, @@ -1134,7 +1171,6 @@ static void softirq_work_init(struct perf_kwork *kwork, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1144,18 +1180,21 @@ static void softirq_work_init(struct perf_kwork *kwork, work->cpu = sample->cpu; if (kwork->report == KWORK_REPORT_TOP) { - work->id = evsel__intval_common(evsel, sample, "common_pid"); + work->id = perf_sample__intval_common(sample, "common_pid"); work->name = NULL; } else { - num = evsel__intval(evsel, sample, "vec"); + num = perf_sample__intval(sample, "vec"); work->id = num; - work->name = evsel__softirq_name(evsel, num); + work->name = evsel__softirq_name(sample->evsel, num); } } static void softirq_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "(s)%s:%" PRIu64 "", work->name, work->id); + if (work->name != NULL) + snprintf(buf, len, "(s)%s:%" PRIu64 "", work->name, work->id); + else + snprintf(buf, len, "(s)%" PRIu64 "", work->id); } static struct kwork_class kwork_softirq = { @@ -1170,7 +1209,6 @@ static struct kwork_class kwork_softirq = { static struct kwork_class kwork_workqueue; static int process_workqueue_activate_work_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1178,13 +1216,12 @@ static int process_workqueue_activate_work_event(const struct perf_tool *tool, if (kwork->tp_handler->raise_event) return kwork->tp_handler->raise_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } static int process_workqueue_execute_start_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1192,13 +1229,12 @@ static int process_workqueue_execute_start_event(const struct perf_tool *tool, if (kwork->tp_handler->entry_event) return kwork->tp_handler->entry_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } static int process_workqueue_execute_end_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1206,12 +1242,12 @@ static int process_workqueue_execute_end_event(const struct perf_tool *tool, if (kwork->tp_handler->exit_event) return kwork->tp_handler->exit_event(kwork, &kwork_workqueue, - evsel, sample, machine); + sample, machine); return 0; } -const struct evsel_str_handler workqueue_tp_handlers[] = { +static const struct evsel_str_handler workqueue_tp_handlers[] = { { "workqueue:workqueue_activate_work", process_workqueue_activate_work_event, }, { "workqueue:workqueue_execute_start", process_workqueue_execute_start_event, }, { "workqueue:workqueue_execute_end", process_workqueue_execute_end_event, }, @@ -1234,19 +1270,23 @@ static void workqueue_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type __maybe_unused, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { char *modp = NULL; - unsigned long long function_addr = evsel__intval(evsel, - sample, "function"); + unsigned long long function_addr = perf_sample__intval(sample, "function"); work->class = class; work->cpu = sample->cpu; - work->id = evsel__intval(evsel, sample, "work"); - work->name = function_addr == 0 ? NULL : - machine__resolve_kernel_addr(machine, &function_addr, &modp); + work->id = perf_sample__intval(sample, "work"); + work->name = NULL; + + if (function_addr != 0) { + const char *name = machine__resolve_kernel_addr(machine, &function_addr, &modp); + + if (name) + work->name = strdup(name); + } } static void workqueue_work_name(struct kwork_work *work, char *buf, int len) @@ -1269,7 +1309,6 @@ static struct kwork_class kwork_workqueue = { static struct kwork_class kwork_sched; static int process_sched_switch_event(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine) { @@ -1277,11 +1316,11 @@ static int process_sched_switch_event(const struct perf_tool *tool, if (kwork->tp_handler->sched_switch_event) return kwork->tp_handler->sched_switch_event(kwork, &kwork_sched, - evsel, sample, machine); + sample, machine); return 0; } -const struct evsel_str_handler sched_tp_handlers[] = { +static const struct evsel_str_handler sched_tp_handlers[] = { { "sched:sched_switch", process_sched_switch_event, }, }; @@ -1302,7 +1341,6 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, struct kwork_class *class, struct kwork_work *work, enum kwork_trace_type src_type, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine __maybe_unused) { @@ -1310,17 +1348,17 @@ static void sched_work_init(struct perf_kwork *kwork __maybe_unused, work->cpu = sample->cpu; if (src_type == KWORK_TRACE_EXIT) { - work->id = evsel__intval(evsel, sample, "prev_pid"); - work->name = strdup(evsel__strval(evsel, sample, "prev_comm")); + work->id = perf_sample__intval(sample, "prev_pid"); + work->name = strdup(perf_sample__strval(sample, "prev_comm") ?: "<unknown>"); } else if (src_type == KWORK_TRACE_ENTRY) { - work->id = evsel__intval(evsel, sample, "next_pid"); - work->name = strdup(evsel__strval(evsel, sample, "next_comm")); + work->id = perf_sample__intval(sample, "next_pid"); + work->name = strdup(perf_sample__strval(sample, "next_comm") ?: "<unknown>"); } } static void sched_work_name(struct kwork_work *work, char *buf, int len) { - snprintf(buf, len, "%s", work->name); + snprintf(buf, len, "%s", work->name ?: ""); } static struct kwork_class kwork_sched = { @@ -1561,13 +1599,13 @@ static void print_bad_events(struct perf_kwork *kwork) } } -const char *graph_load = "||||||||||||||||||||||||||||||||||||||||||||||||"; -const char *graph_idle = " "; static void top_print_per_cpu_load(struct perf_kwork *kwork) { int i, load_width; u64 total, load, load_ratio; struct kwork_top_stat *stat = &kwork->top_stat; + const char *graph_load = "||||||||||||||||||||||||||||||||||||||||||||||||"; + const char *graph_idle = " "; for (i = 0; i < MAX_NR_CPUS; i++) { total = stat->cpus_runtime[i].total; @@ -1804,7 +1842,7 @@ static int perf_kwork__read_events(struct perf_kwork *kwork) return PTR_ERR(session); } - symbol__init(&session->header.env); + symbol__init(perf_session__env(session)); if (perf_kwork__check_config(kwork, session) != 0) goto out_delete; @@ -1948,22 +1986,21 @@ static int perf_kwork__report(struct perf_kwork *kwork) } typedef int (*tracepoint_handler)(const struct perf_tool *tool, - struct evsel *evsel, struct perf_sample *sample, struct machine *machine); static int perf_kwork__process_tracepoint_sample(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, - struct evsel *evsel, struct machine *machine) { + struct evsel *evsel = sample->evsel; int err = 0; if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - err = f(tool, evsel, sample, machine); + err = f(tool, sample, machine); } return err; @@ -2008,7 +2045,18 @@ static void top_calc_total_runtime(struct perf_kwork *kwork) next = rb_first_cached(&class->work_root); while (next) { work = rb_entry(next, struct kwork_work, node); - BUG_ON(work->cpu >= MAX_NR_CPUS); + /* + * work->cpu comes from sample->cpu which is -1 when + * PERF_SAMPLE_CPU is absent. As int that's -1, but as + * unsigned it exceeds MAX_NR_CPUS — skip to avoid OOB + * on cpus_runtime[]. + */ + /* Counted and reported in perf_kwork__top_report() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) { + stat->nr_skipped_cpu++; + next = rb_next(next); + continue; + } stat->cpus_runtime[work->cpu].total += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].total += work->total_runtime; next = rb_next(next); @@ -2020,7 +2068,8 @@ static void top_calc_idle_time(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; - if (work->id == 0) { + /* See comment in top_calc_total_runtime() */ + if (work->id == 0 && (unsigned int)work->cpu < MAX_NR_CPUS) { stat->cpus_runtime[work->cpu].idle += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].idle += work->total_runtime; } @@ -2032,6 +2081,10 @@ static void top_calc_irq_runtime(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; + /* See comment in top_calc_total_runtime() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) + return; + if (type == KWORK_CLASS_IRQ) { stat->cpus_runtime[work->cpu].irq += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].irq += work->total_runtime; @@ -2084,12 +2137,19 @@ static void top_calc_cpu_usage(struct perf_kwork *kwork) if (work->total_runtime == 0) goto next; + /* See comment in top_calc_total_runtime() */ + if ((unsigned int)work->cpu >= MAX_NR_CPUS) + goto next; + __set_bit(work->cpu, stat->all_cpus_bitmap); top_subtract_irq_runtime(kwork, work); - work->cpu_usage = work->total_runtime * 10000 / - stat->cpus_runtime[work->cpu].total; + /* Guard against division by zero if no runtime was accumulated */ + if (stat->cpus_runtime[work->cpu].total) { + work->cpu_usage = work->total_runtime * 10000 / + stat->cpus_runtime[work->cpu].total; + } top_calc_idle_time(kwork, work); next: @@ -2102,7 +2162,8 @@ static void top_calc_load_runtime(struct perf_kwork *kwork, { struct kwork_top_stat *stat = &kwork->top_stat; - if (work->id != 0) { + /* See comment in top_calc_total_runtime() */ + if (work->id != 0 && (unsigned int)work->cpu < MAX_NR_CPUS) { stat->cpus_runtime[work->cpu].load += work->total_runtime; stat->cpus_runtime[MAX_NR_CPUS].load += work->total_runtime; } @@ -2128,8 +2189,10 @@ static void top_merge_tasks(struct perf_kwork *kwork) rb_erase_cached(node, &class->work_root); data = rb_entry(node, struct kwork_work, node); - if (!profile_name_match(kwork, data)) + if (!profile_name_match(kwork, data)) { + work_delete(data); continue; + } cpu = data->cpu; merged_work = find_work_by_id(&merged_root, data->id, @@ -2137,11 +2200,17 @@ static void top_merge_tasks(struct perf_kwork *kwork) if (!merged_work) { work_insert(&merged_root, data, &kwork->cmp_id); } else { + if (merged_work->name == NULL && data->name != NULL) + merged_work->name = strdup(data->name); + merged_work->total_runtime += data->total_runtime; merged_work->cpu_usage += data->cpu_usage; } top_calc_load_runtime(kwork, data); + + if (merged_work) + work_delete(data); } work_sort(kwork, class, &merged_root); @@ -2170,6 +2239,13 @@ next: next = rb_next(next); } + if (kwork->top_stat.nr_skipped_cpu) { + printf(" Warning: %u work entries with invalid CPU were excluded from totals.\n" + " Task runtimes may appear inflated (IRQ time not subtracted).\n" + " Consider re-recording with PERF_SAMPLE_CPU enabled.\n", + kwork->top_stat.nr_skipped_cpu); + } + printf("\n"); } @@ -2208,7 +2284,7 @@ static int perf_kwork__top(struct perf_kwork *kwork) struct __top_cpus_runtime *cpus_runtime; int ret = 0; - cpus_runtime = zalloc(sizeof(struct __top_cpus_runtime) * (MAX_NR_CPUS + 1)); + cpus_runtime = calloc(MAX_NR_CPUS + 1, sizeof(struct __top_cpus_runtime)); if (!cpus_runtime) return -1; @@ -2273,12 +2349,23 @@ static void setup_event_list(struct perf_kwork *kwork, pr_debug("\n"); } +#define STRDUP_FAIL_EXIT(s) \ + ({ char *_p; \ + _p = strdup(s); \ + if (!_p) { \ + ret = -ENOMEM; \ + goto EXIT; \ + } \ + _p; \ + }) + static int perf_kwork__record(struct perf_kwork *kwork, int argc, const char **argv) { - const char **rec_argv; + const char **rec_argv, **to_free = NULL; unsigned int rec_argc, i, j; struct kwork_class *class; + int ret; const char *const record_args[] = { "record", @@ -2298,26 +2385,43 @@ static int perf_kwork__record(struct perf_kwork *kwork, return -ENOMEM; for (i = 0; i < ARRAY_SIZE(record_args); i++) - rec_argv[i] = strdup(record_args[i]); + rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); list_for_each_entry(class, &kwork->class_list, list) { for (j = 0; j < class->nr_tracepoints; j++) { - rec_argv[i++] = strdup("-e"); - rec_argv[i++] = strdup(class->tp_handlers[j].name); + rec_argv[i++] = STRDUP_FAIL_EXIT("-e"); + rec_argv[i++] = STRDUP_FAIL_EXIT(class->tp_handlers[j].name); } } for (j = 1; j < (unsigned int)argc; j++, i++) - rec_argv[i] = argv[j]; + rec_argv[i] = STRDUP_FAIL_EXIT(argv[j]); BUG_ON(i != rec_argc); + /* Save the pointers as the array will be mutated by cmd_record. */ + to_free = calloc(rec_argc + 1, sizeof(char *)); + if (to_free == NULL) { + ret = -ENOMEM; + goto EXIT; + } + pr_debug("record comm: "); - for (j = 0; j < rec_argc; j++) + for (j = 0; j < rec_argc; j++) { pr_debug("%s ", rec_argv[j]); + to_free[j] = rec_argv[j]; + } pr_debug("\n"); - return cmd_record(i, rec_argv); + ret = cmd_record(i, rec_argv); + +EXIT: + for (i = 0; i < rec_argc; i++) + free((void *)(to_free ? to_free[i] : rec_argv[i])); + + free(to_free); + free(rec_argv); + return ret; } int cmd_kwork(int argc, const char **argv) @@ -2406,8 +2510,8 @@ int cmd_kwork(int argc, const char **argv) "Display call chains if present"), OPT_UINTEGER(0, "max-stack", &kwork.max_stack, "Maximum number of functions to display backtrace."), - OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", - "Look for files with symbols relative to this directory"), + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, + symbol__config_symfs), OPT_STRING(0, "time", &kwork.time_str, "str", "Time span for analysis (start,stop)"), OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu", @@ -2458,6 +2562,7 @@ int cmd_kwork(int argc, const char **argv) const char *const kwork_subcommands[] = { "record", "report", "latency", "timehist", "top", NULL }; + int ret = 0; perf_tool__init(&kwork.tool, /*ordered_events=*/true); kwork.tool.mmap = perf_event__process_mmap; @@ -2474,7 +2579,7 @@ int cmd_kwork(int argc, const char **argv) if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) { setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__record(&kwork, argc, argv); + ret = perf_kwork__record(&kwork, argc, argv); } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) { kwork.sort_order = default_report_sort_order; if (argc > 1) { @@ -2485,7 +2590,7 @@ int cmd_kwork(int argc, const char **argv) kwork.report = KWORK_REPORT_RUNTIME; setup_sorting(&kwork, report_options, report_usage); setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__report(&kwork); + ret = perf_kwork__report(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) { kwork.sort_order = default_latency_sort_order; if (argc > 1) { @@ -2496,7 +2601,7 @@ int cmd_kwork(int argc, const char **argv) kwork.report = KWORK_REPORT_LATENCY; setup_sorting(&kwork, latency_options, latency_usage); setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__report(&kwork); + ret = perf_kwork__report(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("timehist", argv[0])) { if (argc > 1) { argc = parse_options(argc, argv, timehist_options, timehist_usage, 0); @@ -2505,7 +2610,7 @@ int cmd_kwork(int argc, const char **argv) } kwork.report = KWORK_REPORT_TIMEHIST; setup_event_list(&kwork, kwork_options, kwork_usage); - return perf_kwork__timehist(&kwork); + ret = perf_kwork__timehist(&kwork); } else if (strlen(argv[0]) > 2 && strstarts("top", argv[0])) { kwork.sort_order = default_top_sort_order; if (argc > 1) { @@ -2518,12 +2623,14 @@ int cmd_kwork(int argc, const char **argv) kwork.event_list_str = "sched, irq, softirq"; setup_event_list(&kwork, kwork_options, kwork_usage); setup_sorting(&kwork, top_options, top_usage); - return perf_kwork__top(&kwork); + ret = perf_kwork__top(&kwork); } else usage_with_options(kwork_usage, kwork_options); + perf_kwork__exit(&kwork); + /* free usage string allocated by parse_options_subcommand */ free((void *)kwork_usage[0]); - return 0; + return ret; } |
