* '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
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.
*/
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"
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 |
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);
}
/*
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);
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",
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));
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));
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));
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;
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;
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;
}
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)
}
static void perf_event__process_sample(const union perf_event *event,
+ struct perf_evsel *evsel,
struct perf_sample *sample,
struct perf_session *session)
{
}
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,
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;
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;
}
}
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;
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
}
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;
}
}
#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"
return 0;
}
-static inline struct ui_progress *ui_progress__new(const char *title __used,
- u64 total __used)
-{
- return (struct ui_progress *)1;
-}
-
-static inline void ui_progress__update(struct ui_progress *self __used,
- u64 curr __used) {}
+static inline void ui_progress__update(u64 curr __used, u64 total __used,
+ const char *title __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 */
{
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;
+}
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);
#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)
{
}
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;
}
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++) {
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);
}
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 {
};
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 */
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,
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 && \
#define _FILE_OFFSET_BITS 64
+#include "util.h"
#include <sys/types.h>
#include <byteswap.h>
#include <unistd.h>
#include "evlist.h"
#include "evsel.h"
-#include "util.h"
#include "header.h"
#include "../perf.h"
#include "trace-event.h"
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);
* been set by, say, the hist_browser.
*/
hists__apply_filters(hists, n);
- hists__inc_nr_entries(hists, n);
}
}
}
hists->entries = RB_ROOT;
hists->nr_entries = 0;
+ hists->stats.total_period = 0;
hists__reset_col_len(hists);
while (next) {
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;
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;
}
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",
.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,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
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)
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)) {
os->last_sample =
list_entry(head->prev, struct sample_queue, list);
}
+
+ os->nr_samples = 0;
}
/*
u64 timestamp = new->timestamp;
struct list_head *p;
+ ++os->nr_samples;
os->last_sample = new;
if (!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) {
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;
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) {
{
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;
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)
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)
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;
struct sample_queue *sample_buffer;
struct sample_queue *last_sample;
int sample_buffer_idx;
+ unsigned int nr_samples;
};
struct perf_session {
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;
int ret = errno;
if (errno)
- perror("trace-cmd");
+ perror("perf");
else
ret = -1;
#include "libslang.h"
#include <newt.h>
#include "ui.h"
+#include "util.h"
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/rbtree.h>
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;
(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)
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;
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);
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;
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);
+#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"
#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;
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;
}
#include "../browser.h"
#include "../helpline.h"
#include "../util.h"
+#include "../ui.h"
#include "map.h"
struct hist_browser {
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)
{
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 */
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;
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"
"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:
}
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':
if (choice == annotate) {
struct hist_entry *he;
+ int err;
do_annotate:
he = hist_browser__selected_entry(browser);
if (he == NULL)
* 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) {
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,
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;
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:
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':
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':
#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)
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);
void ui_helpline__fpush(const char *fmt, ...);
void ui_helpline__puts(const char *msg);
+extern char ui_helpline__current[];
+
#endif /* _PERF_UI_HELPLINE_H_ */
-#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);
}
#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
#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();
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();
}
}
#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_ */
-#include <newt.h>
+#include "../util.h"
#include <signal.h>
-#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#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);
-static newtComponent newt_form__new(void)
-{
- newtComponent self = newtForm(NULL, NULL, 0);
- if (self)
- newt_form__set_exit_keys(self);
- return self;
+ ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
+ HE_COLORSET_NORMAL);
+ slsmg_write_nstring(*arg, browser->width);
}
-int ui__popup_menu(int argc, char * const argv[])
+static int popup_menu__run(struct ui_browser *menu)
{
- struct newtExitStruct es;
- int i, rc = -1, max_len = 5;
- newtComponent listbox, form = newt_form__new();
+ int key;
- if (form == NULL)
+ if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
- listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
- if (listbox == NULL)
- goto out_destroy_form;
+ while (1) {
+ key = ui_browser__run(menu, 0);
- newtFormAddComponent(form, listbox);
+ 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;
+ }
- 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;
+ break;
}
- 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;
+ ui_browser__hide(menu);
+ return key;
}
-int ui__help_window(const char *text)
+int ui__popup_menu(int argc, char * const argv[])
{
- struct newtExitStruct es;
- newtComponent tb, form = newt_form__new();
- int rc = -1;
+ 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,
+ };
+
+ return popup_menu__run(&menu);
+}
+
+int ui__question_window(const char *title, const char *text,
+ const char *exit_msg, int delay_secs)
+{
+ 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');
t = sep + 1;
}
- tb = newtTextbox(0, 0, max_len, nr_lines, 0);
- if (tb == NULL)
- goto out_destroy_form;
-
- 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;
+ max_len += 2;
+ nr_lines += 4;
+ y = SLtt_Screen_Rows / 2 - nr_lines / 2,
+ x = SLtt_Screen_Cols / 2 - max_len / 2;
+
+ SLsmg_set_color(0);
+ SLsmg_draw_box(y, x++, nr_lines, max_len);
+ if (title) {
+ SLsmg_gotorc(y, x + 1);
+ SLsmg_write_string((char *)title);
+ }
+ SLsmg_gotorc(++y, x);
+ nr_lines -= 2;
+ max_len -= 2;
+ SLsmg_write_wrapped_string((unsigned char *)text, y, x,
+ nr_lines, max_len, 1);
+ SLsmg_gotorc(y + nr_lines - 2, x);
+ SLsmg_write_nstring((char *)" ", max_len);
+ SLsmg_gotorc(y + nr_lines - 1, x);
+ SLsmg_write_nstring((char *)exit_msg, max_len);
+ SLsmg_refresh();
+ return ui__getch(delay_secs);
}
-static const char yes[] = "Yes", no[] = "No",
- warning_str[] = "Warning!", ok[] = "Ok";
+int ui__help_window(const char *text)
+{
+ return ui__question_window("Help", text, "Press any key...", 0);
+}
-bool ui__dialog_yesno(const char *msg)
+int ui__dialog_yesno(const char *msg)
{
- /* newtWinChoice should really be accepting const char pointers... */
- return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
+ return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
-void ui__warning(const char *format, ...)
+int __ui__warning(const char *title, const char *format, va_list args)
{
- va_list args;
+ char *s;
+
+ if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
+ int key;
- va_start(args, format);
- if (use_browser > 0) {
pthread_mutex_lock(&ui__lock);
- newtWinMessagev((char *)warning_str, (char *)ok,
- (char *)format, args);
+ key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
- } else
- vfprintf(stderr, format, args);
+ 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);
+ 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;
}
#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_ */