Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  perf top: Fix live annotation in the --stdio interface
  perf top tui: Don't recalc column widths considering just the first page
  perf report: Add progress bar when processing time ordered events
  perf hists browser: Warn about lost events
  perf tools: Fix a typo of command name as trace-cmd
  perf hists: Fix recalculation of total_period when sorting entries
  perf header: Fix build on old systems
  perf ui browser: Handle K_RESIZE in dialog windows
  perf ui browser: No need to switch char sets that often
  perf hists browser: Use K_TIMER
  perf ui: Rename ui__warning_paranoid to ui__error_paranoid
  perf ui: Reimplement the popup windows using libslang
  perf ui: Reimplement ui__popup_menu using ui__browser
  perf ui: Reimplement ui_helpline using libslang
  perf ui: Improve handling sigwinch a bit
  perf ui progress: Reimplement using slang
  perf evlist: Fix grouping of multiple events
This commit is contained in:
Linus Torvalds 2011-11-07 12:38:11 -08:00
commit 54a0f91301
31 changed files with 627 additions and 291 deletions

View File

@ -262,13 +262,16 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
static void open_counters(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) if (evlist->cpus->map[0] < 0)
no_inherit = true; no_inherit = true;
first = list_entry(evlist->entries.next, struct perf_evsel, node);
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr; struct perf_event_attr *attr = &pos->attr;
struct xyarray *group_fd = NULL;
/* /*
* Check if parse_single_tracepoint_event has already asked for * Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME. * PERF_SAMPLE_TIME.
@ -283,15 +286,19 @@ static void open_counters(struct perf_evlist *evlist)
*/ */
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
if (group && pos != first)
group_fd = first->fd;
config_attr(pos, evlist); config_attr(pos, evlist);
retry_sample_id: retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0; attr->sample_id_all = sample_id_all_avail ? 1 : 0;
try_again: 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; int err = errno;
if (err == EPERM || err == EACCES) { if (err == EPERM || err == EACCES) {
ui__warning_paranoid(); ui__error_paranoid();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else if (err == ENODEV && cpu_list) { } else if (err == ENODEV && cpu_list) {
die("No such device - did you specify" die("No such device - did you specify"

View File

@ -278,9 +278,14 @@ struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
struct stats walltime_nsecs_stats; 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 perf_event_attr *attr = &evsel->attr;
struct xyarray *group_fd = NULL;
if (group && evsel != first)
group_fd = first->fd;
if (scale) if (scale)
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | 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; attr->inherit = !no_inherit;
if (system_wide) 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) { if (target_pid == -1 && target_tid == -1) {
attr->disabled = 1; attr->disabled = 1;
attr->enable_on_exec = 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) static int run_perf_stat(int argc __used, const char **argv)
{ {
unsigned long long t0, t1; unsigned long long t0, t1;
struct perf_evsel *counter; struct perf_evsel *counter, *first;
int status = 0; int status = 0;
int child_ready_pipe[2], go_pipe[2]; int child_ready_pipe[2], go_pipe[2];
const bool forks = (argc > 0); 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]); 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) { 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 (errno == EINVAL || errno == ENOSYS || errno == ENOENT) {
if (verbose) if (verbose)
ui__warning("%s event is not supported by the kernel.\n", ui__warning("%s event is not supported by the kernel.\n",

View File

@ -291,7 +291,7 @@ static int test__open_syscall_event(void)
goto out_thread_map_delete; 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, " pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n", "tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno)); strerror(errno));
@ -366,7 +366,7 @@ static int test__open_syscall_event_on_all_cpus(void)
goto out_thread_map_delete; 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, " pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n", "tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno)); strerror(errno));
@ -531,7 +531,7 @@ static int test__basic_mmap(void)
perf_evlist__add(evlist, evsels[i]); 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, " pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n", "tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno)); strerror(errno));

View File

@ -89,6 +89,7 @@ static bool vmlinux_warned;
static bool inherit = false; static bool inherit = false;
static int realtime_prio = 0; static int realtime_prio = 0;
static bool group = false; static bool group = false;
static bool sample_id_all_avail = true;
static unsigned int mmap_pages = 128; static unsigned int mmap_pages = 128;
static bool dump_symtab = false; 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; struct symbol *sym;
if (he == NULL || he->ms.sym == NULL || 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; return;
sym = he->ms.sym; sym = he->ms.sym;
@ -289,11 +291,13 @@ static void print_sym_table(void)
printf("%-*.*s\n", win_width, win_width, graph_dotted_line); printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
if (top.total_lost_warned != top.session->hists.stats.total_lost) { if (top.sym_evsel->hists.stats.nr_lost_warned !=
top.total_lost_warned = top.session->hists.stats.total_lost; top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) {
color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); top.sym_evsel->hists.stats.nr_lost_warned =
printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST];
top.total_lost_warned); color_fprintf(stdout, PERF_COLOR_RED,
"WARNING: LOST %d chunks, Check IO/CPU overload",
top.sym_evsel->hists.stats.nr_lost_warned);
++printed; ++printed;
} }
@ -561,7 +565,6 @@ static void perf_top__sort_new_samples(void *arg)
hists__decay_entries_threaded(&t->sym_evsel->hists, hists__decay_entries_threaded(&t->sym_evsel->hists,
top.hide_user_symbols, top.hide_user_symbols,
top.hide_kernel_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) 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, static void perf_event__process_sample(const union perf_event *event,
struct perf_evsel *evsel,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_session *session) 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) { if (al.sym == NULL || !al.sym->ignore) {
struct perf_evsel *evsel;
struct hist_entry *he; struct hist_entry *he;
evsel = perf_evlist__id2evsel(top.evlist, sample->id);
assert(evsel != NULL);
if ((sort__has_parent || symbol_conf.use_callchain) && if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) { sample->callchain) {
err = perf_session__resolve_callchain(session, al.thread, 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) static void perf_session__mmap_read_idx(struct perf_session *self, int idx)
{ {
struct perf_sample sample; struct perf_sample sample;
struct perf_evsel *evsel;
union perf_event *event; union perf_event *event;
int ret; int ret;
@ -817,10 +818,16 @@ static void perf_session__mmap_read_idx(struct perf_session *self, int idx)
continue; continue;
} }
evsel = perf_evlist__id2evsel(self->evlist, sample.id);
assert(evsel != NULL);
if (event->header.type == PERF_RECORD_SAMPLE) if (event->header.type == PERF_RECORD_SAMPLE)
perf_event__process_sample(event, &sample, self); perf_event__process_sample(event, evsel, &sample, self);
else else if (event->header.type < PERF_RECORD_MAX) {
hists__inc_nr_events(&evsel->hists, event->header.type);
perf_event__process(event, &sample, self); 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) 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) { list_for_each_entry(counter, &evlist->entries, node) {
struct perf_event_attr *attr = &counter->attr; 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; 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->mmap = 1;
attr->comm = 1; attr->comm = 1;
attr->inherit = inherit; attr->inherit = inherit;
retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
try_again: try_again:
if (perf_evsel__open(counter, top.evlist->cpus, if (perf_evsel__open(counter, top.evlist->cpus,
top.evlist->threads, group) < 0) { top.evlist->threads, group,
group_fd) < 0) {
int err = errno; int err = errno;
if (err == EPERM || err == EACCES) { if (err == EPERM || err == EACCES) {
ui__warning_paranoid(); ui__error_paranoid();
goto out_err; 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 * If it's cycles then fall back to hrtimer

View File

@ -310,9 +310,12 @@ fallback:
} }
err = -ENOENT; err = -ENOENT;
dso->annotate_warned = 1; dso->annotate_warned = 1;
pr_err("Can't annotate %s: No vmlinux file%s was found in the " pr_err("Can't annotate %s:\n\n"
"path.\nPlease use 'perf buildid-cache -av vmlinux' or " "No vmlinux file%s\nwas found in the path.\n\n"
"--vmlinux vmlinux.\n", "Please use:\n\n"
" perf buildid-cache -av vmlinux\n\n"
"or:\n\n"
" --vmlinux vmlinux",
sym->name, build_id_msg ?: ""); sym->name, build_id_msg ?: "");
goto out_free_filename; goto out_free_filename;
} }

View File

@ -47,19 +47,20 @@ int dump_printf(const char *fmt, ...)
} }
#ifdef NO_NEWT_SUPPORT #ifdef NO_NEWT_SUPPORT
void ui__warning(const char *format, ...) int ui__warning(const char *format, ...)
{ {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
vfprintf(stderr, format, args); vfprintf(stderr, format, args);
va_end(args); va_end(args);
return 0;
} }
#endif #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" "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
" -1 - Not paranoid at all\n" " -1 - Not paranoid at all\n"
" 0 - Disallow raw tracepoint access for unpriv\n" " 0 - Disallow raw tracepoint access for unpriv\n"

View File

@ -19,23 +19,18 @@ static inline int ui_helpline__show_help(const char *format __used, va_list ap _
return 0; return 0;
} }
static inline struct ui_progress *ui_progress__new(const char *title __used, static inline void ui_progress__update(u64 curr __used, u64 total __used,
u64 total __used) const char *title __used) {}
{
return (struct ui_progress *)1;
}
static inline void ui_progress__update(struct ui_progress *self __used, #define ui__error(format, arg...) ui__warning(format, ##arg)
u64 curr __used) {}
static inline void ui_progress__delete(struct ui_progress *self __used) {}
#else #else
extern char ui_helpline__last_msg[]; extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap); int ui_helpline__show_help(const char *format, va_list ap);
#include "ui/progress.h" #include "ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif #endif
void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
void ui__warning_paranoid(void); int ui__error_paranoid(void);
#endif /* __PERF_DEBUG_H */ #endif /* __PERF_DEBUG_H */

View File

@ -539,3 +539,33 @@ void perf_evlist__set_selected(struct perf_evlist *evlist,
{ {
evlist->selected = evsel; 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;
}

View File

@ -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); 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__alloc_mmap(struct perf_evlist *evlist);
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
void perf_evlist__munmap(struct perf_evlist *evlist); void perf_evlist__munmap(struct perf_evlist *evlist);

View File

@ -16,6 +16,7 @@
#include "thread_map.h" #include "thread_map.h"
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #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) 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, 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; int cpu, thread;
unsigned long flags = 0; unsigned long flags = 0;
int pid = -1; int pid = -1, err;
if (evsel->fd == NULL && if (evsel->fd == NULL &&
perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
return -1; return -ENOMEM;
if (evsel->cgrp) { if (evsel->cgrp) {
flags = PERF_FLAG_PID_CGROUP; 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++) { 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++) { 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, pid,
cpus->map[cpu], cpus->map[cpu],
group_fd, flags); group_fd, flags);
if (FD(evsel, cpu, thread) < 0) if (FD(evsel, cpu, thread) < 0) {
err = -errno;
goto out_close; goto out_close;
}
if (group && group_fd == -1) if (group && group_fd == -1)
group_fd = FD(evsel, cpu, thread); group_fd = FD(evsel, cpu, thread);
@ -249,7 +253,17 @@ out_close:
} }
thread = threads->nr; thread = threads->nr;
} while (--cpu >= 0); } 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 { static struct {
@ -269,7 +283,8 @@ static struct {
}; };
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, 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) { if (cpus == NULL) {
/* Work around old compiler warnings about strict aliasing */ /* 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) if (threads == NULL)
threads = &empty_thread_map.map; 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, 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, 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, static int perf_event__parse_id_sample(const union perf_event *event, u64 type,

View File

@ -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); void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, 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, 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, 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) \ #define perf_evsel__match(evsel, t, c) \
(evsel->attr.type == PERF_TYPE_##t && \ (evsel->attr.type == PERF_TYPE_##t && \

View File

@ -1,5 +1,6 @@
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#include "util.h"
#include <sys/types.h> #include <sys/types.h>
#include <byteswap.h> #include <byteswap.h>
#include <unistd.h> #include <unistd.h>
@ -11,7 +12,6 @@
#include "evlist.h" #include "evlist.h"
#include "evsel.h" #include "evsel.h"
#include "util.h"
#include "header.h" #include "header.h"
#include "../perf.h" #include "../perf.h"
#include "trace-event.h" #include "trace-event.h"

View File

@ -365,7 +365,6 @@ static void __hists__collapse_resort(struct hists *hists, bool threaded)
root = hists__get_rotate_entries_in(hists); root = hists__get_rotate_entries_in(hists);
next = rb_first(root); next = rb_first(root);
hists->stats.total_period = 0;
while (next) { while (next) {
n = rb_entry(next, struct hist_entry, rb_node_in); 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. * been set by, say, the hist_browser.
*/ */
hists__apply_filters(hists, n); 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->entries = RB_ROOT;
hists->nr_entries = 0; hists->nr_entries = 0;
hists->stats.total_period = 0;
hists__reset_col_len(hists); hists__reset_col_len(hists);
while (next) { while (next) {

View File

@ -28,6 +28,7 @@ struct events_stats {
u64 total_lost; u64 total_lost;
u64 total_invalid_chains; u64 total_invalid_chains;
u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_events[PERF_RECORD_HEADER_MAX];
u32 nr_lost_warned;
u32 nr_unknown_events; u32 nr_unknown_events;
u32 nr_invalid_chains; u32 nr_invalid_chains;
u32 nr_unknown_id; u32 nr_unknown_id;

View File

@ -623,7 +623,11 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,
cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; cpus = ((struct pyrf_cpu_map *)pcpus)->cpus;
evsel->attr.inherit = inherit; 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); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
@ -814,6 +818,25 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
return Py_None; 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[] = { static PyMethodDef pyrf_evlist__methods[] = {
{ {
.ml_name = "mmap", .ml_name = "mmap",
@ -821,6 +844,12 @@ static PyMethodDef pyrf_evlist__methods[] = {
.ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = PyDoc_STR("mmap the file descriptor table.") .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_name = "poll",
.ml_meth = (PyCFunction)pyrf_evlist__poll, .ml_meth = (PyCFunction)pyrf_evlist__poll,

View File

@ -502,6 +502,7 @@ static void flush_sample_queue(struct perf_session *s,
struct perf_sample sample; struct perf_sample sample;
u64 limit = os->next_flush; u64 limit = os->next_flush;
u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
unsigned idx = 0, progress_next = os->nr_samples / 16;
int ret; int ret;
if (!ops->ordered_samples || !limit) if (!ops->ordered_samples || !limit)
@ -521,6 +522,11 @@ static void flush_sample_queue(struct perf_session *s,
os->last_flush = iter->timestamp; os->last_flush = iter->timestamp;
list_del(&iter->list); list_del(&iter->list);
list_add(&iter->list, &os->sample_cache); 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)) { if (list_empty(head)) {
@ -529,6 +535,8 @@ static void flush_sample_queue(struct perf_session *s,
os->last_sample = os->last_sample =
list_entry(head->prev, struct sample_queue, list); 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; u64 timestamp = new->timestamp;
struct list_head *p; struct list_head *p;
++os->nr_samples;
os->last_sample = new; os->last_sample = new;
if (!sample) { if (!sample) {
@ -738,10 +747,27 @@ static int perf_session_deliver_event(struct perf_session *session,
dump_event(session, event, file_offset, sample); 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) { switch (event->header.type) {
case PERF_RECORD_SAMPLE: case PERF_RECORD_SAMPLE:
dump_sample(session, event, sample); dump_sample(session, event, sample);
evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (evsel == NULL) { if (evsel == NULL) {
++session->hists.stats.nr_unknown_id; ++session->hists.stats.nr_unknown_id;
return -1; return -1;
@ -874,11 +900,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
const struct perf_event_ops *ops) const struct perf_event_ops *ops)
{ {
if (ops->lost == perf_event__process_lost && if (ops->lost == perf_event__process_lost &&
session->hists.stats.total_lost != 0) { session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 ui__warning("Processed %d events and lost %d chunks!\n\n"
"!\n\nCheck IO/CPU overload!\n\n", "Check IO/CPU overload!\n\n",
session->hists.stats.total_period, session->hists.stats.nr_events[0],
session->hists.stats.total_lost); session->hists.stats.nr_events[PERF_RECORD_LOST]);
} }
if (session->hists.stats.nr_unknown_events != 0) { 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; u64 head, page_offset, file_offset, file_pos, progress_next;
int err, mmap_prot, mmap_flags, map_idx = 0; int err, mmap_prot, mmap_flags, map_idx = 0;
struct ui_progress *progress;
size_t page_size, mmap_size; size_t page_size, mmap_size;
char *buf, *mmaps[8]; char *buf, *mmaps[8];
union perf_event *event; union perf_event *event;
@ -1030,9 +1055,6 @@ int __perf_session__process_events(struct perf_session *session,
file_size = data_offset + data_size; file_size = data_offset + data_size;
progress_next = file_size / 16; progress_next = file_size / 16;
progress = ui_progress__new("Processing events...", file_size);
if (progress == NULL)
return -1;
mmap_size = session->mmap_window; mmap_size = session->mmap_window;
if (mmap_size > file_size) if (mmap_size > file_size)
@ -1095,7 +1117,8 @@ more:
if (file_pos >= progress_next) { if (file_pos >= progress_next) {
progress_next += file_size / 16; 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) if (file_pos < file_size)
@ -1106,7 +1129,6 @@ more:
session->ordered_samples.next_flush = ULLONG_MAX; session->ordered_samples.next_flush = ULLONG_MAX;
flush_sample_queue(session, ops); flush_sample_queue(session, ops);
out_err: out_err:
ui_progress__delete(progress);
perf_session__warn_about_errors(session, ops); perf_session__warn_about_errors(session, ops);
perf_session_free_sample_buffers(session); perf_session_free_sample_buffers(session);
return err; return err;

View File

@ -23,6 +23,7 @@ struct ordered_samples {
struct sample_queue *sample_buffer; struct sample_queue *sample_buffer;
struct sample_queue *last_sample; struct sample_queue *last_sample;
int sample_buffer_idx; int sample_buffer_idx;
unsigned int nr_samples;
}; };
struct perf_session { struct perf_session {

View File

@ -19,7 +19,6 @@ struct perf_top {
u64 kernel_samples, us_samples; u64 kernel_samples, us_samples;
u64 exact_samples; u64 exact_samples;
u64 guest_us_samples, guest_kernel_samples; u64 guest_us_samples, guest_kernel_samples;
u64 total_lost_warned;
int print_entries, count_filter, delay_secs; int print_entries, count_filter, delay_secs;
int freq; int freq;
pid_t target_pid, target_tid; pid_t target_pid, target_tid;

View File

@ -80,7 +80,7 @@ static void die(const char *fmt, ...)
int ret = errno; int ret = errno;
if (errno) if (errno)
perror("trace-cmd"); perror("perf");
else else
ret = -1; ret = -1;

View File

@ -4,6 +4,7 @@
#include "libslang.h" #include "libslang.h"
#include <newt.h> #include <newt.h>
#include "ui.h" #include "ui.h"
#include "util.h"
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
@ -168,6 +169,59 @@ void ui_browser__refresh_dimensions(struct ui_browser *self)
self->x = 0; 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) void ui_browser__reset_index(struct ui_browser *self)
{ {
self->index = self->top_idx = 0; self->index = self->top_idx = 0;
@ -230,13 +284,15 @@ static void ui_browser__scrollbar_set(struct ui_browser *browser)
(browser->nr_entries - 1)); (browser->nr_entries - 1));
} }
SLsmg_set_char_set(1);
while (h < height) { while (h < height) {
ui_browser__gotorc(browser, row++, col); ui_browser__gotorc(browser, row++, col);
SLsmg_set_char_set(1); SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR);
SLsmg_set_char_set(0);
++h; ++h;
} }
SLsmg_set_char_set(0);
} }
static int __ui_browser__refresh(struct ui_browser *browser) 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); 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 ui_browser__run(struct ui_browser *self, int delay_secs)
{ {
int err, key; int err, key;
pthread__unblock_sigwinch();
while (1) { while (1) {
off_t offset; off_t offset;
@ -351,10 +364,7 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)
key = ui__getch(delay_secs); key = ui__getch(delay_secs);
if (key == K_RESIZE) { if (key == K_RESIZE) {
pthread_mutex_lock(&ui__lock); ui__refresh_dimensions(false);
SLtt_get_screen_size();
SLsmg_reinit_smg();
pthread_mutex_unlock(&ui__lock);
ui_browser__refresh_dimensions(self); ui_browser__refresh_dimensions(self);
__ui_browser__show_title(self, self->title); __ui_browser__show_title(self, self->title);
ui_helpline__puts(self->helpline); ui_helpline__puts(self->helpline);
@ -533,6 +543,47 @@ static int ui_browser__color_config(const char *var, const char *value,
return -1; 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) void ui_browser__init(void)
{ {
int i = 0; int i = 0;

View File

@ -43,6 +43,15 @@ void ui_browser__hide(struct ui_browser *self);
int ui_browser__refresh(struct ui_browser *self); int ui_browser__refresh(struct ui_browser *self);
int ui_browser__run(struct ui_browser *browser, int delay_secs); 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__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); 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); unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);

View File

@ -1,6 +1,9 @@
#include "../../util.h"
#include "../browser.h" #include "../browser.h"
#include "../helpline.h" #include "../helpline.h"
#include "../libslang.h" #include "../libslang.h"
#include "../ui.h"
#include "../util.h"
#include "../../annotate.h" #include "../../annotate.h"
#include "../../hist.h" #include "../../hist.h"
#include "../../sort.h" #include "../../sort.h"
@ -8,15 +11,6 @@
#include <pthread.h> #include <pthread.h>
#include <newt.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 annotate_browser {
struct ui_browser b; struct ui_browser b;
struct rb_root entries; struct rb_root entries;
@ -400,7 +394,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
return -1; return -1;
if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { 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; return -1;
} }

View File

@ -17,6 +17,7 @@
#include "../browser.h" #include "../browser.h"
#include "../helpline.h" #include "../helpline.h"
#include "../util.h" #include "../util.h"
#include "../ui.h"
#include "map.h" #include "map.h"
struct hist_browser { 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); 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, static int hist_browser__run(struct hist_browser *self, const char *ev_name,
void(*timer)(void *arg), void *arg, int delay_secs) 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); key = ui_browser__run(&self->b, delay_secs);
switch (key) { switch (key) {
case -1: case K_TIMER:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
timer(arg); timer(arg);
ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); 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); ui_browser__show_title(&self->b, title);
continue; continue;
case 'D': { /* Debug */ case 'D': { /* Debug */
@ -883,7 +899,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
goto out_free_stack; goto out_free_stack;
case 'a': case 'a':
if (!browser->has_symbols) { if (!browser->has_symbols) {
ui__warning( ui_browser__warning(&browser->b, delay_secs * 2,
"Annotation is only available for symbolic views, " "Annotation is only available for symbolic views, "
"include \"sym\" in --sort to use it."); "include \"sym\" in --sort to use it.");
continue; continue;
@ -901,7 +917,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
case K_F1: case K_F1:
case 'h': case 'h':
case '?': 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" "UP/DOWN/PGUP\n"
"PGDN/SPACE Navigate\n" "PGDN/SPACE Navigate\n"
"q/ESC/CTRL+C Exit browser\n\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" "C Collapse all callchains\n"
"E Expand all callchains\n" "E Expand all callchains\n"
"d Zoom into current DSO\n" "d Zoom into current DSO\n"
"t Zoom into current Thread\n"); "t Zoom into current Thread");
continue; continue;
case K_ENTER: case K_ENTER:
case K_RIGHT: case K_RIGHT:
@ -940,7 +957,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
} }
case K_ESC: case K_ESC:
if (!left_exits && 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; continue;
/* Fall thru */ /* Fall thru */
case 'q': case 'q':
@ -993,6 +1011,7 @@ add_exit_option:
if (choice == annotate) { if (choice == annotate) {
struct hist_entry *he; struct hist_entry *he;
int err;
do_annotate: do_annotate:
he = hist_browser__selected_entry(browser); he = hist_browser__selected_entry(browser);
if (he == NULL) if (he == NULL)
@ -1001,10 +1020,12 @@ do_annotate:
* Don't let this be freed, say, by hists__decay_entry. * Don't let this be freed, say, by hists__decay_entry.
*/ */
he->used = true; he->used = true;
hist_entry__tui_annotate(he, evsel->idx, nr_events, err = hist_entry__tui_annotate(he, evsel->idx, nr_events,
timer, arg, delay_secs); timer, arg, delay_secs);
he->used = false; he->used = false;
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
if (err)
ui_browser__handle_resize(&browser->b);
} else if (choice == browse_map) } else if (choice == browse_map)
map__browse(browser->selection->map); map__browse(browser->selection->map);
else if (choice == zoom_dso) { else if (choice == zoom_dso) {
@ -1056,6 +1077,7 @@ out:
struct perf_evsel_menu { struct perf_evsel_menu {
struct ui_browser b; struct ui_browser b;
struct perf_evsel *selection; struct perf_evsel *selection;
bool lost_events, lost_events_warned;
}; };
static void perf_evsel_menu__write(struct ui_browser *browser, 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]; unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
const char *ev_name = event_name(evsel); const char *ev_name = event_name(evsel);
char bf[256], unit; char bf[256], unit;
const char *warn = " ";
size_t printed;
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL); HE_COLORSET_NORMAL);
nr_events = convert_unit(nr_events, &unit); nr_events = convert_unit(nr_events, &unit);
snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
unit, unit == ' ' ? "" : " ", ev_name); unit, unit == ' ' ? "" : " ", ev_name);
slsmg_write_nstring(bf, browser->width); 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) if (current_entry)
menu->selection = evsel; menu->selection = evsel;
@ -1100,6 +1137,11 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
switch (key) { switch (key) {
case K_TIMER: case K_TIMER:
timer(arg); timer(arg);
if (!menu->lost_events_warned && menu->lost_events) {
ui_browser__warn_lost_events(&menu->b);
menu->lost_events_warned = true;
}
continue; continue;
case K_RIGHT: case K_RIGHT:
case K_ENTER: case K_ENTER:
@ -1133,7 +1175,8 @@ browse_hists:
pos = list_entry(pos->node.prev, struct perf_evsel, node); pos = list_entry(pos->node.prev, struct perf_evsel, node);
goto browse_hists; goto browse_hists;
case K_ESC: 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; continue;
/* Fall thru */ /* Fall thru */
case 'q': case 'q':
@ -1145,7 +1188,8 @@ browse_hists:
case K_LEFT: case K_LEFT:
continue; continue;
case K_ESC: 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; continue;
/* Fall thru */ /* Fall thru */
case 'q': case 'q':

View File

@ -1,20 +1,28 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <newt.h> #include <string.h>
#include "../debug.h" #include "../debug.h"
#include "helpline.h" #include "helpline.h"
#include "ui.h" #include "ui.h"
#include "libslang.h"
void ui_helpline__pop(void) void ui_helpline__pop(void)
{ {
newtPopHelpLine();
} }
char ui_helpline__current[512];
void ui_helpline__push(const char *msg) 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) 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') { if (ui_helpline__last_msg[backlog - 1] == '\n') {
ui_helpline__puts(ui_helpline__last_msg); ui_helpline__puts(ui_helpline__last_msg);
newtRefresh(); SLsmg_refresh();
backlog = 0; backlog = 0;
} }
pthread_mutex_unlock(&ui__lock); pthread_mutex_unlock(&ui__lock);

View File

@ -11,4 +11,6 @@ void ui_helpline__vpush(const char *fmt, va_list ap);
void ui_helpline__fpush(const char *fmt, ...); void ui_helpline__fpush(const char *fmt, ...);
void ui_helpline__puts(const char *msg); void ui_helpline__puts(const char *msg);
extern char ui_helpline__current[];
#endif /* _PERF_UI_HELPLINE_H_ */ #endif /* _PERF_UI_HELPLINE_H_ */

View File

@ -1,60 +1,29 @@
#include <stdlib.h>
#include <newt.h>
#include "../cache.h" #include "../cache.h"
#include "progress.h" #include "progress.h"
#include "libslang.h"
#include "ui.h"
#include "browser.h"
struct ui_progress { void ui_progress__update(u64 curr, u64 total, const char *title)
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)
{ {
int bar, y;
/* /*
* FIXME: We should have a per UI backend way of showing progress, * FIXME: We should have a per UI backend way of showing progress,
* stdio will just show a percentage as NN%, etc. * stdio will just show a percentage as NN%, etc.
*/ */
if (use_browser <= 0) if (use_browser <= 0)
return; return;
newtScaleSet(self->scale, curr);
newtRefresh();
}
void ui_progress__delete(struct ui_progress *self) ui__refresh_dimensions(true);
{ pthread_mutex_lock(&ui__lock);
if (use_browser > 0) { y = SLtt_Screen_Rows / 2 - 2;
newtFormDestroy(self->form); SLsmg_set_color(0);
newtPopWindow(); SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
} SLsmg_gotorc(y++, 1);
free(self); 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);
} }

View File

@ -1,11 +1,8 @@
#ifndef _PERF_UI_PROGRESS_H_ #ifndef _PERF_UI_PROGRESS_H_
#define _PERF_UI_PROGRESS_H_ 1 #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__update(u64 curr, u64 total, const char *title);
void ui_progress__delete(struct ui_progress *self);
void ui_progress__update(struct ui_progress *self, u64 curr);
#endif #endif

View File

@ -7,10 +7,85 @@
#include "browser.h" #include "browser.h"
#include "helpline.h" #include "helpline.h"
#include "ui.h" #include "ui.h"
#include "util.h"
#include "libslang.h" #include "libslang.h"
#include "keysyms.h"
pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; 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) static void newt_suspend(void *d __used)
{ {
newtSuspend(); newtSuspend();
@ -71,10 +146,10 @@ void setup_browser(bool fallback_to_pager)
void exit_browser(bool wait_for_ok) void exit_browser(bool wait_for_ok)
{ {
if (use_browser > 0) { if (use_browser > 0) {
if (wait_for_ok) { if (wait_for_ok)
char title[] = "Fatal Error", ok[] = "Ok"; ui__question_window("Fatal Error",
newtWinMessage(title, ok, ui_helpline__last_msg); ui_helpline__last_msg,
} "Press any key...", 0);
ui__exit(); ui__exit();
} }
} }

View File

@ -2,7 +2,10 @@
#define _PERF_UI_H_ 1 #define _PERF_UI_H_ 1
#include <pthread.h> #include <pthread.h>
#include <stdbool.h>
extern pthread_mutex_t ui__lock; extern pthread_mutex_t ui__lock;
void ui__refresh_dimensions(bool force);
#endif /* _PERF_UI_H_ */ #endif /* _PERF_UI_H_ */

View File

@ -1,6 +1,5 @@
#include <newt.h> #include "../util.h"
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/ttydefaults.h> #include <sys/ttydefaults.h>
@ -8,72 +7,75 @@
#include "../cache.h" #include "../cache.h"
#include "../debug.h" #include "../debug.h"
#include "browser.h" #include "browser.h"
#include "keysyms.h"
#include "helpline.h" #include "helpline.h"
#include "ui.h" #include "ui.h"
#include "util.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); char **arg = entry;
newtFormAddHotKey(self, NEWT_KEY_ESCAPE); bool current_entry = ui_browser__is_current_entry(browser, row);
newtFormAddHotKey(self, 'Q');
newtFormAddHotKey(self, 'q'); ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
newtFormAddHotKey(self, CTRL('c')); 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); int key;
if (self)
newt_form__set_exit_keys(self); if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return self; 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[]) int ui__popup_menu(int argc, char * const argv[])
{ {
struct newtExitStruct es; struct ui_browser menu = {
int i, rc = -1, max_len = 5; .entries = (void *)argv,
newtComponent listbox, form = newt_form__new(); .refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
if (form == NULL) return popup_menu__run(&menu);
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;
} }
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; int x, y;
newtComponent tb, form = newt_form__new();
int rc = -1;
int max_len = 0, nr_lines = 0; int max_len = 0, nr_lines = 0;
const char *t; const char *t;
if (form == NULL)
return -1;
t = text; t = text;
while (1) { while (1) {
const char *sep = strchr(t, '\n'); const char *sep = strchr(t, '\n');
@ -90,41 +92,77 @@ int ui__help_window(const char *text)
t = sep + 1; t = sep + 1;
} }
tb = newtTextbox(0, 0, max_len, nr_lines, 0); max_len += 2;
if (tb == NULL) nr_lines += 4;
goto out_destroy_form; y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
newtTextboxSetText(tb, text); SLsmg_set_color(0);
newtFormAddComponent(form, tb); SLsmg_draw_box(y, x++, nr_lines, max_len);
newtCenteredWindow(max_len, nr_lines, NULL); if (title) {
newtFormRun(form, &es); SLsmg_gotorc(y, x + 1);
newtPopWindow(); SLsmg_write_string((char *)title);
rc = 0; }
out_destroy_form: SLsmg_gotorc(++y, x);
newtFormDestroy(form); nr_lines -= 2;
return rc; 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", int ui__help_window(const char *text)
warning_str[] = "Warning!", ok[] = "Ok";
bool ui__dialog_yesno(const char *msg)
{ {
/* newtWinChoice should really be accepting const char pointers... */ return ui__question_window("Help", text, "Press any key...", 0);
return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
} }
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_list args;
va_start(args, format); va_start(args, format);
if (use_browser > 0) { key = __ui__warning("Warning", format, args);
pthread_mutex_lock(&ui__lock);
newtWinMessagev((char *)warning_str, (char *)ok,
(char *)format, args);
pthread_mutex_unlock(&ui__lock);
} else
vfprintf(stderr, format, args);
va_end(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;
} }

View File

@ -1,10 +1,14 @@
#ifndef _PERF_UI_UTIL_H_ #ifndef _PERF_UI_UTIL_H_
#define _PERF_UI_UTIL_H_ 1 #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__popup_menu(int argc, char * const argv[]);
int ui__help_window(const char *text); 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_ */ #endif /* _PERF_UI_UTIL_H_ */