perf/core improvements and fixes
. Check for flex and bison before continuing building, from Borislav Petkov. . Make event_copy local to mmaps, fixing buffer wrap around problems, from David Ahern. . Add option for runtime switching perf data file in perf report, just press 's' and a menu with the valid files found in the current directory will be presented, from Feng Tang. . Add support to display whole group data for raw columns, from Jiri Olsa. . Fix SIGALRM and pipe read race for the rwtop perl script. from Jiri Olsa. . Fix perf_evsel::exclude_GH handling and add a test to catch regressions, from Jiri Olsa. . Error checking fixes, from Namhyung Kim. . Fix calloc argument ordering, from Paul Gortmaker. . Fix set event list leader, from Stephane Eranian. . Add per processor socket count aggregation in perf stat, from Stephane Eranian. . Fix perf python binding breakage. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJREsv5AAoJENZQFvNTUqpADZsQALpIJPCptf3euQ7RS7Iknht3 qmNdluEl3hCKqx2ZrvmlhhqobDkdf2u8diZiGLkU5b33n28GYO/36cOXH+QPgPXR BliGTay5iYfor2Y5z/5ju1+lggMLrKH5gTey8+3th8+SrWg/3JVeMHK8itlxzI2M qp7eRi2DK1PMqdi0sTua6a7JfJwetY12dkzsmC48ngGYE1uJ1IpTjRwS5IKQbRFM YXyQ4O94zwvRYmUMdqFxT2vfI1iE8m/aIjbg3043/ZJhHUYHBiCqZYtu7U9HHAUw es4RXJiwEiUhZxhPgCDayUg0krLlhdy4Pv4JngME+na9Mtl7WmxNKCN1A3onpn2b Xf1hOWUslpZSFzoAzXQXHMhSoe3uBkFS6V0ZnB0r2jmTxZv0a4Z+mUeziDiaro4S 7g/jwBjLElpa8eCONHX5onRH68h6+A0HMrUCaY0i906jf0vpsVfkpIEBqzyTnf88 cdH7LTJRaJxVraMtd1P4GdGM/I8GjWYfpHXq8LtPMBNHMwIGSRxxSpunn3t/wXzO enOtJ+GuqOFOSwP3JbXnRbzYHjUcPTQYNknKzJbQpXqofNrtts2Nz4AttjhutC5O /Tx/zAZ48fvFUPK/86a7V/xlXAnP3WBaB2sXSFwf/AvLQYHDnJXHYVlMp3PGqFiu MQxhCemjC1y3xvkFkVds =ZPTB -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: . Check for flex and bison before continuing building, from Borislav Petkov. . Make event_copy local to mmaps, fixing buffer wrap around problems, from David Ahern. . Add option for runtime switching perf data file in perf report, just press 's' and a menu with the valid files found in the current directory will be presented, from Feng Tang. . Add support to display whole group data for raw columns, from Jiri Olsa. . Fix SIGALRM and pipe read race for the rwtop perl script. from Jiri Olsa. . Fix perf_evsel::exclude_GH handling and add a test to catch regressions, from Jiri Olsa. . Error checking fixes, from Namhyung Kim. . Fix calloc argument ordering, from Paul Gortmaker. . Fix set event list leader, from Stephane Eranian. . Add per processor socket count aggregation in perf stat, from Stephane Eranian. . Fix perf python binding breakage. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
661e591525
|
@ -116,9 +116,16 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m
|
|||
|
||||
-I msecs::
|
||||
--interval-print msecs::
|
||||
print count deltas every N milliseconds (minimum: 100ms)
|
||||
Print count deltas every N milliseconds (minimum: 100ms)
|
||||
example: perf stat -I 1000 -e cycles -a sleep 5
|
||||
|
||||
--aggr-socket::
|
||||
Aggregate counts per processor socket for system-wide mode measurements. This
|
||||
is a useful mode to detect imbalance between sockets. To enable this mode,
|
||||
use --aggr-socket in addition to -a. (system-wide). The output includes the
|
||||
socket number and the number of online processors on that socket. This is
|
||||
useful to gauge the amount of aggregation.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
|
|
|
@ -149,6 +149,8 @@ RM = rm -f
|
|||
MKDIR = mkdir
|
||||
FIND = find
|
||||
INSTALL = install
|
||||
FLEX = flex
|
||||
BISON= bison
|
||||
|
||||
# sparse is architecture-neutral, which means that we need to tell it
|
||||
# explicitly what architecture to check for. Fix this up for yours..
|
||||
|
@ -158,6 +160,14 @@ ifneq ($(MAKECMDGOALS),clean)
|
|||
ifneq ($(MAKECMDGOALS),tags)
|
||||
-include config/feature-tests.mak
|
||||
|
||||
ifeq ($(call get-executable,$(FLEX)),)
|
||||
dummy := $(error Error: $(FLEX) is missing on this system, please install it)
|
||||
endif
|
||||
|
||||
ifeq ($(call get-executable,$(BISON)),)
|
||||
dummy := $(error Error: $(BISON) is missing on this system, please install it)
|
||||
endif
|
||||
|
||||
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y)
|
||||
CFLAGS := $(CFLAGS) -fstack-protector-all
|
||||
endif
|
||||
|
@ -282,9 +292,6 @@ endif
|
|||
|
||||
export PERL_PATH
|
||||
|
||||
FLEX = flex
|
||||
BISON= bison
|
||||
|
||||
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
|
||||
|
||||
|
|
|
@ -309,7 +309,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
setup_sorting(annotate_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
if (argc) {
|
||||
/*
|
||||
|
|
|
@ -605,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
ui_init();
|
||||
|
||||
setup_sorting(diff_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(diff_usage, options);
|
||||
|
||||
setup_pager();
|
||||
|
||||
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
|
||||
|
|
|
@ -39,7 +39,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
|
||||
OPT_BOOLEAN('v', "verbose", &details.verbose,
|
||||
"Show all event attr details"),
|
||||
OPT_BOOLEAN('g', "group", &symbol_conf.event_group,
|
||||
OPT_BOOLEAN('g', "group", &details.event_group,
|
||||
"Show event group information"),
|
||||
OPT_END()
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (argc)
|
||||
usage_with_options(evlist_usage, options);
|
||||
|
||||
if (symbol_conf.event_group && (details.verbose || details.freq)) {
|
||||
if (details.event_group && (details.verbose || details.freq)) {
|
||||
pr_err("--group option is not compatible with other options\n");
|
||||
usage_with_options(evlist_usage, options);
|
||||
}
|
||||
|
|
|
@ -468,9 +468,17 @@ static int __cmd_report(struct perf_report *rep)
|
|||
|
||||
if (use_browser > 0) {
|
||||
if (use_browser == 1) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL,
|
||||
&session->header.env);
|
||||
ret = perf_evlist__tui_browse_hists(session->evlist,
|
||||
help,
|
||||
NULL,
|
||||
&session->header.env);
|
||||
/*
|
||||
* Usually "ret" is the last pressed key, and we only
|
||||
* care if the key notifies us to switch data file.
|
||||
*/
|
||||
if (ret != K_SWITCH_INPUT_DATA)
|
||||
ret = 0;
|
||||
|
||||
} else if (use_browser == 2) {
|
||||
perf_evlist__gtk_browse_hists(session->evlist, help,
|
||||
NULL);
|
||||
|
@ -708,6 +716,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
else
|
||||
input_name = "perf.data";
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
repeat:
|
||||
session = perf_session__new(input_name, O_RDONLY,
|
||||
report.force, false, &report.tool);
|
||||
if (session == NULL)
|
||||
|
@ -733,15 +751,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
}
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
setup_sorting(report_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(report_usage, options);
|
||||
|
||||
/*
|
||||
* Only in the newt browser we are doing integrated annotation,
|
||||
|
@ -809,6 +820,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
}
|
||||
|
||||
ret = __cmd_report(&report);
|
||||
if (ret == K_SWITCH_INPUT_DATA) {
|
||||
perf_session__delete(session);
|
||||
goto repeat;
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
perf_session__delete(session);
|
||||
return ret;
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
static void print_stat(int argc, const char **argv);
|
||||
static void print_counter_aggr(struct perf_evsel *counter, char *prefix);
|
||||
static void print_counter(struct perf_evsel *counter, char *prefix);
|
||||
static void print_aggr_socket(char *prefix);
|
||||
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
|
@ -79,6 +80,7 @@ static int run_count = 1;
|
|||
static bool no_inherit = false;
|
||||
static bool scale = true;
|
||||
static bool no_aggr = false;
|
||||
static bool aggr_socket = false;
|
||||
static pid_t child_pid = -1;
|
||||
static bool null_run = false;
|
||||
static int detailed_run = 0;
|
||||
|
@ -93,6 +95,7 @@ static const char *post_cmd = NULL;
|
|||
static bool sync_run = false;
|
||||
static unsigned int interval = 0;
|
||||
static struct timespec ref_time;
|
||||
static struct cpu_map *sock_map;
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
|
@ -312,7 +315,9 @@ static void print_interval(void)
|
|||
sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep);
|
||||
|
||||
if (num_print_interval == 0 && !csv_output) {
|
||||
if (no_aggr)
|
||||
if (aggr_socket)
|
||||
fprintf(output, "# time socket cpus counts events\n");
|
||||
else if (no_aggr)
|
||||
fprintf(output, "# time CPU counts events\n");
|
||||
else
|
||||
fprintf(output, "# time counts events\n");
|
||||
|
@ -321,7 +326,9 @@ static void print_interval(void)
|
|||
if (++num_print_interval == 25)
|
||||
num_print_interval = 0;
|
||||
|
||||
if (no_aggr) {
|
||||
if (aggr_socket)
|
||||
print_aggr_socket(prefix);
|
||||
else if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, prefix);
|
||||
} else {
|
||||
|
@ -349,6 +356,12 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
|||
ts.tv_nsec = 0;
|
||||
}
|
||||
|
||||
if (aggr_socket
|
||||
&& cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) {
|
||||
perror("cannot build socket map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
|
||||
perror("failed to create pipes");
|
||||
return -1;
|
||||
|
@ -529,13 +542,21 @@ static void print_noise(struct perf_evsel *evsel, double avg)
|
|||
print_noise_pct(stddev_stats(&ps->res_stats[0]), avg);
|
||||
}
|
||||
|
||||
static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double msecs = avg / 1e6;
|
||||
char cpustr[16] = { '\0', };
|
||||
const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s";
|
||||
|
||||
if (no_aggr)
|
||||
if (aggr_socket)
|
||||
sprintf(cpustr, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
cpu,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
else if (no_aggr)
|
||||
sprintf(cpustr, "CPU%*d%s",
|
||||
csv_output ? 0 : -4,
|
||||
perf_evsel__cpus(evsel)->map[cpu], csv_sep);
|
||||
|
@ -734,7 +755,7 @@ static void print_ll_cache_misses(int cpu,
|
|||
fprintf(output, " of all LL-cache hits ");
|
||||
}
|
||||
|
||||
static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
||||
static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
|
||||
{
|
||||
double total, ratio = 0.0;
|
||||
char cpustr[16] = { '\0', };
|
||||
|
@ -747,7 +768,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
|||
else
|
||||
fmt = "%s%18.0f%s%-25s";
|
||||
|
||||
if (no_aggr)
|
||||
if (aggr_socket)
|
||||
sprintf(cpustr, "S%*d%s%*d%s",
|
||||
csv_output ? 0 : -5,
|
||||
cpu,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep);
|
||||
else if (no_aggr)
|
||||
sprintf(cpustr, "CPU%*d%s",
|
||||
csv_output ? 0 : -4,
|
||||
perf_evsel__cpus(evsel)->map[cpu], csv_sep);
|
||||
|
@ -853,6 +882,70 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_aggr_socket(char *prefix)
|
||||
{
|
||||
struct perf_evsel *counter;
|
||||
u64 ena, run, val;
|
||||
int cpu, s, s2, sock, nr;
|
||||
|
||||
if (!sock_map)
|
||||
return;
|
||||
|
||||
for (s = 0; s < sock_map->nr; s++) {
|
||||
sock = cpu_map__socket(sock_map, s);
|
||||
list_for_each_entry(counter, &evsel_list->entries, node) {
|
||||
val = ena = run = 0;
|
||||
nr = 0;
|
||||
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
|
||||
s2 = cpu_map__get_socket(evsel_list->cpus, cpu);
|
||||
if (s2 != sock)
|
||||
continue;
|
||||
val += counter->counts->cpu[cpu].val;
|
||||
ena += counter->counts->cpu[cpu].ena;
|
||||
run += counter->counts->cpu[cpu].run;
|
||||
nr++;
|
||||
}
|
||||
if (prefix)
|
||||
fprintf(output, "%s", prefix);
|
||||
|
||||
if (run == 0 || ena == 0) {
|
||||
fprintf(output, "S%*d%s%*d%s%*s%s%*s",
|
||||
csv_output ? 0 : -5,
|
||||
s,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 4,
|
||||
nr,
|
||||
csv_sep,
|
||||
csv_output ? 0 : 18,
|
||||
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
perf_evsel__name(counter));
|
||||
if (counter->cgrp)
|
||||
fprintf(output, "%s%s",
|
||||
csv_sep, counter->cgrp->name);
|
||||
|
||||
fputc('\n', output);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(sock, nr, counter, val);
|
||||
else
|
||||
abs_printout(sock, nr, counter, val);
|
||||
|
||||
if (!csv_output) {
|
||||
print_noise(counter, 1.0);
|
||||
|
||||
if (run != ena)
|
||||
fprintf(output, " (%.2f%%)",
|
||||
100.0 * run / ena);
|
||||
}
|
||||
fputc('\n', output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out the results of a single counter:
|
||||
* aggregated counts in system-wide mode
|
||||
|
@ -882,9 +975,9 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
|
|||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(-1, counter, avg);
|
||||
nsec_printout(-1, 0, counter, avg);
|
||||
else
|
||||
abs_printout(-1, counter, avg);
|
||||
abs_printout(-1, 0, counter, avg);
|
||||
|
||||
print_noise(counter, avg);
|
||||
|
||||
|
@ -940,9 +1033,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
|
|||
}
|
||||
|
||||
if (nsec_counter(counter))
|
||||
nsec_printout(cpu, counter, val);
|
||||
nsec_printout(cpu, 0, counter, val);
|
||||
else
|
||||
abs_printout(cpu, counter, val);
|
||||
abs_printout(cpu, 0, counter, val);
|
||||
|
||||
if (!csv_output) {
|
||||
print_noise(counter, 1.0);
|
||||
|
@ -980,7 +1073,9 @@ static void print_stat(int argc, const char **argv)
|
|||
fprintf(output, ":\n\n");
|
||||
}
|
||||
|
||||
if (no_aggr) {
|
||||
if (aggr_socket)
|
||||
print_aggr_socket(NULL);
|
||||
else if (no_aggr) {
|
||||
list_for_each_entry(counter, &evsel_list->entries, node)
|
||||
print_counter(counter, NULL);
|
||||
} else {
|
||||
|
@ -1228,6 +1323,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"command to run after to the measured command"),
|
||||
OPT_UINTEGER('I', "interval-print", &interval,
|
||||
"print counts at regular interval in ms (>= 100)"),
|
||||
OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const stat_usage[] = {
|
||||
|
@ -1314,6 +1410,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
usage_with_options(stat_usage, options);
|
||||
}
|
||||
|
||||
if (aggr_socket) {
|
||||
if (!perf_target__has_cpu(&target)) {
|
||||
fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n");
|
||||
usage_with_options(stat_usage, options);
|
||||
}
|
||||
no_aggr = true;
|
||||
}
|
||||
|
||||
if (add_default_attributes())
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -1129,7 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (sort_order == default_sort_order)
|
||||
sort_order = "dso,symbol";
|
||||
|
||||
setup_sorting(top_usage, options);
|
||||
if (setup_sorting() < 0)
|
||||
usage_with_options(top_usage, options);
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
|
|
|
@ -103,32 +103,6 @@
|
|||
#include "util/types.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
struct perf_mmap {
|
||||
void *base;
|
||||
int mask;
|
||||
unsigned int prev;
|
||||
};
|
||||
|
||||
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = mm->base;
|
||||
int head = pc->data_head;
|
||||
rmb();
|
||||
return head;
|
||||
}
|
||||
|
||||
static inline void perf_mmap__write_tail(struct perf_mmap *md,
|
||||
unsigned long tail)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = md->base;
|
||||
|
||||
/*
|
||||
* ensure all reads are done before we write the tail out.
|
||||
*/
|
||||
/* mb(); */
|
||||
pc->data_tail = tail;
|
||||
}
|
||||
|
||||
/*
|
||||
* prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
|
||||
* counters in the current task.
|
||||
|
|
|
@ -17,6 +17,7 @@ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
|||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
use POSIX qw/SIGALRM SA_RESTART/;
|
||||
|
||||
my $default_interval = 3;
|
||||
my $nlines = 20;
|
||||
|
@ -90,7 +91,10 @@ sub syscalls::sys_enter_write
|
|||
|
||||
sub trace_begin
|
||||
{
|
||||
$SIG{ALRM} = \&set_print_pending;
|
||||
my $sa = POSIX::SigAction->new(\&set_print_pending);
|
||||
$sa->flags(SA_RESTART);
|
||||
$sa->safe(1);
|
||||
POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n";
|
||||
alarm 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -449,7 +449,8 @@ int test__hists_link(void)
|
|||
goto out;
|
||||
|
||||
/* default sort order (comm,dso,sym) will be used */
|
||||
setup_sorting(NULL, NULL);
|
||||
if (setup_sorting() < 0)
|
||||
goto out;
|
||||
|
||||
machines__init(&machines);
|
||||
|
||||
|
|
|
@ -577,7 +577,7 @@ static int test__group2(struct perf_evlist *evlist)
|
|||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
|
@ -811,6 +811,166 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh1(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles + :H group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:G + :H group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh2(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles + :G group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :G group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh3(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles:G + :u group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :u group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__group_gh4(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel, *leader;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
|
||||
|
||||
/* cycles:G + :uG group modifier */
|
||||
evsel = leader = perf_evlist__first(evlist);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
|
||||
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
|
||||
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
|
||||
|
||||
/* cache-misses:H + :uG group modifier */
|
||||
evsel = perf_evsel__next(evsel);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
|
||||
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
|
||||
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_tracepoints(void)
|
||||
{
|
||||
char events_path[PATH_MAX];
|
||||
|
@ -1011,6 +1171,22 @@ static struct evlist_test test__events[] = {
|
|||
.name = "*:*",
|
||||
.check = test__all_tracepoints,
|
||||
},
|
||||
[34] = {
|
||||
.name = "{cycles,cache-misses:G}:H",
|
||||
.check = test__group_gh1,
|
||||
},
|
||||
[35] = {
|
||||
.name = "{cycles,cache-misses:H}:G",
|
||||
.check = test__group_gh2,
|
||||
},
|
||||
[36] = {
|
||||
.name = "{cycles:G,cache-misses:H}:u",
|
||||
.check = test__group_gh3,
|
||||
},
|
||||
[37] = {
|
||||
.name = "{cycles:G,cache-misses:H}:uG",
|
||||
.check = test__group_gh4,
|
||||
},
|
||||
};
|
||||
|
||||
static struct evlist_test test__events_pmu[] = {
|
||||
|
|
|
@ -1241,6 +1241,96 @@ static inline bool is_report_browser(void *timer)
|
|||
return timer == NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only runtime switching of perf data file will make "input_name" point
|
||||
* to a malloced buffer. So add "is_input_name_malloced" flag to decide
|
||||
* whether we need to call free() for current "input_name" during the switch.
|
||||
*/
|
||||
static bool is_input_name_malloced = false;
|
||||
|
||||
static int switch_data_file(void)
|
||||
{
|
||||
char *pwd, *options[32], *abs_path[32], *tmp;
|
||||
DIR *pwd_dir;
|
||||
int nr_options = 0, choice = -1, ret = -1;
|
||||
struct dirent *dent;
|
||||
|
||||
pwd = getenv("PWD");
|
||||
if (!pwd)
|
||||
return ret;
|
||||
|
||||
pwd_dir = opendir(pwd);
|
||||
if (!pwd_dir)
|
||||
return ret;
|
||||
|
||||
memset(options, 0, sizeof(options));
|
||||
memset(options, 0, sizeof(abs_path));
|
||||
|
||||
while ((dent = readdir(pwd_dir))) {
|
||||
char path[PATH_MAX];
|
||||
u64 magic;
|
||||
char *name = dent->d_name;
|
||||
FILE *file;
|
||||
|
||||
if (!(dent->d_type == DT_REG))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", pwd, name);
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
continue;
|
||||
|
||||
if (fread(&magic, 1, 8, file) < 8)
|
||||
goto close_file_and_continue;
|
||||
|
||||
if (is_perf_magic(magic)) {
|
||||
options[nr_options] = strdup(name);
|
||||
if (!options[nr_options])
|
||||
goto close_file_and_continue;
|
||||
|
||||
abs_path[nr_options] = strdup(path);
|
||||
if (!abs_path[nr_options]) {
|
||||
free(options[nr_options]);
|
||||
ui__warning("Can't search all data files due to memory shortage.\n");
|
||||
fclose(file);
|
||||
break;
|
||||
}
|
||||
|
||||
nr_options++;
|
||||
}
|
||||
|
||||
close_file_and_continue:
|
||||
fclose(file);
|
||||
if (nr_options >= 32) {
|
||||
ui__warning("Too many perf data files in PWD!\n"
|
||||
"Only the first 32 files will be listed.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(pwd_dir);
|
||||
|
||||
if (nr_options) {
|
||||
choice = ui__popup_menu(nr_options, options);
|
||||
if (choice < nr_options && choice >= 0) {
|
||||
tmp = strdup(abs_path[choice]);
|
||||
if (tmp) {
|
||||
if (is_input_name_malloced)
|
||||
free((void *)input_name);
|
||||
input_name = tmp;
|
||||
is_input_name_malloced = true;
|
||||
ret = 0;
|
||||
} else
|
||||
ui__warning("Data switch failed due to memory shortage!\n");
|
||||
}
|
||||
}
|
||||
|
||||
free_popup_options(options, nr_options);
|
||||
free_popup_options(abs_path, nr_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
const char *helpline, const char *ev_name,
|
||||
bool left_exits,
|
||||
|
@ -1275,7 +1365,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
int choice = 0,
|
||||
annotate = -2, zoom_dso = -2, zoom_thread = -2,
|
||||
annotate_f = -2, annotate_t = -2, browse_map = -2;
|
||||
int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
|
||||
int scripts_comm = -2, scripts_symbol = -2,
|
||||
scripts_all = -2, switch_data = -2;
|
||||
|
||||
nr_options = 0;
|
||||
|
||||
|
@ -1332,6 +1423,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
if (is_report_browser(hbt))
|
||||
goto do_scripts;
|
||||
continue;
|
||||
case 's':
|
||||
if (is_report_browser(hbt))
|
||||
goto do_data_switch;
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
|
@ -1351,6 +1446,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"r Run available scripts('perf report' only)\n"
|
||||
"s Switch to another data file in PWD ('perf report' only)\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"V Verbose (DSO names in callchains, etc)\n"
|
||||
"/ Filter symbol by name");
|
||||
|
@ -1458,6 +1554,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
|
||||
scripts_all = nr_options++;
|
||||
|
||||
if (is_report_browser(hbt) && asprintf(&options[nr_options],
|
||||
"Switch to another data file in PWD") > 0)
|
||||
switch_data = nr_options++;
|
||||
add_exit_option:
|
||||
options[nr_options++] = (char *)"Exit";
|
||||
retry_popup_menu:
|
||||
|
@ -1568,6 +1667,16 @@ do_scripts:
|
|||
|
||||
script_browse(script_opt);
|
||||
}
|
||||
/* Switch to another data file */
|
||||
else if (choice == switch_data) {
|
||||
do_data_switch:
|
||||
if (!switch_data_file()) {
|
||||
key = K_SWITCH_INPUT_DATA;
|
||||
break;
|
||||
} else
|
||||
ui__warning("Won't switch the data files due to\n"
|
||||
"no valid data file get selected!\n");
|
||||
}
|
||||
}
|
||||
out_free_stack:
|
||||
pstack__delete(fstack);
|
||||
|
@ -1694,6 +1803,7 @@ browse_hists:
|
|||
"Do you really want to exit?"))
|
||||
continue;
|
||||
/* Fall thru */
|
||||
case K_SWITCH_INPUT_DATA:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
goto out;
|
||||
|
|
|
@ -9,18 +9,24 @@
|
|||
|
||||
typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
|
||||
|
||||
static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *),
|
||||
const char *fmt, hpp_snprint_fn print_fn)
|
||||
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *),
|
||||
const char *fmt, hpp_snprint_fn print_fn,
|
||||
bool fmt_percent)
|
||||
{
|
||||
int ret;
|
||||
double percent = 0.0;
|
||||
struct hists *hists = he->hists;
|
||||
|
||||
if (hists->stats.total_period)
|
||||
percent = 100.0 * get_field(he) / hists->stats.total_period;
|
||||
if (fmt_percent) {
|
||||
double percent = 0.0;
|
||||
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, percent);
|
||||
if (hists->stats.total_period)
|
||||
percent = 100.0 * get_field(he) /
|
||||
hists->stats.total_period;
|
||||
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, percent);
|
||||
} else
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
int prev_idx, idx_delta;
|
||||
|
@ -49,11 +55,15 @@ static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
* have no sample
|
||||
*/
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 0.0);
|
||||
fmt, 0);
|
||||
}
|
||||
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 100.0 * period / total);
|
||||
if (fmt_percent)
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 100.0 * period / total);
|
||||
else
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, period);
|
||||
|
||||
prev_idx = perf_evsel__group_idx(evsel);
|
||||
}
|
||||
|
@ -65,23 +75,12 @@ static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
* zero-fill group members at last which have no sample
|
||||
*/
|
||||
ret += print_fn(hpp->buf + ret, hpp->size - ret,
|
||||
fmt, 0.0);
|
||||
fmt, 0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __hpp__raw_fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
u64 (*get_field)(struct hist_entry *),
|
||||
const char *fmt, hpp_snprint_fn print_fn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
static int hpp__header_##_type(struct perf_hpp *hpp) \
|
||||
{ \
|
||||
|
@ -116,16 +115,16 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
|||
\
|
||||
static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__percent_fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
(hpp_snprint_fn)percent_color_snprintf); \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
(hpp_snprint_fn)percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__percent_fmt(hpp, he, he_get_##_field, fmt, \
|
||||
scnprintf); \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
|
||||
scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
|
@ -137,7 +136,7 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \
|
|||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
|
||||
return __hpp__raw_fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf); \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \
|
||||
}
|
||||
|
||||
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
|
||||
|
|
|
@ -23,5 +23,6 @@
|
|||
#define K_TIMER -1
|
||||
#define K_ERROR -2
|
||||
#define K_RESIZE -3
|
||||
#define K_SWITCH_INPUT_DATA -4
|
||||
|
||||
#endif /* _PERF_KEYSYMS_H_ */
|
||||
|
|
|
@ -444,7 +444,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
|
|||
struct callchain_cursor_node *node = *cursor->last;
|
||||
|
||||
if (!node) {
|
||||
node = calloc(sizeof(*node), 1);
|
||||
node = calloc(1, sizeof(*node));
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "util.h"
|
||||
#include "sysfs.h"
|
||||
#include "../perf.h"
|
||||
#include "cpumap.h"
|
||||
#include <assert.h>
|
||||
|
@ -201,3 +202,56 @@ void cpu_map__delete(struct cpu_map *map)
|
|||
{
|
||||
free(map);
|
||||
}
|
||||
|
||||
int cpu_map__get_socket(struct cpu_map *map, int idx)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *mnt;
|
||||
char path[PATH_MAX];
|
||||
int cpu, ret;
|
||||
|
||||
if (idx > map->nr)
|
||||
return -1;
|
||||
|
||||
cpu = map->map[idx];
|
||||
|
||||
mnt = sysfs_find_mountpoint();
|
||||
if (!mnt)
|
||||
return -1;
|
||||
|
||||
sprintf(path,
|
||||
"%s/devices/system/cpu/cpu%d/topology/physical_package_id",
|
||||
mnt, cpu);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
ret = fscanf(fp, "%d", &cpu);
|
||||
fclose(fp);
|
||||
return ret == 1 ? cpu : -1;
|
||||
}
|
||||
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
|
||||
{
|
||||
struct cpu_map *sock;
|
||||
int nr = cpus->nr;
|
||||
int cpu, s1, s2;
|
||||
|
||||
sock = calloc(1, sizeof(*sock) + nr * sizeof(int));
|
||||
if (!sock)
|
||||
return -1;
|
||||
|
||||
for (cpu = 0; cpu < nr; cpu++) {
|
||||
s1 = cpu_map__get_socket(cpus, cpu);
|
||||
for (s2 = 0; s2 < sock->nr; s2++) {
|
||||
if (s1 == sock->map[s2])
|
||||
break;
|
||||
}
|
||||
if (s2 == sock->nr) {
|
||||
sock->map[sock->nr] = s1;
|
||||
sock->nr++;
|
||||
}
|
||||
}
|
||||
*sockp = sock;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,15 @@ struct cpu_map *cpu_map__dummy_new(void);
|
|||
void cpu_map__delete(struct cpu_map *map);
|
||||
struct cpu_map *cpu_map__read(FILE *file);
|
||||
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
||||
int cpu_map__get_socket(struct cpu_map *map, int idx);
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
||||
|
||||
static inline int cpu_map__socket(struct cpu_map *sock, int s)
|
||||
{
|
||||
if (!sock || s > sock->nr || s < 0)
|
||||
return 0;
|
||||
return sock->map[s];
|
||||
}
|
||||
|
||||
static inline int cpu_map__nr(const struct cpu_map *map)
|
||||
{
|
||||
|
|
|
@ -122,8 +122,7 @@ void __perf_evlist__set_leader(struct list_head *list)
|
|||
leader->nr_members = evsel->idx - leader->idx + 1;
|
||||
|
||||
list_for_each_entry(evsel, list, node) {
|
||||
if (evsel != leader)
|
||||
evsel->leader = leader;
|
||||
evsel->leader = leader;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,7 +375,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
|||
if ((old & md->mask) + size != ((old + size) & md->mask)) {
|
||||
unsigned int offset = old;
|
||||
unsigned int len = min(sizeof(*event), size), cpy;
|
||||
void *dst = &evlist->event_copy;
|
||||
void *dst = &md->event_copy;
|
||||
|
||||
do {
|
||||
cpy = min(md->mask + 1 - (offset & md->mask), len);
|
||||
|
@ -386,7 +385,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
|||
len -= cpy;
|
||||
} while (len);
|
||||
|
||||
event = &evlist->event_copy;
|
||||
event = &md->event_copy;
|
||||
}
|
||||
|
||||
old += size;
|
||||
|
|
|
@ -17,6 +17,13 @@ struct perf_record_opts;
|
|||
#define PERF_EVLIST__HLIST_BITS 8
|
||||
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
|
||||
|
||||
struct perf_mmap {
|
||||
void *base;
|
||||
int mask;
|
||||
unsigned int prev;
|
||||
union perf_event event_copy;
|
||||
};
|
||||
|
||||
struct perf_evlist {
|
||||
struct list_head entries;
|
||||
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
|
||||
|
@ -30,7 +37,6 @@ struct perf_evlist {
|
|||
pid_t pid;
|
||||
} workload;
|
||||
bool overwrite;
|
||||
union perf_event event_copy;
|
||||
struct perf_mmap *mmap;
|
||||
struct pollfd *pollfd;
|
||||
struct thread_map *threads;
|
||||
|
@ -136,4 +142,25 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
|
|||
}
|
||||
|
||||
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
|
||||
|
||||
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = mm->base;
|
||||
int head = pc->data_head;
|
||||
rmb();
|
||||
return head;
|
||||
}
|
||||
|
||||
static inline void perf_mmap__write_tail(struct perf_mmap *md,
|
||||
unsigned long tail)
|
||||
{
|
||||
struct perf_event_mmap_page *pc = md->base;
|
||||
|
||||
/*
|
||||
* ensure all reads are done before we write the tail out.
|
||||
*/
|
||||
/* mb(); */
|
||||
pc->data_tail = tail;
|
||||
}
|
||||
|
||||
#endif /* __PERF_EVLIST_H */
|
||||
|
|
|
@ -1391,7 +1391,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
|
|||
bool first = true;
|
||||
int printed = 0;
|
||||
|
||||
if (symbol_conf.event_group) {
|
||||
if (details->event_group) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
if (!perf_evsel__is_group_leader(evsel))
|
||||
|
|
|
@ -254,6 +254,7 @@ static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
|
|||
struct perf_attr_details {
|
||||
bool freq;
|
||||
bool verbose;
|
||||
bool event_group;
|
||||
};
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
|
|
|
@ -2253,7 +2253,7 @@ static int perf_header__adds_write(struct perf_header *header,
|
|||
if (!nr_sections)
|
||||
return 0;
|
||||
|
||||
feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
|
||||
feat_sec = p = calloc(nr_sections, sizeof(*feat_sec));
|
||||
if (feat_sec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2425,7 +2425,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
|
|||
if (!nr_sections)
|
||||
return 0;
|
||||
|
||||
feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections);
|
||||
feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec));
|
||||
if (!feat_sec)
|
||||
return -1;
|
||||
|
||||
|
|
|
@ -699,14 +699,6 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
|
|||
int exclude = eu | ek | eh;
|
||||
int exclude_GH = evsel ? evsel->exclude_GH : 0;
|
||||
|
||||
/*
|
||||
* We are here for group and 'GH' was not set as event
|
||||
* modifier and whatever event/group modifier override
|
||||
* default 'GH' setup.
|
||||
*/
|
||||
if (evsel && !exclude_GH)
|
||||
eH = eG = 0;
|
||||
|
||||
memset(mod, 0, sizeof(*mod));
|
||||
|
||||
while (*str) {
|
||||
|
|
|
@ -18,4 +18,5 @@ util/cgroup.c
|
|||
util/debugfs.c
|
||||
util/rblist.c
|
||||
util/strlist.c
|
||||
util/sysfs.c
|
||||
../../lib/rbtree.c
|
||||
|
|
|
@ -160,9 +160,10 @@ struct sort_entry sort_dso = {
|
|||
|
||||
/* --sort symbol */
|
||||
|
||||
static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
|
||||
u64 ip_l, u64 ip_r)
|
||||
static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
|
||||
{
|
||||
u64 ip_l, ip_r;
|
||||
|
||||
if (!sym_l || !sym_r)
|
||||
return cmp_null(sym_l, sym_r);
|
||||
|
||||
|
@ -178,21 +179,10 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
|
|||
static int64_t
|
||||
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
u64 ip_l, ip_r;
|
||||
|
||||
if (!left->ms.sym && !right->ms.sym)
|
||||
return right->level - left->level;
|
||||
|
||||
if (!left->ms.sym || !right->ms.sym)
|
||||
return cmp_null(left->ms.sym, right->ms.sym);
|
||||
|
||||
if (left->ms.sym == right->ms.sym)
|
||||
return 0;
|
||||
|
||||
ip_l = left->ms.sym->start;
|
||||
ip_r = right->ms.sym->start;
|
||||
|
||||
return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
|
||||
return _sort__sym_cmp(left->ms.sym, right->ms.sym);
|
||||
}
|
||||
|
||||
static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
|
||||
|
@ -383,8 +373,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
if (!from_l->sym && !from_r->sym)
|
||||
return right->level - left->level;
|
||||
|
||||
return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
|
||||
from_r->addr);
|
||||
return _sort__sym_cmp(from_l->sym, from_r->sym);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
|
@ -396,7 +385,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
if (!to_l->sym && !to_r->sym)
|
||||
return right->level - left->level;
|
||||
|
||||
return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
|
||||
return _sort__sym_cmp(to_l->sym, to_r->sym);
|
||||
}
|
||||
|
||||
static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
|
||||
|
@ -576,23 +565,30 @@ int sort_dimension__add(const char *tok)
|
|||
return -ESRCH;
|
||||
}
|
||||
|
||||
void setup_sorting(const char * const usagestr[], const struct option *opts)
|
||||
int setup_sorting(void)
|
||||
{
|
||||
char *tmp, *tok, *str = strdup(sort_order);
|
||||
int ret = 0;
|
||||
|
||||
if (str == NULL) {
|
||||
error("Not enough memory to setup sort keys");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
int ret = sort_dimension__add(tok);
|
||||
ret = sort_dimension__add(tok);
|
||||
if (ret == -EINVAL) {
|
||||
error("Invalid --sort key: `%s'", tok);
|
||||
usage_with_options(usagestr, opts);
|
||||
break;
|
||||
} else if (ret == -ESRCH) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
usage_with_options(usagestr, opts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
|
||||
|
|
|
@ -160,7 +160,7 @@ struct sort_entry {
|
|||
extern struct sort_entry sort_thread;
|
||||
extern struct list_head hist_entry__sort_list;
|
||||
|
||||
void setup_sorting(const char * const usagestr[], const struct option *opts);
|
||||
int setup_sorting(void);
|
||||
extern int sort_dimension__add(const char *);
|
||||
void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
|
||||
const char *list_name, FILE *fp);
|
||||
|
|
Loading…
Reference in New Issue