diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f82480fa7f27..6ab58cc99d53 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,15 +286,19 @@ 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) { - 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-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..c9cdedb58134 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; @@ -199,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; @@ -289,11 +291,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; } @@ -561,7 +565,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) @@ -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; } } @@ -834,10 +841,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; @@ -858,14 +871,23 @@ 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) < 0) { + top.evlist->threads, group, + group_fd) < 0) { int err = errno; if (err == EPERM || err == EACCES) { - ui__warning_paranoid(); + 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/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.c b/tools/perf/util/debug.c index 155749d74350..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__warning_paranoid(void) +int ui__error_paranoid(void) { - ui__warning("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 fd53db47e3de..f2ce88d04f54 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -19,23 +19,18 @@ 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(u64 curr __used, u64 total __used, + const char *title __used) {} -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) {} +#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" +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__warning_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/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/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 <sys/types.h> #include <byteswap.h> #include <unistd.h> @@ -11,7 +12,6 @@ #include "evlist.h" #include "evsel.h" -#include "util.h" #include "header.h" #include "../perf.h" #include "trace-event.h" 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) { 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/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, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 20e011c99a94..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) { @@ -738,10 +747,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 +900,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) { @@ -1012,7 +1038,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 +1055,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 +1117,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 +1129,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/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 { 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/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; diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 5359f371d30a..556829124b02 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -4,6 +4,7 @@ #include "libslang.h" #include <newt.h> #include "ui.h" +#include "util.h" #include <linux/compiler.h> #include <linux/list.h> #include <linux/rbtree.h> @@ -168,6 +169,59 @@ 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, int timeout, + const char *format, ...) +{ + va_list args; + char *text; + int key = 0, err; + + va_start(args, format); + 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; +} + +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; @@ -230,13 +284,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) @@ -291,53 +347,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 +364,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); @@ -533,6 +543,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..84d761b730c1 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -43,6 +43,15 @@ 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, 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); + +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/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 <pthread.h> #include <newt.h> -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/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 4663dcb2a19b..d0c94b459685 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 { @@ -294,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) { @@ -314,12 +324,18 @@ 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), - 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__warning( + ui_browser__warning(&browser->b, delay_secs * 2, "Annotation is only available for symbolic views, " "include \"sym\" in --sort to use it."); continue; @@ -901,7 +917,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" @@ -914,7 +931,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: @@ -940,7 +957,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': @@ -993,6 +1011,7 @@ add_exit_option: if (choice == annotate) { struct hist_entry *he; + int err; do_annotate: he = hist_browser__selected_entry(browser); if (he == NULL) @@ -1001,10 +1020,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) { @@ -1056,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, @@ -1068,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; @@ -1100,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: @@ -1133,7 +1175,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': @@ -1145,7 +1188,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 f36d2ff509ed..6ef3c5691762 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -1,20 +1,28 @@ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> -#include <newt.h> +#include <string.h> #include "../debug.h" #include "helpline.h" #include "ui.h" +#include "libslang.h" void ui_helpline__pop(void) { - newtPopHelpLine(); } +char ui_helpline__current[512]; + void ui_helpline__push(const char *msg) { - newtPushHelpLine(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) @@ -63,7 +71,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); 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/progress.c b/tools/perf/util/ui/progress.c index d7fc399d36b3..295e366b6311 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/util/ui/progress.c @@ -1,60 +1,29 @@ -#include <stdlib.h> -#include <newt.h> #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); + ui__refresh_dimensions(true); + 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 diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 1e6ba06980c4..85a69faa09aa 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(); @@ -71,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/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 <pthread.h> +#include <stdbool.h> extern pthread_mutex_t ui__lock; +void ui__refresh_dimensions(bool force); + #endif /* _PERF_UI_H_ */ diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index fdf1fc8f08bc..45daa7c41dad 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c @@ -1,6 +1,5 @@ -#include <newt.h> +#include "../util.h" #include <signal.h> -#include <stdio.h> #include <stdbool.h> #include <string.h> #include <sys/ttydefaults.h> @@ -8,72 +7,75 @@ #include "../cache.h" #include "../debug.h" #include "browser.h" +#include "keysyms.h" #include "helpline.h" #include "ui.h" #include "util.h" +#include "libslang.h" -static void newt_form__set_exit_keys(newtComponent self) +static void ui_browser__argv_write(struct ui_browser *browser, + void *entry, int row) { - newtFormAddHotKey(self, NEWT_KEY_LEFT); - newtFormAddHotKey(self, NEWT_KEY_ESCAPE); - newtFormAddHotKey(self, 'Q'); - newtFormAddHotKey(self, 'q'); - newtFormAddHotKey(self, CTRL('c')); + 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 newtComponent newt_form__new(void) +static int popup_menu__run(struct ui_browser *menu) { - newtComponent self = newtForm(NULL, NULL, 0); - if (self) - newt_form__set_exit_keys(self); - return self; + 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; } 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) +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'); @@ -90,41 +92,77 @@ 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"; - -bool ui__dialog_yesno(const char *msg) +int ui__help_window(const char *text) { - /* newtWinChoice should really be accepting const char pointers... */ - return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; + return ui__question_window("Help", text, "Press any key...", 0); } -void ui__warning(const char *format, ...) +int ui__dialog_yesno(const char *msg) { + return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); +} + +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); + key = ui__question_window(title, s, "Press any key...", 0); + pthread_mutex_unlock(&ui__lock); + free(s); + return key; + } + + fprintf(stderr, "%s:\n", title); + vfprintf(stderr, format, args); + return K_ESC; +} + +int ui__warning(const char *format, ...) +{ + int key; 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); + key = __ui__warning("Warning", format, args); va_end(args); + return key; +} + +int ui__error(const char *format, ...) +{ + int key; + va_list args; + + va_start(args, format); + 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 afcbc1d99531..2d1738bd71c8 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h @@ -1,10 +1,14 @@ #ifndef _PERF_UI_UTIL_H_ #define _PERF_UI_UTIL_H_ 1 -#include <stdbool.h> +#include <stdarg.h> +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_ */