summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-kwork.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-kwork.c')
-rw-r--r--tools/perf/builtin-kwork.c345
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;
}