提交 7588bada 编写于 作者: I Ingo Molnar

Merge branch 'perf/core' of git://github.com/acmel/linux into perf/core

...@@ -73,8 +73,7 @@ OPTIONS ...@@ -73,8 +73,7 @@ OPTIONS
CPUs. CPUs.
--asm-raw:: --asm-raw::
Show raw instruction encoding of assembly instructions. They Show raw instruction encoding of assembly instructions.
are displayed by default, disable with --no-asm-raw.
--source:: --source::
Interleave source code with assembly code. Enabled by default, Interleave source code with assembly code. Enabled by default,
......
...@@ -137,6 +137,21 @@ OPTIONS ...@@ -137,6 +137,21 @@ OPTIONS
-M:: -M::
--disassembler-style=:: Set disassembler style for objdump. --disassembler-style=:: Set disassembler style for objdump.
--source::
Interleave source code with assembly code. Enabled by default,
disable with --no-source.
--asm-raw::
Show raw instruction encoding of assembly instructions.
--show-total-period:: Show a column with the sum of periods.
-I::
--show-info::
Display extended information about the perf.data file. This adds
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1] linkperf:perf-stat[1], linkperf:perf-annotate[1]
...@@ -188,6 +188,13 @@ OPTIONS ...@@ -188,6 +188,13 @@ OPTIONS
CPUs are specified with -: 0-2. Default is to report samples on all CPUs are specified with -: 0-2. Default is to report samples on all
CPUs. CPUs.
-I::
--show-info::
Display extended information about the perf.data file. This adds
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
It can only be used with the perf script report mode.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-record[1], linkperf:perf-script-perl[1], linkperf:perf-record[1], linkperf:perf-script-perl[1],
......
...@@ -106,6 +106,51 @@ Default is to monitor all CPUS. ...@@ -106,6 +106,51 @@ Default is to monitor all CPUS.
--zero:: --zero::
Zero history across display updates. Zero history across display updates.
-s::
--sort::
Sort by key(s): pid, comm, dso, symbol, parent
-n::
--show-nr-samples::
Show a column with the number of samples.
--show-total-period::
Show a column with the sum of periods.
--dsos::
Only consider symbols in these dsos.
--comms::
Only consider symbols in these comms.
--symbols::
Only consider these symbols.
-M::
--disassembler-style=:: Set disassembler style for objdump.
--source::
Interleave source code with assembly code. Enabled by default,
disable with --no-source.
--asm-raw::
Show raw instruction encoding of assembly instructions.
-G [type,min,order]::
--call-graph::
Display call chains using type, min percent threshold and order.
type can be either:
- flat: single column, linear exposure of call chains.
- graph: use a graph tree, displaying absolute overhead rates.
- fractal: like graph, but displays relative rates. Each branch of
the tree is considered as a new profiled object.
order can be either:
- callee: callee based call graph.
- caller: inverted caller based call graph.
Default: fractal,0.5,callee.
INTERACTIVE PROMPTING KEYS INTERACTIVE PROMPTING KEYS
-------------------------- --------------------------
...@@ -130,9 +175,6 @@ INTERACTIVE PROMPTING KEYS ...@@ -130,9 +175,6 @@ INTERACTIVE PROMPTING KEYS
[S]:: [S]::
Stop annotation, return to full profile display. Stop annotation, return to full profile display.
[w]::
Toggle between weighted sum and individual count[E]r profile.
[z]:: [z]::
Toggle event count zeroing across display updates. Toggle event count zeroing across display updates.
......
...@@ -466,7 +466,6 @@ else ...@@ -466,7 +466,6 @@ else
LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o
LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o
LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_OBJS += $(OUTPUT)util/ui/progress.o
LIB_OBJS += $(OUTPUT)util/ui/util.o LIB_OBJS += $(OUTPUT)util/ui/util.o
...@@ -729,9 +728,6 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS ...@@ -729,9 +728,6 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
$(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
......
...@@ -2,3 +2,4 @@ ifndef NO_DWARF ...@@ -2,3 +2,4 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1 PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../util/header.h"
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define SPRN_PVR 0x11F /* Processor Version Register */
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
int
get_cpuid(char *buffer, size_t sz)
{
unsigned long pvr;
int nb;
pvr = mfspr(SPRN_PVR);
nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
buffer[nb-1] = '\0';
return 0;
}
return -1;
}
...@@ -2,3 +2,4 @@ ifndef NO_DWARF ...@@ -2,3 +2,4 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1 PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../util/header.h"
static inline void
cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
unsigned int *d)
{
__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
"movl %%ebx, %%esi\n\t.byte 0x5b"
: "=a" (*a),
"=S" (*b),
"=c" (*c),
"=d" (*d)
: "a" (op));
}
int
get_cpuid(char *buffer, size_t sz)
{
unsigned int a, b, c, d, lvl;
int family = -1, model = -1, step = -1;
int nb;
char vendor[16];
cpuid(0, &lvl, &b, &c, &d);
strncpy(&vendor[0], (char *)(&b), 4);
strncpy(&vendor[4], (char *)(&d), 4);
strncpy(&vendor[8], (char *)(&c), 4);
vendor[12] = '\0';
if (lvl >= 1) {
cpuid(1, &a, &b, &c, &d);
family = (a >> 8) & 0xf; /* bits 11 - 8 */
model = (a >> 4) & 0xf; /* Bits 7 - 4 */
step = a & 0xf;
/* extended family */
if (family == 0xf)
family += (a >> 20) & 0xff;
/* extended model */
if (family >= 0x6)
model += ((a >> 16) & 0xf) << 4;
}
nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
buffer[nb-1] = '\0';
return 0;
}
return -1;
}
...@@ -114,7 +114,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) ...@@ -114,7 +114,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
print_line, full_paths, 0, 0); print_line, full_paths, 0, 0);
} }
static void hists__find_annotations(struct hists *self, int evidx) static void hists__find_annotations(struct hists *self, int evidx,
int nr_events)
{ {
struct rb_node *nd = rb_first(&self->entries), *next; struct rb_node *nd = rb_first(&self->entries), *next;
int key = KEY_RIGHT; int key = KEY_RIGHT;
...@@ -137,7 +138,8 @@ static void hists__find_annotations(struct hists *self, int evidx) ...@@ -137,7 +138,8 @@ static void hists__find_annotations(struct hists *self, int evidx)
} }
if (use_browser > 0) { if (use_browser > 0) {
key = hist_entry__tui_annotate(he, evidx); key = hist_entry__tui_annotate(he, evidx, nr_events,
NULL, NULL, 0);
switch (key) { switch (key) {
case KEY_RIGHT: case KEY_RIGHT:
next = rb_next(nd); next = rb_next(nd);
...@@ -215,7 +217,8 @@ static int __cmd_annotate(void) ...@@ -215,7 +217,8 @@ static int __cmd_annotate(void)
total_nr_samples += nr_samples; total_nr_samples += nr_samples;
hists__collapse_resort(hists); hists__collapse_resort(hists);
hists__output_resort(hists); hists__output_resort(hists);
hists__find_annotations(hists, pos->idx); hists__find_annotations(hists, pos->idx,
session->evlist->nr_entries);
} }
} }
...@@ -269,9 +272,9 @@ static const struct option options[] = { ...@@ -269,9 +272,9 @@ static const struct option options[] = {
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"), "Look for files with symbols relative to this directory"),
OPT_BOOLEAN('0', "source", &symbol_conf.annotate_src, OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
"Interleave source code with assembly code (default)"), "Interleave source code with assembly code (default)"),
OPT_BOOLEAN('0', "asm-raw", &symbol_conf.annotate_asm_raw, OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
"Display raw encoding of assembly instructions (default)"), "Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
"Specify disassembler style (e.g. -M intel for intel syntax)"), "Specify disassembler style (e.g. -M intel for intel syntax)"),
......
...@@ -162,7 +162,7 @@ static int __cmd_diff(void) ...@@ -162,7 +162,7 @@ static int __cmd_diff(void)
hists__match(&session[0]->hists, &session[1]->hists); hists__match(&session[0]->hists, &session[1]->hists);
hists__fprintf(&session[1]->hists, &session[0]->hists, hists__fprintf(&session[1]->hists, &session[0]->hists,
show_displacement, stdout); show_displacement, true, 0, 0, stdout);
out_delete: out_delete:
for (i = 0; i < 2; ++i) for (i = 0; i < 2; ++i)
perf_session__delete(session[i]); perf_session__delete(session[i]);
......
...@@ -529,6 +529,19 @@ static int __cmd_record(int argc, const char **argv) ...@@ -529,6 +529,19 @@ static int __cmd_record(int argc, const char **argv)
if (have_tracepoints(&evsel_list->entries)) if (have_tracepoints(&evsel_list->entries))
perf_header__set_feat(&session->header, HEADER_TRACE_INFO); perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
perf_header__set_feat(&session->header, HEADER_HOSTNAME);
perf_header__set_feat(&session->header, HEADER_OSRELEASE);
perf_header__set_feat(&session->header, HEADER_ARCH);
perf_header__set_feat(&session->header, HEADER_CPUDESC);
perf_header__set_feat(&session->header, HEADER_NRCPUS);
perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
perf_header__set_feat(&session->header, HEADER_CMDLINE);
perf_header__set_feat(&session->header, HEADER_VERSION);
perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
perf_header__set_feat(&session->header, HEADER_CPUID);
/* 512 kiB: default amount of unprivileged mlocked memory */ /* 512 kiB: default amount of unprivileged mlocked memory */
if (mmap_pages == UINT_MAX) if (mmap_pages == UINT_MAX)
mmap_pages = (512 * 1024) / page_size; mmap_pages = (512 * 1024) / page_size;
...@@ -800,6 +813,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -800,6 +813,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
int err = -ENOMEM; int err = -ENOMEM;
struct perf_evsel *pos; struct perf_evsel *pos;
perf_header__set_cmdline(argc, argv);
evsel_list = perf_evlist__new(NULL, NULL); evsel_list = perf_evlist__new(NULL, NULL);
if (evsel_list == NULL) if (evsel_list == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -40,6 +40,7 @@ static char const *input_name = "perf.data"; ...@@ -40,6 +40,7 @@ static char const *input_name = "perf.data";
static bool force, use_tui, use_stdio; static bool force, use_tui, use_stdio;
static bool hide_unresolved; static bool hide_unresolved;
static bool dont_use_callchains; static bool dont_use_callchains;
static bool show_full_info;
static bool show_threads; static bool show_threads;
static struct perf_read_values show_threads_values; static struct perf_read_values show_threads_values;
...@@ -232,7 +233,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, ...@@ -232,7 +233,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
const char *evname = event_name(pos); const char *evname = event_name(pos);
hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, NULL, false, stdout); hists__fprintf(hists, NULL, false, true, 0, 0, stdout);
fprintf(stdout, "\n\n"); fprintf(stdout, "\n\n");
} }
...@@ -273,6 +274,9 @@ static int __cmd_report(void) ...@@ -273,6 +274,9 @@ static int __cmd_report(void)
goto out_delete; goto out_delete;
} }
if (use_browser <= 0)
perf_session__fprintf_info(session, stdout, show_full_info);
if (show_threads) if (show_threads)
perf_read_values_init(&show_threads_values); perf_read_values_init(&show_threads_values);
...@@ -327,9 +331,10 @@ static int __cmd_report(void) ...@@ -327,9 +331,10 @@ static int __cmd_report(void)
goto out_delete; goto out_delete;
} }
if (use_browser > 0) if (use_browser > 0) {
perf_evlist__tui_browse_hists(session->evlist, help); perf_evlist__tui_browse_hists(session->evlist, help,
else NULL, NULL, 0);
} else
perf_evlist__tty_browse_hists(session->evlist, help); perf_evlist__tty_browse_hists(session->evlist, help);
out_delete: out_delete:
...@@ -484,8 +489,16 @@ static const struct option options[] = { ...@@ -484,8 +489,16 @@ static const struct option options[] = {
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"), "Look for files with symbols relative to this directory"),
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_BOOLEAN('I', "show-info", &show_full_info,
"Display extended information about perf.data file"),
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
"Interleave source code with assembly code (default)"),
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
"Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
"Specify disassembler style (e.g. -M intel for intel syntax)"), "Specify disassembler style (e.g. -M intel for intel syntax)"),
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
"Show a column with the sum of periods"),
OPT_END() OPT_END()
}; };
......
...@@ -22,6 +22,7 @@ static u64 last_timestamp; ...@@ -22,6 +22,7 @@ static u64 last_timestamp;
static u64 nr_unordered; static u64 nr_unordered;
extern const struct option record_options[]; extern const struct option record_options[];
static bool no_callchain; static bool no_callchain;
static bool show_full_info;
static const char *cpu_list; static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
...@@ -1083,7 +1084,8 @@ static const struct option options[] = { ...@@ -1083,7 +1084,8 @@ static const struct option options[] = {
"comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
parse_output_fields), parse_output_fields),
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"),
OPT_END() OPT_END()
}; };
...@@ -1268,6 +1270,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) ...@@ -1268,6 +1270,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
return -1; return -1;
} }
perf_session__fprintf_info(session, stdout, show_full_info);
if (!no_callchain) if (!no_callchain)
symbol_conf.use_callchain = true; symbol_conf.use_callchain = true;
else else
......
此差异已折叠。
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "util/util.h" #include "util/util.h"
#include "util/strbuf.h" #include "util/strbuf.h"
extern const char perf_version_string[];
extern const char perf_usage_string[]; extern const char perf_usage_string[];
extern const char perf_more_info_string[]; extern const char perf_more_info_string[];
......
...@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws); ...@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
#include "../../arch/x86/include/asm/unistd.h" #include "../../arch/x86/include/asm/unistd.h"
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
#endif #endif
#if defined(__x86_64__) #if defined(__x86_64__)
#include "../../arch/x86/include/asm/unistd.h" #include "../../arch/x86/include/asm/unistd.h"
#define rmb() asm volatile("lfence" ::: "memory") #define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory"); #define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
#endif #endif
#ifdef __powerpc__ #ifdef __powerpc__
#include "../../arch/powerpc/include/asm/unistd.h" #include "../../arch/powerpc/include/asm/unistd.h"
#define rmb() asm volatile ("sync" ::: "memory") #define rmb() asm volatile ("sync" ::: "memory")
#define cpu_relax() asm volatile ("" ::: "memory"); #define cpu_relax() asm volatile ("" ::: "memory");
#define CPUINFO_PROC "cpu"
#endif #endif
#ifdef __s390__ #ifdef __s390__
...@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws); ...@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
# define rmb() asm volatile("" ::: "memory") # define rmb() asm volatile("" ::: "memory")
#endif #endif
#define cpu_relax() asm volatile("" ::: "memory") #define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu type"
#endif #endif
#ifdef __hppa__ #ifdef __hppa__
#include "../../arch/parisc/include/asm/unistd.h" #include "../../arch/parisc/include/asm/unistd.h"
#define rmb() asm volatile("" ::: "memory") #define rmb() asm volatile("" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory"); #define cpu_relax() asm volatile("" ::: "memory");
#define CPUINFO_PROC "cpu"
#endif #endif
#ifdef __sparc__ #ifdef __sparc__
#include "../../arch/sparc/include/asm/unistd.h" #include "../../arch/sparc/include/asm/unistd.h"
#define rmb() asm volatile("":::"memory") #define rmb() asm volatile("":::"memory")
#define cpu_relax() asm volatile("":::"memory") #define cpu_relax() asm volatile("":::"memory")
#define CPUINFO_PROC "cpu"
#endif #endif
#ifdef __alpha__ #ifdef __alpha__
#include "../../arch/alpha/include/asm/unistd.h" #include "../../arch/alpha/include/asm/unistd.h"
#define rmb() asm volatile("mb" ::: "memory") #define rmb() asm volatile("mb" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory") #define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu model"
#endif #endif
#ifdef __ia64__ #ifdef __ia64__
#include "../../arch/ia64/include/asm/unistd.h" #include "../../arch/ia64/include/asm/unistd.h"
#define rmb() asm volatile ("mf" ::: "memory") #define rmb() asm volatile ("mf" ::: "memory")
#define cpu_relax() asm volatile ("hint @pause" ::: "memory") #define cpu_relax() asm volatile ("hint @pause" ::: "memory")
#define CPUINFO_PROC "model name"
#endif #endif
#ifdef __arm__ #ifdef __arm__
...@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws); ...@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
*/ */
#define rmb() ((void(*)(void))0xffff0fa0)() #define rmb() ((void(*)(void))0xffff0fa0)()
#define cpu_relax() asm volatile("":::"memory") #define cpu_relax() asm volatile("":::"memory")
#define CPUINFO_PROC "Processor"
#endif #endif
#ifdef __mips__ #ifdef __mips__
...@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws); ...@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
: /* no input */ \ : /* no input */ \
: "memory") : "memory")
#define cpu_relax() asm volatile("" ::: "memory") #define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu model"
#endif #endif
#include <time.h> #include <time.h>
...@@ -171,5 +181,6 @@ struct ip_callchain { ...@@ -171,5 +181,6 @@ struct ip_callchain {
}; };
extern bool perf_host, perf_guest; extern bool perf_host, perf_guest;
extern const char perf_version_string[];
#endif #endif
...@@ -91,13 +91,16 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -91,13 +91,16 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
#ifdef NO_NEWT_SUPPORT #ifdef NO_NEWT_SUPPORT
static inline int symbol__tui_annotate(struct symbol *sym __used, static inline int symbol__tui_annotate(struct symbol *sym __used,
struct map *map __used, struct map *map __used,
int evidx __used, int refresh __used) int evidx __used,
void(*timer)(void *arg) __used,
void *arg __used, int delay_secs __used)
{ {
return 0; return 0;
} }
#else #else
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
int refresh); int nr_events, void(*timer)(void *arg), void *arg,
int delay_secs);
#endif #endif
extern const char *disassembler_style; extern const char *disassembler_style;
......
...@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) ...@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist)
first = list_entry(evlist->entries.next, struct perf_evsel, node); first = list_entry(evlist->entries.next, struct perf_evsel, node);
return first->attr.sample_id_all; return first->attr.sample_id_all;
} }
void perf_evlist__set_selected(struct perf_evlist *evlist,
struct perf_evsel *evsel)
{
evlist->selected = evsel;
}
...@@ -25,6 +25,7 @@ struct perf_evlist { ...@@ -25,6 +25,7 @@ struct perf_evlist {
struct pollfd *pollfd; struct pollfd *pollfd;
struct thread_map *threads; struct thread_map *threads;
struct cpu_map *cpus; struct cpu_map *cpus;
struct perf_evsel *selected;
}; };
struct perf_evsel; struct perf_evsel;
...@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist); ...@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist);
void perf_evlist__disable(struct perf_evlist *evlist); void perf_evlist__disable(struct perf_evlist *evlist);
void perf_evlist__enable(struct perf_evlist *evlist); void perf_evlist__enable(struct perf_evlist *evlist);
void perf_evlist__set_selected(struct perf_evlist *evlist,
struct perf_evsel *evsel);
static inline void perf_evlist__set_maps(struct perf_evlist *evlist, static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
struct cpu_map *cpus, struct cpu_map *cpus,
struct thread_map *threads) struct thread_map *threads)
......
...@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel, ...@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->idx = idx; evsel->idx = idx;
evsel->attr = *attr; evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node); INIT_LIST_HEAD(&evsel->node);
hists__init(&evsel->hists);
} }
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
......
此差异已折叠。
...@@ -12,6 +12,20 @@ ...@@ -12,6 +12,20 @@
enum { enum {
HEADER_TRACE_INFO = 1, HEADER_TRACE_INFO = 1,
HEADER_BUILD_ID, HEADER_BUILD_ID,
HEADER_HOSTNAME,
HEADER_OSRELEASE,
HEADER_VERSION,
HEADER_ARCH,
HEADER_NRCPUS,
HEADER_CPUDESC,
HEADER_CPUID,
HEADER_TOTAL_MEM,
HEADER_CMDLINE,
HEADER_EVENT_DESC,
HEADER_CPU_TOPOLOGY,
HEADER_NUMA_TOPOLOGY,
HEADER_LAST_FEATURE, HEADER_LAST_FEATURE,
}; };
...@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat); ...@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
void perf_header__clear_feat(struct perf_header *header, int feat); void perf_header__clear_feat(struct perf_header *header, int feat);
bool perf_header__has_feat(const struct perf_header *header, int feat); bool perf_header__has_feat(const struct perf_header *header, int feat);
int perf_header__set_cmdline(int argc, const char **argv);
int perf_header__process_sections(struct perf_header *header, int fd, int perf_header__process_sections(struct perf_header *header, int fd,
void *data,
int (*process)(struct perf_file_section *section, int (*process)(struct perf_file_section *section,
struct perf_header *ph, struct perf_header *ph,
int feat, int fd)); int feat, int fd, void *data));
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms); const char *name, bool is_kallsyms);
...@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc, ...@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
struct perf_session *session); struct perf_session *session);
int perf_event__process_build_id(union perf_event *event, int perf_event__process_build_id(union perf_event *event,
struct perf_session *session); struct perf_session *session);
/*
* arch specific callback
*/
int get_cpuid(char *buffer, size_t sz);
#endif /* __PERF_HEADER_H */ #endif /* __PERF_HEADER_H */
...@@ -18,56 +18,56 @@ struct callchain_param callchain_param = { ...@@ -18,56 +18,56 @@ struct callchain_param callchain_param = {
.order = ORDER_CALLEE .order = ORDER_CALLEE
}; };
u16 hists__col_len(struct hists *self, enum hist_column col) u16 hists__col_len(struct hists *hists, enum hist_column col)
{ {
return self->col_len[col]; return hists->col_len[col];
} }
void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
{ {
self->col_len[col] = len; hists->col_len[col] = len;
} }
bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
{ {
if (len > hists__col_len(self, col)) { if (len > hists__col_len(hists, col)) {
hists__set_col_len(self, col, len); hists__set_col_len(hists, col, len);
return true; return true;
} }
return false; return false;
} }
static void hists__reset_col_len(struct hists *self) static void hists__reset_col_len(struct hists *hists)
{ {
enum hist_column col; enum hist_column col;
for (col = 0; col < HISTC_NR_COLS; ++col) for (col = 0; col < HISTC_NR_COLS; ++col)
hists__set_col_len(self, col, 0); hists__set_col_len(hists, col, 0);
} }
static void hists__calc_col_len(struct hists *self, struct hist_entry *h) static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
{ {
u16 len; u16 len;
if (h->ms.sym) if (h->ms.sym)
hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
else { else {
const unsigned int unresolved_col_width = BITS_PER_LONG / 4; const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
!symbol_conf.col_width_list_str && !symbol_conf.field_sep && !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
!symbol_conf.dso_list) !symbol_conf.dso_list)
hists__set_col_len(self, HISTC_DSO, hists__set_col_len(hists, HISTC_DSO,
unresolved_col_width); unresolved_col_width);
} }
len = thread__comm_len(h->thread); len = thread__comm_len(h->thread);
if (hists__new_col_len(self, HISTC_COMM, len)) if (hists__new_col_len(hists, HISTC_COMM, len))
hists__set_col_len(self, HISTC_THREAD, len + 6); hists__set_col_len(hists, HISTC_THREAD, len + 6);
if (h->ms.map) { if (h->ms.map) {
len = dso__name_len(h->ms.map->dso); len = dso__name_len(h->ms.map->dso);
hists__new_col_len(self, HISTC_DSO, len); hists__new_col_len(hists, HISTC_DSO, len);
} }
} }
...@@ -92,6 +92,41 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, ...@@ -92,6 +92,41 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
} }
} }
static void hist_entry__decay(struct hist_entry *he)
{
he->period = (he->period * 7) / 8;
he->nr_events = (he->nr_events * 7) / 8;
}
static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
{
hists->stats.total_period -= he->period;
hist_entry__decay(he);
hists->stats.total_period += he->period;
return he->period == 0;
}
void hists__decay_entries(struct hists *hists)
{
struct rb_node *next = rb_first(&hists->entries);
struct hist_entry *n;
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node);
if (hists__decay_entry(hists, n)) {
rb_erase(&n->rb_node, &hists->entries);
if (sort__need_collapse)
rb_erase(&n->rb_node_in, &hists->entries_collapsed);
hist_entry__free(n);
--hists->nr_entries;
}
}
}
/* /*
* histogram, sorted on item, collects periods * histogram, sorted on item, collects periods
*/ */
...@@ -113,11 +148,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) ...@@ -113,11 +148,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
return self; return self;
} }
static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
{ {
if (!h->filtered) { if (!h->filtered) {
hists__calc_col_len(self, h); hists__calc_col_len(hists, h);
++self->nr_entries; ++hists->nr_entries;
hists->stats.total_period += h->period;
} }
} }
...@@ -128,11 +164,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) ...@@ -128,11 +164,11 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0; return 0;
} }
struct hist_entry *__hists__add_entry(struct hists *self, struct hist_entry *__hists__add_entry(struct hists *hists,
struct addr_location *al, struct addr_location *al,
struct symbol *sym_parent, u64 period) struct symbol *sym_parent, u64 period)
{ {
struct rb_node **p = &self->entries.rb_node; struct rb_node **p;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct hist_entry *he; struct hist_entry *he;
struct hist_entry entry = { struct hist_entry entry = {
...@@ -150,9 +186,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, ...@@ -150,9 +186,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
}; };
int cmp; int cmp;
pthread_mutex_lock(&hists->lock);
p = &hists->entries_in->rb_node;
while (*p != NULL) { while (*p != NULL) {
parent = *p; parent = *p;
he = rb_entry(parent, struct hist_entry, rb_node); he = rb_entry(parent, struct hist_entry, rb_node_in);
cmp = hist_entry__cmp(&entry, he); cmp = hist_entry__cmp(&entry, he);
...@@ -170,12 +210,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, ...@@ -170,12 +210,14 @@ struct hist_entry *__hists__add_entry(struct hists *self,
he = hist_entry__new(&entry); he = hist_entry__new(&entry);
if (!he) if (!he)
return NULL; goto out_unlock;
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, &self->entries); rb_link_node(&he->rb_node_in, parent, p);
hists__inc_nr_entries(self, he); rb_insert_color(&he->rb_node_in, hists->entries_in);
out: out:
hist_entry__add_cpumode_period(he, al->cpumode, period); hist_entry__add_cpumode_period(he, al->cpumode, period);
out_unlock:
pthread_mutex_unlock(&hists->lock);
return he; return he;
} }
...@@ -222,7 +264,7 @@ void hist_entry__free(struct hist_entry *he) ...@@ -222,7 +264,7 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram * collapse the histogram
*/ */
static bool hists__collapse_insert_entry(struct hists *self, static bool hists__collapse_insert_entry(struct hists *hists,
struct rb_root *root, struct rb_root *root,
struct hist_entry *he) struct hist_entry *he)
{ {
...@@ -233,15 +275,16 @@ static bool hists__collapse_insert_entry(struct hists *self, ...@@ -233,15 +275,16 @@ static bool hists__collapse_insert_entry(struct hists *self,
while (*p != NULL) { while (*p != NULL) {
parent = *p; parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node); iter = rb_entry(parent, struct hist_entry, rb_node_in);
cmp = hist_entry__collapse(iter, he); cmp = hist_entry__collapse(iter, he);
if (!cmp) { if (!cmp) {
iter->period += he->period; iter->period += he->period;
iter->nr_events += he->nr_events;
if (symbol_conf.use_callchain) { if (symbol_conf.use_callchain) {
callchain_cursor_reset(&self->callchain_cursor); callchain_cursor_reset(&hists->callchain_cursor);
callchain_merge(&self->callchain_cursor, iter->callchain, callchain_merge(&hists->callchain_cursor, iter->callchain,
he->callchain); he->callchain);
} }
hist_entry__free(he); hist_entry__free(he);
...@@ -254,35 +297,57 @@ static bool hists__collapse_insert_entry(struct hists *self, ...@@ -254,35 +297,57 @@ static bool hists__collapse_insert_entry(struct hists *self,
p = &(*p)->rb_right; p = &(*p)->rb_right;
} }
rb_link_node(&he->rb_node, parent, p); rb_link_node(&he->rb_node_in, parent, p);
rb_insert_color(&he->rb_node, root); rb_insert_color(&he->rb_node_in, root);
return true; return true;
} }
void hists__collapse_resort(struct hists *self) static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
{
struct rb_root *root;
pthread_mutex_lock(&hists->lock);
root = hists->entries_in;
if (++hists->entries_in > &hists->entries_in_array[1])
hists->entries_in = &hists->entries_in_array[0];
pthread_mutex_unlock(&hists->lock);
return root;
}
static void __hists__collapse_resort(struct hists *hists, bool threaded)
{ {
struct rb_root tmp; struct rb_root *root;
struct rb_node *next; struct rb_node *next;
struct hist_entry *n; struct hist_entry *n;
if (!sort__need_collapse) if (!sort__need_collapse && !threaded)
return; return;
tmp = RB_ROOT; root = hists__get_rotate_entries_in(hists);
next = rb_first(&self->entries); next = rb_first(root);
self->nr_entries = 0; hists->stats.total_period = 0;
hists__reset_col_len(self);
while (next) { while (next) {
n = rb_entry(next, struct hist_entry, rb_node); n = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&n->rb_node); next = rb_next(&n->rb_node_in);
rb_erase(&n->rb_node, &self->entries); rb_erase(&n->rb_node_in, root);
if (hists__collapse_insert_entry(self, &tmp, n)) if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n))
hists__inc_nr_entries(self, n); hists__inc_nr_entries(hists, n);
} }
}
self->entries = tmp; void hists__collapse_resort(struct hists *hists)
{
return __hists__collapse_resort(hists, false);
}
void hists__collapse_resort_threaded(struct hists *hists)
{
return __hists__collapse_resort(hists, true);
} }
/* /*
...@@ -315,31 +380,43 @@ static void __hists__insert_output_entry(struct rb_root *entries, ...@@ -315,31 +380,43 @@ static void __hists__insert_output_entry(struct rb_root *entries,
rb_insert_color(&he->rb_node, entries); rb_insert_color(&he->rb_node, entries);
} }
void hists__output_resort(struct hists *self) static void __hists__output_resort(struct hists *hists, bool threaded)
{ {
struct rb_root tmp; struct rb_root *root;
struct rb_node *next; struct rb_node *next;
struct hist_entry *n; struct hist_entry *n;
u64 min_callchain_hits; u64 min_callchain_hits;
min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);
if (sort__need_collapse || threaded)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
tmp = RB_ROOT; next = rb_first(root);
next = rb_first(&self->entries); hists->entries = RB_ROOT;
self->nr_entries = 0; hists->nr_entries = 0;
hists__reset_col_len(self); hists__reset_col_len(hists);
while (next) { while (next) {
n = rb_entry(next, struct hist_entry, rb_node); n = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&n->rb_node); next = rb_next(&n->rb_node_in);
rb_erase(&n->rb_node, &self->entries); __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
__hists__insert_output_entry(&tmp, n, min_callchain_hits); hists__inc_nr_entries(hists, n);
hists__inc_nr_entries(self, n);
} }
}
void hists__output_resort(struct hists *hists)
{
return __hists__output_resort(hists, false);
}
self->entries = tmp; void hists__output_resort_threaded(struct hists *hists)
{
return __hists__output_resort(hists, true);
} }
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
...@@ -594,6 +671,21 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, ...@@ -594,6 +671,21 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
return ret; return ret;
} }
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
{
struct rb_node *next = rb_first(&hists->entries);
struct hist_entry *n;
int row = 0;
hists__reset_col_len(hists);
while (next && row++ < max_rows) {
n = rb_entry(next, struct hist_entry, rb_node);
hists__calc_col_len(hists, n);
next = rb_next(&n->rb_node);
}
}
int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
struct hists *hists, struct hists *pair_hists, struct hists *hists, struct hists *pair_hists,
bool show_displacement, long displacement, bool show_displacement, long displacement,
...@@ -664,6 +756,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ...@@ -664,6 +756,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
} }
if (symbol_conf.show_total_period) {
if (sep)
ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
else
ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
}
if (pair_hists) { if (pair_hists) {
char bf[32]; char bf[32];
double old_percent = 0, new_percent = 0, diff; double old_percent = 0, new_percent = 0, diff;
...@@ -710,12 +809,16 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ...@@ -710,12 +809,16 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
return ret; return ret;
} }
int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
struct hists *pair_hists, bool show_displacement, struct hists *pair_hists, bool show_displacement,
long displacement, FILE *fp, u64 session_total) long displacement, FILE *fp, u64 session_total)
{ {
char bf[512]; char bf[512];
hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
if (size == 0 || size > sizeof(bf))
size = sizeof(bf);
hist_entry__snprintf(he, bf, size, hists, pair_hists,
show_displacement, displacement, show_displacement, displacement,
true, session_total); true, session_total);
return fprintf(fp, "%s\n", bf); return fprintf(fp, "%s\n", bf);
...@@ -738,8 +841,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, ...@@ -738,8 +841,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
left_margin); left_margin);
} }
size_t hists__fprintf(struct hists *self, struct hists *pair, size_t hists__fprintf(struct hists *hists, struct hists *pair,
bool show_displacement, FILE *fp) bool show_displacement, bool show_header, int max_rows,
int max_cols, FILE *fp)
{ {
struct sort_entry *se; struct sort_entry *se;
struct rb_node *nd; struct rb_node *nd;
...@@ -749,9 +853,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -749,9 +853,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
unsigned int width; unsigned int width;
const char *sep = symbol_conf.field_sep; const char *sep = symbol_conf.field_sep;
const char *col_width = symbol_conf.col_width_list_str; const char *col_width = symbol_conf.col_width_list_str;
int nr_rows = 0;
init_rem_hits(); init_rem_hits();
if (!show_header)
goto print_entries;
fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
if (symbol_conf.show_nr_samples) { if (symbol_conf.show_nr_samples) {
...@@ -761,6 +869,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -761,6 +869,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
fputs(" Samples ", fp); fputs(" Samples ", fp);
} }
if (symbol_conf.show_total_period) {
if (sep)
ret += fprintf(fp, "%cPeriod", *sep);
else
ret += fprintf(fp, " Period ");
}
if (symbol_conf.show_cpu_utilization) { if (symbol_conf.show_cpu_utilization) {
if (sep) { if (sep) {
ret += fprintf(fp, "%csys", *sep); ret += fprintf(fp, "%csys", *sep);
...@@ -803,18 +918,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -803,18 +918,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
width = strlen(se->se_header); width = strlen(se->se_header);
if (symbol_conf.col_width_list_str) { if (symbol_conf.col_width_list_str) {
if (col_width) { if (col_width) {
hists__set_col_len(self, se->se_width_idx, hists__set_col_len(hists, se->se_width_idx,
atoi(col_width)); atoi(col_width));
col_width = strchr(col_width, ','); col_width = strchr(col_width, ',');
if (col_width) if (col_width)
++col_width; ++col_width;
} }
} }
if (!hists__new_col_len(self, se->se_width_idx, width)) if (!hists__new_col_len(hists, se->se_width_idx, width))
width = hists__col_len(self, se->se_width_idx); width = hists__col_len(hists, se->se_width_idx);
fprintf(fp, " %*s", width, se->se_header); fprintf(fp, " %*s", width, se->se_header);
} }
fprintf(fp, "\n"); fprintf(fp, "\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
if (sep) if (sep)
goto print_entries; goto print_entries;
...@@ -822,6 +940,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -822,6 +940,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
fprintf(fp, "# ........"); fprintf(fp, "# ........");
if (symbol_conf.show_nr_samples) if (symbol_conf.show_nr_samples)
fprintf(fp, " .........."); fprintf(fp, " ..........");
if (symbol_conf.show_total_period)
fprintf(fp, " ............");
if (pair) { if (pair) {
fprintf(fp, " .........."); fprintf(fp, " ..........");
if (show_displacement) if (show_displacement)
...@@ -834,17 +954,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -834,17 +954,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
continue; continue;
fprintf(fp, " "); fprintf(fp, " ");
width = hists__col_len(self, se->se_width_idx); width = hists__col_len(hists, se->se_width_idx);
if (width == 0) if (width == 0)
width = strlen(se->se_header); width = strlen(se->se_header);
for (i = 0; i < width; i++) for (i = 0; i < width; i++)
fprintf(fp, "."); fprintf(fp, ".");
} }
fprintf(fp, "\n#\n"); fprintf(fp, "\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
fprintf(fp, "#\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
print_entries: print_entries:
for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
if (h->filtered) if (h->filtered)
...@@ -858,19 +984,22 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -858,19 +984,22 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
displacement = 0; displacement = 0;
++position; ++position;
} }
ret += hist_entry__fprintf(h, self, pair, show_displacement, ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
displacement, fp, self->stats.total_period); displacement, fp, hists->stats.total_period);
if (symbol_conf.use_callchain) if (symbol_conf.use_callchain)
ret += hist_entry__fprintf_callchain(h, self, fp, ret += hist_entry__fprintf_callchain(h, hists, fp,
self->stats.total_period); hists->stats.total_period);
if (max_rows && ++nr_rows >= max_rows)
goto out;
if (h->ms.map == NULL && verbose > 1) { if (h->ms.map == NULL && verbose > 1) {
__map_groups__fprintf_maps(&h->thread->mg, __map_groups__fprintf_maps(&h->thread->mg,
MAP__FUNCTION, verbose, fp); MAP__FUNCTION, verbose, fp);
fprintf(fp, "%.10s end\n", graph_dotted_line); fprintf(fp, "%.10s end\n", graph_dotted_line);
} }
} }
out:
free(rem_sq_bracket); free(rem_sq_bracket);
return ret; return ret;
...@@ -879,7 +1008,7 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, ...@@ -879,7 +1008,7 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
/* /*
* See hists__fprintf to match the column widths * See hists__fprintf to match the column widths
*/ */
unsigned int hists__sort_list_width(struct hists *self) unsigned int hists__sort_list_width(struct hists *hists)
{ {
struct sort_entry *se; struct sort_entry *se;
int ret = 9; /* total % */ int ret = 9; /* total % */
...@@ -896,9 +1025,12 @@ unsigned int hists__sort_list_width(struct hists *self) ...@@ -896,9 +1025,12 @@ unsigned int hists__sort_list_width(struct hists *self)
if (symbol_conf.show_nr_samples) if (symbol_conf.show_nr_samples)
ret += 11; ret += 11;
if (symbol_conf.show_total_period)
ret += 13;
list_for_each_entry(se, &hist_entry__sort_list, list) list_for_each_entry(se, &hist_entry__sort_list, list)
if (!se->elide) if (!se->elide)
ret += 2 + hists__col_len(self, se->se_width_idx); ret += 2 + hists__col_len(hists, se->se_width_idx);
if (verbose) /* Addr + origin */ if (verbose) /* Addr + origin */
ret += 3 + BITS_PER_LONG / 4; ret += 3 + BITS_PER_LONG / 4;
...@@ -906,32 +1038,32 @@ unsigned int hists__sort_list_width(struct hists *self) ...@@ -906,32 +1038,32 @@ unsigned int hists__sort_list_width(struct hists *self)
return ret; return ret;
} }
static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
enum hist_filter filter) enum hist_filter filter)
{ {
h->filtered &= ~(1 << filter); h->filtered &= ~(1 << filter);
if (h->filtered) if (h->filtered)
return; return;
++self->nr_entries; ++hists->nr_entries;
if (h->ms.unfolded) if (h->ms.unfolded)
self->nr_entries += h->nr_rows; hists->nr_entries += h->nr_rows;
h->row_offset = 0; h->row_offset = 0;
self->stats.total_period += h->period; hists->stats.total_period += h->period;
self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
hists__calc_col_len(self, h); hists__calc_col_len(hists, h);
} }
void hists__filter_by_dso(struct hists *self, const struct dso *dso) void hists__filter_by_dso(struct hists *hists, const struct dso *dso)
{ {
struct rb_node *nd; struct rb_node *nd;
self->nr_entries = self->stats.total_period = 0; hists->nr_entries = hists->stats.total_period = 0;
self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
hists__reset_col_len(self); hists__reset_col_len(hists);
for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
if (symbol_conf.exclude_other && !h->parent) if (symbol_conf.exclude_other && !h->parent)
...@@ -942,19 +1074,19 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) ...@@ -942,19 +1074,19 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
continue; continue;
} }
hists__remove_entry_filter(self, h, HIST_FILTER__DSO); hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
} }
} }
void hists__filter_by_thread(struct hists *self, const struct thread *thread) void hists__filter_by_thread(struct hists *hists, const struct thread *thread)
{ {
struct rb_node *nd; struct rb_node *nd;
self->nr_entries = self->stats.total_period = 0; hists->nr_entries = hists->stats.total_period = 0;
self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
hists__reset_col_len(self); hists__reset_col_len(hists);
for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
if (thread != NULL && h->thread != thread) { if (thread != NULL && h->thread != thread) {
...@@ -962,7 +1094,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) ...@@ -962,7 +1094,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
continue; continue;
} }
hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
} }
} }
...@@ -976,13 +1108,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) ...@@ -976,13 +1108,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
return symbol__annotate(he->ms.sym, he->ms.map, privsize); return symbol__annotate(he->ms.sym, he->ms.map, privsize);
} }
void hists__inc_nr_events(struct hists *self, u32 type) void hists__inc_nr_events(struct hists *hists, u32 type)
{ {
++self->stats.nr_events[0]; ++hists->stats.nr_events[0];
++self->stats.nr_events[type]; ++hists->stats.nr_events[type];
} }
size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
{ {
int i; int i;
size_t ret = 0; size_t ret = 0;
...@@ -990,7 +1122,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) ...@@ -990,7 +1122,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
const char *name; const char *name;
if (self->stats.nr_events[i] == 0) if (hists->stats.nr_events[i] == 0)
continue; continue;
name = perf_event__name(i); name = perf_event__name(i);
...@@ -998,8 +1130,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) ...@@ -998,8 +1130,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
continue; continue;
ret += fprintf(fp, "%16s events: %10d\n", name, ret += fprintf(fp, "%16s events: %10d\n", name,
self->stats.nr_events[i]); hists->stats.nr_events[i]);
} }
return ret; return ret;
} }
void hists__init(struct hists *hists)
{
memset(hists, 0, sizeof(*hists));
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
hists->entries_in = &hists->entries_in_array[0];
hists->entries_collapsed = RB_ROOT;
hists->entries = RB_ROOT;
pthread_mutex_init(&hists->lock, NULL);
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define __PERF_HIST_H #define __PERF_HIST_H
#include <linux/types.h> #include <linux/types.h>
#include <pthread.h>
#include "callchain.h" #include "callchain.h"
extern struct callchain_param callchain_param; extern struct callchain_param callchain_param;
...@@ -43,8 +44,12 @@ enum hist_column { ...@@ -43,8 +44,12 @@ enum hist_column {
}; };
struct hists { struct hists {
struct rb_root entries_in_array[2];
struct rb_root *entries_in;
struct rb_root entries; struct rb_root entries;
struct rb_root entries_collapsed;
u64 nr_entries; u64 nr_entries;
pthread_mutex_t lock;
struct events_stats stats; struct events_stats stats;
u64 event_stream; u64 event_stream;
u16 col_len[HISTC_NR_COLS]; u16 col_len[HISTC_NR_COLS];
...@@ -52,14 +57,16 @@ struct hists { ...@@ -52,14 +57,16 @@ struct hists {
struct callchain_cursor callchain_cursor; struct callchain_cursor callchain_cursor;
}; };
void hists__init(struct hists *hists);
struct hist_entry *__hists__add_entry(struct hists *self, struct hist_entry *__hists__add_entry(struct hists *self,
struct addr_location *al, struct addr_location *al,
struct symbol *parent, u64 period); struct symbol *parent, u64 period);
extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
struct hists *pair_hists, bool show_displacement, struct hists *pair_hists, bool show_displacement,
long displacement, FILE *fp, u64 total); long displacement, FILE *fp, u64 session_total);
int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
struct hists *hists, struct hists *pair_hists, struct hists *hists, struct hists *pair_hists,
bool show_displacement, long displacement, bool show_displacement, long displacement,
...@@ -67,13 +74,19 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, ...@@ -67,13 +74,19 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
void hist_entry__free(struct hist_entry *); void hist_entry__free(struct hist_entry *);
void hists__output_resort(struct hists *self); void hists__output_resort(struct hists *self);
void hists__output_resort_threaded(struct hists *hists);
void hists__collapse_resort(struct hists *self); void hists__collapse_resort(struct hists *self);
void hists__collapse_resort_threaded(struct hists *hists);
void hists__decay_entries(struct hists *hists);
void hists__output_recalc_col_len(struct hists *hists, int max_rows);
void hists__inc_nr_events(struct hists *self, u32 type); void hists__inc_nr_events(struct hists *self, u32 type);
size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
size_t hists__fprintf(struct hists *self, struct hists *pair, size_t hists__fprintf(struct hists *self, struct hists *pair,
bool show_displacement, FILE *fp); bool show_displacement, bool show_header,
int max_rows, int max_cols, FILE *fp);
int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr);
int hist_entry__annotate(struct hist_entry *self, size_t privsize); int hist_entry__annotate(struct hist_entry *self, size_t privsize);
...@@ -90,13 +103,16 @@ struct perf_evlist; ...@@ -90,13 +103,16 @@ struct perf_evlist;
#ifdef NO_NEWT_SUPPORT #ifdef NO_NEWT_SUPPORT
static inline static inline
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used,
const char *help __used) const char *help __used, void(*timer)(void *arg) __used, void *arg,
int refresh __used)
{ {
return 0; return 0;
} }
static inline int hist_entry__tui_annotate(struct hist_entry *self __used, static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
int evidx __used) int evidx __used, int nr_events __used,
void(*timer)(void *arg) __used,
void *arg __used, int delay_secs __used);
{ {
return 0; return 0;
} }
...@@ -104,12 +120,15 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used, ...@@ -104,12 +120,15 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
#define KEY_RIGHT -2 #define KEY_RIGHT -2
#else #else
#include <newt.h> #include <newt.h>
int hist_entry__tui_annotate(struct hist_entry *self, int evidx); int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
void(*timer)(void *arg), void *arg, int delay_secs);
#define KEY_LEFT NEWT_KEY_LEFT #define KEY_LEFT NEWT_KEY_LEFT
#define KEY_RIGHT NEWT_KEY_RIGHT #define KEY_RIGHT NEWT_KEY_RIGHT
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
void(*timer)(void *arg), void *arg,
int refresh);
#endif #endif
unsigned int hists__sort_list_width(struct hists *self); unsigned int hists__sort_list_width(struct hists *self);
......
...@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session, ...@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session,
return 0; return 0;
} }
void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
bool full)
{
struct stat st;
int ret;
if (session == NULL || fp == NULL)
return;
ret = fstat(session->fd, &st);
if (ret == -1)
return;
fprintf(fp, "# ========\n");
fprintf(fp, "# captured on: %s", ctime(&st.st_ctime));
perf_header__fprintf_info(session, fp, full);
fprintf(fp, "# ========\n#\n");
}
...@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event, ...@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event,
int perf_session__cpu_bitmap(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap); const char *cpu_list, unsigned long *cpu_bitmap);
void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full);
#endif /* __PERF_SESSION_H */ #endif /* __PERF_SESSION_H */
...@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension; ...@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension;
* @nr_rows - rows expanded in callchain, recalculated on folding/unfolding * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
*/ */
struct hist_entry { struct hist_entry {
struct rb_node rb_node_in;
struct rb_node rb_node; struct rb_node rb_node;
u64 period; u64 period;
u64 period_sys; u64 period_sys;
......
...@@ -46,7 +46,6 @@ struct symbol_conf symbol_conf = { ...@@ -46,7 +46,6 @@ struct symbol_conf symbol_conf = {
.exclude_other = true, .exclude_other = true,
.use_modules = true, .use_modules = true,
.try_vmlinux_path = true, .try_vmlinux_path = true,
.annotate_asm_raw = true,
.annotate_src = true, .annotate_src = true,
.symfs = "", .symfs = "",
}; };
......
...@@ -72,6 +72,7 @@ struct symbol_conf { ...@@ -72,6 +72,7 @@ struct symbol_conf {
use_modules, use_modules,
sort_by_name, sort_by_name,
show_nr_samples, show_nr_samples,
show_total_period,
use_callchain, use_callchain,
exclude_other, exclude_other,
show_cpu_utilization, show_cpu_utilization,
......
...@@ -15,52 +15,6 @@ ...@@ -15,52 +15,6 @@
#include "top.h" #include "top.h"
#include <inttypes.h> #include <inttypes.h>
/*
* Ordering weight: count-1 * count-2 * ... / count-n
*/
static double sym_weight(const struct sym_entry *sym, struct perf_top *top)
{
double weight = sym->snap_count;
int counter;
if (!top->display_weighted)
return weight;
for (counter = 1; counter < top->evlist->nr_entries - 1; counter++)
weight *= sym->count[counter];
weight /= (sym->count[counter] + 1);
return weight;
}
static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme)
{
pthread_mutex_lock(&top->active_symbols_lock);
list_del_init(&syme->node);
pthread_mutex_unlock(&top->active_symbols_lock);
}
static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
{
struct rb_node **p = &tree->rb_node;
struct rb_node *parent = NULL;
struct sym_entry *iter;
while (*p != NULL) {
parent = *p;
iter = rb_entry(parent, struct sym_entry, rb_node);
if (se->weight > iter->weight)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&se->rb_node, parent, p);
rb_insert_color(&se->rb_node, tree);
}
#define SNPRINTF(buf, size, fmt, args...) \ #define SNPRINTF(buf, size, fmt, args...) \
({ \ ({ \
size_t r = snprintf(buf, size, fmt, ## args); \ size_t r = snprintf(buf, size, fmt, ## args); \
...@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) ...@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
{ {
struct perf_evsel *counter;
float samples_per_sec = top->samples / top->delay_secs; float samples_per_sec = top->samples / top->delay_secs;
float ksamples_per_sec = top->kernel_samples / top->delay_secs; float ksamples_per_sec = top->kernel_samples / top->delay_secs;
float esamples_percent = (100.0 * top->exact_samples) / top->samples; float esamples_percent = (100.0 * top->exact_samples) / top->samples;
...@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ...@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
esamples_percent); esamples_percent);
} }
if (top->evlist->nr_entries == 1 || !top->display_weighted) { if (top->evlist->nr_entries == 1) {
struct perf_evsel *first; struct perf_evsel *first;
first = list_entry(top->evlist->entries.next, struct perf_evsel, node); first = list_entry(top->evlist->entries.next, struct perf_evsel, node);
ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ",
...@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ...@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
top->freq ? "Hz" : ""); top->freq ? "Hz" : "");
} }
if (!top->display_weighted) { ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel));
ret += SNPRINTF(bf + ret, size - ret, "%s",
event_name(top->sym_evsel));
} else {
/*
* Don't let events eat all the space. Leaving 30 bytes
* for the rest should be enough.
*/
size_t last_pos = size - 30;
list_for_each_entry(counter, &top->evlist->entries, node) {
ret += SNPRINTF(bf + ret, size - ret, "%s%s",
counter->idx ? "/" : "",
event_name(counter));
if (ret > last_pos) {
sprintf(bf + last_pos - 3, "..");
ret = last_pos - 1;
break;
}
}
}
ret += SNPRINTF(bf + ret, size - ret, "], "); ret += SNPRINTF(bf + ret, size - ret, "], ");
...@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top) ...@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top)
top->exact_samples = top->guest_kernel_samples = top->exact_samples = top->guest_kernel_samples =
top->guest_us_samples = 0; top->guest_us_samples = 0;
} }
float perf_top__decay_samples(struct perf_top *top, struct rb_root *root)
{
struct sym_entry *syme, *n;
float sum_ksamples = 0.0;
int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j;
/* Sort the active symbols */
pthread_mutex_lock(&top->active_symbols_lock);
syme = list_entry(top->active_symbols.next, struct sym_entry, node);
pthread_mutex_unlock(&top->active_symbols_lock);
top->rb_entries = 0;
list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) {
syme->snap_count = syme->count[snap];
if (syme->snap_count != 0) {
if ((top->hide_user_symbols &&
syme->map->dso->kernel == DSO_TYPE_USER) ||
(top->hide_kernel_symbols &&
syme->map->dso->kernel == DSO_TYPE_KERNEL)) {
perf_top__remove_active_sym(top, syme);
continue;
}
syme->weight = sym_weight(syme, top);
if ((int)syme->snap_count >= top->count_filter) {
rb_insert_active_sym(root, syme);
++top->rb_entries;
}
sum_ksamples += syme->snap_count;
for (j = 0; j < top->evlist->nr_entries; j++)
syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8;
} else
perf_top__remove_active_sym(top, syme);
}
return sum_ksamples;
}
/*
* Find the longest symbol name that will be displayed
*/
void perf_top__find_widths(struct perf_top *top, struct rb_root *root,
int *dso_width, int *dso_short_width, int *sym_width)
{
struct rb_node *nd;
int printed = 0;
*sym_width = *dso_width = *dso_short_width = 0;
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node);
struct symbol *sym = sym_entry__symbol(syme);
if (++printed > top->print_entries ||
(int)syme->snap_count < top->count_filter)
continue;
if (syme->map->dso->long_name_len > *dso_width)
*dso_width = syme->map->dso->long_name_len;
if (syme->map->dso->short_name_len > *dso_short_width)
*dso_short_width = syme->map->dso->short_name_len;
if (sym->namelen > *sym_width)
*sym_width = sym->namelen;
}
}
...@@ -4,64 +4,32 @@ ...@@ -4,64 +4,32 @@
#include "types.h" #include "types.h"
#include "../perf.h" #include "../perf.h"
#include <stddef.h> #include <stddef.h>
#include <pthread.h>
#include <linux/list.h>
#include <linux/rbtree.h>
struct perf_evlist; struct perf_evlist;
struct perf_evsel; struct perf_evsel;
struct perf_session; struct perf_session;
struct sym_entry {
struct rb_node rb_node;
struct list_head node;
unsigned long snap_count;
double weight;
struct map *map;
unsigned long count[0];
};
static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
{
return ((void *)self) + symbol_conf.priv_size;
}
struct perf_top { struct perf_top {
struct perf_evlist *evlist; struct perf_evlist *evlist;
/* /*
* Symbols will be added here in perf_event__process_sample and will * Symbols will be added here in perf_event__process_sample and will
* get out after decayed. * get out after decayed.
*/ */
struct list_head active_symbols;
pthread_mutex_t active_symbols_lock;
pthread_cond_t active_symbols_cond;
u64 samples; u64 samples;
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; u64 total_lost_warned;
int print_entries, count_filter, delay_secs; int print_entries, count_filter, delay_secs;
int display_weighted, freq, rb_entries; int freq;
pid_t target_pid, target_tid; pid_t target_pid, target_tid;
bool hide_kernel_symbols, hide_user_symbols, zero; bool hide_kernel_symbols, hide_user_symbols, zero;
const char *cpu_list; const char *cpu_list;
struct sym_entry *sym_filter_entry; struct hist_entry *sym_filter_entry;
struct perf_evsel *sym_evsel; struct perf_evsel *sym_evsel;
struct perf_session *session; struct perf_session *session;
}; };
size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
void perf_top__reset_sample_counters(struct perf_top *top); void perf_top__reset_sample_counters(struct perf_top *top);
float perf_top__decay_samples(struct perf_top *top, struct rb_root *root);
void perf_top__find_widths(struct perf_top *top, struct rb_root *root,
int *dso_width, int *dso_short_width, int *sym_width);
#ifdef NO_NEWT_SUPPORT
static inline int perf_top__tui_browser(struct perf_top *top __used)
{
return 0;
}
#else
int perf_top__tui_browser(struct perf_top *top);
#endif
#endif /* __PERF_TOP_H */ #endif /* __PERF_TOP_H */
...@@ -20,6 +20,7 @@ struct annotate_browser { ...@@ -20,6 +20,7 @@ struct annotate_browser {
struct ui_browser b; struct ui_browser b;
struct rb_root entries; struct rb_root entries;
struct rb_node *curr_hot; struct rb_node *curr_hot;
struct objdump_line *selection;
}; };
struct objdump_line_rb_node { struct objdump_line_rb_node {
...@@ -36,6 +37,7 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) ...@@ -36,6 +37,7 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
static void annotate_browser__write(struct ui_browser *self, void *entry, int row) static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{ {
struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
bool current_entry = ui_browser__is_current_entry(self, row); bool current_entry = ui_browser__is_current_entry(self, row);
int width = self->width; int width = self->width;
...@@ -58,6 +60,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro ...@@ -58,6 +60,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if (!current_entry) if (!current_entry)
ui_browser__set_color(self, HE_COLORSET_CODE); ui_browser__set_color(self, HE_COLORSET_CODE);
else
ab->selection = ol;
} }
static double objdump_line__calc_percent(struct objdump_line *self, static double objdump_line__calc_percent(struct objdump_line *self,
...@@ -141,7 +145,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, ...@@ -141,7 +145,8 @@ static void annotate_browser__set_top(struct annotate_browser *self,
static void annotate_browser__calc_percent(struct annotate_browser *browser, static void annotate_browser__calc_percent(struct annotate_browser *browser,
int evidx) int evidx)
{ {
struct symbol *sym = browser->b.priv; struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos; struct objdump_line *pos;
...@@ -164,21 +169,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, ...@@ -164,21 +169,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
} }
static int annotate_browser__run(struct annotate_browser *self, int evidx, static int annotate_browser__run(struct annotate_browser *self, int evidx,
int refresh) int nr_events, void(*timer)(void *arg),
void *arg, int delay_secs)
{ {
struct rb_node *nd = NULL; struct rb_node *nd = NULL;
struct symbol *sym = self->b.priv; struct map_symbol *ms = self->b.priv;
struct symbol *sym = ms->sym;
/* /*
* RIGHT To allow builtin-annotate to cycle thru multiple symbols by * RIGHT To allow builtin-annotate to cycle thru multiple symbols by
* examining the exit key for this function. * examining the exit key for this function.
*/ */
int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
NEWT_KEY_RIGHT, 0 }; NEWT_KEY_RIGHT, NEWT_KEY_ENTER, 0 };
int key; int key;
if (ui_browser__show(&self->b, sym->name, if (ui_browser__show(&self->b, sym->name,
"<-, -> or ESC: exit, TAB/shift+TAB: " "<- or ESC: exit, TAB/shift+TAB: "
"cycle hottest lines, H: Hottest") < 0) "cycle hottest lines, H: Hottest, -> Line action") < 0)
return -1; return -1;
ui_browser__add_exit_keys(&self->b, exit_keys); ui_browser__add_exit_keys(&self->b, exit_keys);
...@@ -189,13 +196,13 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, ...@@ -189,13 +196,13 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
nd = self->curr_hot; nd = self->curr_hot;
if (refresh != 0) if (delay_secs != 0)
newtFormSetTimer(self->b.form, refresh); newtFormSetTimer(self->b.form, delay_secs * 1000);
while (1) { while (1) {
key = ui_browser__run(&self->b); key = ui_browser__run(&self->b);
if (refresh != 0) { if (delay_secs != 0) {
annotate_browser__calc_percent(self, evidx); annotate_browser__calc_percent(self, evidx);
/* /*
* Current line focus got out of the list of most active * Current line focus got out of the list of most active
...@@ -212,7 +219,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, ...@@ -212,7 +219,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
* FIXME we need to check if it was * FIXME we need to check if it was
* es.reason == NEWT_EXIT_TIMER * es.reason == NEWT_EXIT_TIMER
*/ */
if (refresh != 0) if (timer != NULL)
timer(arg);
if (delay_secs != 0)
symbol__annotate_decay_histogram(sym, evidx); symbol__annotate_decay_histogram(sym, evidx);
continue; continue;
case NEWT_KEY_TAB: case NEWT_KEY_TAB:
...@@ -234,6 +244,57 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, ...@@ -234,6 +244,57 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
case 'H': case 'H':
nd = self->curr_hot; nd = self->curr_hot;
break; break;
case NEWT_KEY_ENTER:
case NEWT_KEY_RIGHT:
if (self->selection == NULL) {
ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
continue;
}
if (self->selection->offset == -1) {
ui_helpline__puts("Actions are only available for assembly lines.");
continue;
} else {
char *s = strstr(self->selection->line, "callq ");
struct annotation *notes;
struct symbol *target;
u64 ip;
if (s == NULL) {
ui_helpline__puts("Actions are only available for the 'callq' instruction.");
continue;
}
s = strchr(s, ' ');
if (s++ == NULL) {
ui_helpline__puts("Invallid callq instruction.");
continue;
}
ip = strtoull(s, NULL, 16);
ip = ms->map->map_ip(ms->map, ip);
target = map__find_symbol(ms->map, ip, NULL);
if (target == NULL) {
ui_helpline__puts("The called function was not found.");
continue;
}
notes = symbol__annotation(target);
pthread_mutex_lock(&notes->lock);
if (notes->src == NULL &&
symbol__alloc_hist(target, nr_events) < 0) {
pthread_mutex_unlock(&notes->lock);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
target->name);
continue;
}
pthread_mutex_unlock(&notes->lock);
symbol__tui_annotate(target, ms->map, evidx, nr_events,
timer, arg, delay_secs);
}
break;
default: default:
goto out; goto out;
} }
...@@ -246,22 +307,29 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, ...@@ -246,22 +307,29 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
return key; return key;
} }
int hist_entry__tui_annotate(struct hist_entry *he, int evidx) int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
void(*timer)(void *arg), void *arg, int delay_secs)
{ {
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events,
timer, arg, delay_secs);
} }
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
int refresh) int nr_events, void(*timer)(void *arg), void *arg,
int delay_secs)
{ {
struct objdump_line *pos, *n; struct objdump_line *pos, *n;
struct annotation *notes; struct annotation *notes;
struct map_symbol ms = {
.map = map,
.sym = sym,
};
struct annotate_browser browser = { struct annotate_browser browser = {
.b = { .b = {
.refresh = ui_browser__list_head_refresh, .refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek, .seek = ui_browser__list_head_seek,
.write = annotate_browser__write, .write = annotate_browser__write,
.priv = sym, .priv = &ms,
}, },
}; };
int ret; int ret;
...@@ -293,7 +361,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -293,7 +361,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
browser.b.entries = &notes->src->source, browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */ browser.b.width += 18; /* Percentage */
ret = annotate_browser__run(&browser, evidx, refresh); ret = annotate_browser__run(&browser, evidx, nr_events,
timer, arg, delay_secs);
list_for_each_entry_safe(pos, n, &notes->src->source, node) { list_for_each_entry_safe(pos, n, &notes->src->source, node) {
list_del(&pos->node); list_del(&pos->node);
objdump_line__free(pos); objdump_line__free(pos);
......
...@@ -24,8 +24,15 @@ struct hist_browser { ...@@ -24,8 +24,15 @@ struct hist_browser {
struct hists *hists; struct hists *hists;
struct hist_entry *he_selection; struct hist_entry *he_selection;
struct map_symbol *selection; struct map_symbol *selection;
const struct thread *thread_filter;
const struct dso *dso_filter;
bool has_symbols;
}; };
static int hists__browser_title(struct hists *self, char *bf, size_t size,
const char *ev_name, const struct dso *dso,
const struct thread *thread);
static void hist_browser__refresh_dimensions(struct hist_browser *self) static void hist_browser__refresh_dimensions(struct hist_browser *self)
{ {
/* 3 == +/- toggle symbol before actual hist_entry rendering */ /* 3 == +/- toggle symbol before actual hist_entry rendering */
...@@ -290,28 +297,53 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) ...@@ -290,28 +297,53 @@ 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 int hist_browser__run(struct hist_browser *self, const char *title) static int hist_browser__run(struct hist_browser *self, const char *ev_name,
void(*timer)(void *arg), void *arg, int delay_secs)
{ {
int key; int key;
int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', int delay_msecs = delay_secs * 1000;
NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, char title[160];
NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; int sym_exit_keys[] = { 'a', 'h', 'C', 'd', 'E', 't', 0, };
int exit_keys[] = { '?', 'h', 'D', NEWT_KEY_LEFT, NEWT_KEY_RIGHT,
NEWT_KEY_TAB, NEWT_KEY_UNTAB, NEWT_KEY_ENTER, 0, };
self->b.entries = &self->hists->entries; self->b.entries = &self->hists->entries;
self->b.nr_entries = self->hists->nr_entries; self->b.nr_entries = self->hists->nr_entries;
hist_browser__refresh_dimensions(self); hist_browser__refresh_dimensions(self);
hists__browser_title(self->hists, title, sizeof(title), ev_name,
self->dso_filter, self->thread_filter);
if (ui_browser__show(&self->b, title, if (ui_browser__show(&self->b, title,
"Press '?' for help on key bindings") < 0) "Press '?' for help on key bindings") < 0)
return -1; return -1;
if (timer != NULL)
newtFormSetTimer(self->b.form, delay_msecs);
ui_browser__add_exit_keys(&self->b, exit_keys); ui_browser__add_exit_keys(&self->b, exit_keys);
if (self->has_symbols)
ui_browser__add_exit_keys(&self->b, sym_exit_keys);
while (1) { while (1) {
key = ui_browser__run(&self->b); key = ui_browser__run(&self->b);
switch (key) { switch (key) {
case -1:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
timer(arg);
/*
* The timer may have changed the number of entries.
* XXX: Find better way to keep this in synch, probably
* removing this timer function altogether and just sync
* using the hists->lock...
*/
self->b.nr_entries = self->hists->nr_entries;
hists__browser_title(self->hists, title, sizeof(title),
ev_name, self->dso_filter,
self->thread_filter);
ui_browser__show_title(&self->b, title);
continue;
case 'D': { /* Debug */ case 'D': { /* Debug */
static int seq; static int seq;
struct hist_entry *h = rb_entry(self->b.top, struct hist_entry *h = rb_entry(self->b.top,
...@@ -761,6 +793,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists) ...@@ -761,6 +793,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
self->hists = hists; self->hists = hists;
self->b.refresh = hist_browser__refresh; self->b.refresh = hist_browser__refresh;
self->b.seek = ui_browser__hists_seek; self->b.seek = ui_browser__hists_seek;
self->has_symbols = sort_sym.list.next != NULL;
} }
return self; return self;
...@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, ...@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
return printed; return printed;
} }
static int perf_evsel__hists_browse(struct perf_evsel *evsel, static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
const char *helpline, const char *ev_name, const char *helpline, const char *ev_name,
bool left_exits) bool left_exits,
void(*timer)(void *arg), void *arg,
int delay_secs)
{ {
struct hists *self = &evsel->hists; struct hists *self = &evsel->hists;
struct hist_browser *browser = hist_browser__new(self); struct hist_browser *browser = hist_browser__new(self);
struct pstack *fstack; struct pstack *fstack;
const struct thread *thread_filter = NULL;
const struct dso *dso_filter = NULL;
char msg[160];
int key = -1; int key = -1;
if (browser == NULL) if (browser == NULL)
...@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
ui_helpline__push(helpline); ui_helpline__push(helpline);
hists__browser_title(self, msg, sizeof(msg), ev_name,
dso_filter, thread_filter);
while (1) { while (1) {
const struct thread *thread = NULL; const struct thread *thread = NULL;
const struct dso *dso = NULL; const struct dso *dso = NULL;
...@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
annotate = -2, zoom_dso = -2, zoom_thread = -2, annotate = -2, zoom_dso = -2, zoom_thread = -2,
browse_map = -2; browse_map = -2;
key = hist_browser__run(browser, msg); key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
if (browser->he_selection != NULL) { if (browser->he_selection != NULL) {
thread = hist_browser__selected_thread(browser); thread = hist_browser__selected_thread(browser);
...@@ -862,16 +892,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -862,16 +892,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
case NEWT_KEY_F1: case NEWT_KEY_F1:
case 'h': case 'h':
case '?': case '?':
ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" ui__help_window("h/?/F1 Show this window\n"
"TAB/UNTAB Switch events\n"
"q/CTRL+C Exit browser\n\n"
"For symbolic views (--sort has sym):\n\n"
"-> Zoom into DSO/Threads & Annotate current symbol\n"
"<- Zoom out\n" "<- Zoom out\n"
"a Annotate current symbol\n" "a Annotate current symbol\n"
"h/?/F1 Show this window\n"
"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\n");
"TAB/UNTAB Switch events\n"
"q/CTRL+C Exit browser");
continue; continue;
case NEWT_KEY_ENTER: case NEWT_KEY_ENTER:
case NEWT_KEY_RIGHT: case NEWT_KEY_RIGHT:
...@@ -889,9 +920,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -889,9 +920,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
continue; continue;
} }
top = pstack__pop(fstack); top = pstack__pop(fstack);
if (top == &dso_filter) if (top == &browser->dso_filter)
goto zoom_out_dso; goto zoom_out_dso;
if (top == &thread_filter) if (top == &browser->thread_filter)
goto zoom_out_thread; goto zoom_out_thread;
continue; continue;
} }
...@@ -904,6 +935,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -904,6 +935,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
goto out_free_stack; goto out_free_stack;
} }
if (!browser->has_symbols)
goto add_exit_option;
if (browser->selection != NULL && if (browser->selection != NULL &&
browser->selection->sym != NULL && browser->selection->sym != NULL &&
!browser->selection->map->dso->annotate_warned && !browser->selection->map->dso->annotate_warned &&
...@@ -913,14 +947,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -913,14 +947,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
if (thread != NULL && if (thread != NULL &&
asprintf(&options[nr_options], "Zoom %s %s(%d) thread", asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
(thread_filter ? "out of" : "into"), (browser->thread_filter ? "out of" : "into"),
(thread->comm_set ? thread->comm : ""), (thread->comm_set ? thread->comm : ""),
thread->pid) > 0) thread->pid) > 0)
zoom_thread = nr_options++; zoom_thread = nr_options++;
if (dso != NULL && if (dso != NULL &&
asprintf(&options[nr_options], "Zoom %s %s DSO", asprintf(&options[nr_options], "Zoom %s %s DSO",
(dso_filter ? "out of" : "into"), (browser->dso_filter ? "out of" : "into"),
(dso->kernel ? "the Kernel" : dso->short_name)) > 0) (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
zoom_dso = nr_options++; zoom_dso = nr_options++;
...@@ -928,7 +962,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -928,7 +962,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
browser->selection->map != NULL && browser->selection->map != NULL &&
asprintf(&options[nr_options], "Browse map details") > 0) asprintf(&options[nr_options], "Browse map details") > 0)
browse_map = nr_options++; browse_map = nr_options++;
add_exit_option:
options[nr_options++] = (char *)"Exit"; options[nr_options++] = (char *)"Exit";
choice = ui__popup_menu(nr_options, options); choice = ui__popup_menu(nr_options, options);
...@@ -949,45 +983,42 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ...@@ -949,45 +983,42 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
if (he == NULL) if (he == NULL)
continue; continue;
hist_entry__tui_annotate(he, evsel->idx); hist_entry__tui_annotate(he, evsel->idx, nr_events,
timer, arg, delay_secs);
} 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) {
zoom_dso: zoom_dso:
if (dso_filter) { if (browser->dso_filter) {
pstack__remove(fstack, &dso_filter); pstack__remove(fstack, &browser->dso_filter);
zoom_out_dso: zoom_out_dso:
ui_helpline__pop(); ui_helpline__pop();
dso_filter = NULL; browser->dso_filter = NULL;
} else { } else {
if (dso == NULL) if (dso == NULL)
continue; continue;
ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
dso->kernel ? "the Kernel" : dso->short_name); dso->kernel ? "the Kernel" : dso->short_name);
dso_filter = dso; browser->dso_filter = dso;
pstack__push(fstack, &dso_filter); pstack__push(fstack, &browser->dso_filter);
} }
hists__filter_by_dso(self, dso_filter); hists__filter_by_dso(self, browser->dso_filter);
hists__browser_title(self, msg, sizeof(msg), ev_name,
dso_filter, thread_filter);
hist_browser__reset(browser); hist_browser__reset(browser);
} else if (choice == zoom_thread) { } else if (choice == zoom_thread) {
zoom_thread: zoom_thread:
if (thread_filter) { if (browser->thread_filter) {
pstack__remove(fstack, &thread_filter); pstack__remove(fstack, &browser->thread_filter);
zoom_out_thread: zoom_out_thread:
ui_helpline__pop(); ui_helpline__pop();
thread_filter = NULL; browser->thread_filter = NULL;
} else { } else {
ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
thread->comm_set ? thread->comm : "", thread->comm_set ? thread->comm : "",
thread->pid); thread->pid);
thread_filter = thread; browser->thread_filter = thread;
pstack__push(fstack, &thread_filter); pstack__push(fstack, &browser->thread_filter);
} }
hists__filter_by_thread(self, thread_filter); hists__filter_by_thread(self, browser->thread_filter);
hists__browser_title(self, msg, sizeof(msg), ev_name,
dso_filter, thread_filter);
hist_browser__reset(browser); hist_browser__reset(browser);
} }
} }
...@@ -1026,9 +1057,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser, ...@@ -1026,9 +1057,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
menu->selection = evsel; menu->selection = evsel;
} }
static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
int nr_events, const char *help,
void(*timer)(void *arg), void *arg, int delay_secs)
{ {
int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
int delay_msecs = delay_secs * 1000;
struct perf_evlist *evlist = menu->b.priv; struct perf_evlist *evlist = menu->b.priv;
struct perf_evsel *pos; struct perf_evsel *pos;
const char *ev_name, *title = "Available samples"; const char *ev_name, *title = "Available samples";
...@@ -1038,20 +1072,30 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) ...@@ -1038,20 +1072,30 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
"ESC: exit, ENTER|->: Browse histograms") < 0) "ESC: exit, ENTER|->: Browse histograms") < 0)
return -1; return -1;
if (timer != NULL)
newtFormSetTimer(menu->b.form, delay_msecs);
ui_browser__add_exit_keys(&menu->b, exit_keys); ui_browser__add_exit_keys(&menu->b, exit_keys);
while (1) { while (1) {
key = ui_browser__run(&menu->b); key = ui_browser__run(&menu->b);
switch (key) { switch (key) {
case -1:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
timer(arg);
continue;
case NEWT_KEY_RIGHT: case NEWT_KEY_RIGHT:
case NEWT_KEY_ENTER: case NEWT_KEY_ENTER:
if (!menu->selection) if (!menu->selection)
continue; continue;
pos = menu->selection; pos = menu->selection;
perf_evlist__set_selected(evlist, pos);
browse_hists: browse_hists:
ev_name = event_name(pos); ev_name = event_name(pos);
key = perf_evsel__hists_browse(pos, help, ev_name, true); key = perf_evsel__hists_browse(pos, nr_events, help,
ev_name, true, timer,
arg, delay_secs);
ui_browser__show_title(&menu->b, title); ui_browser__show_title(&menu->b, title);
break; break;
case NEWT_KEY_LEFT: case NEWT_KEY_LEFT:
...@@ -1070,12 +1114,14 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) ...@@ -1070,12 +1114,14 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
pos = list_entry(evlist->entries.next, struct perf_evsel, node); pos = list_entry(evlist->entries.next, struct perf_evsel, node);
else else
pos = list_entry(pos->node.next, struct perf_evsel, node); pos = list_entry(pos->node.next, struct perf_evsel, node);
perf_evlist__set_selected(evlist, pos);
goto browse_hists; goto browse_hists;
case NEWT_KEY_UNTAB: case NEWT_KEY_UNTAB:
if (pos->node.prev == &evlist->entries) if (pos->node.prev == &evlist->entries)
pos = list_entry(evlist->entries.prev, struct perf_evsel, node); pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
else else
pos = list_entry(pos->node.prev, struct perf_evsel, node); pos = list_entry(pos->node.prev, struct perf_evsel, node);
perf_evlist__set_selected(evlist, pos);
goto browse_hists; goto browse_hists;
case 'q': case 'q':
case CTRL('c'): case CTRL('c'):
...@@ -1091,7 +1137,9 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) ...@@ -1091,7 +1137,9 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
} }
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
const char *help) const char *help,
void(*timer)(void *arg), void *arg,
int delay_secs)
{ {
struct perf_evsel *pos; struct perf_evsel *pos;
struct perf_evsel_menu menu = { struct perf_evsel_menu menu = {
...@@ -1121,18 +1169,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, ...@@ -1121,18 +1169,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
pos->name = strdup(ev_name); pos->name = strdup(ev_name);
} }
return perf_evsel_menu__run(&menu, help); return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
arg, delay_secs);
} }
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
void(*timer)(void *arg), void *arg,
int delay_secs)
{ {
if (evlist->nr_entries == 1) { if (evlist->nr_entries == 1) {
struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel *first = list_entry(evlist->entries.next,
struct perf_evsel, node); struct perf_evsel, node);
const char *ev_name = event_name(first); const char *ev_name = event_name(first);
return perf_evsel__hists_browse(first, help, ev_name, false); return perf_evsel__hists_browse(first, evlist->nr_entries, help,
ev_name, false, timer, arg,
delay_secs);
} }
return __perf_evlist__tui_browse_hists(evlist, help); return __perf_evlist__tui_browse_hists(evlist, help,
timer, arg, delay_secs);
} }
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from builtin-{top,stat,record}.c, see those files for further
* copyright notes.
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include "../browser.h"
#include "../../annotate.h"
#include "../helpline.h"
#include "../libslang.h"
#include "../util.h"
#include "../ui.h"
#include "../../evlist.h"
#include "../../hist.h"
#include "../../sort.h"
#include "../../symbol.h"
#include "../../session.h"
#include "../../top.h"
struct perf_top_browser {
struct ui_browser b;
struct rb_root root;
struct sym_entry *selection;
float sum_ksamples;
int dso_width;
int dso_short_width;
int sym_width;
};
static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row)
{
struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b);
struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node);
bool current_entry = ui_browser__is_current_entry(browser, row);
struct symbol *symbol = sym_entry__symbol(syme);
struct perf_top *top = browser->priv;
int width = browser->width;
double pcnt;
pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) /
top_browser->sum_ksamples));
ui_browser__set_percent_color(browser, pcnt, current_entry);
if (top->evlist->nr_entries == 1 || !top->display_weighted) {
slsmg_printf("%20.2f ", syme->weight);
width -= 21;
} else {
slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count);
width -= 20;
}
slsmg_printf("%4.1f%%", pcnt);
width -= 7;
if (verbose) {
slsmg_printf(" %016" PRIx64, symbol->start);
width -= 17;
}
slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width,
symbol->name);
width -= top_browser->sym_width;
slsmg_write_nstring(width >= syme->map->dso->long_name_len ?
syme->map->dso->long_name :
syme->map->dso->short_name, width);
if (current_entry)
top_browser->selection = syme;
}
static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser)
{
struct perf_top *top = browser->b.priv;
u64 top_idx = browser->b.top_idx;
browser->root = RB_ROOT;
browser->b.top = NULL;
browser->sum_ksamples = perf_top__decay_samples(top, &browser->root);
/*
* No active symbols
*/
if (top->rb_entries == 0)
return;
perf_top__find_widths(top, &browser->root, &browser->dso_width,
&browser->dso_short_width,
&browser->sym_width);
if (browser->sym_width + browser->dso_width > browser->b.width - 29) {
browser->dso_width = browser->dso_short_width;
if (browser->sym_width + browser->dso_width > browser->b.width - 29)
browser->sym_width = browser->b.width - browser->dso_width - 29;
}
/*
* Adjust the ui_browser indexes since the entries in the browser->root
* rb_tree may have changed, then seek it from start, so that we get a
* possible new top of the screen.
*/
browser->b.nr_entries = top->rb_entries;
if (top_idx >= browser->b.nr_entries) {
if (browser->b.height >= browser->b.nr_entries)
top_idx = browser->b.nr_entries - browser->b.height;
else
top_idx = 0;
}
if (browser->b.index >= top_idx + browser->b.height)
browser->b.index = top_idx + browser->b.index - browser->b.top_idx;
if (browser->b.index >= browser->b.nr_entries)
browser->b.index = browser->b.nr_entries - 1;
browser->b.top_idx = top_idx;
browser->b.seek(&browser->b, top_idx, SEEK_SET);
}
static void perf_top_browser__annotate(struct perf_top_browser *browser)
{
struct sym_entry *syme = browser->selection;
struct symbol *sym = sym_entry__symbol(syme);
struct annotation *notes = symbol__annotation(sym);
struct perf_top *top = browser->b.priv;
if (notes->src != NULL)
goto do_annotation;
pthread_mutex_lock(&notes->lock);
top->sym_filter_entry = NULL;
if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) {
pr_err("Not enough memory for annotating '%s' symbol!\n",
sym->name);
pthread_mutex_unlock(&notes->lock);
return;
}
top->sym_filter_entry = syme;
pthread_mutex_unlock(&notes->lock);
do_annotation:
symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000);
}
static void perf_top_browser__warn_lost(struct perf_top_browser *browser)
{
struct perf_top *top = browser->b.priv;
char msg[128];
int len;
top->total_lost_warned = top->session->hists.stats.total_lost;
pthread_mutex_lock(&ui__lock);
ui_browser__set_color(&browser->b, HE_COLORSET_TOP);
len = snprintf(msg, sizeof(msg),
" WARNING: LOST %" PRIu64 " events, Check IO/CPU overload",
top->total_lost_warned);
if (len > browser->b.width)
len = browser->b.width;
SLsmg_gotorc(0, browser->b.width - len);
slsmg_write_nstring(msg, len);
pthread_mutex_unlock(&ui__lock);
}
static int perf_top_browser__run(struct perf_top_browser *browser)
{
int key;
char title[160];
struct perf_top *top = browser->b.priv;
int delay_msecs = top->delay_secs * 1000;
int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
perf_top_browser__update_rb_tree(browser);
perf_top__header_snprintf(top, title, sizeof(title));
perf_top__reset_sample_counters(top);
if (ui_browser__show(&browser->b, title,
"ESC: exit, ENTER|->|a: Live Annotate") < 0)
return -1;
newtFormSetTimer(browser->b.form, delay_msecs);
ui_browser__add_exit_keys(&browser->b, exit_keys);
while (1) {
key = ui_browser__run(&browser->b);
switch (key) {
case -1:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
perf_top_browser__update_rb_tree(browser);
perf_top__header_snprintf(top, title, sizeof(title));
perf_top__reset_sample_counters(top);
ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT);
SLsmg_gotorc(0, 0);
slsmg_write_nstring(title, browser->b.width);
if (top->total_lost_warned != top->session->hists.stats.total_lost)
perf_top_browser__warn_lost(browser);
break;
case 'a':
case NEWT_KEY_RIGHT:
case NEWT_KEY_ENTER:
if (browser->selection)
perf_top_browser__annotate(browser);
break;
case NEWT_KEY_LEFT:
continue;
case NEWT_KEY_ESCAPE:
if (!ui__dialog_yesno("Do you really want to exit?"))
continue;
/* Fall thru */
default:
goto out;
}
}
out:
ui_browser__hide(&browser->b);
return key;
}
int perf_top__tui_browser(struct perf_top *top)
{
struct perf_top_browser browser = {
.b = {
.entries = &browser.root,
.refresh = ui_browser__rb_tree_refresh,
.seek = ui_browser__rb_tree_seek,
.write = perf_top_browser__write,
.priv = top,
},
};
return perf_top_browser__run(&browser);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册