From 727ab04edbc4767711a7aeff5e00249b267ed4c1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Oct 2011 10:42:19 -0200 Subject: [PATCH 01/17] perf evlist: Fix grouping of multiple events The __perf_evsel__open routing was grouping just the threads for that specific events per cpu when we want to group all threads in all events to the first fd opened on that cpu. So pass the xyarray with the first event, where the other events will be able to get that first per cpu fd. At some point top and record will switch to using perf_evlist__open that takes care of this detail and probably will also handle the fallback from hw to soft counters, etc. Reported-by: Deng-Cheng Zhu Tested-by: Deng-Cheng Zhu Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ebm34rh098i9y9v4cytfdp0x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 11 ++++++++-- tools/perf/builtin-stat.c | 20 +++++++++++------ tools/perf/builtin-test.c | 6 +++--- tools/perf/builtin-top.c | 11 ++++++++-- tools/perf/util/evlist.c | 30 ++++++++++++++++++++++++++ tools/perf/util/evlist.h | 2 ++ tools/perf/util/evsel.c | 43 ++++++++++++++++++++++++++----------- tools/perf/util/evsel.h | 10 ++++++--- tools/perf/util/python.c | 31 +++++++++++++++++++++++++- 9 files changed, 135 insertions(+), 29 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f82480fa7f27..40088a5848d6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -262,13 +262,16 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, static void open_counters(struct perf_evlist *evlist) { - struct perf_evsel *pos; + struct perf_evsel *pos, *first; if (evlist->cpus->map[0] < 0) no_inherit = true; + first = list_entry(evlist->entries.next, struct perf_evsel, node); + list_for_each_entry(pos, &evlist->entries, node) { struct perf_event_attr *attr = &pos->attr; + struct xyarray *group_fd = NULL; /* * Check if parse_single_tracepoint_event has already asked for * PERF_SAMPLE_TIME. @@ -283,11 +286,15 @@ static void open_counters(struct perf_evlist *evlist) */ bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; + if (group && pos != first) + group_fd = first->fd; + config_attr(pos, evlist); retry_sample_id: attr->sample_id_all = sample_id_all_avail ? 1 : 0; try_again: - if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group) < 0) { + if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group, + group_fd) < 0) { int err = errno; if (err == EPERM || err == EACCES) { diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 7ce65f52415e..7d98676808d8 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -278,9 +278,14 @@ struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; struct stats walltime_nsecs_stats; -static int create_perf_stat_counter(struct perf_evsel *evsel) +static int create_perf_stat_counter(struct perf_evsel *evsel, + struct perf_evsel *first) { struct perf_event_attr *attr = &evsel->attr; + struct xyarray *group_fd = NULL; + + if (group && evsel != first) + group_fd = first->fd; if (scale) attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -289,14 +294,15 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->inherit = !no_inherit; if (system_wide) - return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, group); - + return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, + group, group_fd); if (target_pid == -1 && target_tid == -1) { attr->disabled = 1; attr->enable_on_exec = 1; } - return perf_evsel__open_per_thread(evsel, evsel_list->threads, group); + return perf_evsel__open_per_thread(evsel, evsel_list->threads, + group, group_fd); } /* @@ -396,7 +402,7 @@ static int read_counter(struct perf_evsel *counter) static int run_perf_stat(int argc __used, const char **argv) { unsigned long long t0, t1; - struct perf_evsel *counter; + struct perf_evsel *counter, *first; int status = 0; int child_ready_pipe[2], go_pipe[2]; const bool forks = (argc > 0); @@ -453,8 +459,10 @@ static int run_perf_stat(int argc __used, const char **argv) close(child_ready_pipe[0]); } + first = list_entry(evsel_list->entries.next, struct perf_evsel, node); + list_for_each_entry(counter, &evsel_list->entries, node) { - if (create_perf_stat_counter(counter) < 0) { + if (create_perf_stat_counter(counter, first) < 0) { if (errno == EINVAL || errno == ENOSYS || errno == ENOENT) { if (verbose) ui__warning("%s event is not supported by the kernel.\n", diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index efe696f936e2..831d1baeac37 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -291,7 +291,7 @@ static int test__open_syscall_event(void) goto out_thread_map_delete; } - if (perf_evsel__open_per_thread(evsel, threads, false) < 0) { + if (perf_evsel__open_per_thread(evsel, threads, false, NULL) < 0) { pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); @@ -366,7 +366,7 @@ static int test__open_syscall_event_on_all_cpus(void) goto out_thread_map_delete; } - if (perf_evsel__open(evsel, cpus, threads, false) < 0) { + if (perf_evsel__open(evsel, cpus, threads, false, NULL) < 0) { pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); @@ -531,7 +531,7 @@ static int test__basic_mmap(void) perf_evlist__add(evlist, evsels[i]); - if (perf_evsel__open(evsels[i], cpus, threads, false) < 0) { + if (perf_evsel__open(evsels[i], cpus, threads, false, NULL) < 0) { pr_debug("failed to open counter: %s, " "tweak /proc/sys/kernel/perf_event_paranoid?\n", strerror(errno)); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7a871714d44e..d2fc7542e826 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -834,10 +834,16 @@ static void perf_session__mmap_read(struct perf_session *self) static void start_counters(struct perf_evlist *evlist) { - struct perf_evsel *counter; + struct perf_evsel *counter, *first; + + first = list_entry(evlist->entries.next, struct perf_evsel, node); list_for_each_entry(counter, &evlist->entries, node) { struct perf_event_attr *attr = &counter->attr; + struct xyarray *group_fd = NULL; + + if (group && counter != first) + group_fd = first->fd; attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; @@ -860,7 +866,8 @@ static void start_counters(struct perf_evlist *evlist) attr->inherit = inherit; try_again: if (perf_evsel__open(counter, top.evlist->cpus, - top.evlist->threads, group) < 0) { + top.evlist->threads, group, + group_fd) < 0) { int err = errno; if (err == EPERM || err == EACCES) { diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 2f6bc89027da..fbb4b4ab9cc6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -539,3 +539,33 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, { evlist->selected = evsel; } + +int perf_evlist__open(struct perf_evlist *evlist, bool group) +{ + struct perf_evsel *evsel, *first; + int err, ncpus, nthreads; + + first = list_entry(evlist->entries.next, struct perf_evsel, node); + + list_for_each_entry(evsel, &evlist->entries, node) { + struct xyarray *group_fd = NULL; + + if (group && evsel != first) + group_fd = first->fd; + + err = perf_evsel__open(evsel, evlist->cpus, evlist->threads, + group, group_fd); + if (err < 0) + goto out_err; + } + + return 0; +out_err: + ncpus = evlist->cpus ? evlist->cpus->nr : 1; + nthreads = evlist->threads ? evlist->threads->nr : 1; + + list_for_each_entry_reverse(evsel, &evlist->entries, node) + perf_evsel__close(evsel, ncpus, nthreads); + + return err; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 6be71fc57794..1779ffef7828 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -50,6 +50,8 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); +int perf_evlist__open(struct perf_evlist *evlist, bool group); + int perf_evlist__alloc_mmap(struct perf_evlist *evlist); int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); void perf_evlist__munmap(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index b46f6e4bff3c..e42626422587 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -16,6 +16,7 @@ #include "thread_map.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) int __perf_evsel__sample_size(u64 sample_type) { @@ -204,15 +205,16 @@ int __perf_evsel__read(struct perf_evsel *evsel, } static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, + struct xyarray *group_fds) { int cpu, thread; unsigned long flags = 0; - int pid = -1; + int pid = -1, err; if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) - return -1; + return -ENOMEM; if (evsel->cgrp) { flags = PERF_FLAG_PID_CGROUP; @@ -220,7 +222,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, } for (cpu = 0; cpu < cpus->nr; cpu++) { - int group_fd = -1; + int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; for (thread = 0; thread < threads->nr; thread++) { @@ -231,8 +233,10 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, pid, cpus->map[cpu], group_fd, flags); - if (FD(evsel, cpu, thread) < 0) + if (FD(evsel, cpu, thread) < 0) { + err = -errno; goto out_close; + } if (group && group_fd == -1) group_fd = FD(evsel, cpu, thread); @@ -249,7 +253,17 @@ out_close: } thread = threads->nr; } while (--cpu >= 0); - return -1; + return err; +} + +void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + if (evsel->fd == NULL) + return; + + perf_evsel__close_fd(evsel, ncpus, nthreads); + perf_evsel__free_fd(evsel); + evsel->fd = NULL; } static struct { @@ -269,7 +283,8 @@ static struct { }; int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, + struct xyarray *group_fd) { if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ @@ -279,19 +294,23 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads, group); + return __perf_evsel__open(evsel, cpus, threads, group, group_fd); } int perf_evsel__open_per_cpu(struct perf_evsel *evsel, - struct cpu_map *cpus, bool group) + struct cpu_map *cpus, bool group, + struct xyarray *group_fd) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, + group_fd); } int perf_evsel__open_per_thread(struct perf_evsel *evsel, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, + struct xyarray *group_fd) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, + group_fd); } static int perf_event__parse_id_sample(const union perf_event *event, u64 type, diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index e9a31554e265..b1d15e6f7ae3 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -82,11 +82,15 @@ void perf_evsel__free_id(struct perf_evsel *evsel); void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, - struct cpu_map *cpus, bool group); + struct cpu_map *cpus, bool group, + struct xyarray *group_fds); int perf_evsel__open_per_thread(struct perf_evsel *evsel, - struct thread_map *threads, bool group); + struct thread_map *threads, bool group, + struct xyarray *group_fds); int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group); + struct thread_map *threads, bool group, + struct xyarray *group_fds); +void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); #define perf_evsel__match(evsel, t, c) \ (evsel->attr.type == PERF_TYPE_##t && \ diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 7624324efad4..9dd47a4f2596 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -623,7 +623,11 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; evsel->attr.inherit = inherit; - if (perf_evsel__open(evsel, cpus, threads, group) < 0) { + /* + * This will group just the fds for this single evsel, to group + * multiple events, use evlist.open(). + */ + if (perf_evsel__open(evsel, cpus, threads, group, NULL) < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -814,6 +818,25 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, return Py_None; } +static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + struct perf_evlist *evlist = &pevlist->evlist; + int group = 0; + static char *kwlist[] = { "group", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) + return NULL; + + if (perf_evlist__open(evlist, group) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef pyrf_evlist__methods[] = { { .ml_name = "mmap", @@ -821,6 +844,12 @@ static PyMethodDef pyrf_evlist__methods[] = { .ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_doc = PyDoc_STR("mmap the file descriptor table.") }, + { + .ml_name = "open", + .ml_meth = (PyCFunction)pyrf_evlist__open, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("open the file descriptors.") + }, { .ml_name = "poll", .ml_meth = (PyCFunction)pyrf_evlist__poll, From ca59bcbceeb7fd412faa35871ec0bd21bdd69229 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Oct 2011 13:29:11 -0200 Subject: [PATCH 02/17] perf ui progress: Reimplement using slang Just another step in stopping the use of libnewt in perf. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vkb9jh5kkzl5ep3puoatd6an@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debug.h | 12 ++----- tools/perf/util/session.c | 8 ++--- tools/perf/util/ui/progress.c | 64 +++++++++-------------------------- tools/perf/util/ui/progress.h | 7 ++-- 4 files changed, 22 insertions(+), 69 deletions(-) diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index fd53db47e3de..510adaccba84 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -19,16 +19,8 @@ static inline int ui_helpline__show_help(const char *format __used, va_list ap _ return 0; } -static inline struct ui_progress *ui_progress__new(const char *title __used, - u64 total __used) -{ - return (struct ui_progress *)1; -} - -static inline void ui_progress__update(struct ui_progress *self __used, - u64 curr __used) {} - -static inline void ui_progress__delete(struct ui_progress *self __used) {} +static inline void ui_progress__update(u64 curr __used, u64 total __used, + const char *title __used) {} #else extern char ui_helpline__last_msg[]; int ui_helpline__show_help(const char *format, va_list ap); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 20e011c99a94..91c6442ef966 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1012,7 +1012,6 @@ int __perf_session__process_events(struct perf_session *session, { u64 head, page_offset, file_offset, file_pos, progress_next; int err, mmap_prot, mmap_flags, map_idx = 0; - struct ui_progress *progress; size_t page_size, mmap_size; char *buf, *mmaps[8]; union perf_event *event; @@ -1030,9 +1029,6 @@ int __perf_session__process_events(struct perf_session *session, file_size = data_offset + data_size; progress_next = file_size / 16; - progress = ui_progress__new("Processing events...", file_size); - if (progress == NULL) - return -1; mmap_size = session->mmap_window; if (mmap_size > file_size) @@ -1095,7 +1091,8 @@ more: if (file_pos >= progress_next) { progress_next += file_size / 16; - ui_progress__update(progress, file_pos); + ui_progress__update(file_pos, file_size, + "Processing events..."); } if (file_pos < file_size) @@ -1106,7 +1103,6 @@ more: session->ordered_samples.next_flush = ULLONG_MAX; flush_sample_queue(session, ops); out_err: - ui_progress__delete(progress); perf_session__warn_about_errors(session, ops); perf_session_free_sample_buffers(session); return err; diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c index d7fc399d36b3..68d2c548abe3 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/util/ui/progress.c @@ -1,60 +1,28 @@ -#include -#include #include "../cache.h" #include "progress.h" +#include "libslang.h" +#include "ui.h" +#include "browser.h" -struct ui_progress { - newtComponent form, scale; -}; - -struct ui_progress *ui_progress__new(const char *title, u64 total) -{ - struct ui_progress *self = malloc(sizeof(*self)); - - if (self != NULL) { - int cols; - - if (use_browser <= 0) - return self; - newtGetScreenSize(&cols, NULL); - cols -= 4; - newtCenteredWindow(cols, 1, title); - self->form = newtForm(NULL, NULL, 0); - if (self->form == NULL) - goto out_free_self; - self->scale = newtScale(0, 0, cols, total); - if (self->scale == NULL) - goto out_free_form; - newtFormAddComponent(self->form, self->scale); - newtRefresh(); - } - - return self; - -out_free_form: - newtFormDestroy(self->form); -out_free_self: - free(self); - return NULL; -} - -void ui_progress__update(struct ui_progress *self, u64 curr) +void ui_progress__update(u64 curr, u64 total, const char *title) { + int bar, y; /* * FIXME: We should have a per UI backend way of showing progress, * stdio will just show a percentage as NN%, etc. */ if (use_browser <= 0) return; - newtScaleSet(self->scale, curr); - newtRefresh(); -} -void ui_progress__delete(struct ui_progress *self) -{ - if (use_browser > 0) { - newtFormDestroy(self->form); - newtPopWindow(); - } - free(self); + pthread_mutex_lock(&ui__lock); + y = SLtt_Screen_Rows / 2 - 2; + SLsmg_set_color(0); + SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); + SLsmg_gotorc(y++, 1); + SLsmg_write_string((char *)title); + SLsmg_set_color(HE_COLORSET_SELECTED); + bar = ((SLtt_Screen_Cols - 2) * curr) / total; + SLsmg_fill_region(y, 1, 1, bar, ' '); + SLsmg_refresh(); + pthread_mutex_unlock(&ui__lock); } diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h index a3820a0beb5b..d9c205b59aa1 100644 --- a/tools/perf/util/ui/progress.h +++ b/tools/perf/util/ui/progress.h @@ -1,11 +1,8 @@ #ifndef _PERF_UI_PROGRESS_H_ #define _PERF_UI_PROGRESS_H_ 1 -struct ui_progress; +#include <../types.h> -struct ui_progress *ui_progress__new(const char *title, u64 total); -void ui_progress__delete(struct ui_progress *self); - -void ui_progress__update(struct ui_progress *self, u64 curr); +void ui_progress__update(u64 curr, u64 total, const char *title); #endif From 71172ed97cd4cd45c6ae70e594ba351798d11909 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Oct 2011 13:45:16 -0200 Subject: [PATCH 03/17] perf ui: Improve handling sigwinch a bit No need to unblock it at each ui__getch() and also allow other users to check if a resize is needed, or force an refresh of terminal dimensions. The 'force' one shouldn't be needed, but its in a slow path, so leave it like that for now, I'll revisit this another day. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-aujchu6yx3bfy64non1rky0w@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browser.c | 49 +---------------------- tools/perf/util/ui/progress.c | 1 + tools/perf/util/ui/setup.c | 75 +++++++++++++++++++++++++++++++++++ tools/perf/util/ui/ui.h | 3 ++ tools/perf/util/ui/util.h | 1 + 5 files changed, 82 insertions(+), 47 deletions(-) diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 5359f371d30a..370c4703cec8 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -4,6 +4,7 @@ #include "libslang.h" #include #include "ui.h" +#include "util.h" #include #include #include @@ -291,53 +292,10 @@ void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) browser->seek(browser, browser->top_idx, SEEK_SET); } -static int ui__getch(int delay_secs) -{ - struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; - fd_set read_set; - int err, key; - - FD_ZERO(&read_set); - FD_SET(0, &read_set); - - if (delay_secs) { - timeout.tv_sec = delay_secs; - timeout.tv_usec = 0; - } - - err = select(1, &read_set, NULL, NULL, ptimeout); - - if (err == 0) - return K_TIMER; - - if (err == -1) { - if (errno == EINTR) - return K_RESIZE; - return K_ERROR; - } - - key = SLang_getkey(); - if (key != K_ESC) - return key; - - FD_ZERO(&read_set); - FD_SET(0, &read_set); - timeout.tv_sec = 0; - timeout.tv_usec = 20; - err = select(1, &read_set, NULL, NULL, &timeout); - if (err == 0) - return K_ESC; - - SLang_ungetkey(key); - return SLkp_getkey(); -} - int ui_browser__run(struct ui_browser *self, int delay_secs) { int err, key; - pthread__unblock_sigwinch(); - while (1) { off_t offset; @@ -351,10 +309,7 @@ int ui_browser__run(struct ui_browser *self, int delay_secs) key = ui__getch(delay_secs); if (key == K_RESIZE) { - pthread_mutex_lock(&ui__lock); - SLtt_get_screen_size(); - SLsmg_reinit_smg(); - pthread_mutex_unlock(&ui__lock); + ui__refresh_dimensions(false); ui_browser__refresh_dimensions(self); __ui_browser__show_title(self, self->title); ui_helpline__puts(self->helpline); diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c index 68d2c548abe3..295e366b6311 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/util/ui/progress.c @@ -14,6 +14,7 @@ void ui_progress__update(u64 curr, u64 total, const char *title) if (use_browser <= 0) return; + ui__refresh_dimensions(true); pthread_mutex_lock(&ui__lock); y = SLtt_Screen_Rows / 2 - 2; SLsmg_set_color(0); diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 1e6ba06980c4..c4e025fbd6b9 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c @@ -7,10 +7,85 @@ #include "browser.h" #include "helpline.h" #include "ui.h" +#include "util.h" #include "libslang.h" +#include "keysyms.h" pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; +static volatile int ui__need_resize; + +void ui__refresh_dimensions(bool force) +{ + if (force || ui__need_resize) { + ui__need_resize = 0; + pthread_mutex_lock(&ui__lock); + SLtt_get_screen_size(); + SLsmg_reinit_smg(); + pthread_mutex_unlock(&ui__lock); + } +} + +static void ui__sigwinch(int sig __used) +{ + ui__need_resize = 1; +} + +static void ui__setup_sigwinch(void) +{ + static bool done; + + if (done) + return; + + done = true; + pthread__unblock_sigwinch(); + signal(SIGWINCH, ui__sigwinch); +} + +int ui__getch(int delay_secs) +{ + struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; + fd_set read_set; + int err, key; + + ui__setup_sigwinch(); + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + + if (delay_secs) { + timeout.tv_sec = delay_secs; + timeout.tv_usec = 0; + } + + err = select(1, &read_set, NULL, NULL, ptimeout); + + if (err == 0) + return K_TIMER; + + if (err == -1) { + if (errno == EINTR) + return K_RESIZE; + return K_ERROR; + } + + key = SLang_getkey(); + if (key != K_ESC) + return key; + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + timeout.tv_sec = 0; + timeout.tv_usec = 20; + err = select(1, &read_set, NULL, NULL, &timeout); + if (err == 0) + return K_ESC; + + SLang_ungetkey(key); + return SLkp_getkey(); +} + static void newt_suspend(void *d __used) { newtSuspend(); diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h index d264e059c829..7b67045479f6 100644 --- a/tools/perf/util/ui/ui.h +++ b/tools/perf/util/ui/ui.h @@ -2,7 +2,10 @@ #define _PERF_UI_H_ 1 #include +#include extern pthread_mutex_t ui__lock; +void ui__refresh_dimensions(bool force); + #endif /* _PERF_UI_H_ */ diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h index afcbc1d99531..e53c3d334903 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h @@ -3,6 +3,7 @@ #include +int ui__getch(int delay_secs); int ui__popup_menu(int argc, char * const argv[]); int ui__help_window(const char *text); bool ui__dialog_yesno(const char *msg); From 2ba908ecfc4697dd856a526a9d4d4bd28e64a9cd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Oct 2011 13:52:05 -0200 Subject: [PATCH 04/17] perf ui: Reimplement ui_helpline using libslang Just another step in stopping the use of libnewt in perf. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-gh7e1v2z7pzqmok02r6zvp17@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/helpline.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index f36d2ff509ed..600243d766c0 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -1,20 +1,22 @@ #define _GNU_SOURCE #include #include -#include #include "../debug.h" #include "helpline.h" #include "ui.h" +#include "libslang.h" void ui_helpline__pop(void) { - newtPopHelpLine(); } void ui_helpline__push(const char *msg) { - newtPushHelpLine(msg); + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + SLsmg_set_color(0); + SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); + SLsmg_refresh(); } void ui_helpline__vpush(const char *fmt, va_list ap) @@ -63,7 +65,7 @@ int ui_helpline__show_help(const char *format, va_list ap) if (ui_helpline__last_msg[backlog - 1] == '\n') { ui_helpline__puts(ui_helpline__last_msg); - newtRefresh(); + SLsmg_refresh(); backlog = 0; } pthread_mutex_unlock(&ui__lock); From 1056d3dd9416740ec7d31348ca5f55009dc06bf3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 07:11:03 -0200 Subject: [PATCH 05/17] perf ui: Reimplement ui__popup_menu using ui__browser Right now let it work just like the other browsers: in full screen, at the top left corner. If people complain we can revisit, I found it OK and the laziest/quickest approach at reusing the ui_browser ;-) Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-4bgeqizcxh04q0sk24cw43gk@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browser.c | 41 ++++++++++++++++++ tools/perf/util/ui/browser.h | 3 ++ tools/perf/util/ui/util.c | 81 +++++++++++++++++++++++------------- 3 files changed, 96 insertions(+), 29 deletions(-) diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 370c4703cec8..29c68aee00df 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -488,6 +488,47 @@ static int ui_browser__color_config(const char *var, const char *value, return -1; } +void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) +{ + switch (whence) { + case SEEK_SET: + browser->top = browser->entries; + break; + case SEEK_CUR: + browser->top = browser->top + browser->top_idx + offset; + break; + case SEEK_END: + browser->top = browser->top + browser->nr_entries + offset; + break; + default: + return; + } +} + +unsigned int ui_browser__argv_refresh(struct ui_browser *browser) +{ + unsigned int row = 0, idx = browser->top_idx; + char **pos; + + if (browser->top == NULL) + browser->top = browser->entries; + + pos = (char **)browser->top; + while (idx < browser->nr_entries) { + if (!browser->filter || !browser->filter(browser, *pos)) { + ui_browser__gotorc(browser, row, 0); + browser->write(browser, pos, row); + if (++row == browser->height) + break; + } + + ++idx; + ++pos; + } + + return row; +} + void ui_browser__init(void) { int i = 0; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index a2c707d33c5e..81a8d2afaa9b 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -44,6 +44,9 @@ int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); +void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); +unsigned int ui_browser__argv_refresh(struct ui_browser *browser); + void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index fdf1fc8f08bc..37e6fe081a58 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c @@ -8,10 +8,54 @@ #include "../cache.h" #include "../debug.h" #include "browser.h" +#include "keysyms.h" #include "helpline.h" #include "ui.h" #include "util.h" +static void ui_browser__argv_write(struct ui_browser *browser, + void *entry, int row) +{ + char **arg = entry; + bool current_entry = ui_browser__is_current_entry(browser, row); + + ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : + HE_COLORSET_NORMAL); + slsmg_write_nstring(*arg, browser->width); +} + +static int popup_menu__run(struct ui_browser *menu) +{ + int key; + + if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) + return -1; + + while (1) { + key = ui_browser__run(menu, 0); + + switch (key) { + case K_RIGHT: + case K_ENTER: + key = menu->index; + break; + case K_LEFT: + case K_ESC: + case 'q': + case CTRL('c'): + key = -1; + break; + default: + continue; + } + + break; + } + + ui_browser__hide(menu); + return key; +} + static void newt_form__set_exit_keys(newtComponent self) { newtFormAddHotKey(self, NEWT_KEY_LEFT); @@ -31,36 +75,15 @@ static newtComponent newt_form__new(void) int ui__popup_menu(int argc, char * const argv[]) { - struct newtExitStruct es; - int i, rc = -1, max_len = 5; - newtComponent listbox, form = newt_form__new(); + struct ui_browser menu = { + .entries = (void *)argv, + .refresh = ui_browser__argv_refresh, + .seek = ui_browser__argv_seek, + .write = ui_browser__argv_write, + .nr_entries = argc, + }; - if (form == NULL) - return -1; - - listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); - if (listbox == NULL) - goto out_destroy_form; - - newtFormAddComponent(form, listbox); - - for (i = 0; i < argc; ++i) { - int len = strlen(argv[i]); - if (len > max_len) - max_len = len; - if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) - goto out_destroy_form; - } - - newtCenteredWindow(max_len, argc, NULL); - newtFormRun(form, &es); - rc = newtListboxGetCurrent(listbox) - NULL; - if (es.reason == NEWT_EXIT_HOTKEY) - rc = -1; - newtPopWindow(); -out_destroy_form: - newtFormDestroy(form); - return rc; + return popup_menu__run(&menu); } int ui__help_window(const char *text) From ae55795ef2d9ba71d46e4111b87a4d0cde93abea Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 08:00:55 -0200 Subject: [PATCH 06/17] perf ui: Reimplement the popup windows using libslang Just another step in stopping the use of libnewt in perf. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vtxnmz1t1807ykprapnk9njl@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 9 ++- tools/perf/util/debug.h | 1 + tools/perf/util/ui/browsers/annotate.c | 14 +--- tools/perf/util/ui/setup.c | 8 +- tools/perf/util/ui/util.c | 108 ++++++++++++++----------- tools/perf/util/ui/util.h | 2 + 6 files changed, 76 insertions(+), 66 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index bc8f4773d4d8..119e996035c8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -310,9 +310,12 @@ fallback: } err = -ENOENT; dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file%s was found in the " - "path.\nPlease use 'perf buildid-cache -av vmlinux' or " - "--vmlinux vmlinux.\n", + pr_err("Can't annotate %s:\n\n" + "No vmlinux file%s\nwas found in the path.\n\n" + "Please use:\n\n" + " perf buildid-cache -av vmlinux\n\n" + "or:\n\n" + " --vmlinux vmlinux", sym->name, build_id_msg ?: ""); goto out_free_filename; } diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 510adaccba84..9c59b9551ada 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -29,5 +29,6 @@ int ui_helpline__show_help(const char *format, va_list ap); void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); void ui__warning_paranoid(void); +void ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 4e0cb7fea7d9..0575905d1205 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -1,6 +1,9 @@ +#include "../../util.h" #include "../browser.h" #include "../helpline.h" #include "../libslang.h" +#include "../ui.h" +#include "../util.h" #include "../../annotate.h" #include "../../hist.h" #include "../../sort.h" @@ -8,15 +11,6 @@ #include #include -static void ui__error_window(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); - va_end(ap); -} - struct annotate_browser { struct ui_browser b; struct rb_root entries; @@ -400,7 +394,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, return -1; if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { - ui__error_window(ui_helpline__last_msg); + ui__error("%s", ui_helpline__last_msg); return -1; } diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index c4e025fbd6b9..85a69faa09aa 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c @@ -146,10 +146,10 @@ void setup_browser(bool fallback_to_pager) void exit_browser(bool wait_for_ok) { if (use_browser > 0) { - if (wait_for_ok) { - char title[] = "Fatal Error", ok[] = "Ok"; - newtWinMessage(title, ok, ui_helpline__last_msg); - } + if (wait_for_ok) + ui__question_window("Fatal Error", + ui_helpline__last_msg, + "Press any key...", 0); ui__exit(); } } diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 37e6fe081a58..ef9b79332fe8 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c @@ -1,6 +1,5 @@ -#include +#include "../util.h" #include -#include #include #include #include @@ -12,6 +11,7 @@ #include "helpline.h" #include "ui.h" #include "util.h" +#include "libslang.h" static void ui_browser__argv_write(struct ui_browser *browser, void *entry, int row) @@ -56,23 +56,6 @@ static int popup_menu__run(struct ui_browser *menu) return key; } -static void newt_form__set_exit_keys(newtComponent self) -{ - newtFormAddHotKey(self, NEWT_KEY_LEFT); - newtFormAddHotKey(self, NEWT_KEY_ESCAPE); - newtFormAddHotKey(self, 'Q'); - newtFormAddHotKey(self, 'q'); - newtFormAddHotKey(self, CTRL('c')); -} - -static newtComponent newt_form__new(void) -{ - newtComponent self = newtForm(NULL, NULL, 0); - if (self) - newt_form__set_exit_keys(self); - return self; -} - int ui__popup_menu(int argc, char * const argv[]) { struct ui_browser menu = { @@ -86,17 +69,13 @@ int ui__popup_menu(int argc, char * const argv[]) return popup_menu__run(&menu); } -int ui__help_window(const char *text) +int ui__question_window(const char *title, const char *text, + const char *exit_msg, int delay_secs) { - struct newtExitStruct es; - newtComponent tb, form = newt_form__new(); - int rc = -1; + int x, y; int max_len = 0, nr_lines = 0; const char *t; - if (form == NULL) - return -1; - t = text; while (1) { const char *sep = strchr(t, '\n'); @@ -113,28 +92,56 @@ int ui__help_window(const char *text) t = sep + 1; } - tb = newtTextbox(0, 0, max_len, nr_lines, 0); - if (tb == NULL) - goto out_destroy_form; + max_len += 2; + nr_lines += 4; + y = SLtt_Screen_Rows / 2 - nr_lines / 2, + x = SLtt_Screen_Cols / 2 - max_len / 2; - newtTextboxSetText(tb, text); - newtFormAddComponent(form, tb); - newtCenteredWindow(max_len, nr_lines, NULL); - newtFormRun(form, &es); - newtPopWindow(); - rc = 0; -out_destroy_form: - newtFormDestroy(form); - return rc; + SLsmg_set_color(0); + SLsmg_draw_box(y, x++, nr_lines, max_len); + if (title) { + SLsmg_gotorc(y, x + 1); + SLsmg_write_string((char *)title); + } + SLsmg_gotorc(++y, x); + nr_lines -= 2; + max_len -= 2; + SLsmg_write_wrapped_string((unsigned char *)text, y, x, + nr_lines, max_len, 1); + SLsmg_gotorc(y + nr_lines - 2, x); + SLsmg_write_nstring((char *)" ", max_len); + SLsmg_gotorc(y + nr_lines - 1, x); + SLsmg_write_nstring((char *)exit_msg, max_len); + SLsmg_refresh(); + return ui__getch(delay_secs); } -static const char yes[] = "Yes", no[] = "No", - warning_str[] = "Warning!", ok[] = "Ok"; +int ui__help_window(const char *text) +{ + return ui__question_window("Help", text, "Press any key...", 0); +} bool ui__dialog_yesno(const char *msg) { - /* newtWinChoice should really be accepting const char pointers... */ - return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; + int answer = ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); + + return answer == K_ENTER; +} + +static void __ui__warning(const char *title, const char *format, va_list args) +{ + char *s; + + if (use_browser > 0 && vasprintf(&s, format, args) > 0) { + pthread_mutex_lock(&ui__lock); + ui__question_window(title, s, "Press any key...", 0); + pthread_mutex_unlock(&ui__lock); + free(s); + return; + } + + fprintf(stderr, "%s:\n", title); + vfprintf(stderr, format, args); } void ui__warning(const char *format, ...) @@ -142,12 +149,15 @@ void ui__warning(const char *format, ...) va_list args; va_start(args, format); - if (use_browser > 0) { - pthread_mutex_lock(&ui__lock); - newtWinMessagev((char *)warning_str, (char *)ok, - (char *)format, args); - pthread_mutex_unlock(&ui__lock); - } else - vfprintf(stderr, format, args); + __ui__warning("Warning", format, args); + va_end(args); +} + +void ui__error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + __ui__warning("Error", format, args); va_end(args); } diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h index e53c3d334903..9a25538d4735 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h @@ -7,5 +7,7 @@ int ui__getch(int delay_secs); int ui__popup_menu(int argc, char * const argv[]); int ui__help_window(const char *text); bool ui__dialog_yesno(const char *msg); +int ui__question_window(const char *title, const char *text, + const char *exit_msg, int delay_secs); #endif /* _PERF_UI_UTIL_H_ */ From b8631e6ebb3aa033e21d68dd75029aceb96b79cd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 08:02:55 -0200 Subject: [PATCH 07/17] perf ui: Rename ui__warning_paranoid to ui__error_paranoid As it will exit the tool after the user is notified. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vy06m8xzlvkhr8tk7nylhbng@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/util/debug.c | 4 ++-- tools/perf/util/debug.h | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 40088a5848d6..6ab58cc99d53 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -298,7 +298,7 @@ try_again: int err = errno; if (err == EPERM || err == EACCES) { - ui__warning_paranoid(); + ui__error_paranoid(); exit(EXIT_FAILURE); } else if (err == ENODEV && cpu_list) { die("No such device - did you specify" diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d2fc7542e826..31aa82c39e2a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -871,7 +871,7 @@ try_again: int err = errno; if (err == EPERM || err == EACCES) { - ui__warning_paranoid(); + ui__error_paranoid(); goto out_err; } /* diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 155749d74350..55634038af6b 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -57,9 +57,9 @@ void ui__warning(const char *format, ...) } #endif -void ui__warning_paranoid(void) +void ui__error_paranoid(void) { - ui__warning("Permission error - are you root?\n" + ui__error("Permission error - are you root?\n" "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" " -1 - Not paranoid at all\n" " 0 - Disallow raw tracepoint access for unpriv\n" diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 9c59b9551ada..16cc75227d2b 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -21,14 +21,16 @@ static inline int ui_helpline__show_help(const char *format __used, va_list ap _ static inline void ui_progress__update(u64 curr __used, u64 total __used, const char *title __used) {} + +#define ui__error(format, arg...) ui__warning(format, ##arg) #else extern char ui_helpline__last_msg[]; int ui_helpline__show_help(const char *format, va_list ap); #include "ui/progress.h" +void ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); #endif void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); -void ui__warning_paranoid(void); -void ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); +void ui__error_paranoid(void); #endif /* __PERF_DEBUG_H */ From 13d8f96c6c98532567b7620210a41d57dbc2039f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 08:05:34 -0200 Subject: [PATCH 08/17] perf hists browser: Use K_TIMER In the switch case entry for the timer routine. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ypw3i9kmxoq28skx7jy914it@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browsers/hists.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 4663dcb2a19b..44210d471719 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -314,8 +314,7 @@ static int hist_browser__run(struct hist_browser *self, const char *ev_name, key = ui_browser__run(&self->b, delay_secs); switch (key) { - case -1: - /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ + case K_TIMER: timer(arg); ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); hists__browser_title(self->hists, title, sizeof(title), @@ -914,7 +913,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "C Collapse all callchains\n" "E Expand all callchains\n" "d Zoom into current DSO\n" - "t Zoom into current Thread\n"); + "t Zoom into current Thread"); continue; case K_ENTER: case K_RIGHT: From 0458122db0a2ebd1d3779469cb6184d8b195be09 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 08:19:05 -0200 Subject: [PATCH 09/17] perf ui browser: No need to switch char sets that often Just before and after the loop. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-0lh91cedngyg1pqarbky5vn7@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/ui/browser.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 29c68aee00df..8607efa13564 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -231,13 +231,15 @@ static void ui_browser__scrollbar_set(struct ui_browser *browser) (browser->nr_entries - 1)); } + SLsmg_set_char_set(1); + while (h < height) { ui_browser__gotorc(browser, row++, col); - SLsmg_set_char_set(1); - SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR); - SLsmg_set_char_set(0); + SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR); ++h; } + + SLsmg_set_char_set(0); } static int __ui_browser__refresh(struct ui_browser *browser) From 4610e4137b5fb93042a248928a2c0049ef7d4190 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 12:04:37 -0200 Subject: [PATCH 10/17] perf ui browser: Handle K_RESIZE in dialog windows Just provide wrappers for things like ui__warning, ui__dialog_yesno and if they return K_RESIZE, refresh dimensions, redraw the entries, etc. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-3ih7hyk9weryxaxb501sfq4u@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debug.c | 7 ++--- tools/perf/util/debug.h | 6 ++--- tools/perf/util/ui/browser.c | 40 +++++++++++++++++++++++++++++ tools/perf/util/ui/browser.h | 5 ++++ tools/perf/util/ui/browsers/hists.c | 22 +++++++++++----- tools/perf/util/ui/helpline.c | 6 +++++ tools/perf/util/ui/helpline.h | 2 ++ tools/perf/util/ui/util.c | 27 +++++++++++-------- tools/perf/util/ui/util.h | 5 ++-- 9 files changed, 94 insertions(+), 26 deletions(-) diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 55634038af6b..26817daa2961 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -47,19 +47,20 @@ int dump_printf(const char *fmt, ...) } #ifdef NO_NEWT_SUPPORT -void ui__warning(const char *format, ...) +int ui__warning(const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); + return 0; } #endif -void ui__error_paranoid(void) +int ui__error_paranoid(void) { - ui__error("Permission error - are you root?\n" + return ui__error("Permission error - are you root?\n" "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" " -1 - Not paranoid at all\n" " 0 - Disallow raw tracepoint access for unpriv\n" diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 16cc75227d2b..f2ce88d04f54 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -27,10 +27,10 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used, extern char ui_helpline__last_msg[]; int ui_helpline__show_help(const char *format, va_list ap); #include "ui/progress.h" -void ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); +int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); #endif -void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); -void ui__error_paranoid(void); +int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +int ui__error_paranoid(void); #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 8607efa13564..d2051be04f12 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -169,6 +169,46 @@ void ui_browser__refresh_dimensions(struct ui_browser *self) self->x = 0; } +void ui_browser__handle_resize(struct ui_browser *browser) +{ + ui__refresh_dimensions(false); + ui_browser__show(browser, browser->title, ui_helpline__current); + ui_browser__refresh(browser); +} + +int ui_browser__warning(struct ui_browser *browser, const char *format, ...) +{ + va_list args; + int key; + + va_start(args, format); + while ((key = __ui__warning("Warning!", format, args)) == K_RESIZE) + ui_browser__handle_resize(browser); + va_end(args); + + return key; +} + +int ui_browser__help_window(struct ui_browser *browser, const char *text) +{ + int key; + + while ((key = ui__help_window(text)) == K_RESIZE) + ui_browser__handle_resize(browser); + + return key; +} + +bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text) +{ + int key; + + while ((key = ui__dialog_yesno(text)) == K_RESIZE) + ui_browser__handle_resize(browser); + + return key == K_ENTER || toupper(key) == 'Y'; +} + void ui_browser__reset_index(struct ui_browser *self) { self->index = self->top_idx = 0; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 81a8d2afaa9b..fb1c59883e6a 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -43,6 +43,11 @@ void ui_browser__hide(struct ui_browser *self); int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); +void ui_browser__handle_resize(struct ui_browser *browser); + +int ui_browser__warning(struct ui_browser *browser, const char *format, ...); +int ui_browser__help_window(struct ui_browser *browser, const char *text); +bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); unsigned int ui_browser__argv_refresh(struct ui_browser *browser); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 44210d471719..b8733c0770cd 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -17,6 +17,7 @@ #include "../browser.h" #include "../helpline.h" #include "../util.h" +#include "../ui.h" #include "map.h" struct hist_browser { @@ -882,7 +883,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, goto out_free_stack; case 'a': if (!browser->has_symbols) { - ui__warning( + ui_browser__warning(&browser->b, "Annotation is only available for symbolic views, " "include \"sym\" in --sort to use it."); continue; @@ -900,7 +901,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, case K_F1: case 'h': case '?': - ui__help_window("h/?/F1 Show this window\n" + ui_browser__help_window(&browser->b, + "h/?/F1 Show this window\n" "UP/DOWN/PGUP\n" "PGDN/SPACE Navigate\n" "q/ESC/CTRL+C Exit browser\n\n" @@ -939,7 +941,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, } case K_ESC: if (!left_exits && - !ui__dialog_yesno("Do you really want to exit?")) + !ui_browser__dialog_yesno(&browser->b, + "Do you really want to exit?")) continue; /* Fall thru */ case 'q': @@ -992,6 +995,7 @@ add_exit_option: if (choice == annotate) { struct hist_entry *he; + int err; do_annotate: he = hist_browser__selected_entry(browser); if (he == NULL) @@ -1000,10 +1004,12 @@ do_annotate: * Don't let this be freed, say, by hists__decay_entry. */ he->used = true; - hist_entry__tui_annotate(he, evsel->idx, nr_events, - timer, arg, delay_secs); + err = hist_entry__tui_annotate(he, evsel->idx, nr_events, + timer, arg, delay_secs); he->used = false; ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); + if (err) + ui_browser__handle_resize(&browser->b); } else if (choice == browse_map) map__browse(browser->selection->map); else if (choice == zoom_dso) { @@ -1132,7 +1138,8 @@ browse_hists: pos = list_entry(pos->node.prev, struct perf_evsel, node); goto browse_hists; case K_ESC: - if (!ui__dialog_yesno("Do you really want to exit?")) + if (!ui_browser__dialog_yesno(&menu->b, + "Do you really want to exit?")) continue; /* Fall thru */ case 'q': @@ -1144,7 +1151,8 @@ browse_hists: case K_LEFT: continue; case K_ESC: - if (!ui__dialog_yesno("Do you really want to exit?")) + if (!ui_browser__dialog_yesno(&menu->b, + "Do you really want to exit?")) continue; /* Fall thru */ case 'q': diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 600243d766c0..6ef3c5691762 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -1,6 +1,7 @@ #define _GNU_SOURCE #include #include +#include #include "../debug.h" #include "helpline.h" @@ -11,12 +12,17 @@ void ui_helpline__pop(void) { } +char ui_helpline__current[512]; + void ui_helpline__push(const char *msg) { + const size_t sz = sizeof(ui_helpline__current); + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); SLsmg_set_color(0); SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); SLsmg_refresh(); + strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; } void ui_helpline__vpush(const char *fmt, va_list ap) diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index fdcbc0270acd..7bab6b34e35e 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -11,4 +11,6 @@ void ui_helpline__vpush(const char *fmt, va_list ap); void ui_helpline__fpush(const char *fmt, ...); void ui_helpline__puts(const char *msg); +extern char ui_helpline__current[]; + #endif /* _PERF_UI_HELPLINE_H_ */ diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index ef9b79332fe8..45daa7c41dad 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c @@ -121,43 +121,48 @@ int ui__help_window(const char *text) return ui__question_window("Help", text, "Press any key...", 0); } -bool ui__dialog_yesno(const char *msg) +int ui__dialog_yesno(const char *msg) { - int answer = ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); - - return answer == K_ENTER; + return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); } -static void __ui__warning(const char *title, const char *format, va_list args) +int __ui__warning(const char *title, const char *format, va_list args) { char *s; if (use_browser > 0 && vasprintf(&s, format, args) > 0) { + int key; + pthread_mutex_lock(&ui__lock); - ui__question_window(title, s, "Press any key...", 0); + key = ui__question_window(title, s, "Press any key...", 0); pthread_mutex_unlock(&ui__lock); free(s); - return; + return key; } fprintf(stderr, "%s:\n", title); vfprintf(stderr, format, args); + return K_ESC; } -void ui__warning(const char *format, ...) +int ui__warning(const char *format, ...) { + int key; va_list args; va_start(args, format); - __ui__warning("Warning", format, args); + key = __ui__warning("Warning", format, args); va_end(args); + return key; } -void ui__error(const char *format, ...) +int ui__error(const char *format, ...) { + int key; va_list args; va_start(args, format); - __ui__warning("Error", format, args); + key = __ui__warning("Error", format, args); va_end(args); + return key; } diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h index 9a25538d4735..2d1738bd71c8 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h @@ -1,13 +1,14 @@ #ifndef _PERF_UI_UTIL_H_ #define _PERF_UI_UTIL_H_ 1 -#include +#include int ui__getch(int delay_secs); int ui__popup_menu(int argc, char * const argv[]); int ui__help_window(const char *text); -bool ui__dialog_yesno(const char *msg); +int ui__dialog_yesno(const char *msg); int ui__question_window(const char *title, const char *text, const char *exit_msg, int delay_secs); +int __ui__warning(const char *title, const char *format, va_list args); #endif /* _PERF_UI_UTIL_H_ */ From a9072bc0b0af991e274b699f17bc50cf201e377b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 26 Oct 2011 12:41:38 -0200 Subject: [PATCH 11/17] perf header: Fix build on old systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For instance, on Fedora 8: CC /home/acme/git/build/perf/util/header.o cc1: warnings being treated as errors util/header.c: In function ‘write_cpudesc’: util/header.c:281: warning: implicit declaration of function ‘getline’ util/header.c:281: warning: nested extern declaration of ‘getline’ make: *** [/home/acme/git/build/perf/util/header.o] Error 1 make: Leaving directory `/home/acme/git/linux/tools/perf' [acme@localhost linux]$ This happens due to header ordering, in perf util.h sets _GNU_SOURCE, so it must come first. Reported-by: Ingo Molnar Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-alfra9wao63euguj7gr8jw7e@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 76c0b2c49eb8..bcd05d05b4f0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1,5 +1,6 @@ #define _FILE_OFFSET_BITS 64 +#include "util.h" #include #include #include @@ -11,7 +12,6 @@ #include "evlist.h" #include "evsel.h" -#include "util.h" #include "header.h" #include "../perf.h" #include "trace-event.h" From 7928631a66c884b18f827fbd1b63cd80198f004b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 27 Oct 2011 09:19:48 -0200 Subject: [PATCH 12/17] perf hists: Fix recalculation of total_period when sorting entries We were doing parts of it in hists__collapse_resort and parts of it in hists__output_resort, leading to a bogus total_period. Fix it by doing just the filtering operation when collapsing because there we know that the Zoom operations adds filters just what is in hists->entries, not to the new batch of entries being collapsed. And move all the nr_entries + total_period recalculation to hists__output_resort since we will traverse all entries anyway there. Problem introduced when developing threaded addition of new batches of hist_entries, i.e. post v3.1. Reported-by: Frederic Weisbecker Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-8xyh165h7hmwy0696hu25en6@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f6a993963a1e..a36a3fa81ffb 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -365,7 +365,6 @@ static void __hists__collapse_resort(struct hists *hists, bool threaded) root = hists__get_rotate_entries_in(hists); next = rb_first(root); - hists->stats.total_period = 0; while (next) { n = rb_entry(next, struct hist_entry, rb_node_in); @@ -379,7 +378,6 @@ static void __hists__collapse_resort(struct hists *hists, bool threaded) * been set by, say, the hist_browser. */ hists__apply_filters(hists, n); - hists__inc_nr_entries(hists, n); } } } @@ -442,6 +440,7 @@ static void __hists__output_resort(struct hists *hists, bool threaded) hists->entries = RB_ROOT; hists->nr_entries = 0; + hists->stats.total_period = 0; hists__reset_col_len(hists); while (next) { From 1ca4ff41a3d887b8211e4a6c6c89c8f153d6bfa0 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 4 Oct 2011 19:44:56 +0900 Subject: [PATCH 13/17] perf tools: Fix a typo of command name as trace-cmd Fix a typo which may be introduced when original code has been copied from trace-cmd. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20111004104456.14591.37395.stgit@fedora15 Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 2d530cf74f43..d2655f08bcc0 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -80,7 +80,7 @@ static void die(const char *fmt, ...) int ret = errno; if (errno) - perror("trace-cmd"); + perror("perf"); else ret = -1; From 7b27509fc62686c53e9301560034e6b0b001174d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 29 Oct 2011 12:15:04 -0200 Subject: [PATCH 14/17] perf hists browser: Warn about lost events Just like the old perf top --tui and the --stdio version. But because we have the initial menu to choose which event to show in a session with multiple events we can see how many chunks were lost in each of the event types, clarifying which events are being affected the most. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-47yyqbubmjzch2chezmb21m6@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 37 +++++++++++++++------- tools/perf/util/hist.h | 1 + tools/perf/util/session.c | 29 +++++++++++++---- tools/perf/util/top.h | 1 - tools/perf/util/ui/browser.c | 21 ++++++++++--- tools/perf/util/ui/browser.h | 3 +- tools/perf/util/ui/browsers/hists.c | 49 +++++++++++++++++++++++++---- 7 files changed, 112 insertions(+), 29 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 31aa82c39e2a..8577bfeb087a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -89,6 +89,7 @@ static bool vmlinux_warned; static bool inherit = false; static int realtime_prio = 0; static bool group = false; +static bool sample_id_all_avail = true; static unsigned int mmap_pages = 128; static bool dump_symtab = false; @@ -289,11 +290,13 @@ static void print_sym_table(void) printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - if (top.total_lost_warned != top.session->hists.stats.total_lost) { - top.total_lost_warned = top.session->hists.stats.total_lost; - color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); - printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", - top.total_lost_warned); + if (top.sym_evsel->hists.stats.nr_lost_warned != + top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { + top.sym_evsel->hists.stats.nr_lost_warned = + top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; + color_fprintf(stdout, PERF_COLOR_RED, + "WARNING: LOST %d chunks, Check IO/CPU overload", + top.sym_evsel->hists.stats.nr_lost_warned); ++printed; } @@ -671,6 +674,7 @@ static int symbol_filter(struct map *map __used, struct symbol *sym) } static void perf_event__process_sample(const union perf_event *event, + struct perf_evsel *evsel, struct perf_sample *sample, struct perf_session *session) { @@ -770,12 +774,8 @@ static void perf_event__process_sample(const union perf_event *event, } if (al.sym == NULL || !al.sym->ignore) { - struct perf_evsel *evsel; struct hist_entry *he; - evsel = perf_evlist__id2evsel(top.evlist, sample->id); - assert(evsel != NULL); - if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { err = perf_session__resolve_callchain(session, al.thread, @@ -807,6 +807,7 @@ static void perf_event__process_sample(const union perf_event *event, static void perf_session__mmap_read_idx(struct perf_session *self, int idx) { struct perf_sample sample; + struct perf_evsel *evsel; union perf_event *event; int ret; @@ -817,10 +818,16 @@ static void perf_session__mmap_read_idx(struct perf_session *self, int idx) continue; } + evsel = perf_evlist__id2evsel(self->evlist, sample.id); + assert(evsel != NULL); + if (event->header.type == PERF_RECORD_SAMPLE) - perf_event__process_sample(event, &sample, self); - else + perf_event__process_sample(event, evsel, &sample, self); + else if (event->header.type < PERF_RECORD_MAX) { + hists__inc_nr_events(&evsel->hists, event->header.type); perf_event__process(event, &sample, self); + } else + ++self->hists.stats.nr_unknown_events; } } @@ -864,6 +871,8 @@ static void start_counters(struct perf_evlist *evlist) attr->mmap = 1; attr->comm = 1; attr->inherit = inherit; +retry_sample_id: + attr->sample_id_all = sample_id_all_avail ? 1 : 0; try_again: if (perf_evsel__open(counter, top.evlist->cpus, top.evlist->threads, group, @@ -873,6 +882,12 @@ try_again: if (err == EPERM || err == EACCES) { ui__error_paranoid(); goto out_err; + } else if (err == EINVAL && sample_id_all_avail) { + /* + * Old kernel, no attr->sample_id_type_all field + */ + sample_id_all_avail = false; + goto retry_sample_id; } /* * If it's cycles then fall back to hrtimer diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ff93ddc91c5c..c86c1d27bd1e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -28,6 +28,7 @@ struct events_stats { u64 total_lost; u64 total_invalid_chains; u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_lost_warned; u32 nr_unknown_events; u32 nr_invalid_chains; u32 nr_unknown_id; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 91c6442ef966..da354fe5e085 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -738,10 +738,27 @@ static int perf_session_deliver_event(struct perf_session *session, dump_event(session, event, file_offset, sample); + evsel = perf_evlist__id2evsel(session->evlist, sample->id); + if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { + /* + * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here + * because the tools right now may apply filters, discarding + * some of the samples. For consistency, in the future we + * should have something like nr_filtered_samples and remove + * the sample->period from total_sample_period, etc, KISS for + * now tho. + * + * Also testing against NULL allows us to handle files without + * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the + * future probably it'll be a good idea to restrict event + * processing via perf_session to files with both set. + */ + hists__inc_nr_events(&evsel->hists, event->header.type); + } + switch (event->header.type) { case PERF_RECORD_SAMPLE: dump_sample(session, event, sample); - evsel = perf_evlist__id2evsel(session->evlist, sample->id); if (evsel == NULL) { ++session->hists.stats.nr_unknown_id; return -1; @@ -874,11 +891,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session, const struct perf_event_ops *ops) { if (ops->lost == perf_event__process_lost && - session->hists.stats.total_lost != 0) { - ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 - "!\n\nCheck IO/CPU overload!\n\n", - session->hists.stats.total_period, - session->hists.stats.total_lost); + session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { + ui__warning("Processed %d events and lost %d chunks!\n\n" + "Check IO/CPU overload!\n\n", + session->hists.stats.nr_events[0], + session->hists.stats.nr_events[PERF_RECORD_LOST]); } if (session->hists.stats.nr_unknown_events != 0) { diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 01d1057f3074..399650967958 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -19,7 +19,6 @@ struct perf_top { u64 kernel_samples, us_samples; u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; - u64 total_lost_warned; int print_entries, count_filter, delay_secs; int freq; pid_t target_pid, target_tid; diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index d2051be04f12..556829124b02 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -176,16 +176,29 @@ void ui_browser__handle_resize(struct ui_browser *browser) ui_browser__refresh(browser); } -int ui_browser__warning(struct ui_browser *browser, const char *format, ...) +int ui_browser__warning(struct ui_browser *browser, int timeout, + const char *format, ...) { va_list args; - int key; + char *text; + int key = 0, err; va_start(args, format); - while ((key = __ui__warning("Warning!", format, args)) == K_RESIZE) - ui_browser__handle_resize(browser); + err = vasprintf(&text, format, args); va_end(args); + if (err < 0) { + va_start(args, format); + ui_helpline__vpush(format, args); + va_end(args); + } else { + while ((key == ui__question_window("Warning!", text, + "Press any key...", + timeout)) == K_RESIZE) + ui_browser__handle_resize(browser); + free(text); + } + return key; } diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index fb1c59883e6a..84d761b730c1 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -45,7 +45,8 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); void ui_browser__handle_resize(struct ui_browser *browser); -int ui_browser__warning(struct ui_browser *browser, const char *format, ...); +int ui_browser__warning(struct ui_browser *browser, int timeout, + const char *format, ...); int ui_browser__help_window(struct ui_browser *browser, const char *text); bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index b8733c0770cd..d0c94b459685 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -295,6 +295,15 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) ui_browser__reset_index(&self->b); } +static void ui_browser__warn_lost_events(struct ui_browser *browser) +{ + ui_browser__warning(browser, 4, + "Events are being lost, check IO/CPU overload!\n\n" + "You may want to run 'perf' using a RT scheduler policy:\n\n" + " perf top -r 80\n\n" + "Or reduce the sampling frequency."); +} + static int hist_browser__run(struct hist_browser *self, const char *ev_name, void(*timer)(void *arg), void *arg, int delay_secs) { @@ -318,8 +327,15 @@ static int hist_browser__run(struct hist_browser *self, const char *ev_name, case K_TIMER: timer(arg); ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); - hists__browser_title(self->hists, title, sizeof(title), - ev_name); + + if (self->hists->stats.nr_lost_warned != + self->hists->stats.nr_events[PERF_RECORD_LOST]) { + self->hists->stats.nr_lost_warned = + self->hists->stats.nr_events[PERF_RECORD_LOST]; + ui_browser__warn_lost_events(&self->b); + } + + hists__browser_title(self->hists, title, sizeof(title), ev_name); ui_browser__show_title(&self->b, title); continue; case 'D': { /* Debug */ @@ -883,7 +899,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, goto out_free_stack; case 'a': if (!browser->has_symbols) { - ui_browser__warning(&browser->b, + ui_browser__warning(&browser->b, delay_secs * 2, "Annotation is only available for symbolic views, " "include \"sym\" in --sort to use it."); continue; @@ -1061,6 +1077,7 @@ out: struct perf_evsel_menu { struct ui_browser b; struct perf_evsel *selection; + bool lost_events, lost_events_warned; }; static void perf_evsel_menu__write(struct ui_browser *browser, @@ -1073,14 +1090,29 @@ static void perf_evsel_menu__write(struct ui_browser *browser, unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; const char *ev_name = event_name(evsel); char bf[256], unit; + const char *warn = " "; + size_t printed; ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : HE_COLORSET_NORMAL); nr_events = convert_unit(nr_events, &unit); - snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, - unit, unit == ' ' ? "" : " ", ev_name); - slsmg_write_nstring(bf, browser->width); + printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, + unit, unit == ' ' ? "" : " ", ev_name); + slsmg_printf("%s", bf); + + nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; + if (nr_events != 0) { + menu->lost_events = true; + if (!current_entry) + ui_browser__set_color(browser, HE_COLORSET_TOP); + nr_events = convert_unit(nr_events, &unit); + snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, + unit, unit == ' ' ? "" : " "); + warn = bf; + } + + slsmg_write_nstring(warn, browser->width - printed); if (current_entry) menu->selection = evsel; @@ -1105,6 +1137,11 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, switch (key) { case K_TIMER: timer(arg); + + if (!menu->lost_events_warned && menu->lost_events) { + ui_browser__warn_lost_events(&menu->b); + menu->lost_events_warned = true; + } continue; case K_RIGHT: case K_ENTER: From 886605636e656afeb6fad5e83dbf36967f65cfa5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 29 Oct 2011 12:41:45 -0200 Subject: [PATCH 15/17] perf report: Add progress bar when processing time ordered events So that for large perf.data files the user can have visual feedback that activity is being performed. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-3ysn01mpspfrbsy56gznzqqz@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 9 +++++++++ tools/perf/util/session.h | 1 + 2 files changed, 10 insertions(+) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index da354fe5e085..85c1e6b76f0a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -502,6 +502,7 @@ static void flush_sample_queue(struct perf_session *s, struct perf_sample sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; + unsigned idx = 0, progress_next = os->nr_samples / 16; int ret; if (!ops->ordered_samples || !limit) @@ -521,6 +522,11 @@ static void flush_sample_queue(struct perf_session *s, os->last_flush = iter->timestamp; list_del(&iter->list); list_add(&iter->list, &os->sample_cache); + if (++idx >= progress_next) { + progress_next += os->nr_samples / 16; + ui_progress__update(idx, os->nr_samples, + "Processing time ordered events..."); + } } if (list_empty(head)) { @@ -529,6 +535,8 @@ static void flush_sample_queue(struct perf_session *s, os->last_sample = list_entry(head->prev, struct sample_queue, list); } + + os->nr_samples = 0; } /* @@ -588,6 +596,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) u64 timestamp = new->timestamp; struct list_head *p; + ++os->nr_samples; os->last_sample = new; if (!sample) { diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 514b06d41f05..6e393c98eb34 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -23,6 +23,7 @@ struct ordered_samples { struct sample_queue *sample_buffer; struct sample_queue *last_sample; int sample_buffer_idx; + unsigned int nr_samples; }; struct perf_session { From 104268a335d8aeb4293301e26a7bfad964ddc1ca Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 2 Nov 2011 12:46:18 -0200 Subject: [PATCH 16/17] perf top tui: Don't recalc column widths considering just the first page It makes sense for the stdio where we can't navigate to the other pages. On the TUI it breaks as soon as we navigate to other pages that have, DSOs with longer names than the ones on the first page. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-zvqfp18mw229agb43cikgb0k@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 8577bfeb087a..a13f8870d94c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -564,7 +564,6 @@ static void perf_top__sort_new_samples(void *arg) hists__decay_entries_threaded(&t->sym_evsel->hists, top.hide_user_symbols, top.hide_kernel_symbols); - hists__output_recalc_col_len(&t->sym_evsel->hists, winsize.ws_row - 3); } static void *display_thread_tui(void *arg __used) From f9e3d4b1a9c86217655997d3ef109b1eaae967bc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 3 Nov 2011 11:31:26 -0200 Subject: [PATCH 17/17] perf top: Fix live annotation in the --stdio interface In the old --stdio interface the annotation is done just after one selects a symbol, while in --tui, now the default when the required libs are installed, we annotate all symbols with samples so that when annotation is asked we see what happened recently on that symbol. To achieve that the --stdio variant checks if the hist_entry being processed is the one selected by the user via the 's' hotkey. What happens now that we share the hist_entry abstractions with 'perf report' is that for minimizing locking contention multiple rb_trees are used, one for collecting the samples and other to browse/show them after resorting it by number of samples and decay them, which is done periodically. So the simple test in record_precise_ip doesn't work as we move hist_entries between those rb_trees. To fix it just check that the underlying struct symbol associated with those hist_entries is the same. Reported-by: Mike Galbraith Tested-by: Mike Galbraith Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-bcfnraqkux88fox9ba9767ds@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a13f8870d94c..c9cdedb58134 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -200,7 +200,8 @@ static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) struct symbol *sym; if (he == NULL || he->ms.sym == NULL || - (he != top.sym_filter_entry && use_browser != 1)) + ((top.sym_filter_entry == NULL || + top.sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1)) return; sym = he->ms.sym;