提交 a2d28d0c 编写于 作者: I Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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:

. Allow skipping problematic entries in 'perf test'.

. Fix some namespace problems in the event parsing routines.

. Add 'perf test' entry to make sure the python binding doesn't have
  linking problems.

. Adjust 'perf test' attr tests verbosity levels.

. Make tools/perf build with GNU make v3.80, fix from Al Cooper.

. Do missing feature fallbacks in just one place, removing duplicated
  code in multiple tools.

. Fix some memory leaks, from David Ahern.

. Fix segfault when drawing out-of-bounds jumps, from Frederik Deweerdt.

. Allow of casting an array of char to string in 'perf probe', from
  Hyeoncheol Lee.

. Add support for wildcard in tracepoint system name, from Jiri Olsa.

. Update FSF postal address to be URL's, from Jon Stanley.

. Add anonymous huge page recognition, from Joshua Zhu.

. Remove some needless feature test checks, from Namhyung Kim.

. Multiple improvements to the sort routines, from Namhyung Kim.

. Fix warning on '>=' operator in libtraceevent, from Namhyung Kim.

. Use ARRAY_SIZE instead of reinventing it in 'perf script' and 'perf kmem',
  from Sasha Levin.

. Remove some redundant checks, from Sasha Levin.

. Test correct variable after allocation in libtraceevent, fix from Sasha Levin.

. Mark branch_info maps as referenced, fix from Stephane Eranian.

. Fix PMU format parsing test failure, from Sukadev Bhattiprolu.

. Fix possible (unlikely) buffer overflow, from Thomas Jarosch.

. Multiple 'perf script' fixes, from Tom Zanussi.

. Add missing field in PERF_RECORD_SAMPLE documentation, from Vince Weaver.
Signed-off-by: NArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: NIngo Molnar <mingo@kernel.org>
...@@ -579,6 +579,7 @@ enum perf_event_type { ...@@ -579,6 +579,7 @@ enum perf_event_type {
* { u32 size; * { u32 size;
* char data[size];}&& PERF_SAMPLE_RAW * char data[size];}&& PERF_SAMPLE_RAW
* *
* { u64 nr;
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
* *
* { u64 abi; # enum perf_sample_regs_abi * { u64 abi; # enum perf_sample_regs_abi
......
...@@ -901,7 +901,6 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume ...@@ -901,7 +901,6 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume
} }
mutex_unlock(uprobes_hash(inode)); mutex_unlock(uprobes_hash(inode));
if (uprobe)
put_uprobe(uprobe); put_uprobe(uprobe);
} }
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software * License along with this program; if not, see <http://www.gnu.org/licenses>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* *
...@@ -1463,7 +1462,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f ...@@ -1463,7 +1462,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f
if (read_expect_type(EVENT_ITEM, &token)) if (read_expect_type(EVENT_ITEM, &token))
goto fail; goto fail;
/* add signed type */ if (strtoul(token, NULL, 0))
field->flags |= FIELD_IS_SIGNED;
free_token(token); free_token(token);
if (read_expected(EVENT_OP, ";") < 0) if (read_expected(EVENT_OP, ";") < 0)
...@@ -1785,6 +1785,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) ...@@ -1785,6 +1785,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
strcmp(token, "/") == 0 || strcmp(token, "/") == 0 ||
strcmp(token, "<") == 0 || strcmp(token, "<") == 0 ||
strcmp(token, ">") == 0 || strcmp(token, ">") == 0 ||
strcmp(token, "<=") == 0 ||
strcmp(token, ">=") == 0 ||
strcmp(token, "==") == 0 || strcmp(token, "==") == 0 ||
strcmp(token, "!=") == 0) { strcmp(token, "!=") == 0) {
...@@ -2481,7 +2483,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** ...@@ -2481,7 +2483,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
free_token(token); free_token(token);
arg = alloc_arg(); arg = alloc_arg();
if (!field) { if (!arg) {
do_warning("%s: not enough memory!", __func__); do_warning("%s: not enough memory!", __func__);
*tok = NULL; *tok = NULL;
return EVENT_ERROR; return EVENT_ERROR;
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software * License along with this program; if not, see <http://www.gnu.org/licenses>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software * License along with this program; if not, see <http://www.gnu.org/licenses>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software * License along with this program; if not, see <http://www.gnu.org/licenses>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
......
/*
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software * License along with this program; if not, see <http://www.gnu.org/licenses>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
......
...@@ -57,11 +57,44 @@ OPTIONS ...@@ -57,11 +57,44 @@ OPTIONS
-s:: -s::
--sort=:: --sort=::
Sort by key(s): pid, comm, dso, symbol, parent, srcline. Sort histogram entries by given key(s) - multiple keys can be specified
in CSV format. Following sort keys are available:
pid, comm, dso, symbol, parent, cpu, srcline.
Each key has following meaning:
- comm: command (name) of the task which can be read via /proc/<pid>/comm
- pid: command and tid of the task
- dso: name of library or module executed at the time of sample
- symbol: name of function executed at the time of sample
- parent: name of function matched to the parent regex filter. Unmatched
entries are displayed as "[other]".
- cpu: cpu number the task ran at the time of sample
- srcline: filename and line number executed at the time of sample. The
DWARF debuggin info must be provided.
By default, comm, dso and symbol keys are used.
(i.e. --sort comm,dso,symbol)
If --branch-stack option is used, following sort keys are also
available:
dso_from, dso_to, symbol_from, symbol_to, mispredict.
- dso_from: name of library or module branched from
- dso_to: name of library or module branched to
- symbol_from: name of function branched from
- symbol_to: name of function branched to
- mispredict: "N" for predicted branch, "Y" for mispredicted branch
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
and symbol_to, see '--branch-stack'.
-p:: -p::
--parent=<regex>:: --parent=<regex>::
regex filter to identify parent, see: '--sort parent' A regex filter to identify parent. The parent is a caller of this
function and searched through the callchain, thus it requires callchain
information recorded. The pattern is in the exteneded regex format and
defaults to "\^sys_|^do_page_fault", see '--sort parent'.
-x:: -x::
--exclude-other:: --exclude-other::
...@@ -74,7 +107,6 @@ OPTIONS ...@@ -74,7 +107,6 @@ OPTIONS
-t:: -t::
--field-separator=:: --field-separator=::
Use a special separator character and don't pad with spaces, replacing Use a special separator character and don't pad with spaces, replacing
all occurrences of this separator in symbol names (and other output) all occurrences of this separator in symbol names (and other output)
with a '.' character, that thus it's the only non valid separator. with a '.' character, that thus it's the only non valid separator.
......
...@@ -336,7 +336,6 @@ scripts listed by the 'perf script -l' command e.g.: ...@@ -336,7 +336,6 @@ scripts listed by the 'perf script -l' command e.g.:
---- ----
root@tropicana:~# perf script -l root@tropicana:~# perf script -l
List of available trace scripts: List of available trace scripts:
workqueue-stats workqueue stats (ins/exe/create/destroy)
wakeup-latency system-wide min/max/avg wakeup latency wakeup-latency system-wide min/max/avg wakeup latency
rw-by-file <comm> r/w activity for a program, by file rw-by-file <comm> r/w activity for a program, by file
rw-by-pid system-wide r/w activity rw-by-pid system-wide r/w activity
...@@ -402,7 +401,6 @@ should show a new entry for your script: ...@@ -402,7 +401,6 @@ should show a new entry for your script:
---- ----
root@tropicana:~# perf script -l root@tropicana:~# perf script -l
List of available trace scripts: List of available trace scripts:
workqueue-stats workqueue stats (ins/exe/create/destroy)
wakeup-latency system-wide min/max/avg wakeup latency wakeup-latency system-wide min/max/avg wakeup latency
rw-by-file <comm> r/w activity for a program, by file rw-by-file <comm> r/w activity for a program, by file
rw-by-pid system-wide r/w activity rw-by-pid system-wide r/w activity
......
...@@ -23,6 +23,10 @@ from 'perf test list'. ...@@ -23,6 +23,10 @@ from 'perf test list'.
OPTIONS OPTIONS
------- -------
-s::
--skip::
Tests to skip (comma separater numeric list).
-v:: -v::
--verbose:: --verbose::
Be more verbose. Be more verbose.
...@@ -50,7 +50,6 @@ include config/utilities.mak ...@@ -50,7 +50,6 @@ include config/utilities.mak
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
-include $(OUTPUT)PERF-VERSION-FILE
uname_M := $(shell uname -m 2>/dev/null || echo not) uname_M := $(shell uname -m 2>/dev/null || echo not)
...@@ -487,6 +486,8 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o ...@@ -487,6 +486,8 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
LIB_OBJS += $(OUTPUT)tests/pmu.o LIB_OBJS += $(OUTPUT)tests/pmu.o
LIB_OBJS += $(OUTPUT)tests/hists_link.o
LIB_OBJS += $(OUTPUT)tests/python-use.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
...@@ -532,9 +533,6 @@ ifneq ($(MAKECMDGOALS),tags) ...@@ -532,9 +533,6 @@ ifneq ($(MAKECMDGOALS),tags)
# because maintaining the nesting to match is a pain. If # because maintaining the nesting to match is a pain. If
# we had "elif" things would have been much nicer... # we had "elif" things would have been much nicer...
-include config.mak.autogen
-include config.mak
ifdef NO_LIBELF ifdef NO_LIBELF
NO_DWARF := 1 NO_DWARF := 1
NO_DEMANGLE := 1 NO_DEMANGLE := 1
...@@ -686,6 +684,7 @@ ifndef NO_GTK2 ...@@ -686,6 +684,7 @@ ifndef NO_GTK2
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null)
EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null)
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
LIB_OBJS += $(OUTPUT)ui/gtk/hists.o
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
LIB_OBJS += $(OUTPUT)ui/gtk/util.o LIB_OBJS += $(OUTPUT)ui/gtk/util.o
LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
...@@ -887,7 +886,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf ...@@ -887,7 +886,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf
$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ $(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \
$(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
...@@ -951,7 +950,13 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS ...@@ -951,7 +950,13 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
'-DBINDIR="$(bindir_SQ)"' \ '-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \
$<
$(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
-DPYTHONPATH='"$(OUTPUT)python"' \
-DPYTHON='"$(PYTHON_WORD)"' \
$< $<
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
......
...@@ -159,6 +159,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */ ...@@ -159,6 +159,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */
printf("# Running %s/%s benchmark...\n", printf("# Running %s/%s benchmark...\n",
subsys->name, subsys->name,
suites[i].name); suites[i].name);
fflush(stdout);
argv[1] = suites[i].name; argv[1] = suites[i].name;
suites[i].fn(1, argv, NULL); suites[i].fn(1, argv, NULL);
...@@ -225,6 +226,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -225,6 +226,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
printf("# Running %s/%s benchmark...\n", printf("# Running %s/%s benchmark...\n",
subsystems[i].name, subsystems[i].name,
subsystems[i].suites[j].name); subsystems[i].suites[j].name);
fflush(stdout);
status = subsystems[i].suites[j].fn(argc - 1, status = subsystems[i].suites[j].fn(argc - 1,
argv + 1, prefix); argv + 1, prefix);
goto end; goto end;
......
...@@ -275,43 +275,6 @@ static struct perf_tool tool = { ...@@ -275,43 +275,6 @@ static struct perf_tool tool = {
.ordering_requires_timestamps = true, .ordering_requires_timestamps = true,
}; };
static void insert_hist_entry_by_name(struct rb_root *root,
struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
while (*p != NULL) {
parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node);
if (hist_entry__cmp(he, iter) < 0)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, root);
}
static void hists__name_resort(struct hists *self)
{
struct rb_root tmp = RB_ROOT;
struct rb_node *next = rb_first(&self->entries);
while (next != NULL) {
struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node);
rb_erase(&n->rb_node, &self->entries);
insert_hist_entry_by_name(&tmp, n);
}
self->entries = tmp;
}
static struct perf_evsel *evsel_match(struct perf_evsel *evsel, static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
struct perf_evlist *evlist) struct perf_evlist *evlist)
{ {
...@@ -324,30 +287,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel, ...@@ -324,30 +287,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
return NULL; return NULL;
} }
static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
list_for_each_entry(evsel, &evlist->entries, node) { list_for_each_entry(evsel, &evlist->entries, node) {
struct hists *hists = &evsel->hists; struct hists *hists = &evsel->hists;
hists__output_resort(hists); hists__collapse_resort(hists);
if (name)
hists__name_resort(hists);
} }
} }
static void hists__baseline_only(struct hists *hists) static void hists__baseline_only(struct hists *hists)
{ {
struct rb_node *next = rb_first(&hists->entries); struct rb_root *root;
struct rb_node *next;
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
next = rb_first(root);
while (next != NULL) { while (next != NULL) {
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&he->rb_node); next = rb_next(&he->rb_node_in);
if (!hist_entry__next_pair(he)) { if (!hist_entry__next_pair(he)) {
rb_erase(&he->rb_node, &hists->entries); rb_erase(&he->rb_node_in, root);
hist_entry__free(he); hist_entry__free(he);
} }
} }
...@@ -447,19 +414,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root, ...@@ -447,19 +414,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root,
static void hists__compute_resort(struct hists *hists) static void hists__compute_resort(struct hists *hists)
{ {
struct rb_root tmp = RB_ROOT; struct rb_root *root;
struct rb_node *next = rb_first(&hists->entries); struct rb_node *next;
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
hists->entries = RB_ROOT;
next = rb_first(root);
hists->nr_entries = 0;
hists->stats.total_period = 0;
hists__reset_col_len(hists);
while (next != NULL) { while (next != NULL) {
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); struct hist_entry *he;
next = rb_next(&he->rb_node); he = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&he->rb_node_in);
rb_erase(&he->rb_node, &hists->entries); insert_hist_entry_by_compute(&hists->entries, he, compute);
insert_hist_entry_by_compute(&tmp, he, compute); hists__inc_nr_entries(hists, he);
} }
hists->entries = tmp;
} }
static void hists__process(struct hists *old, struct hists *new) static void hists__process(struct hists *old, struct hists *new)
...@@ -474,6 +452,8 @@ static void hists__process(struct hists *old, struct hists *new) ...@@ -474,6 +452,8 @@ static void hists__process(struct hists *old, struct hists *new)
if (sort_compute) { if (sort_compute) {
hists__precompute(new); hists__precompute(new);
hists__compute_resort(new); hists__compute_resort(new);
} else {
hists__output_resort(new);
} }
hists__fprintf(new, true, 0, 0, stdout); hists__fprintf(new, true, 0, 0, stdout);
...@@ -505,8 +485,8 @@ static int __cmd_diff(void) ...@@ -505,8 +485,8 @@ static int __cmd_diff(void)
evlist_old = older->evlist; evlist_old = older->evlist;
evlist_new = newer->evlist; evlist_new = newer->evlist;
perf_evlist__resort_hists(evlist_old, true); perf_evlist__collapse_resort(evlist_old);
perf_evlist__resort_hists(evlist_new, false); perf_evlist__collapse_resort(evlist_new);
list_for_each_entry(evsel, &evlist_new->entries, node) { list_for_each_entry(evsel, &evlist_new->entries, node) {
struct perf_evsel *evsel_old; struct perf_evsel *evsel_old;
......
...@@ -340,7 +340,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, ...@@ -340,7 +340,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
int n_lines, int is_caller) int n_lines, int is_caller)
{ {
struct rb_node *next; struct rb_node *next;
struct machine *machine; struct machine *machine = &session->machines.host;
printf("%.102s\n", graph_dotted_line); printf("%.102s\n", graph_dotted_line);
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
...@@ -349,11 +349,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session, ...@@ -349,11 +349,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
next = rb_first(root); next = rb_first(root);
machine = perf_session__find_host_machine(session);
if (!machine) {
pr_err("__print_result: couldn't find kernel information\n");
return;
}
while (next && n_lines--) { while (next && n_lines--) {
struct alloc_stat *data = rb_entry(next, struct alloc_stat, struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node); node);
...@@ -614,8 +609,7 @@ static struct sort_dimension *avail_sorts[] = { ...@@ -614,8 +609,7 @@ static struct sort_dimension *avail_sorts[] = {
&pingpong_sort_dimension, &pingpong_sort_dimension,
}; };
#define NUM_AVAIL_SORTS \ #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts))
(int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
static int sort_dimension__add(const char *tok, struct list_head *list) static int sort_dimension__add(const char *tok, struct list_head *list)
{ {
......
...@@ -973,8 +973,7 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv) ...@@ -973,8 +973,7 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv)
int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
const char *file_name; const char *file_name = NULL;
const struct option kvm_options[] = { const struct option kvm_options[] = {
OPT_STRING('i', "input", &file_name, "file", OPT_STRING('i', "input", &file_name, "file",
"Input file name"), "Input file name"),
......
...@@ -224,6 +224,7 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, ...@@ -224,6 +224,7 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
static int perf_record__open(struct perf_record *rec) static int perf_record__open(struct perf_record *rec)
{ {
char msg[512];
struct perf_evsel *pos; struct perf_evsel *pos;
struct perf_evlist *evlist = rec->evlist; struct perf_evlist *evlist = rec->evlist;
struct perf_session *session = rec->session; struct perf_session *session = rec->session;
...@@ -233,114 +234,18 @@ static int perf_record__open(struct perf_record *rec) ...@@ -233,114 +234,18 @@ static int perf_record__open(struct perf_record *rec)
perf_evlist__config(evlist, opts); perf_evlist__config(evlist, opts);
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
/*
* Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME.
*
* XXX this is kludgy but short term fix for problems introduced by
* eac23d1c that broke 'perf script' by having different sample_types
* when using multiple tracepoint events when we use a perf binary
* that tries to use sample_id_all on an older kernel.
*
* We need to move counter creation to perf_session, support
* different sample_types, etc.
*/
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
fallback_missing_features:
if (opts->exclude_guest_missing)
attr->exclude_guest = attr->exclude_host = 0;
retry_sample_id:
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
try_again: try_again:
if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
int err = errno; if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
if (err == EPERM || err == EACCES) {
ui__error_paranoid();
rc = -err;
goto out;
} else if (err == ENODEV && opts->target.cpu_list) {
pr_err("No such device - did you specify"
" an out-of-range profile CPU?\n");
rc = -err;
goto out;
} else if (err == EINVAL) {
if (!opts->exclude_guest_missing &&
(attr->exclude_guest || attr->exclude_host)) {
pr_debug("Old kernel, cannot exclude "
"guest or host samples.\n");
opts->exclude_guest_missing = true;
goto fallback_missing_features;
} else if (!opts->sample_id_all_missing) {
/*
* Old kernel, no attr->sample_id_type_all field
*/
opts->sample_id_all_missing = true;
if (!opts->sample_time && !opts->raw_samples && !time_needed)
perf_evsel__reset_sample_bit(pos, TIME);
goto retry_sample_id;
}
}
/*
* If it's cycles then fall back to hrtimer
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support.
*
* PPC returns ENXIO until 2.6.37 (behavior changed
* with commit b0a873e).
*/
if ((err == ENOENT || err == ENXIO)
&& attr->type == PERF_TYPE_HARDWARE
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) {
if (verbose) if (verbose)
ui__warning("The cycles event is not supported, " ui__warning("%s\n", msg);
"trying to fall back to cpu-clock-ticks\n");
attr->type = PERF_TYPE_SOFTWARE;
attr->config = PERF_COUNT_SW_CPU_CLOCK;
if (pos->name) {
free(pos->name);
pos->name = NULL;
}
goto try_again; goto try_again;
} }
if (err == ENOENT) { rc = -errno;
ui__error("The %s event is not supported.\n", perf_evsel__open_strerror(pos, &opts->target,
perf_evsel__name(pos)); errno, msg, sizeof(msg));
rc = -err; ui__error("%s\n", msg);
goto out;
} else if ((err == EOPNOTSUPP) && (attr->precise_ip)) {
ui__error("\'precise\' request may not be supported. "
"Try removing 'p' modifier\n");
rc = -err;
goto out;
}
printf("\n");
error("sys_perf_event_open() syscall returned with %d "
"(%s) for event %s. /bin/dmesg may provide "
"additional information.\n",
err, strerror(err), perf_evsel__name(pos));
#if defined(__i386__) || defined(__x86_64__)
if (attr->type == PERF_TYPE_HARDWARE &&
err == EOPNOTSUPP) {
pr_err("No hardware sampling interrupt available."
" No APIC? If so then you can boot the kernel"
" with the \"lapic\" boot parameter to"
" force-enable it.\n");
rc = -err;
goto out;
}
#endif
pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
rc = -err;
goto out; goto out;
} }
} }
...@@ -423,10 +328,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data) ...@@ -423,10 +328,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
{ {
int err; int err;
struct perf_tool *tool = data; struct perf_tool *tool = data;
if (machine__is_host(machine))
return;
/* /*
*As for guest kernel when processing subcommand record&report, *As for guest kernel when processing subcommand record&report,
*we arrange module mmap prior to guest kernel mmap and trigger *we arrange module mmap prior to guest kernel mmap and trigger
...@@ -611,12 +512,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -611,12 +512,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
rec->post_processing_offset = lseek(output, 0, SEEK_CUR); rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
machine = perf_session__find_host_machine(session); machine = &session->machines.host;
if (!machine) {
pr_err("Couldn't find native kernel information.\n");
err = -1;
goto out_delete_session;
}
if (opts->pipe_output) { if (opts->pipe_output) {
err = perf_event__synthesize_attrs(tool, session, err = perf_event__synthesize_attrs(tool, session,
...@@ -669,9 +565,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -669,9 +565,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
"Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
"Check /proc/modules permission or run as root.\n"); "Check /proc/modules permission or run as root.\n");
if (perf_guest) if (perf_guest) {
perf_session__process_machines(session, tool, machines__process_guests(&session->machines,
perf_event__synthesize_guest_os); perf_event__synthesize_guest_os, tool);
}
if (!opts->target.system_wide) if (!opts->target.system_wide)
err = perf_event__synthesize_thread_map(tool, evsel_list->threads, err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
......
...@@ -372,7 +372,7 @@ static int __cmd_report(struct perf_report *rep) ...@@ -372,7 +372,7 @@ static int __cmd_report(struct perf_report *rep)
if (ret) if (ret)
goto out_delete; goto out_delete;
kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION]; kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION];
kernel_kmap = map__kmap(kernel_map); kernel_kmap = map__kmap(kernel_map);
if (kernel_map == NULL || if (kernel_map == NULL ||
(kernel_map->dso->hit && (kernel_map->dso->hit &&
...@@ -595,8 +595,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -595,8 +595,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN(0, "stdio", &report.use_stdio, OPT_BOOLEAN(0, "stdio", &report.use_stdio,
"Use the stdio interface"), "Use the stdio interface"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]", OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
"sort by key(s): pid, comm, dso, symbol, parent, dso_to," "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
" dso_from, symbol_to, symbol_from, mispredict"), " dso_to, dso_from, symbol_to, symbol_from, mispredict"),
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
"Show sample percentage for different cpu modes"), "Show sample percentage for different cpu modes"),
OPT_STRING('p', "parent", &parent_pattern, "regex", OPT_STRING('p', "parent", &parent_pattern, "regex",
......
...@@ -1475,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, ...@@ -1475,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
goto out_delete; goto out_delete;
} }
sched->nr_events = session->hists.stats.nr_events[0]; sched->nr_events = session->stats.nr_events[0];
sched->nr_lost_events = session->hists.stats.total_lost; sched->nr_lost_events = session->stats.total_lost;
sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST];
} }
if (destroy) if (destroy)
......
...@@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused, ...@@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
const char *arg, int unset __maybe_unused) const char *arg, int unset __maybe_unused)
{ {
char *tok; char *tok;
int i, imax = sizeof(all_output_options) / sizeof(struct output_option); int i, imax = ARRAY_SIZE(all_output_options);
int j; int j;
int rc = 0; int rc = 0;
char *str = strdup(arg); char *str = strdup(arg);
...@@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix) ...@@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix)
return NULL; return NULL;
} }
static char *ltrim(char *str)
{
int len = strlen(str);
while (len && isspace(*str)) {
len--;
str++;
}
return str;
}
static int read_script_info(struct script_desc *desc, const char *filename) static int read_script_info(struct script_desc *desc, const char *filename)
{ {
char line[BUFSIZ], *p; char line[BUFSIZ], *p;
...@@ -1487,6 +1475,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1487,6 +1475,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
return -1; return -1;
} }
if (!script_name && !generate_script_lang)
perf_session__fprintf_info(session, stdout, show_full_info); perf_session__fprintf_info(session, stdout, show_full_info);
if (!no_callchain) if (!no_callchain)
......
...@@ -132,8 +132,6 @@ static struct stats walltime_nsecs_stats; ...@@ -132,8 +132,6 @@ static struct stats walltime_nsecs_stats;
static int create_perf_stat_counter(struct perf_evsel *evsel) static int create_perf_stat_counter(struct perf_evsel *evsel)
{ {
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
bool exclude_guest_missing = false;
int ret;
if (scale) if (scale)
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
...@@ -141,16 +139,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) ...@@ -141,16 +139,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
attr->inherit = !no_inherit; attr->inherit = !no_inherit;
retry: if (perf_target__has_cpu(&target))
if (exclude_guest_missing) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
if (perf_target__has_cpu(&target)) {
ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
if (ret)
goto check_ret;
return 0;
}
if (!perf_target__has_task(&target) && if (!perf_target__has_task(&target) &&
perf_evsel__is_group_leader(evsel)) { perf_evsel__is_group_leader(evsel)) {
...@@ -158,21 +148,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) ...@@ -158,21 +148,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
attr->enable_on_exec = 1; attr->enable_on_exec = 1;
} }
ret = perf_evsel__open_per_thread(evsel, evsel_list->threads); return perf_evsel__open_per_thread(evsel, evsel_list->threads);
if (!ret)
return 0;
/* fall through */
check_ret:
if (ret && errno == EINVAL) {
if (!exclude_guest_missing &&
(evsel->attr.exclude_guest || evsel->attr.exclude_host)) {
pr_debug("Old kernel, cannot exclude "
"guest or host samples.\n");
exclude_guest_missing = true;
goto retry;
}
}
return ret;
} }
/* /*
...@@ -271,6 +247,7 @@ static int read_counter(struct perf_evsel *counter) ...@@ -271,6 +247,7 @@ static int read_counter(struct perf_evsel *counter)
static int __run_perf_stat(int argc __maybe_unused, const char **argv) static int __run_perf_stat(int argc __maybe_unused, const char **argv)
{ {
char msg[512];
unsigned long long t0, t1; unsigned long long t0, t1;
struct perf_evsel *counter; struct perf_evsel *counter;
int status = 0; int status = 0;
...@@ -348,20 +325,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) ...@@ -348,20 +325,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
continue; continue;
} }
if (errno == EPERM || errno == EACCES) { perf_evsel__open_strerror(counter, &target,
error("You may not have permission to collect %sstats.\n" errno, msg, sizeof(msg));
"\t Consider tweaking" ui__error("%s\n", msg);
" /proc/sys/kernel/perf_event_paranoid or running as root.",
target.system_wide ? "system-wide " : "");
} else {
error("open_counter returned with %d (%s). "
"/bin/dmesg may provide additional information.\n",
errno, strerror(errno));
}
if (child_pid != -1) if (child_pid != -1)
kill(child_pid, SIGTERM); kill(child_pid, SIGTERM);
pr_err("Not all events could be opened.\n");
return -1; return -1;
} }
counter->supported = true; counter->supported = true;
......
...@@ -68,28 +68,6 @@ ...@@ -68,28 +68,6 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/types.h> #include <linux/types.h>
void get_term_dimensions(struct winsize *ws)
{
char *s = getenv("LINES");
if (s != NULL) {
ws->ws_row = atoi(s);
s = getenv("COLUMNS");
if (s != NULL) {
ws->ws_col = atoi(s);
if (ws->ws_row && ws->ws_col)
return;
}
}
#ifdef TIOCGWINSZ
if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
ws->ws_row && ws->ws_col)
return;
#endif
ws->ws_row = 25;
ws->ws_col = 80;
}
static void perf_top__update_print_entries(struct perf_top *top) static void perf_top__update_print_entries(struct perf_top *top)
{ {
if (top->print_entries > 9) if (top->print_entries > 9)
...@@ -716,7 +694,7 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -716,7 +694,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
static struct intlist *seen; static struct intlist *seen;
if (!seen) if (!seen)
seen = intlist__new(); seen = intlist__new(NULL);
if (!intlist__has_entry(seen, event->ip.pid)) { if (!intlist__has_entry(seen, event->ip.pid)) {
pr_err("Can't find guest [%d]'s kernel information\n", pr_err("Can't find guest [%d]'s kernel information\n",
...@@ -728,7 +706,7 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -728,7 +706,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if (!machine) { if (!machine) {
pr_err("%u unprocessable samples recorded.\n", pr_err("%u unprocessable samples recorded.\n",
top->session->hists.stats.nr_unprocessable_samples++); top->session->stats.nr_unprocessable_samples++);
return; return;
} }
...@@ -847,13 +825,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ...@@ -847,13 +825,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
++top->us_samples; ++top->us_samples;
if (top->hide_user_symbols) if (top->hide_user_symbols)
continue; continue;
machine = perf_session__find_host_machine(session); machine = &session->machines.host;
break; break;
case PERF_RECORD_MISC_KERNEL: case PERF_RECORD_MISC_KERNEL:
++top->kernel_samples; ++top->kernel_samples;
if (top->hide_kernel_symbols) if (top->hide_kernel_symbols)
continue; continue;
machine = perf_session__find_host_machine(session); machine = &session->machines.host;
break; break;
case PERF_RECORD_MISC_GUEST_KERNEL: case PERF_RECORD_MISC_GUEST_KERNEL:
++top->guest_kernel_samples; ++top->guest_kernel_samples;
...@@ -878,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ...@@ -878,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
hists__inc_nr_events(&evsel->hists, event->header.type); hists__inc_nr_events(&evsel->hists, event->header.type);
machine__process_event(machine, event); machine__process_event(machine, event);
} else } else
++session->hists.stats.nr_unknown_events; ++session->stats.nr_unknown_events;
} }
} }
...@@ -892,6 +870,7 @@ static void perf_top__mmap_read(struct perf_top *top) ...@@ -892,6 +870,7 @@ static void perf_top__mmap_read(struct perf_top *top)
static void perf_top__start_counters(struct perf_top *top) static void perf_top__start_counters(struct perf_top *top)
{ {
char msg[512];
struct perf_evsel *counter; struct perf_evsel *counter;
struct perf_evlist *evlist = top->evlist; struct perf_evlist *evlist = top->evlist;
struct perf_record_opts *opts = &top->record_opts; struct perf_record_opts *opts = &top->record_opts;
...@@ -899,77 +878,18 @@ static void perf_top__start_counters(struct perf_top *top) ...@@ -899,77 +878,18 @@ static void perf_top__start_counters(struct perf_top *top)
perf_evlist__config(evlist, opts); perf_evlist__config(evlist, opts);
list_for_each_entry(counter, &evlist->entries, node) { list_for_each_entry(counter, &evlist->entries, node) {
struct perf_event_attr *attr = &counter->attr;
fallback_missing_features:
if (top->exclude_guest_missing)
attr->exclude_guest = attr->exclude_host = 0;
retry_sample_id:
attr->sample_id_all = top->sample_id_all_missing ? 0 : 1;
try_again: try_again:
if (perf_evsel__open(counter, top->evlist->cpus, if (perf_evsel__open(counter, top->evlist->cpus,
top->evlist->threads) < 0) { top->evlist->threads) < 0) {
int err = errno; if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
if (err == EPERM || err == EACCES) {
ui__error_paranoid();
goto out_err;
} else if (err == EINVAL) {
if (!top->exclude_guest_missing &&
(attr->exclude_guest || attr->exclude_host)) {
pr_debug("Old kernel, cannot exclude "
"guest or host samples.\n");
top->exclude_guest_missing = true;
goto fallback_missing_features;
} else if (!top->sample_id_all_missing) {
/*
* Old kernel, no attr->sample_id_type_all field
*/
top->sample_id_all_missing = true;
goto retry_sample_id;
}
}
/*
* If it's cycles then fall back to hrtimer
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support:
*/
if ((err == ENOENT || err == ENXIO) &&
(attr->type == PERF_TYPE_HARDWARE) &&
(attr->config == PERF_COUNT_HW_CPU_CYCLES)) {
if (verbose) if (verbose)
ui__warning("Cycles event not supported,\n" ui__warning("%s\n", msg);
"trying to fall back to cpu-clock-ticks\n");
attr->type = PERF_TYPE_SOFTWARE;
attr->config = PERF_COUNT_SW_CPU_CLOCK;
if (counter->name) {
free(counter->name);
counter->name = NULL;
}
goto try_again; goto try_again;
} }
if (err == ENOENT) { perf_evsel__open_strerror(counter, &opts->target,
ui__error("The %s event is not supported.\n", errno, msg, sizeof(msg));
perf_evsel__name(counter)); ui__error("%s\n", msg);
goto out_err;
} else if (err == EMFILE) {
ui__error("Too many events are opened.\n"
"Try again after reducing the number of events\n");
goto out_err;
} else if ((err == EOPNOTSUPP) && (attr->precise_ip)) {
ui__error("\'precise\' request may not be supported. "
"Try removing 'p' modifier\n");
goto out_err;
}
ui__error("The sys_perf_event_open() syscall "
"returned with %d (%s). /bin/dmesg "
"may provide additional information.\n"
"No CONFIG_PERF_EVENTS=y kernel support "
"configured?\n", err, strerror(err));
goto out_err; goto out_err;
} }
} }
...@@ -1024,10 +944,10 @@ static int __cmd_top(struct perf_top *top) ...@@ -1024,10 +944,10 @@ static int __cmd_top(struct perf_top *top)
if (perf_target__has_task(&opts->target)) if (perf_target__has_task(&opts->target))
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
perf_event__process, perf_event__process,
&top->session->host_machine); &top->session->machines.host);
else else
perf_event__synthesize_threads(&top->tool, perf_event__process, perf_event__synthesize_threads(&top->tool, perf_event__process,
&top->session->host_machine); &top->session->machines.host);
perf_top__start_counters(top); perf_top__start_counters(top);
top->session->evlist = top->evlist; top->session->evlist = top->evlist;
perf_session__set_id_hdr_size(top->session); perf_session__set_id_hdr_size(top->session);
......
...@@ -13,7 +13,7 @@ newline := $(newline) ...@@ -13,7 +13,7 @@ newline := $(newline)
# what should replace a newline when escaping # what should replace a newline when escaping
# newlines; the default is a bizarre string. # newlines; the default is a bizarre string.
# #
nl-escape = $(or $(1),m822df3020w6a44id34bt574ctac44eb9f4n) nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n)
# escape-nl # escape-nl
# #
...@@ -173,9 +173,9 @@ _ge-abspath = $(if $(is-executable),$(1)) ...@@ -173,9 +173,9 @@ _ge-abspath = $(if $(is-executable),$(1))
# Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default)
# #
define get-executable-or-default define get-executable-or-default
$(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2),$(1)))
endef endef
_ge_attempt = $(or $(get-executable),$(_gea_warn),$(call _gea_err,$(2))) _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2)))
_gea_warn = $(warning The path '$(1)' is not executable.) _gea_warn = $(warning The path '$(1)' is not executable.)
_gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately))
......
...@@ -328,14 +328,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) ...@@ -328,14 +328,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
return 0; return 0;
status = 1;
/* Check for ENOSPC and EIO errors.. */ /* Check for ENOSPC and EIO errors.. */
if (fflush(stdout)) if (fflush(stdout)) {
die("write failure on standard output: %s", strerror(errno)); fprintf(stderr, "write failure on standard output: %s", strerror(errno));
if (ferror(stdout)) goto out;
die("unknown write failure on standard output"); }
if (fclose(stdout)) if (ferror(stdout)) {
die("close failed on standard output: %s", strerror(errno)); fprintf(stderr, "unknown write failure on standard output");
return 0; goto out;
}
if (fclose(stdout)) {
fprintf(stderr, "close failed on standard output: %s", strerror(errno));
goto out;
}
status = 0;
out:
return status;
} }
static void handle_internal_command(int argc, const char **argv) static void handle_internal_command(int argc, const char **argv)
...@@ -467,7 +476,8 @@ int main(int argc, const char **argv) ...@@ -467,7 +476,8 @@ int main(int argc, const char **argv)
cmd += 5; cmd += 5;
argv[0] = cmd; argv[0] = cmd;
handle_internal_command(argc, argv); handle_internal_command(argc, argv);
die("cannot handle %s internally", cmd); fprintf(stderr, "cannot handle %s internally", cmd);
goto out;
} }
/* Look for flags.. */ /* Look for flags.. */
...@@ -485,7 +495,7 @@ int main(int argc, const char **argv) ...@@ -485,7 +495,7 @@ int main(int argc, const char **argv)
printf("\n usage: %s\n\n", perf_usage_string); printf("\n usage: %s\n\n", perf_usage_string);
list_common_cmds_help(); list_common_cmds_help();
printf("\n %s\n\n", perf_more_info_string); printf("\n %s\n\n", perf_more_info_string);
exit(1); goto out;
} }
cmd = argv[0]; cmd = argv[0];
...@@ -517,7 +527,7 @@ int main(int argc, const char **argv) ...@@ -517,7 +527,7 @@ int main(int argc, const char **argv)
fprintf(stderr, "Expansion of alias '%s' failed; " fprintf(stderr, "Expansion of alias '%s' failed; "
"'%s' is not a perf-command\n", "'%s' is not a perf-command\n",
cmd, argv[0]); cmd, argv[0]);
exit(1); goto out;
} }
if (!done_help) { if (!done_help) {
cmd = argv[0] = help_unknown_cmd(cmd); cmd = argv[0] = help_unknown_cmd(cmd);
...@@ -528,6 +538,6 @@ int main(int argc, const char **argv) ...@@ -528,6 +538,6 @@ int main(int argc, const char **argv)
fprintf(stderr, "Failed to run command '%s': %s\n", fprintf(stderr, "Failed to run command '%s': %s\n",
cmd, strerror(errno)); cmd, strerror(errno));
out:
return 1; return 1;
} }
#ifndef _PERF_PERF_H #ifndef _PERF_PERF_H
#define _PERF_PERF_H #define _PERF_PERF_H
struct winsize;
void get_term_dimensions(struct winsize *ws);
#include <asm/unistd.h> #include <asm/unistd.h>
#if defined(__i386__) #if defined(__i386__)
...@@ -237,8 +233,6 @@ struct perf_record_opts { ...@@ -237,8 +233,6 @@ struct perf_record_opts {
bool raw_samples; bool raw_samples;
bool sample_address; bool sample_address;
bool sample_time; bool sample_time;
bool sample_id_all_missing;
bool exclude_guest_missing;
bool period; bool period;
unsigned int freq; unsigned int freq;
unsigned int mmap_pages; unsigned int mmap_pages;
......
#!/bin/bash
perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
#!/bin/bash
# description: workqueue stats (ins/exe/create/destroy)
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
#!/usr/bin/perl -w
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
# Licensed under the terms of the GNU GPL License version 2
# Displays workqueue stats
#
# Usage:
#
# perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e
# workqueue:workqueue_destruction -e workqueue:workqueue_execution
# -e workqueue:workqueue_insertion
#
# perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl
use 5.010000;
use strict;
use warnings;
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;
my @cpus;
sub workqueue::workqueue_destruction
{
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
$common_pid, $common_comm,
$thread_comm, $thread_pid) = @_;
$cpus[$common_cpu]{$thread_pid}{destroyed}++;
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
}
sub workqueue::workqueue_creation
{
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
$common_pid, $common_comm,
$thread_comm, $thread_pid, $cpu) = @_;
$cpus[$common_cpu]{$thread_pid}{created}++;
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
}
sub workqueue::workqueue_execution
{
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
$common_pid, $common_comm,
$thread_comm, $thread_pid, $func) = @_;
$cpus[$common_cpu]{$thread_pid}{executed}++;
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
}
sub workqueue::workqueue_insertion
{
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
$common_pid, $common_comm,
$thread_comm, $thread_pid, $func) = @_;
$cpus[$common_cpu]{$thread_pid}{inserted}++;
$cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm;
}
sub trace_end
{
print "workqueue work stats:\n\n";
my $cpu = 0;
printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name");
printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----");
foreach my $pidhash (@cpus) {
while ((my $pid, my $wqhash) = each %$pidhash) {
my $ins = $$wqhash{'inserted'} || 0;
my $exe = $$wqhash{'executed'} || 0;
my $comm = $$wqhash{'comm'} || "";
if ($ins || $exe) {
printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm);
}
}
$cpu++;
}
$cpu = 0;
print "\nworkqueue lifecycle stats:\n\n";
printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name");
printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----");
foreach my $pidhash (@cpus) {
while ((my $pid, my $wqhash) = each %$pidhash) {
my $created = $$wqhash{'created'} || 0;
my $destroyed = $$wqhash{'destroyed'} || 0;
my $comm = $$wqhash{'comm'} || "";
if ($created || $destroyed) {
printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed,
$comm);
}
}
$cpu++;
}
print_unhandled();
}
my %unhandled;
sub print_unhandled
{
if ((scalar keys %unhandled) == 0) {
return;
}
print "\nunhandled events:\n\n";
printf("%-40s %10s\n", "event", "count");
printf("%-40s %10s\n", "----------------------------------------",
"-----------");
foreach my $event_name (keys %unhandled) {
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
}
}
sub trace_unhandled
{
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
$common_pid, $common_comm) = @_;
$unhandled{$event_name}++;
}
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
extern int verbose; extern int verbose;
bool test_attr__enabled;
static char *dir; static char *dir;
void test_attr__init(void) void test_attr__init(void)
...@@ -146,7 +144,7 @@ static int run_dir(const char *d, const char *perf) ...@@ -146,7 +144,7 @@ static int run_dir(const char *d, const char *perf)
{ {
char cmd[3*PATH_MAX]; char cmd[3*PATH_MAX];
snprintf(cmd, 3*PATH_MAX, "python %s/attr.py -d %s/attr/ -p %s %s", snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s",
d, d, perf, verbose ? "-v" : ""); d, d, perf, verbose ? "-v" : "");
return system(cmd); return system(cmd);
......
...@@ -68,7 +68,7 @@ class Event(dict): ...@@ -68,7 +68,7 @@ class Event(dict):
self[key] = val self[key] = val
def __init__(self, name, data, base): def __init__(self, name, data, base):
log.info(" Event %s" % name); log.debug(" Event %s" % name);
self.name = name; self.name = name;
self.group = '' self.group = ''
self.add(base) self.add(base)
...@@ -97,6 +97,14 @@ class Event(dict): ...@@ -97,6 +97,14 @@ class Event(dict):
return False return False
return True return True
def diff(self, other):
for t in Event.terms:
if not self.has_key(t) or not other.has_key(t):
continue
if not self.compare_data(self[t], other[t]):
log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
# Test file description needs to have following sections: # Test file description needs to have following sections:
# [config] # [config]
# - just single instance in file # - just single instance in file
...@@ -113,7 +121,7 @@ class Test(object): ...@@ -113,7 +121,7 @@ class Test(object):
parser = ConfigParser.SafeConfigParser() parser = ConfigParser.SafeConfigParser()
parser.read(path) parser.read(path)
log.warning("running '%s'" % path) log.debug("running '%s'" % path)
self.path = path self.path = path
self.test_dir = options.test_dir self.test_dir = options.test_dir
...@@ -128,7 +136,7 @@ class Test(object): ...@@ -128,7 +136,7 @@ class Test(object):
self.expect = {} self.expect = {}
self.result = {} self.result = {}
log.info(" loading expected events"); log.debug(" loading expected events");
self.load_events(path, self.expect) self.load_events(path, self.expect)
def is_event(self, name): def is_event(self, name):
...@@ -164,7 +172,7 @@ class Test(object): ...@@ -164,7 +172,7 @@ class Test(object):
self.perf, self.command, tempdir, self.args) self.perf, self.command, tempdir, self.args)
ret = os.WEXITSTATUS(os.system(cmd)) ret = os.WEXITSTATUS(os.system(cmd))
log.info(" running '%s' ret %d " % (cmd, ret)) log.warning(" running '%s' ret %d " % (cmd, ret))
if ret != int(self.ret): if ret != int(self.ret):
raise Unsup(self) raise Unsup(self)
...@@ -172,7 +180,7 @@ class Test(object): ...@@ -172,7 +180,7 @@ class Test(object):
def compare(self, expect, result): def compare(self, expect, result):
match = {} match = {}
log.info(" compare"); log.debug(" compare");
# For each expected event find all matching # For each expected event find all matching
# events in result. Fail if there's not any. # events in result. Fail if there's not any.
...@@ -187,10 +195,11 @@ class Test(object): ...@@ -187,10 +195,11 @@ class Test(object):
else: else:
log.debug(" ->FAIL"); log.debug(" ->FAIL");
log.info(" match: [%s] matches %s" % (exp_name, str(exp_list))) log.debug(" match: [%s] matches %s" % (exp_name, str(exp_list)))
# we did not any matching event - fail # we did not any matching event - fail
if (not exp_list): if (not exp_list):
exp_event.diff(res_event)
raise Fail(self, 'match failure'); raise Fail(self, 'match failure');
match[exp_name] = exp_list match[exp_name] = exp_list
...@@ -208,10 +217,10 @@ class Test(object): ...@@ -208,10 +217,10 @@ class Test(object):
if res_group not in match[group]: if res_group not in match[group]:
raise Fail(self, 'group failure') raise Fail(self, 'group failure')
log.info(" group: [%s] matches group leader %s" % log.debug(" group: [%s] matches group leader %s" %
(exp_name, str(match[group]))) (exp_name, str(match[group])))
log.info(" matched") log.debug(" matched")
def resolve_groups(self, events): def resolve_groups(self, events):
for name, event in events.items(): for name, event in events.items():
...@@ -233,7 +242,7 @@ class Test(object): ...@@ -233,7 +242,7 @@ class Test(object):
self.run_cmd(tempdir); self.run_cmd(tempdir);
# load events expectation for the test # load events expectation for the test
log.info(" loading result events"); log.debug(" loading result events");
for f in glob.glob(tempdir + '/event*'): for f in glob.glob(tempdir + '/event*'):
self.load_events(f, self.result); self.load_events(f, self.result);
......
[config] [config]
command = record command = record
args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1 args = -e '{cycles,instructions}' kill >/dev/null 2>&1
[event-1:base-record] [event-1:base-record]
fd=1 fd=1
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Builtin regression testing command: ever growing number of sanity tests * Builtin regression testing command: ever growing number of sanity tests
*/ */
#include "builtin.h" #include "builtin.h"
#include "intlist.h"
#include "tests.h" #include "tests.h"
#include "debug.h" #include "debug.h"
#include "color.h" #include "color.h"
...@@ -68,6 +69,14 @@ static struct test { ...@@ -68,6 +69,14 @@ static struct test {
.desc = "struct perf_event_attr setup", .desc = "struct perf_event_attr setup",
.func = test__attr, .func = test__attr,
}, },
{
.desc = "Test matching and linking mutliple hists",
.func = test__hists_link,
},
{
.desc = "Try 'use perf' in python, checking link problems",
.func = test__python_use,
},
{ {
.func = NULL, .func = NULL,
}, },
...@@ -97,7 +106,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) ...@@ -97,7 +106,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
return false; return false;
} }
static int __cmd_test(int argc, const char *argv[]) static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{ {
int i = 0; int i = 0;
int width = 0; int width = 0;
...@@ -118,13 +127,28 @@ static int __cmd_test(int argc, const char *argv[]) ...@@ -118,13 +127,28 @@ static int __cmd_test(int argc, const char *argv[])
continue; continue;
pr_info("%2d: %-*s:", i, width, tests[curr].desc); pr_info("%2d: %-*s:", i, width, tests[curr].desc);
if (intlist__find(skiplist, i)) {
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n");
continue;
}
pr_debug("\n--- start ---\n"); pr_debug("\n--- start ---\n");
err = tests[curr].func(); err = tests[curr].func();
pr_debug("---- end ----\n%s:", tests[curr].desc); pr_debug("---- end ----\n%s:", tests[curr].desc);
if (err)
color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); switch (err) {
else case TEST_OK:
pr_info(" Ok\n"); pr_info(" Ok\n");
break;
case TEST_SKIP:
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
break;
case TEST_FAIL:
default:
color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
break;
}
} }
return 0; return 0;
...@@ -152,11 +176,14 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -152,11 +176,14 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
NULL, NULL,
}; };
const char *skip = NULL;
const struct option test_options[] = { const struct option test_options[] = {
OPT_STRING('s', "skip", &skip, "tests", "tests to skip"),
OPT_INCR('v', "verbose", &verbose, OPT_INCR('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"), "be more verbose (show symbol address, etc)"),
OPT_END() OPT_END()
}; };
struct intlist *skiplist = NULL;
argc = parse_options(argc, argv, test_options, test_usage, 0); argc = parse_options(argc, argv, test_options, test_usage, 0);
if (argc >= 1 && !strcmp(argv[0], "list")) if (argc >= 1 && !strcmp(argv[0], "list"))
...@@ -169,5 +196,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -169,5 +196,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
if (symbol__init() < 0) if (symbol__init() < 0)
return -1; return -1;
return __cmd_test(argc, argv); if (skip != NULL)
skiplist = intlist__new(skip);
return __cmd_test(argc, argv, skiplist);
} }
...@@ -22,7 +22,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) ...@@ -22,7 +22,7 @@ static int perf_evsel__roundtrip_cache_name_test(void)
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
__perf_evsel__hw_cache_type_op_res_name(type, op, i, __perf_evsel__hw_cache_type_op_res_name(type, op, i,
name, sizeof(name)); name, sizeof(name));
err = parse_events(evlist, name, 0); err = parse_events(evlist, name);
if (err) if (err)
ret = err; ret = err;
} }
...@@ -70,7 +70,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) ...@@ -70,7 +70,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < nr_names; ++i) { for (i = 0; i < nr_names; ++i) {
err = parse_events(evlist, names[i], 0); err = parse_events(evlist, names[i]);
if (err) { if (err) {
pr_debug("failed to parse event '%s', err %d\n", pr_debug("failed to parse event '%s', err %d\n",
names[i], err); names[i], err);
......
#include "perf.h"
#include "tests.h"
#include "debug.h"
#include "symbol.h"
#include "sort.h"
#include "evsel.h"
#include "evlist.h"
#include "machine.h"
#include "thread.h"
#include "parse-events.h"
static struct {
u32 pid;
const char *comm;
} fake_threads[] = {
{ 100, "perf" },
{ 200, "perf" },
{ 300, "bash" },
};
static struct {
u32 pid;
u64 start;
const char *filename;
} fake_mmap_info[] = {
{ 100, 0x40000, "perf" },
{ 100, 0x50000, "libc" },
{ 100, 0xf0000, "[kernel]" },
{ 200, 0x40000, "perf" },
{ 200, 0x50000, "libc" },
{ 200, 0xf0000, "[kernel]" },
{ 300, 0x40000, "bash" },
{ 300, 0x50000, "libc" },
{ 300, 0xf0000, "[kernel]" },
};
struct fake_sym {
u64 start;
u64 length;
const char *name;
};
static struct fake_sym perf_syms[] = {
{ 700, 100, "main" },
{ 800, 100, "run_command" },
{ 900, 100, "cmd_record" },
};
static struct fake_sym bash_syms[] = {
{ 700, 100, "main" },
{ 800, 100, "xmalloc" },
{ 900, 100, "xfree" },
};
static struct fake_sym libc_syms[] = {
{ 700, 100, "malloc" },
{ 800, 100, "free" },
{ 900, 100, "realloc" },
};
static struct fake_sym kernel_syms[] = {
{ 700, 100, "schedule" },
{ 800, 100, "page_fault" },
{ 900, 100, "sys_perf_event_open" },
};
static struct {
const char *dso_name;
struct fake_sym *syms;
size_t nr_syms;
} fake_symbols[] = {
{ "perf", perf_syms, ARRAY_SIZE(perf_syms) },
{ "bash", bash_syms, ARRAY_SIZE(bash_syms) },
{ "libc", libc_syms, ARRAY_SIZE(libc_syms) },
{ "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) },
};
static struct machine *setup_fake_machine(struct machines *machines)
{
struct machine *machine = machines__find(machines, HOST_KERNEL_ID);
size_t i;
if (machine == NULL) {
pr_debug("Not enough memory for machine setup\n");
return NULL;
}
for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
struct thread *thread;
thread = machine__findnew_thread(machine, fake_threads[i].pid);
if (thread == NULL)
goto out;
thread__set_comm(thread, fake_threads[i].comm);
}
for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
union perf_event fake_mmap_event = {
.mmap = {
.header = { .misc = PERF_RECORD_MISC_USER, },
.pid = fake_mmap_info[i].pid,
.start = fake_mmap_info[i].start,
.len = 0x1000ULL,
.pgoff = 0ULL,
},
};
strcpy(fake_mmap_event.mmap.filename,
fake_mmap_info[i].filename);
machine__process_mmap_event(machine, &fake_mmap_event);
}
for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
size_t k;
struct dso *dso;
dso = __dsos__findnew(&machine->user_dsos,
fake_symbols[i].dso_name);
if (dso == NULL)
goto out;
/* emulate dso__load() */
dso__set_loaded(dso, MAP__FUNCTION);
for (k = 0; k < fake_symbols[i].nr_syms; k++) {
struct symbol *sym;
struct fake_sym *fsym = &fake_symbols[i].syms[k];
sym = symbol__new(fsym->start, fsym->length,
STB_GLOBAL, fsym->name);
if (sym == NULL)
goto out;
symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
}
}
return machine;
out:
pr_debug("Not enough memory for machine setup\n");
machine__delete_threads(machine);
machine__delete(machine);
return NULL;
}
struct sample {
u32 pid;
u64 ip;
struct thread *thread;
struct map *map;
struct symbol *sym;
};
static struct sample fake_common_samples[] = {
/* perf [kernel] schedule() */
{ .pid = 100, .ip = 0xf0000 + 700, },
/* perf [perf] main() */
{ .pid = 200, .ip = 0x40000 + 700, },
/* perf [perf] cmd_record() */
{ .pid = 200, .ip = 0x40000 + 900, },
/* bash [bash] xmalloc() */
{ .pid = 300, .ip = 0x40000 + 800, },
/* bash [libc] malloc() */
{ .pid = 300, .ip = 0x50000 + 700, },
};
static struct sample fake_samples[][5] = {
{
/* perf [perf] run_command() */
{ .pid = 100, .ip = 0x40000 + 800, },
/* perf [libc] malloc() */
{ .pid = 100, .ip = 0x50000 + 700, },
/* perf [kernel] page_fault() */
{ .pid = 100, .ip = 0xf0000 + 800, },
/* perf [kernel] sys_perf_event_open() */
{ .pid = 200, .ip = 0xf0000 + 900, },
/* bash [libc] free() */
{ .pid = 300, .ip = 0x50000 + 800, },
},
{
/* perf [libc] free() */
{ .pid = 200, .ip = 0x50000 + 800, },
/* bash [libc] malloc() */
{ .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */
/* bash [bash] xfee() */
{ .pid = 300, .ip = 0x40000 + 900, },
/* bash [libc] realloc() */
{ .pid = 300, .ip = 0x50000 + 900, },
/* bash [kernel] page_fault() */
{ .pid = 300, .ip = 0xf0000 + 800, },
},
};
static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
{
struct perf_evsel *evsel;
struct addr_location al;
struct hist_entry *he;
struct perf_sample sample = { .cpu = 0, };
size_t i = 0, k;
/*
* each evsel will have 10 samples - 5 common and 5 distinct.
* However the second evsel also has a collapsed entry for
* "bash [libc] malloc" so total 9 entries will be in the tree.
*/
list_for_each_entry(evsel, &evlist->entries, node) {
for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
const union perf_event event = {
.ip = {
.header = {
.misc = PERF_RECORD_MISC_USER,
},
.pid = fake_common_samples[k].pid,
.ip = fake_common_samples[k].ip,
},
};
if (perf_event__preprocess_sample(&event, machine, &al,
&sample, 0) < 0)
goto out;
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
if (he == NULL)
goto out;
fake_common_samples[k].thread = al.thread;
fake_common_samples[k].map = al.map;
fake_common_samples[k].sym = al.sym;
}
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
const union perf_event event = {
.ip = {
.header = {
.misc = PERF_RECORD_MISC_USER,
},
.pid = fake_samples[i][k].pid,
.ip = fake_samples[i][k].ip,
},
};
if (perf_event__preprocess_sample(&event, machine, &al,
&sample, 0) < 0)
goto out;
he = __hists__add_entry(&evsel->hists, &al, NULL, 1);
if (he == NULL)
goto out;
fake_samples[i][k].thread = al.thread;
fake_samples[i][k].map = al.map;
fake_samples[i][k].sym = al.sym;
}
i++;
}
return 0;
out:
pr_debug("Not enough memory for adding a hist entry\n");
return -1;
}
static int find_sample(struct sample *samples, size_t nr_samples,
struct thread *t, struct map *m, struct symbol *s)
{
while (nr_samples--) {
if (samples->thread == t && samples->map == m &&
samples->sym == s)
return 1;
samples++;
}
return 0;
}
static int __validate_match(struct hists *hists)
{
size_t count = 0;
struct rb_root *root;
struct rb_node *node;
/*
* Only entries from fake_common_samples should have a pair.
*/
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
node = rb_first(root);
while (node) {
struct hist_entry *he;
he = rb_entry(node, struct hist_entry, rb_node_in);
if (hist_entry__has_pairs(he)) {
if (find_sample(fake_common_samples,
ARRAY_SIZE(fake_common_samples),
he->thread, he->ms.map, he->ms.sym)) {
count++;
} else {
pr_debug("Can't find the matched entry\n");
return -1;
}
}
node = rb_next(node);
}
if (count != ARRAY_SIZE(fake_common_samples)) {
pr_debug("Invalid count for matched entries: %zd of %zd\n",
count, ARRAY_SIZE(fake_common_samples));
return -1;
}
return 0;
}
static int validate_match(struct hists *leader, struct hists *other)
{
return __validate_match(leader) || __validate_match(other);
}
static int __validate_link(struct hists *hists, int idx)
{
size_t count = 0;
size_t count_pair = 0;
size_t count_dummy = 0;
struct rb_root *root;
struct rb_node *node;
/*
* Leader hists (idx = 0) will have dummy entries from other,
* and some entries will have no pair. However every entry
* in other hists should have (dummy) pair.
*/
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
node = rb_first(root);
while (node) {
struct hist_entry *he;
he = rb_entry(node, struct hist_entry, rb_node_in);
if (hist_entry__has_pairs(he)) {
if (!find_sample(fake_common_samples,
ARRAY_SIZE(fake_common_samples),
he->thread, he->ms.map, he->ms.sym) &&
!find_sample(fake_samples[idx],
ARRAY_SIZE(fake_samples[idx]),
he->thread, he->ms.map, he->ms.sym)) {
count_dummy++;
}
count_pair++;
} else if (idx) {
pr_debug("A entry from the other hists should have pair\n");
return -1;
}
count++;
node = rb_next(node);
}
/*
* Note that we have a entry collapsed in the other (idx = 1) hists.
*/
if (idx == 0) {
if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) {
pr_debug("Invalid count of dummy entries: %zd of %zd\n",
count_dummy, ARRAY_SIZE(fake_samples[1]) - 1);
return -1;
}
if (count != count_pair + ARRAY_SIZE(fake_samples[0])) {
pr_debug("Invalid count of total leader entries: %zd of %zd\n",
count, count_pair + ARRAY_SIZE(fake_samples[0]));
return -1;
}
} else {
if (count != count_pair) {
pr_debug("Invalid count of total other entries: %zd of %zd\n",
count, count_pair);
return -1;
}
if (count_dummy > 0) {
pr_debug("Other hists should not have dummy entries: %zd\n",
count_dummy);
return -1;
}
}
return 0;
}
static int validate_link(struct hists *leader, struct hists *other)
{
return __validate_link(leader, 0) || __validate_link(other, 1);
}
static void print_hists(struct hists *hists)
{
int i = 0;
struct rb_root *root;
struct rb_node *node;
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
pr_info("----- %s --------\n", __func__);
node = rb_first(root);
while (node) {
struct hist_entry *he;
he = rb_entry(node, struct hist_entry, rb_node_in);
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
i, he->thread->comm, he->ms.map->dso->short_name,
he->ms.sym->name, he->stat.period);
i++;
node = rb_next(node);
}
}
int test__hists_link(void)
{
int err = -1;
struct machines machines;
struct machine *machine = NULL;
struct perf_evsel *evsel, *first;
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
if (evlist == NULL)
return -ENOMEM;
err = parse_events(evlist, "cpu-clock");
if (err)
goto out;
err = parse_events(evlist, "task-clock");
if (err)
goto out;
/* default sort order (comm,dso,sym) will be used */
setup_sorting(NULL, NULL);
machines__init(&machines);
/* setup threads/dso/map/symbols also */
machine = setup_fake_machine(&machines);
if (!machine)
goto out;
if (verbose > 1)
machine__fprintf(machine, stderr);
/* process sample events */
err = add_hist_entries(evlist, machine);
if (err < 0)
goto out;
list_for_each_entry(evsel, &evlist->entries, node) {
hists__collapse_resort(&evsel->hists);
if (verbose > 2)
print_hists(&evsel->hists);
}
first = perf_evlist__first(evlist);
evsel = perf_evlist__last(evlist);
/* match common entries */
hists__match(&first->hists, &evsel->hists);
err = validate_match(&first->hists, &evsel->hists);
if (err)
goto out;
/* link common and/or dummy entries */
hists__link(&first->hists, &evsel->hists);
err = validate_link(&first->hists, &evsel->hists);
if (err)
goto out;
err = 0;
out:
/* tear down everything */
perf_evlist__delete(evlist);
machines__exit(&machines);
return err;
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "evsel.h" #include "evsel.h"
#include "evlist.h" #include "evlist.h"
#include "sysfs.h" #include "sysfs.h"
#include "debugfs.h"
#include "tests.h" #include "tests.h"
#include <linux/hw_breakpoint.h> #include <linux/hw_breakpoint.h>
...@@ -463,10 +464,10 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) ...@@ -463,10 +464,10 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
static int test__checkterms_simple(struct list_head *terms) static int test__checkterms_simple(struct list_head *terms)
{ {
struct parse_events__term *term; struct parse_events_term *term;
/* config=10 */ /* config=10 */
term = list_entry(terms->next, struct parse_events__term, list); term = list_entry(terms->next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term", TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
TEST_ASSERT_VAL("wrong type val", TEST_ASSERT_VAL("wrong type val",
...@@ -475,7 +476,7 @@ static int test__checkterms_simple(struct list_head *terms) ...@@ -475,7 +476,7 @@ static int test__checkterms_simple(struct list_head *terms)
TEST_ASSERT_VAL("wrong config", !term->config); TEST_ASSERT_VAL("wrong config", !term->config);
/* config1 */ /* config1 */
term = list_entry(term->list.next, struct parse_events__term, list); term = list_entry(term->list.next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term", TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1);
TEST_ASSERT_VAL("wrong type val", TEST_ASSERT_VAL("wrong type val",
...@@ -484,7 +485,7 @@ static int test__checkterms_simple(struct list_head *terms) ...@@ -484,7 +485,7 @@ static int test__checkterms_simple(struct list_head *terms)
TEST_ASSERT_VAL("wrong config", !term->config); TEST_ASSERT_VAL("wrong config", !term->config);
/* config2=3 */ /* config2=3 */
term = list_entry(term->list.next, struct parse_events__term, list); term = list_entry(term->list.next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term", TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2);
TEST_ASSERT_VAL("wrong type val", TEST_ASSERT_VAL("wrong type val",
...@@ -493,7 +494,7 @@ static int test__checkterms_simple(struct list_head *terms) ...@@ -493,7 +494,7 @@ static int test__checkterms_simple(struct list_head *terms)
TEST_ASSERT_VAL("wrong config", !term->config); TEST_ASSERT_VAL("wrong config", !term->config);
/* umask=1*/ /* umask=1*/
term = list_entry(term->list.next, struct parse_events__term, list); term = list_entry(term->list.next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term", TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_USER); term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
TEST_ASSERT_VAL("wrong type val", TEST_ASSERT_VAL("wrong type val",
...@@ -782,13 +783,70 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -782,13 +783,70 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
return 0; return 0;
} }
struct test__event_st { static int count_tracepoints(void)
{
char events_path[PATH_MAX];
struct dirent *events_ent;
DIR *events_dir;
int cnt = 0;
scnprintf(events_path, PATH_MAX, "%s/tracing/events",
debugfs_find_mountpoint());
events_dir = opendir(events_path);
TEST_ASSERT_VAL("Can't open events dir", events_dir);
while ((events_ent = readdir(events_dir))) {
char sys_path[PATH_MAX];
struct dirent *sys_ent;
DIR *sys_dir;
if (!strcmp(events_ent->d_name, ".")
|| !strcmp(events_ent->d_name, "..")
|| !strcmp(events_ent->d_name, "enable")
|| !strcmp(events_ent->d_name, "header_event")
|| !strcmp(events_ent->d_name, "header_page"))
continue;
scnprintf(sys_path, PATH_MAX, "%s/%s",
events_path, events_ent->d_name);
sys_dir = opendir(sys_path);
TEST_ASSERT_VAL("Can't open sys dir", sys_dir);
while ((sys_ent = readdir(sys_dir))) {
if (!strcmp(sys_ent->d_name, ".")
|| !strcmp(sys_ent->d_name, "..")
|| !strcmp(sys_ent->d_name, "enable")
|| !strcmp(sys_ent->d_name, "filter"))
continue;
cnt++;
}
closedir(sys_dir);
}
closedir(events_dir);
return cnt;
}
static int test__all_tracepoints(struct perf_evlist *evlist)
{
TEST_ASSERT_VAL("wrong events count",
count_tracepoints() == evlist->nr_entries);
return test__checkevent_tracepoint_multi(evlist);
}
struct evlist_test {
const char *name; const char *name;
__u32 type; __u32 type;
int (*check)(struct perf_evlist *evlist); int (*check)(struct perf_evlist *evlist);
}; };
static struct test__event_st test__events[] = { static struct evlist_test test__events[] = {
[0] = { [0] = {
.name = "syscalls:sys_enter_open", .name = "syscalls:sys_enter_open",
.check = test__checkevent_tracepoint, .check = test__checkevent_tracepoint,
...@@ -921,9 +979,13 @@ static struct test__event_st test__events[] = { ...@@ -921,9 +979,13 @@ static struct test__event_st test__events[] = {
.name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",
.check = test__group5, .check = test__group5,
}, },
[33] = {
.name = "*:*",
.check = test__all_tracepoints,
},
}; };
static struct test__event_st test__events_pmu[] = { static struct evlist_test test__events_pmu[] = {
[0] = { [0] = {
.name = "cpu/config=10,config1,config2=3,period=1000/u", .name = "cpu/config=10,config1,config2=3,period=1000/u",
.check = test__checkevent_pmu, .check = test__checkevent_pmu,
...@@ -934,20 +996,20 @@ static struct test__event_st test__events_pmu[] = { ...@@ -934,20 +996,20 @@ static struct test__event_st test__events_pmu[] = {
}, },
}; };
struct test__term { struct terms_test {
const char *str; const char *str;
__u32 type; __u32 type;
int (*check)(struct list_head *terms); int (*check)(struct list_head *terms);
}; };
static struct test__term test__terms[] = { static struct terms_test test__terms[] = {
[0] = { [0] = {
.str = "config=10,config1,config2=3,umask=1", .str = "config=10,config1,config2=3,umask=1",
.check = test__checkterms_simple, .check = test__checkterms_simple,
}, },
}; };
static int test_event(struct test__event_st *e) static int test_event(struct evlist_test *e)
{ {
struct perf_evlist *evlist; struct perf_evlist *evlist;
int ret; int ret;
...@@ -956,7 +1018,7 @@ static int test_event(struct test__event_st *e) ...@@ -956,7 +1018,7 @@ static int test_event(struct test__event_st *e)
if (evlist == NULL) if (evlist == NULL)
return -ENOMEM; return -ENOMEM;
ret = parse_events(evlist, e->name, 0); ret = parse_events(evlist, e->name);
if (ret) { if (ret) {
pr_debug("failed to parse event '%s', err %d\n", pr_debug("failed to parse event '%s', err %d\n",
e->name, ret); e->name, ret);
...@@ -969,13 +1031,13 @@ static int test_event(struct test__event_st *e) ...@@ -969,13 +1031,13 @@ static int test_event(struct test__event_st *e)
return ret; return ret;
} }
static int test_events(struct test__event_st *events, unsigned cnt) static int test_events(struct evlist_test *events, unsigned cnt)
{ {
int ret1, ret2 = 0; int ret1, ret2 = 0;
unsigned i; unsigned i;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
struct test__event_st *e = &events[i]; struct evlist_test *e = &events[i];
pr_debug("running test %d '%s'\n", i, e->name); pr_debug("running test %d '%s'\n", i, e->name);
ret1 = test_event(e); ret1 = test_event(e);
...@@ -986,7 +1048,7 @@ static int test_events(struct test__event_st *events, unsigned cnt) ...@@ -986,7 +1048,7 @@ static int test_events(struct test__event_st *events, unsigned cnt)
return ret2; return ret2;
} }
static int test_term(struct test__term *t) static int test_term(struct terms_test *t)
{ {
struct list_head *terms; struct list_head *terms;
int ret; int ret;
...@@ -1010,13 +1072,13 @@ static int test_term(struct test__term *t) ...@@ -1010,13 +1072,13 @@ static int test_term(struct test__term *t)
return ret; return ret;
} }
static int test_terms(struct test__term *terms, unsigned cnt) static int test_terms(struct terms_test *terms, unsigned cnt)
{ {
int ret = 0; int ret = 0;
unsigned i; unsigned i;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
struct test__term *t = &terms[i]; struct terms_test *t = &terms[i];
pr_debug("running test %d '%s'\n", i, t->str); pr_debug("running test %d '%s'\n", i, t->str);
ret = test_term(t); ret = test_term(t);
...@@ -1067,7 +1129,7 @@ static int test_pmu_events(void) ...@@ -1067,7 +1129,7 @@ static int test_pmu_events(void)
while (!ret && (ent = readdir(dir))) { while (!ret && (ent = readdir(dir))) {
#define MAX_NAME 100 #define MAX_NAME 100
struct test__event_st e; struct evlist_test e;
char name[MAX_NAME]; char name[MAX_NAME];
if (!strcmp(ent->d_name, ".") || if (!strcmp(ent->d_name, ".") ||
......
...@@ -19,10 +19,8 @@ static struct test_format { ...@@ -19,10 +19,8 @@ static struct test_format {
{ "krava23", "config2:28-29,38\n", }, { "krava23", "config2:28-29,38\n", },
}; };
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
/* Simulated users input. */ /* Simulated users input. */
static struct parse_events__term test_terms[] = { static struct parse_events_term test_terms[] = {
{ {
.config = (char *) "krava01", .config = (char *) "krava01",
.val.num = 15, .val.num = 15,
...@@ -78,7 +76,6 @@ static struct parse_events__term test_terms[] = { ...@@ -78,7 +76,6 @@ static struct parse_events__term test_terms[] = {
.type_term = PARSE_EVENTS__TERM_TYPE_USER, .type_term = PARSE_EVENTS__TERM_TYPE_USER,
}, },
}; };
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
/* /*
* Prepare format directory data, exported by kernel * Prepare format directory data, exported by kernel
...@@ -93,7 +90,7 @@ static char *test_format_dir_get(void) ...@@ -93,7 +90,7 @@ static char *test_format_dir_get(void)
if (!mkdtemp(dir)) if (!mkdtemp(dir))
return NULL; return NULL;
for (i = 0; i < TEST_FORMATS_CNT; i++) { for (i = 0; i < ARRAY_SIZE(test_formats); i++) {
static char name[PATH_MAX]; static char name[PATH_MAX];
struct test_format *format = &test_formats[i]; struct test_format *format = &test_formats[i];
FILE *file; FILE *file;
...@@ -130,14 +127,12 @@ static struct list_head *test_terms_list(void) ...@@ -130,14 +127,12 @@ static struct list_head *test_terms_list(void)
static LIST_HEAD(terms); static LIST_HEAD(terms);
unsigned int i; unsigned int i;
for (i = 0; i < TERMS_CNT; i++) for (i = 0; i < ARRAY_SIZE(test_terms); i++)
list_add_tail(&test_terms[i].list, &terms); list_add_tail(&test_terms[i].list, &terms);
return &terms; return &terms;
} }
#undef TERMS_CNT
int test__pmu(void) int test__pmu(void)
{ {
char *format = test_format_dir_get(); char *format = test_format_dir_get();
......
/*
* Just test if we can load the python binding.
*/
#include <stdio.h>
#include <stdlib.h>
#include "tests.h"
extern int verbose;
int test__python_use(void)
{
char *cmd;
int ret;
if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s",
PYTHONPATH, PYTHON, verbose ? "" : "2> /dev/null") < 0)
return -1;
ret = system(cmd) ? -1 : 0;
free(cmd);
return ret;
}
#ifndef TESTS_H #ifndef TESTS_H
#define TESTS_H #define TESTS_H
enum {
TEST_OK = 0,
TEST_FAIL = -1,
TEST_SKIP = -2,
};
/* Tests */ /* Tests */
int test__vmlinux_matches_kallsyms(void); int test__vmlinux_matches_kallsyms(void);
int test__open_syscall_event(void); int test__open_syscall_event(void);
...@@ -15,5 +21,7 @@ int test__pmu(void); ...@@ -15,5 +21,7 @@ int test__pmu(void);
int test__attr(void); int test__attr(void);
int test__dso_data(void); int test__dso_data(void);
int test__parse_events(void); int test__parse_events(void);
int test__hists_link(void);
int test__python_use(void);
#endif /* TESTS_H */ #endif /* TESTS_H */
...@@ -101,7 +101,8 @@ int test__vmlinux_matches_kallsyms(void) ...@@ -101,7 +101,8 @@ int test__vmlinux_matches_kallsyms(void)
*/ */
if (machine__load_vmlinux_path(&vmlinux, type, if (machine__load_vmlinux_path(&vmlinux, type,
vmlinux_matches_kallsyms_filter) <= 0) { vmlinux_matches_kallsyms_filter) <= 0) {
pr_debug("machine__load_vmlinux_path "); pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n");
err = TEST_SKIP;
goto out; goto out;
} }
......
...@@ -471,7 +471,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) ...@@ -471,7 +471,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
return row; return row;
} }
static struct ui_browser__colorset { static struct ui_browser_colorset {
const char *name, *fg, *bg; const char *name, *fg, *bg;
int colorset; int colorset;
} ui_browser__colorsets[] = { } ui_browser__colorsets[] = {
...@@ -706,7 +706,7 @@ void ui_browser__init(void) ...@@ -706,7 +706,7 @@ void ui_browser__init(void)
perf_config(ui_browser__color_config, NULL); perf_config(ui_browser__color_config, NULL);
while (ui_browser__colorsets[i].name) { while (ui_browser__colorsets[i].name) {
struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
sltt_set_color(c->colorset, c->name, c->fg, c->bg); sltt_set_color(c->colorset, c->name, c->fg, c->bg);
} }
......
...@@ -182,6 +182,16 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int ...@@ -182,6 +182,16 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
ab->selection = dl; ab->selection = dl;
} }
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
if (!dl || !dl->ins || !ins__is_jump(dl->ins)
|| !disasm_line__has_offset(dl)
|| dl->ops.target.offset >= symbol__size(sym))
return false;
return true;
}
static void annotate_browser__draw_current_jump(struct ui_browser *browser) static void annotate_browser__draw_current_jump(struct ui_browser *browser)
{ {
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
...@@ -195,8 +205,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) ...@@ -195,8 +205,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
if (strstr(sym->name, "@plt")) if (strstr(sym->name, "@plt"))
return; return;
if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || if (!disasm_line__is_valid_jump(cursor, sym))
!disasm_line__has_offset(cursor))
return; return;
target = ab->offsets[cursor->ops.target.offset]; target = ab->offsets[cursor->ops.target.offset];
...@@ -788,16 +797,8 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser ...@@ -788,16 +797,8 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
struct disasm_line *dl = browser->offsets[offset], *dlt; struct disasm_line *dl = browser->offsets[offset], *dlt;
struct browser_disasm_line *bdlt; struct browser_disasm_line *bdlt;
if (!dl || !dl->ins || !ins__is_jump(dl->ins) || if (!disasm_line__is_valid_jump(dl, sym))
!disasm_line__has_offset(dl))
continue;
if (dl->ops.target.offset >= size) {
ui__error("jump to after symbol!\n"
"size: %zx, jump target: %" PRIx64,
size, dl->ops.target.offset);
continue; continue;
}
dlt = browser->offsets[dl->ops.target.offset]; dlt = browser->offsets[dl->ops.target.offset];
/* /*
...@@ -925,7 +926,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -925,7 +926,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
/* /*
* Keep the entries sorted, they are bsearch'ed * Keep the entries sorted, they are bsearch'ed
*/ */
static struct annotate__config { static struct annotate_config {
const char *name; const char *name;
bool *value; bool *value;
} annotate__configs[] = { } annotate__configs[] = {
...@@ -939,7 +940,7 @@ static struct annotate__config { ...@@ -939,7 +940,7 @@ static struct annotate__config {
static int annotate_config__cmp(const void *name, const void *cfgp) static int annotate_config__cmp(const void *name, const void *cfgp)
{ {
const struct annotate__config *cfg = cfgp; const struct annotate_config *cfg = cfgp;
return strcmp(name, cfg->name); return strcmp(name, cfg->name);
} }
...@@ -947,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp) ...@@ -947,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp)
static int annotate__config(const char *var, const char *value, static int annotate__config(const char *var, const char *value,
void *data __maybe_unused) void *data __maybe_unused)
{ {
struct annotate__config *cfg; struct annotate_config *cfg;
const char *name; const char *name;
if (prefixcmp(var, "annotate.") != 0) if (prefixcmp(var, "annotate.") != 0)
...@@ -955,7 +956,7 @@ static int annotate__config(const char *var, const char *value, ...@@ -955,7 +956,7 @@ static int annotate__config(const char *var, const char *value,
name = var + 9; name = var + 9;
cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
sizeof(struct annotate__config), annotate_config__cmp); sizeof(struct annotate_config), annotate_config__cmp);
if (cfg == NULL) if (cfg == NULL)
return -1; return -1;
......
...@@ -8,15 +8,13 @@ ...@@ -8,15 +8,13 @@
#include <signal.h> #include <signal.h>
#define MAX_COLUMNS 32 void perf_gtk__signal(int sig)
static void perf_gtk__signal(int sig)
{ {
perf_gtk__exit(false); perf_gtk__exit(false);
psignal(sig, "perf"); psignal(sig, "perf");
} }
static void perf_gtk__resize_window(GtkWidget *window) void perf_gtk__resize_window(GtkWidget *window)
{ {
GdkRectangle rect; GdkRectangle rect;
GdkScreen *screen; GdkScreen *screen;
...@@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window) ...@@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window)
gtk_window_resize(GTK_WINDOW(window), width, height); gtk_window_resize(GTK_WINDOW(window), width, height);
} }
static const char *perf_gtk__get_percent_color(double percent) const char *perf_gtk__get_percent_color(double percent)
{ {
if (percent >= MIN_RED) if (percent >= MIN_RED)
return "<span fgcolor='red'>"; return "<span fgcolor='red'>";
...@@ -45,147 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent) ...@@ -45,147 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent)
return NULL; return NULL;
} }
#define HPP__COLOR_FN(_name, _field) \
static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
struct hists *hists = he->hists; \
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
const char *markup; \
int ret = 0; \
\
markup = perf_gtk__get_percent_color(percent); \
if (markup) \
ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \
if (markup) \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
\
return ret; \
}
HPP__COLOR_FN(overhead, period)
HPP__COLOR_FN(overhead_sys, period_sys)
HPP__COLOR_FN(overhead_us, period_us)
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
HPP__COLOR_FN(overhead_guest_us, period_guest_us)
#undef HPP__COLOR_FN
void perf_gtk__init_hpp(void)
{
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
perf_hpp__init();
perf_hpp__format[PERF_HPP__OVERHEAD].color =
perf_gtk__hpp_color_overhead;
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
perf_gtk__hpp_color_overhead_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
perf_gtk__hpp_color_overhead_us;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
perf_gtk__hpp_color_overhead_guest_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
perf_gtk__hpp_color_overhead_guest_us;
}
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
GType col_types[MAX_COLUMNS];
GtkCellRenderer *renderer;
struct sort_entry *se;
GtkListStore *store;
struct rb_node *nd;
GtkWidget *view;
int col_idx;
int nr_cols;
char s[512];
struct perf_hpp hpp = {
.buf = s,
.size = sizeof(s),
};
nr_cols = 0;
perf_hpp__for_each_format(fmt)
col_types[nr_cols++] = G_TYPE_STRING;
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
col_types[nr_cols++] = G_TYPE_STRING;
}
store = gtk_list_store_newv(nr_cols, col_types);
view = gtk_tree_view_new();
renderer = gtk_cell_renderer_text_new();
col_idx = 0;
perf_hpp__for_each_format(fmt) {
fmt->header(&hpp);
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, s,
renderer, "markup",
col_idx++, NULL);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, se->se_header,
renderer, "text",
col_idx++, NULL);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
g_object_unref(GTK_TREE_MODEL(store));
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
GtkTreeIter iter;
if (h->filtered)
continue;
gtk_list_store_append(store, &iter);
col_idx = 0;
perf_hpp__for_each_format(fmt) {
if (fmt->color)
fmt->color(&hpp, h);
else
fmt->entry(&hpp, h);
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
se->se_snprintf(h, s, ARRAY_SIZE(s),
hists__col_len(hists, se->se_width_idx));
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
}
gtk_container_add(GTK_CONTAINER(window), view);
}
#ifdef HAVE_GTK_INFO_BAR #ifdef HAVE_GTK_INFO_BAR
static GtkWidget *perf_gtk__setup_info_bar(void) GtkWidget *perf_gtk__setup_info_bar(void)
{ {
GtkWidget *info_bar; GtkWidget *info_bar;
GtkWidget *label; GtkWidget *label;
...@@ -212,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void) ...@@ -212,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void)
} }
#endif #endif
static GtkWidget *perf_gtk__setup_statusbar(void) GtkWidget *perf_gtk__setup_statusbar(void)
{ {
GtkWidget *stbar; GtkWidget *stbar;
unsigned ctxid; unsigned ctxid;
...@@ -226,79 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void) ...@@ -226,79 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
return stbar; return stbar;
} }
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help,
struct hist_browser_timer *hbt __maybe_unused)
{
struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal);
signal(SIGFPE, perf_gtk__signal);
signal(SIGINT, perf_gtk__signal);
signal(SIGQUIT, perf_gtk__signal);
signal(SIGTERM, perf_gtk__signal);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "perf report");
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists;
const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
perf_gtk__show_hists(scrolled_window, hists);
tab_label = gtk_label_new(evname);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
}
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
info_bar = perf_gtk__setup_info_bar();
if (info_bar)
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window);
perf_gtk__resize_window(window);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
ui_helpline__push(help);
gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0;
}
...@@ -33,7 +33,14 @@ void perf_gtk__init_helpline(void); ...@@ -33,7 +33,14 @@ void perf_gtk__init_helpline(void);
void perf_gtk__init_progress(void); void perf_gtk__init_progress(void);
void perf_gtk__init_hpp(void); void perf_gtk__init_hpp(void);
#ifndef HAVE_GTK_INFO_BAR void perf_gtk__signal(int sig);
void perf_gtk__resize_window(GtkWidget *window);
const char *perf_gtk__get_percent_color(double percent);
GtkWidget *perf_gtk__setup_statusbar(void);
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *perf_gtk__setup_info_bar(void);
#else
static inline GtkWidget *perf_gtk__setup_info_bar(void) static inline GtkWidget *perf_gtk__setup_info_bar(void)
{ {
return NULL; return NULL;
......
#include "../evlist.h"
#include "../cache.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
#include "../helpline.h"
#include "gtk.h"
#define MAX_COLUMNS 32
#define HPP__COLOR_FN(_name, _field) \
static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
struct hists *hists = he->hists; \
double percent = 100.0 * he->stat._field / hists->stats.total_period; \
const char *markup; \
int ret = 0; \
\
markup = perf_gtk__get_percent_color(percent); \
if (markup) \
ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \
if (markup) \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
\
return ret; \
}
HPP__COLOR_FN(overhead, period)
HPP__COLOR_FN(overhead_sys, period_sys)
HPP__COLOR_FN(overhead_us, period_us)
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
HPP__COLOR_FN(overhead_guest_us, period_guest_us)
#undef HPP__COLOR_FN
void perf_gtk__init_hpp(void)
{
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
perf_hpp__init();
perf_hpp__format[PERF_HPP__OVERHEAD].color =
perf_gtk__hpp_color_overhead;
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
perf_gtk__hpp_color_overhead_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
perf_gtk__hpp_color_overhead_us;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
perf_gtk__hpp_color_overhead_guest_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
perf_gtk__hpp_color_overhead_guest_us;
}
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
GType col_types[MAX_COLUMNS];
GtkCellRenderer *renderer;
struct sort_entry *se;
GtkListStore *store;
struct rb_node *nd;
GtkWidget *view;
int col_idx;
int nr_cols;
char s[512];
struct perf_hpp hpp = {
.buf = s,
.size = sizeof(s),
};
nr_cols = 0;
perf_hpp__for_each_format(fmt)
col_types[nr_cols++] = G_TYPE_STRING;
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
col_types[nr_cols++] = G_TYPE_STRING;
}
store = gtk_list_store_newv(nr_cols, col_types);
view = gtk_tree_view_new();
renderer = gtk_cell_renderer_text_new();
col_idx = 0;
perf_hpp__for_each_format(fmt) {
fmt->header(&hpp);
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, s,
renderer, "markup",
col_idx++, NULL);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, se->se_header,
renderer, "text",
col_idx++, NULL);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
g_object_unref(GTK_TREE_MODEL(store));
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
GtkTreeIter iter;
if (h->filtered)
continue;
gtk_list_store_append(store, &iter);
col_idx = 0;
perf_hpp__for_each_format(fmt) {
if (fmt->color)
fmt->color(&hpp, h);
else
fmt->entry(&hpp, h);
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
se->se_snprintf(h, s, ARRAY_SIZE(s),
hists__col_len(hists, se->se_width_idx));
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
}
gtk_container_add(GTK_CONTAINER(window), view);
}
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help,
struct hist_browser_timer *hbt __maybe_unused)
{
struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal);
signal(SIGFPE, perf_gtk__signal);
signal(SIGINT, perf_gtk__signal);
signal(SIGQUIT, perf_gtk__signal);
signal(SIGTERM, perf_gtk__signal);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "perf report");
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
info_bar = perf_gtk__setup_info_bar();
if (info_bar)
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists;
const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
perf_gtk__show_hists(scrolled_window, hists);
tab_label = gtk_label_new(evname);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
}
gtk_widget_show_all(window);
perf_gtk__resize_window(window);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
ui_helpline__push(help);
gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0;
}
...@@ -459,7 +459,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, ...@@ -459,7 +459,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
return ret; return ret;
} }
size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
{ {
int i; int i;
size_t ret = 0; size_t ret = 0;
...@@ -467,7 +467,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) ...@@ -467,7 +467,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, 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 (hists->stats.nr_events[i] == 0) if (stats->nr_events[i] == 0)
continue; continue;
name = perf_event__name(i); name = perf_event__name(i);
...@@ -475,7 +475,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) ...@@ -475,7 +475,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
continue; continue;
ret += fprintf(fp, "%16s events: %10d\n", name, ret += fprintf(fp, "%16s events: %10d\n", name,
hists->stats.nr_events[i]); stats->nr_events[i]);
} }
return ret; return ret;
......
...@@ -52,17 +52,6 @@ int ui__warning(const char *format, ...) ...@@ -52,17 +52,6 @@ int ui__warning(const char *format, ...)
return ret; return ret;
} }
int ui__error_paranoid(void)
{
return ui__error("Permission error - are you root?\n"
"Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
" -1 - Not paranoid at all\n"
" 0 - Disallow raw tracepoint access for unpriv\n"
" 1 - Disallow cpu events for unpriv\n"
" 2 - Disallow kernel profiling for unpriv\n");
}
/** /**
* perf_error__register - Register error logging functions * perf_error__register - Register error logging functions
* @eops: The pointer to error logging function struct * @eops: The pointer to error logging function struct
......
...@@ -26,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)') ...@@ -26,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)')
if test -r $GVF if test -r $GVF
then then
VC=$(sed -e 's/^PERF_VERSION = //' <$GVF) VC=$(sed -e 's/^#define PERF_VERSION "\(.*\)"/\1/' <$GVF)
else else
VC=unset VC=unset
fi fi
test "$VN" = "$VC" || { test "$VN" = "$VC" || {
echo >&2 "PERF_VERSION = $VN" echo >&2 "PERF_VERSION = $VN"
echo "PERF_VERSION = $VN" >$GVF echo "#define PERF_VERSION \"$VN\"" >$GVF
} }
...@@ -16,6 +16,5 @@ void trace_event(union perf_event *event); ...@@ -16,6 +16,5 @@ void trace_event(union perf_event *event);
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
int ui__error_paranoid(void);
#endif /* __PERF_DEBUG_H */ #endif /* __PERF_DEBUG_H */
...@@ -22,6 +22,11 @@ ...@@ -22,6 +22,11 @@
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include "perf_regs.h" #include "perf_regs.h"
static struct {
bool sample_id_all;
bool exclude_guest;
} perf_missing_features;
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
static int __perf_evsel__sample_size(u64 sample_type) static int __perf_evsel__sample_size(u64 sample_type)
...@@ -463,7 +468,7 @@ void perf_evsel__config(struct perf_evsel *evsel, ...@@ -463,7 +468,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */ int track = !evsel->idx; /* only the first counter needs these */
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
attr->inherit = !opts->no_inherit; attr->inherit = !opts->no_inherit;
perf_evsel__set_sample_bit(evsel, IP); perf_evsel__set_sample_bit(evsel, IP);
...@@ -513,7 +518,7 @@ void perf_evsel__config(struct perf_evsel *evsel, ...@@ -513,7 +518,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
if (opts->period) if (opts->period)
perf_evsel__set_sample_bit(evsel, PERIOD); perf_evsel__set_sample_bit(evsel, PERIOD);
if (!opts->sample_id_all_missing && if (!perf_missing_features.sample_id_all &&
(opts->sample_time || !opts->no_inherit || (opts->sample_time || !opts->no_inherit ||
perf_target__has_cpu(&opts->target))) perf_target__has_cpu(&opts->target)))
perf_evsel__set_sample_bit(evsel, TIME); perf_evsel__set_sample_bit(evsel, TIME);
...@@ -761,6 +766,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -761,6 +766,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
pid = evsel->cgrp->fd; pid = evsel->cgrp->fd;
} }
fallback_missing_features:
if (perf_missing_features.exclude_guest)
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
retry_sample_id:
if (perf_missing_features.sample_id_all)
evsel->attr.sample_id_all = 0;
for (cpu = 0; cpu < cpus->nr; cpu++) { for (cpu = 0; cpu < cpus->nr; cpu++) {
for (thread = 0; thread < threads->nr; thread++) { for (thread = 0; thread < threads->nr; thread++) {
...@@ -777,13 +789,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -777,13 +789,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
group_fd, flags); group_fd, flags);
if (FD(evsel, cpu, thread) < 0) { if (FD(evsel, cpu, thread) < 0) {
err = -errno; err = -errno;
goto out_close; goto try_fallback;
} }
} }
} }
return 0; return 0;
try_fallback:
if (err != -EINVAL || cpu > 0 || thread > 0)
goto out_close;
if (!perf_missing_features.exclude_guest &&
(evsel->attr.exclude_guest || evsel->attr.exclude_host)) {
perf_missing_features.exclude_guest = true;
goto fallback_missing_features;
} else if (!perf_missing_features.sample_id_all) {
perf_missing_features.sample_id_all = true;
goto retry_sample_id;
}
out_close: out_close:
do { do {
while (--thread >= 0) { while (--thread >= 0) {
...@@ -1353,3 +1378,80 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, ...@@ -1353,3 +1378,80 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
fputc('\n', fp); fputc('\n', fp);
return ++printed; return ++printed;
} }
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
char *msg, size_t msgsize)
{
if ((err == ENOENT || err == ENXIO) &&
evsel->attr.type == PERF_TYPE_HARDWARE &&
evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
/*
* If it's cycles then fall back to hrtimer based
* cpu-clock-tick sw counter, which is always available even if
* no PMU support.
*
* PPC returns ENXIO until 2.6.37 (behavior changed with commit
* b0a873e).
*/
scnprintf(msg, msgsize, "%s",
"The cycles event is not supported, trying to fall back to cpu-clock-ticks");
evsel->attr.type = PERF_TYPE_SOFTWARE;
evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK;
free(evsel->name);
evsel->name = NULL;
return true;
}
return false;
}
int perf_evsel__open_strerror(struct perf_evsel *evsel,
struct perf_target *target,
int err, char *msg, size_t size)
{
switch (err) {
case EPERM:
case EACCES:
return scnprintf(msg, size, "%s",
"You may not have permission to collect %sstats.\n"
"Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
" -1 - Not paranoid at all\n"
" 0 - Disallow raw tracepoint access for unpriv\n"
" 1 - Disallow cpu events for unpriv\n"
" 2 - Disallow kernel profiling for unpriv",
target->system_wide ? "system-wide " : "");
case ENOENT:
return scnprintf(msg, size, "The %s event is not supported.",
perf_evsel__name(evsel));
case EMFILE:
return scnprintf(msg, size, "%s",
"Too many events are opened.\n"
"Try again after reducing the number of events.");
case ENODEV:
if (target->cpu_list)
return scnprintf(msg, size, "%s",
"No such device - did you specify an out-of-range profile CPU?\n");
break;
case EOPNOTSUPP:
if (evsel->attr.precise_ip)
return scnprintf(msg, size, "%s",
"\'precise\' request may not be supported. Try removing 'p' modifier.");
#if defined(__i386__) || defined(__x86_64__)
if (evsel->attr.type == PERF_TYPE_HARDWARE)
return scnprintf(msg, size, "%s",
"No hardware sampling interrupt available.\n"
"No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.");
#endif
break;
default:
break;
}
return scnprintf(msg, size,
"The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n"
"/bin/dmesg may provide additional information.\n"
"No CONFIG_PERF_EVENTS=y kernel support configured?\n",
err, strerror(err), perf_evsel__name(evsel));
}
...@@ -251,4 +251,10 @@ struct perf_attr_details { ...@@ -251,4 +251,10 @@ struct perf_attr_details {
int perf_evsel__fprintf(struct perf_evsel *evsel, int perf_evsel__fprintf(struct perf_evsel *evsel,
struct perf_attr_details *details, FILE *fp); struct perf_attr_details *details, FILE *fp);
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
char *msg, size_t msgsize);
int perf_evsel__open_strerror(struct perf_evsel *evsel,
struct perf_target *target,
int err, char *msg, size_t size);
#endif /* __PERF_EVSEL_H */ #endif /* __PERF_EVSEL_H */
...@@ -148,7 +148,7 @@ static char *do_read_string(int fd, struct perf_header *ph) ...@@ -148,7 +148,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
u32 len; u32 len;
char *buf; char *buf;
sz = read(fd, &len, sizeof(len)); sz = readn(fd, &len, sizeof(len));
if (sz < (ssize_t)sizeof(len)) if (sz < (ssize_t)sizeof(len))
return NULL; return NULL;
...@@ -159,7 +159,7 @@ static char *do_read_string(int fd, struct perf_header *ph) ...@@ -159,7 +159,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
if (!buf) if (!buf)
return NULL; return NULL;
ret = read(fd, buf, len); ret = readn(fd, buf, len);
if (ret == (ssize_t)len) { if (ret == (ssize_t)len) {
/* /*
* strings are padded by zeroes * strings are padded by zeroes
...@@ -287,12 +287,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) ...@@ -287,12 +287,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
struct perf_session *session = container_of(header, struct perf_session *session = container_of(header,
struct perf_session, header); struct perf_session, header);
struct rb_node *nd; struct rb_node *nd;
int err = machine__write_buildid_table(&session->host_machine, fd); int err = machine__write_buildid_table(&session->machines.host, fd);
if (err) if (err)
return err; return err;
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__write_buildid_table(pos, fd); err = machine__write_buildid_table(pos, fd);
if (err) if (err)
...@@ -448,9 +448,9 @@ static int perf_session__cache_build_ids(struct perf_session *session) ...@@ -448,9 +448,9 @@ static int perf_session__cache_build_ids(struct perf_session *session)
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
return -1; return -1;
ret = machine__cache_build_ids(&session->host_machine, debugdir); ret = machine__cache_build_ids(&session->machines.host, debugdir);
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__cache_build_ids(pos, debugdir); ret |= machine__cache_build_ids(pos, debugdir);
} }
...@@ -467,9 +467,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits) ...@@ -467,9 +467,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits)
static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
{ {
struct rb_node *nd; struct rb_node *nd;
bool ret = machine__read_build_ids(&session->host_machine, with_hits); bool ret = machine__read_build_ids(&session->machines.host, with_hits);
for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__read_build_ids(pos, with_hits); ret |= machine__read_build_ids(pos, with_hits);
} }
...@@ -1051,16 +1051,25 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, ...@@ -1051,16 +1051,25 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
struct perf_pmu *pmu = NULL; struct perf_pmu *pmu = NULL;
off_t offset = lseek(fd, 0, SEEK_CUR); off_t offset = lseek(fd, 0, SEEK_CUR);
__u32 pmu_num = 0; __u32 pmu_num = 0;
int ret;
/* write real pmu_num later */ /* write real pmu_num later */
do_write(fd, &pmu_num, sizeof(pmu_num)); ret = do_write(fd, &pmu_num, sizeof(pmu_num));
if (ret < 0)
return ret;
while ((pmu = perf_pmu__scan(pmu))) { while ((pmu = perf_pmu__scan(pmu))) {
if (!pmu->name) if (!pmu->name)
continue; continue;
pmu_num++; pmu_num++;
do_write(fd, &pmu->type, sizeof(pmu->type));
do_write_string(fd, pmu->name); ret = do_write(fd, &pmu->type, sizeof(pmu->type));
if (ret < 0)
return ret;
ret = do_write_string(fd, pmu->name);
if (ret < 0)
return ret;
} }
if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) {
...@@ -1209,14 +1218,14 @@ read_event_desc(struct perf_header *ph, int fd) ...@@ -1209,14 +1218,14 @@ read_event_desc(struct perf_header *ph, int fd)
size_t msz; size_t msz;
/* number of events */ /* number of events */
ret = read(fd, &nre, sizeof(nre)); ret = readn(fd, &nre, sizeof(nre));
if (ret != (ssize_t)sizeof(nre)) if (ret != (ssize_t)sizeof(nre))
goto error; goto error;
if (ph->needs_swap) if (ph->needs_swap)
nre = bswap_32(nre); nre = bswap_32(nre);
ret = read(fd, &sz, sizeof(sz)); ret = readn(fd, &sz, sizeof(sz));
if (ret != (ssize_t)sizeof(sz)) if (ret != (ssize_t)sizeof(sz))
goto error; goto error;
...@@ -1244,7 +1253,7 @@ read_event_desc(struct perf_header *ph, int fd) ...@@ -1244,7 +1253,7 @@ read_event_desc(struct perf_header *ph, int fd)
* must read entire on-file attr struct to * must read entire on-file attr struct to
* sync up with layout. * sync up with layout.
*/ */
ret = read(fd, buf, sz); ret = readn(fd, buf, sz);
if (ret != (ssize_t)sz) if (ret != (ssize_t)sz)
goto error; goto error;
...@@ -1253,7 +1262,7 @@ read_event_desc(struct perf_header *ph, int fd) ...@@ -1253,7 +1262,7 @@ read_event_desc(struct perf_header *ph, int fd)
memcpy(&evsel->attr, buf, msz); memcpy(&evsel->attr, buf, msz);
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != (ssize_t)sizeof(nr)) if (ret != (ssize_t)sizeof(nr))
goto error; goto error;
...@@ -1274,7 +1283,7 @@ read_event_desc(struct perf_header *ph, int fd) ...@@ -1274,7 +1283,7 @@ read_event_desc(struct perf_header *ph, int fd)
evsel->id = id; evsel->id = id;
for (j = 0 ; j < nr; j++) { for (j = 0 ; j < nr; j++) {
ret = read(fd, id, sizeof(*id)); ret = readn(fd, id, sizeof(*id));
if (ret != (ssize_t)sizeof(*id)) if (ret != (ssize_t)sizeof(*id))
goto error; goto error;
if (ph->needs_swap) if (ph->needs_swap)
...@@ -1506,14 +1515,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, ...@@ -1506,14 +1515,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
while (offset < limit) { while (offset < limit) {
ssize_t len; ssize_t len;
if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
return -1; return -1;
if (header->needs_swap) if (header->needs_swap)
perf_event_header__bswap(&old_bev.header); perf_event_header__bswap(&old_bev.header);
len = old_bev.header.size - sizeof(old_bev); len = old_bev.header.size - sizeof(old_bev);
if (read(input, filename, len) != len) if (readn(input, filename, len) != len)
return -1; return -1;
bev.header = old_bev.header; bev.header = old_bev.header;
...@@ -1548,14 +1557,14 @@ static int perf_header__read_build_ids(struct perf_header *header, ...@@ -1548,14 +1557,14 @@ static int perf_header__read_build_ids(struct perf_header *header,
while (offset < limit) { while (offset < limit) {
ssize_t len; ssize_t len;
if (read(input, &bev, sizeof(bev)) != sizeof(bev)) if (readn(input, &bev, sizeof(bev)) != sizeof(bev))
goto out; goto out;
if (header->needs_swap) if (header->needs_swap)
perf_event_header__bswap(&bev.header); perf_event_header__bswap(&bev.header);
len = bev.header.size - sizeof(bev); len = bev.header.size - sizeof(bev);
if (read(input, filename, len) != len) if (readn(input, filename, len) != len)
goto out; goto out;
/* /*
* The a1645ce1 changeset: * The a1645ce1 changeset:
...@@ -1641,7 +1650,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, ...@@ -1641,7 +1650,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
size_t ret; size_t ret;
u32 nr; u32 nr;
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
return -1; return -1;
...@@ -1650,7 +1659,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, ...@@ -1650,7 +1659,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
ph->env.nr_cpus_online = nr; ph->env.nr_cpus_online = nr;
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
return -1; return -1;
...@@ -1684,7 +1693,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused, ...@@ -1684,7 +1693,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused,
uint64_t mem; uint64_t mem;
size_t ret; size_t ret;
ret = read(fd, &mem, sizeof(mem)); ret = readn(fd, &mem, sizeof(mem));
if (ret != sizeof(mem)) if (ret != sizeof(mem))
return -1; return -1;
...@@ -1756,7 +1765,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused, ...@@ -1756,7 +1765,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,
u32 nr, i; u32 nr, i;
struct strbuf sb; struct strbuf sb;
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
return -1; return -1;
...@@ -1792,7 +1801,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused ...@@ -1792,7 +1801,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
char *str; char *str;
struct strbuf sb; struct strbuf sb;
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
return -1; return -1;
...@@ -1813,7 +1822,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused ...@@ -1813,7 +1822,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
} }
ph->env.sibling_cores = strbuf_detach(&sb, NULL); ph->env.sibling_cores = strbuf_detach(&sb, NULL);
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
return -1; return -1;
...@@ -1850,7 +1859,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse ...@@ -1850,7 +1859,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
struct strbuf sb; struct strbuf sb;
/* nr nodes */ /* nr nodes */
ret = read(fd, &nr, sizeof(nr)); ret = readn(fd, &nr, sizeof(nr));
if (ret != sizeof(nr)) if (ret != sizeof(nr))
goto error; goto error;
...@@ -1862,15 +1871,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse ...@@ -1862,15 +1871,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
/* node number */ /* node number */
ret = read(fd, &node, sizeof(node)); ret = readn(fd, &node, sizeof(node));
if (ret != sizeof(node)) if (ret != sizeof(node))
goto error; goto error;
ret = read(fd, &mem_total, sizeof(u64)); ret = readn(fd, &mem_total, sizeof(u64));
if (ret != sizeof(u64)) if (ret != sizeof(u64))
goto error; goto error;
ret = read(fd, &mem_free, sizeof(u64)); ret = readn(fd, &mem_free, sizeof(u64));
if (ret != sizeof(u64)) if (ret != sizeof(u64))
goto error; goto error;
...@@ -1909,7 +1918,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused ...@@ -1909,7 +1918,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
u32 type; u32 type;
struct strbuf sb; struct strbuf sb;
ret = read(fd, &pmu_num, sizeof(pmu_num)); ret = readn(fd, &pmu_num, sizeof(pmu_num));
if (ret != sizeof(pmu_num)) if (ret != sizeof(pmu_num))
return -1; return -1;
...@@ -1925,7 +1934,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused ...@@ -1925,7 +1934,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
strbuf_init(&sb, 128); strbuf_init(&sb, 128);
while (pmu_num) { while (pmu_num) {
if (read(fd, &type, sizeof(type)) != sizeof(type)) if (readn(fd, &type, sizeof(type)) != sizeof(type))
goto error; goto error;
if (ph->needs_swap) if (ph->needs_swap)
type = bswap_32(type); type = bswap_32(type);
...@@ -2912,7 +2921,7 @@ int perf_event__process_tracing_data(union perf_event *event, ...@@ -2912,7 +2921,7 @@ int perf_event__process_tracing_data(union perf_event *event,
session->repipe); session->repipe);
padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read;
if (read(session->fd, buf, padding) < 0) if (readn(session->fd, buf, padding) < 0)
die("reading input file"); die("reading input file");
if (session->repipe) { if (session->repipe) {
int retw = write(STDOUT_FILENO, buf, padding); int retw = write(STDOUT_FILENO, buf, padding);
......
...@@ -82,6 +82,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) ...@@ -82,6 +82,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_DSO, len); hists__new_col_len(hists, HISTC_DSO, len);
} }
if (h->parent)
hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
if (h->branch_info) { if (h->branch_info) {
int symlen; int symlen;
/* /*
...@@ -242,6 +245,14 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) ...@@ -242,6 +245,14 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
if (he->ms.map) if (he->ms.map)
he->ms.map->referenced = true; he->ms.map->referenced = true;
if (he->branch_info) {
if (he->branch_info->from.map)
he->branch_info->from.map->referenced = true;
if (he->branch_info->to.map)
he->branch_info->to.map->referenced = true;
}
if (symbol_conf.use_callchain) if (symbol_conf.use_callchain)
callchain_init(he->callchain); callchain_init(he->callchain);
...@@ -251,7 +262,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) ...@@ -251,7 +262,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
return he; return he;
} }
static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
{ {
if (!h->filtered) { if (!h->filtered) {
hists__calc_col_len(hists, h); hists__calc_col_len(hists, h);
...@@ -285,7 +296,13 @@ static struct hist_entry *add_hist_entry(struct hists *hists, ...@@ -285,7 +296,13 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
parent = *p; parent = *p;
he = rb_entry(parent, struct hist_entry, rb_node_in); he = rb_entry(parent, struct hist_entry, rb_node_in);
cmp = hist_entry__cmp(entry, he); /*
* Make sure that it receives arguments in a same order as
* hist_entry__collapse() so that we can use an appropriate
* function when searching an entry regardless which sort
* keys were used.
*/
cmp = hist_entry__cmp(he, entry);
if (!cmp) { if (!cmp) {
he_stat__add_period(&he->stat, period); he_stat__add_period(&he->stat, period);
...@@ -711,25 +728,38 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) ...@@ -711,25 +728,38 @@ 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 events_stats__inc(struct events_stats *stats, u32 type)
{
++stats->nr_events[0];
++stats->nr_events[type];
}
void hists__inc_nr_events(struct hists *hists, u32 type) void hists__inc_nr_events(struct hists *hists, u32 type)
{ {
++hists->stats.nr_events[0]; events_stats__inc(&hists->stats, type);
++hists->stats.nr_events[type];
} }
static struct hist_entry *hists__add_dummy_entry(struct hists *hists, static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
struct hist_entry *pair) struct hist_entry *pair)
{ {
struct rb_node **p = &hists->entries.rb_node; struct rb_root *root;
struct rb_node **p;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct hist_entry *he; struct hist_entry *he;
int cmp; int cmp;
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
p = &root->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(pair, he); cmp = hist_entry__collapse(he, pair);
if (!cmp) if (!cmp)
goto out; goto out;
...@@ -744,8 +774,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, ...@@ -744,8 +774,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
if (he) { if (he) {
memset(&he->stat, 0, sizeof(he->stat)); memset(&he->stat, 0, sizeof(he->stat));
he->hists = hists; he->hists = hists;
rb_link_node(&he->rb_node, parent, p); rb_link_node(&he->rb_node_in, parent, p);
rb_insert_color(&he->rb_node, &hists->entries); rb_insert_color(&he->rb_node_in, root);
hists__inc_nr_entries(hists, he); hists__inc_nr_entries(hists, he);
} }
out: out:
...@@ -755,11 +785,16 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, ...@@ -755,11 +785,16 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
static struct hist_entry *hists__find_entry(struct hists *hists, static struct hist_entry *hists__find_entry(struct hists *hists,
struct hist_entry *he) struct hist_entry *he)
{ {
struct rb_node *n = hists->entries.rb_node; struct rb_node *n;
if (sort__need_collapse)
n = hists->entries_collapsed.rb_node;
else
n = hists->entries_in->rb_node;
while (n) { while (n) {
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in);
int64_t cmp = hist_entry__cmp(he, iter); int64_t cmp = hist_entry__collapse(iter, he);
if (cmp < 0) if (cmp < 0)
n = n->rb_left; n = n->rb_left;
...@@ -777,11 +812,17 @@ static struct hist_entry *hists__find_entry(struct hists *hists, ...@@ -777,11 +812,17 @@ static struct hist_entry *hists__find_entry(struct hists *hists,
*/ */
void hists__match(struct hists *leader, struct hists *other) void hists__match(struct hists *leader, struct hists *other)
{ {
struct rb_root *root;
struct rb_node *nd; struct rb_node *nd;
struct hist_entry *pos, *pair; struct hist_entry *pos, *pair;
for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) { if (sort__need_collapse)
pos = rb_entry(nd, struct hist_entry, rb_node); root = &leader->entries_collapsed;
else
root = leader->entries_in;
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
pos = rb_entry(nd, struct hist_entry, rb_node_in);
pair = hists__find_entry(other, pos); pair = hists__find_entry(other, pos);
if (pair) if (pair)
...@@ -796,11 +837,17 @@ void hists__match(struct hists *leader, struct hists *other) ...@@ -796,11 +837,17 @@ void hists__match(struct hists *leader, struct hists *other)
*/ */
int hists__link(struct hists *leader, struct hists *other) int hists__link(struct hists *leader, struct hists *other)
{ {
struct rb_root *root;
struct rb_node *nd; struct rb_node *nd;
struct hist_entry *pos, *pair; struct hist_entry *pos, *pair;
for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) { if (sort__need_collapse)
pos = rb_entry(nd, struct hist_entry, rb_node); root = &other->entries_collapsed;
else
root = other->entries_in;
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
pos = rb_entry(nd, struct hist_entry, rb_node_in);
if (!hist_entry__has_pairs(pos)) { if (!hist_entry__has_pairs(pos)) {
pair = hists__add_dummy_entry(leader, pos); pair = hists__add_dummy_entry(leader, pos);
......
...@@ -96,8 +96,10 @@ void hists__decay_entries_threaded(struct hists *hists, bool zap_user, ...@@ -96,8 +96,10 @@ void hists__decay_entries_threaded(struct hists *hists, bool zap_user,
bool zap_kernel); bool zap_kernel);
void hists__output_recalc_col_len(struct hists *hists, int max_rows); void hists__output_recalc_col_len(struct hists *hists, int max_rows);
void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
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); void events_stats__inc(struct events_stats *stats, u32 type);
size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, size_t hists__fprintf(struct hists *self, bool show_header, int max_rows,
int max_cols, FILE *fp); int max_cols, FILE *fp);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
#define for_each_set_bit(bit, addr, size) \ #define for_each_set_bit(bit, addr, size) \
for ((bit) = find_first_bit((addr), (size)); \ for ((bit) = find_first_bit((addr), (size)); \
......
...@@ -59,16 +59,40 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) ...@@ -59,16 +59,40 @@ void intlist__remove(struct intlist *ilist, struct int_node *node)
struct int_node *intlist__find(struct intlist *ilist, int i) struct int_node *intlist__find(struct intlist *ilist, int i)
{ {
struct int_node *node = NULL; struct int_node *node;
struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); struct rb_node *rb_node;
if (ilist == NULL)
return NULL;
node = NULL;
rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
if (rb_node) if (rb_node)
node = container_of(rb_node, struct int_node, rb_node); node = container_of(rb_node, struct int_node, rb_node);
return node; return node;
} }
struct intlist *intlist__new(void) static int intlist__parse_list(struct intlist *ilist, const char *s)
{
char *sep;
int err;
do {
long value = strtol(s, &sep, 10);
err = -EINVAL;
if (*sep != ',' && *sep != '\0')
break;
err = intlist__add(ilist, value);
if (err)
break;
s = sep + 1;
} while (*sep != '\0');
return err;
}
struct intlist *intlist__new(const char *slist)
{ {
struct intlist *ilist = malloc(sizeof(*ilist)); struct intlist *ilist = malloc(sizeof(*ilist));
...@@ -77,9 +101,15 @@ struct intlist *intlist__new(void) ...@@ -77,9 +101,15 @@ struct intlist *intlist__new(void)
ilist->rblist.node_cmp = intlist__node_cmp; ilist->rblist.node_cmp = intlist__node_cmp;
ilist->rblist.node_new = intlist__node_new; ilist->rblist.node_new = intlist__node_new;
ilist->rblist.node_delete = intlist__node_delete; ilist->rblist.node_delete = intlist__node_delete;
if (slist && intlist__parse_list(ilist, slist))
goto out_delete;
} }
return ilist; return ilist;
out_delete:
intlist__delete(ilist);
return NULL;
} }
void intlist__delete(struct intlist *ilist) void intlist__delete(struct intlist *ilist)
......
...@@ -15,7 +15,7 @@ struct intlist { ...@@ -15,7 +15,7 @@ struct intlist {
struct rblist rblist; struct rblist rblist;
}; };
struct intlist *intlist__new(void); struct intlist *intlist__new(const char *slist);
void intlist__delete(struct intlist *ilist); void intlist__delete(struct intlist *ilist);
void intlist__remove(struct intlist *ilist, struct int_node *in); void intlist__remove(struct intlist *ilist, struct int_node *in);
......
...@@ -91,10 +91,22 @@ void machine__delete(struct machine *machine) ...@@ -91,10 +91,22 @@ void machine__delete(struct machine *machine)
free(machine); free(machine);
} }
struct machine *machines__add(struct rb_root *machines, pid_t pid, void machines__init(struct machines *machines)
{
machine__init(&machines->host, "", HOST_KERNEL_ID);
machines->guests = RB_ROOT;
}
void machines__exit(struct machines *machines)
{
machine__exit(&machines->host);
/* XXX exit guest */
}
struct machine *machines__add(struct machines *machines, pid_t pid,
const char *root_dir) const char *root_dir)
{ {
struct rb_node **p = &machines->rb_node; struct rb_node **p = &machines->guests.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct machine *pos, *machine = malloc(sizeof(*machine)); struct machine *pos, *machine = malloc(sizeof(*machine));
...@@ -116,18 +128,21 @@ struct machine *machines__add(struct rb_root *machines, pid_t pid, ...@@ -116,18 +128,21 @@ struct machine *machines__add(struct rb_root *machines, pid_t pid,
} }
rb_link_node(&machine->rb_node, parent, p); rb_link_node(&machine->rb_node, parent, p);
rb_insert_color(&machine->rb_node, machines); rb_insert_color(&machine->rb_node, &machines->guests);
return machine; return machine;
} }
struct machine *machines__find(struct rb_root *machines, pid_t pid) struct machine *machines__find(struct machines *machines, pid_t pid)
{ {
struct rb_node **p = &machines->rb_node; struct rb_node **p = &machines->guests.rb_node;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct machine *machine; struct machine *machine;
struct machine *default_machine = NULL; struct machine *default_machine = NULL;
if (pid == HOST_KERNEL_ID)
return &machines->host;
while (*p != NULL) { while (*p != NULL) {
parent = *p; parent = *p;
machine = rb_entry(parent, struct machine, rb_node); machine = rb_entry(parent, struct machine, rb_node);
...@@ -144,7 +159,7 @@ struct machine *machines__find(struct rb_root *machines, pid_t pid) ...@@ -144,7 +159,7 @@ struct machine *machines__find(struct rb_root *machines, pid_t pid)
return default_machine; return default_machine;
} }
struct machine *machines__findnew(struct rb_root *machines, pid_t pid) struct machine *machines__findnew(struct machines *machines, pid_t pid)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
const char *root_dir = ""; const char *root_dir = "";
...@@ -178,12 +193,12 @@ struct machine *machines__findnew(struct rb_root *machines, pid_t pid) ...@@ -178,12 +193,12 @@ struct machine *machines__findnew(struct rb_root *machines, pid_t pid)
return machine; return machine;
} }
void machines__process(struct rb_root *machines, void machines__process_guests(struct machines *machines,
machine__process_t process, void *data) machine__process_t process, void *data)
{ {
struct rb_node *nd; struct rb_node *nd;
for (nd = rb_first(machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
process(pos, data); process(pos, data);
} }
...@@ -203,12 +218,14 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size) ...@@ -203,12 +218,14 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size)
return bf; return bf;
} }
void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
{ {
struct rb_node *node; struct rb_node *node;
struct machine *machine; struct machine *machine;
for (node = rb_first(machines); node; node = rb_next(node)) { machines->host.id_hdr_size = id_hdr_size;
for (node = rb_first(&machines->guests); node; node = rb_next(node)) {
machine = rb_entry(node, struct machine, rb_node); machine = rb_entry(node, struct machine, rb_node);
machine->id_hdr_size = id_hdr_size; machine->id_hdr_size = id_hdr_size;
} }
...@@ -313,12 +330,13 @@ struct map *machine__new_module(struct machine *machine, u64 start, ...@@ -313,12 +330,13 @@ struct map *machine__new_module(struct machine *machine, u64 start,
return map; return map;
} }
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
{ {
struct rb_node *nd; struct rb_node *nd;
size_t ret = 0; size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) +
__dsos__fprintf(&machines->host.user_dsos, fp);
for (nd = rb_first(machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += __dsos__fprintf(&pos->kernel_dsos, fp); ret += __dsos__fprintf(&pos->kernel_dsos, fp);
ret += __dsos__fprintf(&pos->user_dsos, fp); ret += __dsos__fprintf(&pos->user_dsos, fp);
...@@ -334,13 +352,13 @@ size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, ...@@ -334,13 +352,13 @@ size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
__dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm);
} }
size_t machines__fprintf_dsos_buildid(struct rb_root *machines, FILE *fp, size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm) bool (skip)(struct dso *dso, int parm), int parm)
{ {
struct rb_node *nd; struct rb_node *nd;
size_t ret = 0; size_t ret = machine__fprintf_dsos_buildid(&machines->host, fp, skip, parm);
for (nd = rb_first(machines); nd; nd = rb_next(nd)) { for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node); struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm); ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm);
} }
...@@ -511,7 +529,7 @@ void machine__destroy_kernel_maps(struct machine *machine) ...@@ -511,7 +529,7 @@ void machine__destroy_kernel_maps(struct machine *machine)
} }
} }
int machines__create_guest_kernel_maps(struct rb_root *machines) int machines__create_guest_kernel_maps(struct machines *machines)
{ {
int ret = 0; int ret = 0;
struct dirent **namelist = NULL; struct dirent **namelist = NULL;
...@@ -560,20 +578,22 @@ int machines__create_guest_kernel_maps(struct rb_root *machines) ...@@ -560,20 +578,22 @@ int machines__create_guest_kernel_maps(struct rb_root *machines)
return ret; return ret;
} }
void machines__destroy_guest_kernel_maps(struct rb_root *machines) void machines__destroy_kernel_maps(struct machines *machines)
{ {
struct rb_node *next = rb_first(machines); struct rb_node *next = rb_first(&machines->guests);
machine__destroy_kernel_maps(&machines->host);
while (next) { while (next) {
struct machine *pos = rb_entry(next, struct machine, rb_node); struct machine *pos = rb_entry(next, struct machine, rb_node);
next = rb_next(&pos->rb_node); next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, machines); rb_erase(&pos->rb_node, &machines->guests);
machine__delete(pos); machine__delete(pos);
} }
} }
int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) int machines__create_kernel_maps(struct machines *machines, pid_t pid)
{ {
struct machine *machine = machines__findnew(machines, pid); struct machine *machine = machines__findnew(machines, pid);
......
...@@ -47,16 +47,24 @@ int machine__process_event(struct machine *machine, union perf_event *event); ...@@ -47,16 +47,24 @@ int machine__process_event(struct machine *machine, union perf_event *event);
typedef void (*machine__process_t)(struct machine *machine, void *data); typedef void (*machine__process_t)(struct machine *machine, void *data);
void machines__process(struct rb_root *machines, struct machines {
struct machine host;
struct rb_root guests;
};
void machines__init(struct machines *machines);
void machines__exit(struct machines *machines);
void machines__process_guests(struct machines *machines,
machine__process_t process, void *data); machine__process_t process, void *data);
struct machine *machines__add(struct rb_root *machines, pid_t pid, struct machine *machines__add(struct machines *machines, pid_t pid,
const char *root_dir); const char *root_dir);
struct machine *machines__find_host(struct rb_root *machines); struct machine *machines__find_host(struct machines *machines);
struct machine *machines__find(struct rb_root *machines, pid_t pid); struct machine *machines__find(struct machines *machines, pid_t pid);
struct machine *machines__findnew(struct rb_root *machines, pid_t pid); struct machine *machines__findnew(struct machines *machines, pid_t pid);
void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size); void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size);
char *machine__mmap_name(struct machine *machine, char *bf, size_t size); char *machine__mmap_name(struct machine *machine, char *bf, size_t size);
int machine__init(struct machine *machine, const char *root_dir, pid_t pid); int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
...@@ -132,17 +140,17 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type, ...@@ -132,17 +140,17 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm); bool (skip)(struct dso *dso, int parm), int parm);
size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp); size_t machines__fprintf_dsos(struct machines *machines, FILE *fp);
size_t machines__fprintf_dsos_buildid(struct rb_root *machines, FILE *fp, size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm); bool (skip)(struct dso *dso, int parm), int parm);
void machine__destroy_kernel_maps(struct machine *machine); void machine__destroy_kernel_maps(struct machine *machine);
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel);
int machine__create_kernel_maps(struct machine *machine); int machine__create_kernel_maps(struct machine *machine);
int machines__create_kernel_maps(struct rb_root *machines, pid_t pid); int machines__create_kernel_maps(struct machines *machines, pid_t pid);
int machines__create_guest_kernel_maps(struct rb_root *machines); int machines__create_guest_kernel_maps(struct machines *machines);
void machines__destroy_guest_kernel_maps(struct rb_root *machines); void machines__destroy_kernel_maps(struct machines *machines);
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
......
...@@ -19,7 +19,8 @@ const char *map_type__name[MAP__NR_TYPES] = { ...@@ -19,7 +19,8 @@ const char *map_type__name[MAP__NR_TYPES] = {
static inline int is_anon_memory(const char *filename) static inline int is_anon_memory(const char *filename)
{ {
return strcmp(filename, "//anon") == 0; return !strcmp(filename, "//anon") ||
!strcmp(filename, "/anon_hugepage (deleted)");
} }
static inline int is_no_dso_memory(const char *filename) static inline int is_no_dso_memory(const char *filename)
......
...@@ -380,7 +380,7 @@ static int add_tracepoint(struct list_head **listp, int *idx, ...@@ -380,7 +380,7 @@ static int add_tracepoint(struct list_head **listp, int *idx,
return 0; return 0;
} }
static int add_tracepoint_multi(struct list_head **list, int *idx, static int add_tracepoint_multi_event(struct list_head **list, int *idx,
char *sys_name, char *evt_name) char *sys_name, char *evt_name)
{ {
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
...@@ -408,6 +408,47 @@ static int add_tracepoint_multi(struct list_head **list, int *idx, ...@@ -408,6 +408,47 @@ static int add_tracepoint_multi(struct list_head **list, int *idx,
ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
} }
closedir(evt_dir);
return ret;
}
static int add_tracepoint_event(struct list_head **list, int *idx,
char *sys_name, char *evt_name)
{
return strpbrk(evt_name, "*?") ?
add_tracepoint_multi_event(list, idx, sys_name, evt_name) :
add_tracepoint(list, idx, sys_name, evt_name);
}
static int add_tracepoint_multi_sys(struct list_head **list, int *idx,
char *sys_name, char *evt_name)
{
struct dirent *events_ent;
DIR *events_dir;
int ret = 0;
events_dir = opendir(tracing_events_path);
if (!events_dir) {
perror("Can't open event dir");
return -1;
}
while (!ret && (events_ent = readdir(events_dir))) {
if (!strcmp(events_ent->d_name, ".")
|| !strcmp(events_ent->d_name, "..")
|| !strcmp(events_ent->d_name, "enable")
|| !strcmp(events_ent->d_name, "header_event")
|| !strcmp(events_ent->d_name, "header_page"))
continue;
if (!strglobmatch(events_ent->d_name, sys_name))
continue;
ret = add_tracepoint_event(list, idx, events_ent->d_name,
evt_name);
}
closedir(events_dir);
return ret; return ret;
} }
...@@ -420,9 +461,10 @@ int parse_events_add_tracepoint(struct list_head **list, int *idx, ...@@ -420,9 +461,10 @@ int parse_events_add_tracepoint(struct list_head **list, int *idx,
if (ret) if (ret)
return ret; return ret;
return strpbrk(event, "*?") ? if (strpbrk(sys, "*?"))
add_tracepoint_multi(list, idx, sys, event) : return add_tracepoint_multi_sys(list, idx, sys, event);
add_tracepoint(list, idx, sys, event); else
return add_tracepoint_event(list, idx, sys, event);
} }
static int static int
...@@ -492,7 +534,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx, ...@@ -492,7 +534,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
} }
static int config_term(struct perf_event_attr *attr, static int config_term(struct perf_event_attr *attr,
struct parse_events__term *term) struct parse_events_term *term)
{ {
#define CHECK_TYPE_VAL(type) \ #define CHECK_TYPE_VAL(type) \
do { \ do { \
...@@ -537,7 +579,7 @@ do { \ ...@@ -537,7 +579,7 @@ do { \
static int config_attr(struct perf_event_attr *attr, static int config_attr(struct perf_event_attr *attr,
struct list_head *head, int fail) struct list_head *head, int fail)
{ {
struct parse_events__term *term; struct parse_events_term *term;
list_for_each_entry(term, head, list) list_for_each_entry(term, head, list)
if (config_term(attr, term) && fail) if (config_term(attr, term) && fail)
...@@ -563,14 +605,14 @@ int parse_events_add_numeric(struct list_head **list, int *idx, ...@@ -563,14 +605,14 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
return add_event(list, idx, &attr, NULL); return add_event(list, idx, &attr, NULL);
} }
static int parse_events__is_name_term(struct parse_events__term *term) static int parse_events__is_name_term(struct parse_events_term *term)
{ {
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
} }
static char *pmu_event_name(struct list_head *head_terms) static char *pmu_event_name(struct list_head *head_terms)
{ {
struct parse_events__term *term; struct parse_events_term *term;
list_for_each_entry(term, head_terms, list) list_for_each_entry(term, head_terms, list)
if (parse_events__is_name_term(term)) if (parse_events__is_name_term(term))
...@@ -814,7 +856,7 @@ static int parse_events__scanner(const char *str, void *data, int start_token) ...@@ -814,7 +856,7 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
*/ */
int parse_events_terms(struct list_head *terms, const char *str) int parse_events_terms(struct list_head *terms, const char *str)
{ {
struct parse_events_data__terms data = { struct parse_events_terms data = {
.terms = NULL, .terms = NULL,
}; };
int ret; int ret;
...@@ -830,10 +872,9 @@ int parse_events_terms(struct list_head *terms, const char *str) ...@@ -830,10 +872,9 @@ int parse_events_terms(struct list_head *terms, const char *str)
return ret; return ret;
} }
int parse_events(struct perf_evlist *evlist, const char *str, int parse_events(struct perf_evlist *evlist, const char *str)
int unset __maybe_unused)
{ {
struct parse_events_data__events data = { struct parse_events_evlist data = {
.list = LIST_HEAD_INIT(data.list), .list = LIST_HEAD_INIT(data.list),
.idx = evlist->nr_entries, .idx = evlist->nr_entries,
}; };
...@@ -858,7 +899,7 @@ int parse_events_option(const struct option *opt, const char *str, ...@@ -858,7 +899,7 @@ int parse_events_option(const struct option *opt, const char *str,
int unset __maybe_unused) int unset __maybe_unused)
{ {
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
int ret = parse_events(evlist, str, unset); int ret = parse_events(evlist, str);
if (ret) { if (ret) {
fprintf(stderr, "invalid or unsupported event: '%s'\n", str); fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
...@@ -1121,16 +1162,16 @@ void print_events(const char *event_glob, bool name_only) ...@@ -1121,16 +1162,16 @@ void print_events(const char *event_glob, bool name_only)
print_tracepoint_events(NULL, NULL, name_only); print_tracepoint_events(NULL, NULL, name_only);
} }
int parse_events__is_hardcoded_term(struct parse_events__term *term) int parse_events__is_hardcoded_term(struct parse_events_term *term)
{ {
return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; return term->type_term != PARSE_EVENTS__TERM_TYPE_USER;
} }
static int new_term(struct parse_events__term **_term, int type_val, static int new_term(struct parse_events_term **_term, int type_val,
int type_term, char *config, int type_term, char *config,
char *str, u64 num) char *str, u64 num)
{ {
struct parse_events__term *term; struct parse_events_term *term;
term = zalloc(sizeof(*term)); term = zalloc(sizeof(*term));
if (!term) if (!term)
...@@ -1156,21 +1197,21 @@ static int new_term(struct parse_events__term **_term, int type_val, ...@@ -1156,21 +1197,21 @@ static int new_term(struct parse_events__term **_term, int type_val,
return 0; return 0;
} }
int parse_events__term_num(struct parse_events__term **term, int parse_events_term__num(struct parse_events_term **term,
int type_term, char *config, u64 num) int type_term, char *config, u64 num)
{ {
return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
config, NULL, num); config, NULL, num);
} }
int parse_events__term_str(struct parse_events__term **term, int parse_events_term__str(struct parse_events_term **term,
int type_term, char *config, char *str) int type_term, char *config, char *str)
{ {
return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term,
config, str, 0); config, str, 0);
} }
int parse_events__term_sym_hw(struct parse_events__term **term, int parse_events_term__sym_hw(struct parse_events_term **term,
char *config, unsigned idx) char *config, unsigned idx)
{ {
struct event_symbol *sym; struct event_symbol *sym;
...@@ -1188,8 +1229,8 @@ int parse_events__term_sym_hw(struct parse_events__term **term, ...@@ -1188,8 +1229,8 @@ int parse_events__term_sym_hw(struct parse_events__term **term,
(char *) "event", (char *) sym->symbol, 0); (char *) "event", (char *) sym->symbol, 0);
} }
int parse_events__term_clone(struct parse_events__term **new, int parse_events_term__clone(struct parse_events_term **new,
struct parse_events__term *term) struct parse_events_term *term)
{ {
return new_term(new, term->type_val, term->type_term, term->config, return new_term(new, term->type_val, term->type_term, term->config,
term->val.str, term->val.num); term->val.str, term->val.num);
...@@ -1197,7 +1238,7 @@ int parse_events__term_clone(struct parse_events__term **new, ...@@ -1197,7 +1238,7 @@ int parse_events__term_clone(struct parse_events__term **new,
void parse_events__free_terms(struct list_head *terms) void parse_events__free_terms(struct list_head *terms)
{ {
struct parse_events__term *term, *h; struct parse_events_term *term, *h;
list_for_each_entry_safe(term, h, terms, list) list_for_each_entry_safe(term, h, terms, list)
free(term); free(term);
......
...@@ -29,8 +29,7 @@ const char *event_type(int type); ...@@ -29,8 +29,7 @@ const char *event_type(int type);
extern int parse_events_option(const struct option *opt, const char *str, extern int parse_events_option(const struct option *opt, const char *str,
int unset); int unset);
extern int parse_events(struct perf_evlist *evlist, const char *str, extern int parse_events(struct perf_evlist *evlist, const char *str);
int unset);
extern int parse_events_terms(struct list_head *terms, const char *str); extern int parse_events_terms(struct list_head *terms, const char *str);
extern int parse_filter(const struct option *opt, const char *str, int unset); extern int parse_filter(const struct option *opt, const char *str, int unset);
...@@ -51,7 +50,7 @@ enum { ...@@ -51,7 +50,7 @@ enum {
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
}; };
struct parse_events__term { struct parse_events_term {
char *config; char *config;
union { union {
char *str; char *str;
...@@ -62,24 +61,24 @@ struct parse_events__term { ...@@ -62,24 +61,24 @@ struct parse_events__term {
struct list_head list; struct list_head list;
}; };
struct parse_events_data__events { struct parse_events_evlist {
struct list_head list; struct list_head list;
int idx; int idx;
}; };
struct parse_events_data__terms { struct parse_events_terms {
struct list_head *terms; struct list_head *terms;
}; };
int parse_events__is_hardcoded_term(struct parse_events__term *term); int parse_events__is_hardcoded_term(struct parse_events_term *term);
int parse_events__term_num(struct parse_events__term **_term, int parse_events_term__num(struct parse_events_term **_term,
int type_term, char *config, u64 num); int type_term, char *config, u64 num);
int parse_events__term_str(struct parse_events__term **_term, int parse_events_term__str(struct parse_events_term **_term,
int type_term, char *config, char *str); int type_term, char *config, char *str);
int parse_events__term_sym_hw(struct parse_events__term **term, int parse_events_term__sym_hw(struct parse_events_term **term,
char *config, unsigned idx); char *config, unsigned idx);
int parse_events__term_clone(struct parse_events__term **new, int parse_events_term__clone(struct parse_events_term **new,
struct parse_events__term *term); struct parse_events_term *term);
void parse_events__free_terms(struct list_head *terms); void parse_events__free_terms(struct list_head *terms);
int parse_events__modifier_event(struct list_head *list, char *str, bool add); int parse_events__modifier_event(struct list_head *list, char *str, bool add);
int parse_events__modifier_group(struct list_head *list, char *event_mod); int parse_events__modifier_group(struct list_head *list, char *event_mod);
......
...@@ -68,7 +68,7 @@ do { \ ...@@ -68,7 +68,7 @@ do { \
char *str; char *str;
u64 num; u64 num;
struct list_head *head; struct list_head *head;
struct parse_events__term *term; struct parse_events_term *term;
} }
%% %%
...@@ -79,7 +79,7 @@ PE_START_TERMS start_terms ...@@ -79,7 +79,7 @@ PE_START_TERMS start_terms
start_events: groups start_events: groups
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
parse_events_update_lists($1, &data->list); parse_events_update_lists($1, &data->list);
} }
...@@ -186,7 +186,7 @@ event_def: event_pmu | ...@@ -186,7 +186,7 @@ event_def: event_pmu |
event_pmu: event_pmu:
PE_NAME '/' event_config '/' PE_NAME '/' event_config '/'
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3));
...@@ -202,7 +202,7 @@ PE_VALUE_SYM_SW ...@@ -202,7 +202,7 @@ PE_VALUE_SYM_SW
event_legacy_symbol: event_legacy_symbol:
value_sym '/' event_config '/' value_sym '/' event_config '/'
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
...@@ -215,7 +215,7 @@ value_sym '/' event_config '/' ...@@ -215,7 +215,7 @@ value_sym '/' event_config '/'
| |
value_sym sep_slash_dc value_sym sep_slash_dc
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
...@@ -228,7 +228,7 @@ value_sym sep_slash_dc ...@@ -228,7 +228,7 @@ value_sym sep_slash_dc
event_legacy_cache: event_legacy_cache:
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5));
...@@ -237,7 +237,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT ...@@ -237,7 +237,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
| |
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL));
...@@ -246,7 +246,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT ...@@ -246,7 +246,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
| |
PE_NAME_CACHE_TYPE PE_NAME_CACHE_TYPE
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL));
...@@ -256,7 +256,7 @@ PE_NAME_CACHE_TYPE ...@@ -256,7 +256,7 @@ PE_NAME_CACHE_TYPE
event_legacy_mem: event_legacy_mem:
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
...@@ -266,7 +266,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc ...@@ -266,7 +266,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
| |
PE_PREFIX_MEM PE_VALUE sep_dc PE_PREFIX_MEM PE_VALUE sep_dc
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
...@@ -277,7 +277,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc ...@@ -277,7 +277,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
event_legacy_tracepoint: event_legacy_tracepoint:
PE_NAME ':' PE_NAME PE_NAME ':' PE_NAME
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3));
...@@ -287,7 +287,7 @@ PE_NAME ':' PE_NAME ...@@ -287,7 +287,7 @@ PE_NAME ':' PE_NAME
event_legacy_numeric: event_legacy_numeric:
PE_VALUE ':' PE_VALUE PE_VALUE ':' PE_VALUE
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL));
...@@ -297,7 +297,7 @@ PE_VALUE ':' PE_VALUE ...@@ -297,7 +297,7 @@ PE_VALUE ':' PE_VALUE
event_legacy_raw: event_legacy_raw:
PE_RAW PE_RAW
{ {
struct parse_events_data__events *data = _data; struct parse_events_evlist *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_numeric(&list, &data->idx, ABORT_ON(parse_events_add_numeric(&list, &data->idx,
...@@ -307,7 +307,7 @@ PE_RAW ...@@ -307,7 +307,7 @@ PE_RAW
start_terms: event_config start_terms: event_config
{ {
struct parse_events_data__terms *data = _data; struct parse_events_terms *data = _data;
data->terms = $1; data->terms = $1;
} }
...@@ -315,7 +315,7 @@ event_config: ...@@ -315,7 +315,7 @@ event_config:
event_config ',' event_term event_config ',' event_term
{ {
struct list_head *head = $1; struct list_head *head = $1;
struct parse_events__term *term = $3; struct parse_events_term *term = $3;
ABORT_ON(!head); ABORT_ON(!head);
list_add_tail(&term->list, head); list_add_tail(&term->list, head);
...@@ -325,7 +325,7 @@ event_config ',' event_term ...@@ -325,7 +325,7 @@ event_config ',' event_term
event_term event_term
{ {
struct list_head *head = malloc(sizeof(*head)); struct list_head *head = malloc(sizeof(*head));
struct parse_events__term *term = $1; struct parse_events_term *term = $1;
ABORT_ON(!head); ABORT_ON(!head);
INIT_LIST_HEAD(head); INIT_LIST_HEAD(head);
...@@ -336,70 +336,70 @@ event_term ...@@ -336,70 +336,70 @@ event_term
event_term: event_term:
PE_NAME '=' PE_NAME PE_NAME '=' PE_NAME
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER, ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $3)); $1, $3));
$$ = term; $$ = term;
} }
| |
PE_NAME '=' PE_VALUE PE_NAME '=' PE_VALUE
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $3)); $1, $3));
$$ = term; $$ = term;
} }
| |
PE_NAME '=' PE_VALUE_SYM_HW PE_NAME '=' PE_VALUE_SYM_HW
{ {
struct parse_events__term *term; struct parse_events_term *term;
int config = $3 & 255; int config = $3 & 255;
ABORT_ON(parse_events__term_sym_hw(&term, $1, config)); ABORT_ON(parse_events_term__sym_hw(&term, $1, config));
$$ = term; $$ = term;
} }
| |
PE_NAME PE_NAME
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, 1)); $1, 1));
$$ = term; $$ = term;
} }
| |
PE_VALUE_SYM_HW PE_VALUE_SYM_HW
{ {
struct parse_events__term *term; struct parse_events_term *term;
int config = $1 & 255; int config = $1 & 255;
ABORT_ON(parse_events__term_sym_hw(&term, NULL, config)); ABORT_ON(parse_events_term__sym_hw(&term, NULL, config));
$$ = term; $$ = term;
} }
| |
PE_TERM '=' PE_NAME PE_TERM '=' PE_NAME
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_str(&term, (int)$1, NULL, $3)); ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3));
$$ = term; $$ = term;
} }
| |
PE_TERM '=' PE_VALUE PE_TERM '=' PE_VALUE
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, $3)); ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3));
$$ = term; $$ = term;
} }
| |
PE_TERM PE_TERM
{ {
struct parse_events__term *term; struct parse_events_term *term;
ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, 1)); ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1));
$$ = term; $$ = term;
} }
......
#include <linux/list.h> #include <linux/list.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -11,6 +10,19 @@ ...@@ -11,6 +10,19 @@
#include "parse-events.h" #include "parse-events.h"
#include "cpumap.h" #include "cpumap.h"
struct perf_pmu_alias {
char *name;
struct list_head terms;
struct list_head list;
};
struct perf_pmu_format {
char *name;
int value;
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
struct list_head list;
};
#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
int perf_pmu_parse(struct list_head *list, char *name); int perf_pmu_parse(struct list_head *list, char *name);
...@@ -85,7 +97,7 @@ static int pmu_format(char *name, struct list_head *format) ...@@ -85,7 +97,7 @@ static int pmu_format(char *name, struct list_head *format)
static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
{ {
struct perf_pmu__alias *alias; struct perf_pmu_alias *alias;
char buf[256]; char buf[256];
int ret; int ret;
...@@ -172,15 +184,15 @@ static int pmu_aliases(char *name, struct list_head *head) ...@@ -172,15 +184,15 @@ static int pmu_aliases(char *name, struct list_head *head)
return 0; return 0;
} }
static int pmu_alias_terms(struct perf_pmu__alias *alias, static int pmu_alias_terms(struct perf_pmu_alias *alias,
struct list_head *terms) struct list_head *terms)
{ {
struct parse_events__term *term, *clone; struct parse_events_term *term, *clone;
LIST_HEAD(list); LIST_HEAD(list);
int ret; int ret;
list_for_each_entry(term, &alias->terms, list) { list_for_each_entry(term, &alias->terms, list) {
ret = parse_events__term_clone(&clone, term); ret = parse_events_term__clone(&clone, term);
if (ret) { if (ret) {
parse_events__free_terms(&list); parse_events__free_terms(&list);
return ret; return ret;
...@@ -360,10 +372,10 @@ struct perf_pmu *perf_pmu__find(char *name) ...@@ -360,10 +372,10 @@ struct perf_pmu *perf_pmu__find(char *name)
return pmu_lookup(name); return pmu_lookup(name);
} }
static struct perf_pmu__format* static struct perf_pmu_format *
pmu_find_format(struct list_head *formats, char *name) pmu_find_format(struct list_head *formats, char *name)
{ {
struct perf_pmu__format *format; struct perf_pmu_format *format;
list_for_each_entry(format, formats, list) list_for_each_entry(format, formats, list)
if (!strcmp(format->name, name)) if (!strcmp(format->name, name))
...@@ -403,9 +415,9 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) ...@@ -403,9 +415,9 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)
*/ */
static int pmu_config_term(struct list_head *formats, static int pmu_config_term(struct list_head *formats,
struct perf_event_attr *attr, struct perf_event_attr *attr,
struct parse_events__term *term) struct parse_events_term *term)
{ {
struct perf_pmu__format *format; struct perf_pmu_format *format;
__u64 *vp; __u64 *vp;
/* /*
...@@ -450,7 +462,7 @@ int perf_pmu__config_terms(struct list_head *formats, ...@@ -450,7 +462,7 @@ int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr, struct perf_event_attr *attr,
struct list_head *head_terms) struct list_head *head_terms)
{ {
struct parse_events__term *term; struct parse_events_term *term;
list_for_each_entry(term, head_terms, list) list_for_each_entry(term, head_terms, list)
if (pmu_config_term(formats, attr, term)) if (pmu_config_term(formats, attr, term))
...@@ -471,10 +483,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, ...@@ -471,10 +483,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
return perf_pmu__config_terms(&pmu->format, attr, head_terms); return perf_pmu__config_terms(&pmu->format, attr, head_terms);
} }
static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
struct parse_events__term *term) struct parse_events_term *term)
{ {
struct perf_pmu__alias *alias; struct perf_pmu_alias *alias;
char *name; char *name;
if (parse_events__is_hardcoded_term(term)) if (parse_events__is_hardcoded_term(term))
...@@ -507,8 +519,8 @@ static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, ...@@ -507,8 +519,8 @@ static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
*/ */
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
{ {
struct parse_events__term *term, *h; struct parse_events_term *term, *h;
struct perf_pmu__alias *alias; struct perf_pmu_alias *alias;
int ret; int ret;
list_for_each_entry_safe(term, h, head_terms, list) { list_for_each_entry_safe(term, h, head_terms, list) {
...@@ -527,7 +539,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) ...@@ -527,7 +539,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
int perf_pmu__new_format(struct list_head *list, char *name, int perf_pmu__new_format(struct list_head *list, char *name,
int config, unsigned long *bits) int config, unsigned long *bits)
{ {
struct perf_pmu__format *format; struct perf_pmu_format *format;
format = zalloc(sizeof(*format)); format = zalloc(sizeof(*format));
if (!format) if (!format)
...@@ -548,7 +560,7 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) ...@@ -548,7 +560,7 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to)
if (!to) if (!to)
to = from; to = from;
memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS));
for (b = from; b <= to; b++) for (b = from; b <= to; b++)
set_bit(b, bits); set_bit(b, bits);
} }
...@@ -12,19 +12,6 @@ enum { ...@@ -12,19 +12,6 @@ enum {
#define PERF_PMU_FORMAT_BITS 64 #define PERF_PMU_FORMAT_BITS 64
struct perf_pmu__format {
char *name;
int value;
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
struct list_head list;
};
struct perf_pmu__alias {
char *name;
struct list_head terms;
struct list_head list;
};
struct perf_pmu { struct perf_pmu {
char *name; char *name;
__u32 type; __u32 type;
......
...@@ -413,12 +413,12 @@ static int convert_variable_type(Dwarf_Die *vr_die, ...@@ -413,12 +413,12 @@ static int convert_variable_type(Dwarf_Die *vr_die,
dwarf_diename(vr_die), dwarf_diename(&type)); dwarf_diename(vr_die), dwarf_diename(&type));
return -EINVAL; return -EINVAL;
} }
if (ret == DW_TAG_pointer_type) {
if (die_get_real_type(&type, &type) == NULL) { if (die_get_real_type(&type, &type) == NULL) {
pr_warning("Failed to get a type" pr_warning("Failed to get a type"
" information.\n"); " information.\n");
return -ENOENT; return -ENOENT;
} }
if (ret == DW_TAG_pointer_type) {
while (*ref_ptr) while (*ref_ptr)
ref_ptr = &(*ref_ptr)->next; ref_ptr = &(*ref_ptr)->next;
/* Add new reference with offset +0 */ /* Add new reference with offset +0 */
......
...@@ -1045,3 +1045,12 @@ PyMODINIT_FUNC initperf(void) ...@@ -1045,3 +1045,12 @@ PyMODINIT_FUNC initperf(void)
if (PyErr_Occurred()) if (PyErr_Occurred())
PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); PyErr_SetString(PyExc_ImportError, "perf: Init failed!");
} }
/*
* Dummy, to avoid dragging all the test_attr infrastructure in the python
* binding.
*/
void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
int fd, int group_fd, unsigned long flags)
{
}
...@@ -292,6 +292,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, ...@@ -292,6 +292,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
ns = nsecs - s * NSECS_PER_SEC; ns = nsecs - s * NSECS_PER_SEC;
scripting_context->event_data = data; scripting_context->event_data = data;
scripting_context->pevent = evsel->tp_format->pevent;
ENTER; ENTER;
SAVETMPS; SAVETMPS;
......
...@@ -265,6 +265,7 @@ static void python_process_tracepoint(union perf_event *perf_event ...@@ -265,6 +265,7 @@ static void python_process_tracepoint(union perf_event *perf_event
ns = nsecs - s * NSECS_PER_SEC; ns = nsecs - s * NSECS_PER_SEC;
scripting_context->event_data = data; scripting_context->event_data = data;
scripting_context->pevent = evsel->tp_format->pevent;
context = PyCObject_FromVoidPtr(scripting_context, NULL); context = PyCObject_FromVoidPtr(scripting_context, NULL);
......
...@@ -86,13 +86,12 @@ void perf_session__set_id_hdr_size(struct perf_session *session) ...@@ -86,13 +86,12 @@ void perf_session__set_id_hdr_size(struct perf_session *session)
{ {
u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist); u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist);
session->host_machine.id_hdr_size = id_hdr_size;
machines__set_id_hdr_size(&session->machines, id_hdr_size); machines__set_id_hdr_size(&session->machines, id_hdr_size);
} }
int perf_session__create_kernel_maps(struct perf_session *self) int perf_session__create_kernel_maps(struct perf_session *self)
{ {
int ret = machine__create_kernel_maps(&self->host_machine); int ret = machine__create_kernel_maps(&self->machines.host);
if (ret >= 0) if (ret >= 0)
ret = machines__create_guest_kernel_maps(&self->machines); ret = machines__create_guest_kernel_maps(&self->machines);
...@@ -101,8 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self) ...@@ -101,8 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self)
static void perf_session__destroy_kernel_maps(struct perf_session *self) static void perf_session__destroy_kernel_maps(struct perf_session *self)
{ {
machine__destroy_kernel_maps(&self->host_machine); machines__destroy_kernel_maps(&self->machines);
machines__destroy_guest_kernel_maps(&self->machines);
} }
struct perf_session *perf_session__new(const char *filename, int mode, struct perf_session *perf_session__new(const char *filename, int mode,
...@@ -127,13 +125,11 @@ struct perf_session *perf_session__new(const char *filename, int mode, ...@@ -127,13 +125,11 @@ struct perf_session *perf_session__new(const char *filename, int mode,
goto out; goto out;
memcpy(self->filename, filename, len); memcpy(self->filename, filename, len);
self->machines = RB_ROOT;
self->repipe = repipe; self->repipe = repipe;
INIT_LIST_HEAD(&self->ordered_samples.samples); INIT_LIST_HEAD(&self->ordered_samples.samples);
INIT_LIST_HEAD(&self->ordered_samples.sample_cache); INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
INIT_LIST_HEAD(&self->ordered_samples.to_free); INIT_LIST_HEAD(&self->ordered_samples.to_free);
machine__init(&self->host_machine, "", HOST_KERNEL_ID); machines__init(&self->machines);
hists__init(&self->hists);
if (mode == O_RDONLY) { if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0) if (perf_session__open(self, force) < 0)
...@@ -163,12 +159,12 @@ struct perf_session *perf_session__new(const char *filename, int mode, ...@@ -163,12 +159,12 @@ struct perf_session *perf_session__new(const char *filename, int mode,
static void perf_session__delete_dead_threads(struct perf_session *session) static void perf_session__delete_dead_threads(struct perf_session *session)
{ {
machine__delete_dead_threads(&session->host_machine); machine__delete_dead_threads(&session->machines.host);
} }
static void perf_session__delete_threads(struct perf_session *session) static void perf_session__delete_threads(struct perf_session *session)
{ {
machine__delete_threads(&session->host_machine); machine__delete_threads(&session->machines.host);
} }
static void perf_session_env__delete(struct perf_session_env *env) static void perf_session_env__delete(struct perf_session_env *env)
...@@ -193,7 +189,7 @@ void perf_session__delete(struct perf_session *self) ...@@ -193,7 +189,7 @@ void perf_session__delete(struct perf_session *self)
perf_session__delete_dead_threads(self); perf_session__delete_dead_threads(self);
perf_session__delete_threads(self); perf_session__delete_threads(self);
perf_session_env__delete(&self->header.env); perf_session_env__delete(&self->header.env);
machine__exit(&self->host_machine); machines__exit(&self->machines);
close(self->fd); close(self->fd);
free(self); free(self);
vdso__exit(); vdso__exit();
...@@ -825,7 +821,7 @@ static struct machine * ...@@ -825,7 +821,7 @@ static struct machine *
return perf_session__findnew_machine(session, pid); return perf_session__findnew_machine(session, pid);
} }
return perf_session__find_host_machine(session); return &session->machines.host;
} }
static int perf_session_deliver_event(struct perf_session *session, static int perf_session_deliver_event(struct perf_session *session,
...@@ -863,11 +859,11 @@ static int perf_session_deliver_event(struct perf_session *session, ...@@ -863,11 +859,11 @@ static int perf_session_deliver_event(struct perf_session *session,
case PERF_RECORD_SAMPLE: case PERF_RECORD_SAMPLE:
dump_sample(evsel, event, sample); dump_sample(evsel, event, sample);
if (evsel == NULL) { if (evsel == NULL) {
++session->hists.stats.nr_unknown_id; ++session->stats.nr_unknown_id;
return 0; return 0;
} }
if (machine == NULL) { if (machine == NULL) {
++session->hists.stats.nr_unprocessable_samples; ++session->stats.nr_unprocessable_samples;
return 0; return 0;
} }
return tool->sample(tool, event, sample, evsel, machine); return tool->sample(tool, event, sample, evsel, machine);
...@@ -881,7 +877,7 @@ static int perf_session_deliver_event(struct perf_session *session, ...@@ -881,7 +877,7 @@ static int perf_session_deliver_event(struct perf_session *session,
return tool->exit(tool, event, sample, machine); return tool->exit(tool, event, sample, machine);
case PERF_RECORD_LOST: case PERF_RECORD_LOST:
if (tool->lost == perf_event__process_lost) if (tool->lost == perf_event__process_lost)
session->hists.stats.total_lost += event->lost.lost; session->stats.total_lost += event->lost.lost;
return tool->lost(tool, event, sample, machine); return tool->lost(tool, event, sample, machine);
case PERF_RECORD_READ: case PERF_RECORD_READ:
return tool->read(tool, event, sample, evsel, machine); return tool->read(tool, event, sample, evsel, machine);
...@@ -890,7 +886,7 @@ static int perf_session_deliver_event(struct perf_session *session, ...@@ -890,7 +886,7 @@ static int perf_session_deliver_event(struct perf_session *session,
case PERF_RECORD_UNTHROTTLE: case PERF_RECORD_UNTHROTTLE:
return tool->unthrottle(tool, event, sample, machine); return tool->unthrottle(tool, event, sample, machine);
default: default:
++session->hists.stats.nr_unknown_events; ++session->stats.nr_unknown_events;
return -1; return -1;
} }
} }
...@@ -904,8 +900,8 @@ static int perf_session__preprocess_sample(struct perf_session *session, ...@@ -904,8 +900,8 @@ static int perf_session__preprocess_sample(struct perf_session *session,
if (!ip_callchain__valid(sample->callchain, event)) { if (!ip_callchain__valid(sample->callchain, event)) {
pr_debug("call-chain problem with event, skipping it.\n"); pr_debug("call-chain problem with event, skipping it.\n");
++session->hists.stats.nr_invalid_chains; ++session->stats.nr_invalid_chains;
session->hists.stats.total_invalid_chains += sample->period; session->stats.total_invalid_chains += sample->period;
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
...@@ -963,7 +959,7 @@ static int perf_session__process_event(struct perf_session *session, ...@@ -963,7 +959,7 @@ static int perf_session__process_event(struct perf_session *session,
if (event->header.type >= PERF_RECORD_HEADER_MAX) if (event->header.type >= PERF_RECORD_HEADER_MAX)
return -EINVAL; return -EINVAL;
hists__inc_nr_events(&session->hists, event->header.type); events_stats__inc(&session->stats, event->header.type);
if (event->header.type >= PERF_RECORD_USER_TYPE_START) if (event->header.type >= PERF_RECORD_USER_TYPE_START)
return perf_session__process_user_event(session, event, tool, file_offset); return perf_session__process_user_event(session, event, tool, file_offset);
...@@ -999,7 +995,7 @@ void perf_event_header__bswap(struct perf_event_header *self) ...@@ -999,7 +995,7 @@ void perf_event_header__bswap(struct perf_event_header *self)
struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
{ {
return machine__findnew_thread(&session->host_machine, pid); return machine__findnew_thread(&session->machines.host, pid);
} }
static struct thread *perf_session__register_idle_thread(struct perf_session *self) static struct thread *perf_session__register_idle_thread(struct perf_session *self)
...@@ -1018,39 +1014,39 @@ static void perf_session__warn_about_errors(const struct perf_session *session, ...@@ -1018,39 +1014,39 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
const struct perf_tool *tool) const struct perf_tool *tool)
{ {
if (tool->lost == perf_event__process_lost && if (tool->lost == perf_event__process_lost &&
session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { session->stats.nr_events[PERF_RECORD_LOST] != 0) {
ui__warning("Processed %d events and lost %d chunks!\n\n" ui__warning("Processed %d events and lost %d chunks!\n\n"
"Check IO/CPU overload!\n\n", "Check IO/CPU overload!\n\n",
session->hists.stats.nr_events[0], session->stats.nr_events[0],
session->hists.stats.nr_events[PERF_RECORD_LOST]); session->stats.nr_events[PERF_RECORD_LOST]);
} }
if (session->hists.stats.nr_unknown_events != 0) { if (session->stats.nr_unknown_events != 0) {
ui__warning("Found %u unknown events!\n\n" ui__warning("Found %u unknown events!\n\n"
"Is this an older tool processing a perf.data " "Is this an older tool processing a perf.data "
"file generated by a more recent tool?\n\n" "file generated by a more recent tool?\n\n"
"If that is not the case, consider " "If that is not the case, consider "
"reporting to linux-kernel@vger.kernel.org.\n\n", "reporting to linux-kernel@vger.kernel.org.\n\n",
session->hists.stats.nr_unknown_events); session->stats.nr_unknown_events);
} }
if (session->hists.stats.nr_unknown_id != 0) { if (session->stats.nr_unknown_id != 0) {
ui__warning("%u samples with id not present in the header\n", ui__warning("%u samples with id not present in the header\n",
session->hists.stats.nr_unknown_id); session->stats.nr_unknown_id);
} }
if (session->hists.stats.nr_invalid_chains != 0) { if (session->stats.nr_invalid_chains != 0) {
ui__warning("Found invalid callchains!\n\n" ui__warning("Found invalid callchains!\n\n"
"%u out of %u events were discarded for this reason.\n\n" "%u out of %u events were discarded for this reason.\n\n"
"Consider reporting to linux-kernel@vger.kernel.org.\n\n", "Consider reporting to linux-kernel@vger.kernel.org.\n\n",
session->hists.stats.nr_invalid_chains, session->stats.nr_invalid_chains,
session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); session->stats.nr_events[PERF_RECORD_SAMPLE]);
} }
if (session->hists.stats.nr_unprocessable_samples != 0) { if (session->stats.nr_unprocessable_samples != 0) {
ui__warning("%u unprocessable samples recorded.\n" ui__warning("%u unprocessable samples recorded.\n"
"Do you have a KVM guest running and not using 'perf kvm'?\n", "Do you have a KVM guest running and not using 'perf kvm'?\n",
session->hists.stats.nr_unprocessable_samples); session->stats.nr_unprocessable_samples);
} }
} }
...@@ -1336,16 +1332,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, ...@@ -1336,16 +1332,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
{ {
return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + return machines__fprintf_dsos(&self->machines, fp);
__dsos__fprintf(&self->host_machine.user_dsos, fp) +
machines__fprintf_dsos(&self->machines, fp);
} }
size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm) bool (skip)(struct dso *dso, int parm), int parm)
{ {
size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, skip, parm); return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm);
return ret + machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm);
} }
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
...@@ -1353,11 +1346,11 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) ...@@ -1353,11 +1346,11 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
struct perf_evsel *pos; struct perf_evsel *pos;
size_t ret = fprintf(fp, "Aggregated stats:\n"); size_t ret = fprintf(fp, "Aggregated stats:\n");
ret += hists__fprintf_nr_events(&session->hists, fp); ret += events_stats__fprintf(&session->stats, fp);
list_for_each_entry(pos, &session->evlist->entries, node) { list_for_each_entry(pos, &session->evlist->entries, node) {
ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
ret += hists__fprintf_nr_events(&pos->hists, fp); ret += events_stats__fprintf(&pos->hists.stats, fp);
} }
return ret; return ret;
...@@ -1369,7 +1362,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp) ...@@ -1369,7 +1362,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
* FIXME: Here we have to actually print all the machines in this * FIXME: Here we have to actually print all the machines in this
* session, not just the host... * session, not just the host...
*/ */
return machine__fprintf(&session->host_machine, fp); return machine__fprintf(&session->machines.host, fp);
} }
void perf_session__remove_thread(struct perf_session *session, void perf_session__remove_thread(struct perf_session *session,
...@@ -1378,10 +1371,10 @@ void perf_session__remove_thread(struct perf_session *session, ...@@ -1378,10 +1371,10 @@ void perf_session__remove_thread(struct perf_session *session,
/* /*
* FIXME: This one makes no sense, we need to remove the thread from * FIXME: This one makes no sense, we need to remove the thread from
* the machine it belongs to, perf_session can have many machines, so * the machine it belongs to, perf_session can have many machines, so
* doing it always on ->host_machine is wrong. Fix when auditing all * doing it always on ->machines.host is wrong. Fix when auditing all
* the 'perf kvm' code. * the 'perf kvm' code.
*/ */
machine__remove_thread(&session->host_machine, th); machine__remove_thread(&session->machines.host, th);
} }
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
......
...@@ -30,15 +30,10 @@ struct ordered_samples { ...@@ -30,15 +30,10 @@ struct ordered_samples {
struct perf_session { struct perf_session {
struct perf_header header; struct perf_header header;
unsigned long size; unsigned long size;
struct machine host_machine; struct machines machines;
struct rb_root machines;
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct pevent *pevent; struct pevent *pevent;
/* struct events_stats stats;
* FIXME: Need to split this up further, we need global
* stats + per event stats.
*/
struct hists hists;
int fd; int fd;
bool fd_pipe; bool fd_pipe;
bool repipe; bool repipe;
...@@ -53,7 +48,7 @@ struct perf_tool; ...@@ -53,7 +48,7 @@ struct perf_tool;
struct perf_session *perf_session__new(const char *filename, int mode, struct perf_session *perf_session__new(const char *filename, int mode,
bool force, bool repipe, bool force, bool repipe,
struct perf_tool *tool); struct perf_tool *tool);
void perf_session__delete(struct perf_session *self); void perf_session__delete(struct perf_session *session);
void perf_event_header__bswap(struct perf_event_header *self); void perf_event_header__bswap(struct perf_event_header *self);
...@@ -79,37 +74,18 @@ int perf_session__create_kernel_maps(struct perf_session *self); ...@@ -79,37 +74,18 @@ int perf_session__create_kernel_maps(struct perf_session *self);
void perf_session__set_id_hdr_size(struct perf_session *session); void perf_session__set_id_hdr_size(struct perf_session *session);
void perf_session__remove_thread(struct perf_session *self, struct thread *th); void perf_session__remove_thread(struct perf_session *self, struct thread *th);
static inline
struct machine *perf_session__find_host_machine(struct perf_session *self)
{
return &self->host_machine;
}
static inline static inline
struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
{ {
if (pid == HOST_KERNEL_ID)
return &self->host_machine;
return machines__find(&self->machines, pid); return machines__find(&self->machines, pid);
} }
static inline static inline
struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
{ {
if (pid == HOST_KERNEL_ID)
return &self->host_machine;
return machines__findnew(&self->machines, pid); return machines__findnew(&self->machines, pid);
} }
static inline
void perf_session__process_machines(struct perf_session *self,
struct perf_tool *tool,
machine__process_t process)
{
process(&self->host_machine, tool);
return machines__process(&self->machines, process, tool);
}
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
size_t perf_session__fprintf(struct perf_session *self, FILE *fp); size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
......
...@@ -60,7 +60,7 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -60,7 +60,7 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width) size_t size, unsigned int width)
{ {
return repsep_snprintf(bf, size, "%*s:%5d", width, return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
self->thread->comm ?: "", self->thread->pid); self->thread->comm ?: "", self->thread->pid);
} }
...@@ -97,6 +97,16 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, ...@@ -97,6 +97,16 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
} }
struct sort_entry sort_comm = {
.se_header = "Command",
.se_cmp = sort__comm_cmp,
.se_collapse = sort__comm_collapse,
.se_snprintf = hist_entry__comm_snprintf,
.se_width_idx = HISTC_COMM,
};
/* --sort dso */
static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
{ {
struct dso *dso_l = map_l ? map_l->dso : NULL; struct dso *dso_l = map_l ? map_l->dso : NULL;
...@@ -117,22 +127,38 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) ...@@ -117,22 +127,38 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
return strcmp(dso_name_l, dso_name_r); return strcmp(dso_name_l, dso_name_r);
} }
struct sort_entry sort_comm = {
.se_header = "Command",
.se_cmp = sort__comm_cmp,
.se_collapse = sort__comm_collapse,
.se_snprintf = hist_entry__comm_snprintf,
.se_width_idx = HISTC_COMM,
};
/* --sort dso */
static int64_t static int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
return _sort__dso_cmp(left->ms.map, right->ms.map); return _sort__dso_cmp(left->ms.map, right->ms.map);
} }
static int _hist_entry__dso_snprintf(struct map *map, char *bf,
size_t size, unsigned int width)
{
if (map && map->dso) {
const char *dso_name = !verbose ? map->dso->short_name :
map->dso->long_name;
return repsep_snprintf(bf, size, "%-*s", width, dso_name);
}
return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
}
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width)
{
return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
}
struct sort_entry sort_dso = {
.se_header = "Shared Object",
.se_cmp = sort__dso_cmp,
.se_snprintf = hist_entry__dso_snprintf,
.se_width_idx = HISTC_DSO,
};
/* --sort symbol */
static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
u64 ip_l, u64 ip_r) u64 ip_l, u64 ip_r)
...@@ -143,35 +169,35 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, ...@@ -143,35 +169,35 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
if (sym_l == sym_r) if (sym_l == sym_r)
return 0; return 0;
if (sym_l)
ip_l = sym_l->start; ip_l = sym_l->start;
if (sym_r)
ip_r = sym_r->start; ip_r = sym_r->start;
return (int64_t)(ip_r - ip_l); return (int64_t)(ip_r - ip_l);
} }
static int _hist_entry__dso_snprintf(struct map *map, char *bf, static int64_t
size_t size, unsigned int width) sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
if (map && map->dso) { u64 ip_l, ip_r;
const char *dso_name = !verbose ? map->dso->short_name :
map->dso->long_name;
return repsep_snprintf(bf, size, "%-*s", width, dso_name);
}
return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); if (!left->ms.sym && !right->ms.sym)
} return right->level - left->level;
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, if (!left->ms.sym || !right->ms.sym)
size_t size, unsigned int width) return cmp_null(left->ms.sym, right->ms.sym);
{
return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); 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);
} }
static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
u64 ip, char level, char *bf, size_t size, u64 ip, char level, char *bf, size_t size,
unsigned int width __maybe_unused) unsigned int width)
{ {
size_t ret = 0; size_t ret = 0;
...@@ -197,43 +223,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, ...@@ -197,43 +223,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
return ret; return ret;
} }
struct sort_entry sort_dso = {
.se_header = "Shared Object",
.se_cmp = sort__dso_cmp,
.se_snprintf = hist_entry__dso_snprintf,
.se_width_idx = HISTC_DSO,
};
static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
size_t size, size_t size, unsigned int width)
unsigned int width __maybe_unused)
{ {
return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
self->level, bf, size, width); self->level, bf, size, width);
} }
/* --sort symbol */
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);
}
struct sort_entry sort_sym = { struct sort_entry sort_sym = {
.se_header = "Symbol", .se_header = "Symbol",
.se_cmp = sort__sym_cmp, .se_cmp = sort__sym_cmp,
...@@ -335,7 +331,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -335,7 +331,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width) size_t size, unsigned int width)
{ {
return repsep_snprintf(bf, size, "%-*d", width, self->cpu); return repsep_snprintf(bf, size, "%*d", width, self->cpu);
} }
struct sort_entry sort_cpu = { struct sort_entry sort_cpu = {
...@@ -345,6 +341,8 @@ struct sort_entry sort_cpu = { ...@@ -345,6 +341,8 @@ struct sort_entry sort_cpu = {
.se_width_idx = HISTC_CPU, .se_width_idx = HISTC_CPU,
}; };
/* sort keys for branch stacks */
static int64_t static int64_t
sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
...@@ -359,13 +357,6 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, ...@@ -359,13 +357,6 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
bf, size, width); bf, size, width);
} }
struct sort_entry sort_dso_from = {
.se_header = "Source Shared Object",
.se_cmp = sort__dso_from_cmp,
.se_snprintf = hist_entry__dso_from_snprintf,
.se_width_idx = HISTC_DSO_FROM,
};
static int64_t static int64_t
sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
...@@ -406,8 +397,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -406,8 +397,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
} }
static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
size_t size, size_t size, unsigned int width)
unsigned int width __maybe_unused)
{ {
struct addr_map_symbol *from = &self->branch_info->from; struct addr_map_symbol *from = &self->branch_info->from;
return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
...@@ -416,8 +406,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, ...@@ -416,8 +406,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
} }
static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
size_t size, size_t size, unsigned int width)
unsigned int width __maybe_unused)
{ {
struct addr_map_symbol *to = &self->branch_info->to; struct addr_map_symbol *to = &self->branch_info->to;
return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
...@@ -425,6 +414,13 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, ...@@ -425,6 +414,13 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
} }
struct sort_entry sort_dso_from = {
.se_header = "Source Shared Object",
.se_cmp = sort__dso_from_cmp,
.se_snprintf = hist_entry__dso_from_snprintf,
.se_width_idx = HISTC_DSO_FROM,
};
struct sort_entry sort_dso_to = { struct sort_entry sort_dso_to = {
.se_header = "Target Shared Object", .se_header = "Target Shared Object",
.se_cmp = sort__dso_to_cmp, .se_cmp = sort__dso_to_cmp,
...@@ -484,30 +480,40 @@ struct sort_dimension { ...@@ -484,30 +480,40 @@ struct sort_dimension {
#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
static struct sort_dimension sort_dimensions[] = { static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_PID, "pid", sort_thread), DIM(SORT_PID, "pid", sort_thread),
DIM(SORT_COMM, "comm", sort_comm), DIM(SORT_COMM, "comm", sort_comm),
DIM(SORT_DSO, "dso", sort_dso), DIM(SORT_DSO, "dso", sort_dso),
DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
DIM(SORT_SYM, "symbol", sort_sym), DIM(SORT_SYM, "symbol", sort_sym),
DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
DIM(SORT_PARENT, "parent", sort_parent), DIM(SORT_PARENT, "parent", sort_parent),
DIM(SORT_CPU, "cpu", sort_cpu), DIM(SORT_CPU, "cpu", sort_cpu),
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
DIM(SORT_SRCLINE, "srcline", sort_srcline), DIM(SORT_SRCLINE, "srcline", sort_srcline),
}; };
#undef DIM
#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
static struct sort_dimension bstack_sort_dimensions[] = {
DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
};
#undef DIM
int sort_dimension__add(const char *tok) int sort_dimension__add(const char *tok)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
struct sort_dimension *sd = &sort_dimensions[i]; struct sort_dimension *sd = &common_sort_dimensions[i];
if (strncasecmp(tok, sd->name, strlen(tok))) if (strncasecmp(tok, sd->name, strlen(tok)))
continue; continue;
if (sd->entry == &sort_parent) { if (sd->entry == &sort_parent) {
int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
if (ret) { if (ret) {
...@@ -518,9 +524,7 @@ int sort_dimension__add(const char *tok) ...@@ -518,9 +524,7 @@ int sort_dimension__add(const char *tok)
return -EINVAL; return -EINVAL;
} }
sort__has_parent = 1; sort__has_parent = 1;
} else if (sd->entry == &sort_sym || } else if (sd->entry == &sort_sym) {
sd->entry == &sort_sym_from ||
sd->entry == &sort_sym_to) {
sort__has_sym = 1; sort__has_sym = 1;
} }
...@@ -530,36 +534,42 @@ int sort_dimension__add(const char *tok) ...@@ -530,36 +534,42 @@ int sort_dimension__add(const char *tok)
if (sd->entry->se_collapse) if (sd->entry->se_collapse)
sort__need_collapse = 1; sort__need_collapse = 1;
if (list_empty(&hist_entry__sort_list)) { if (list_empty(&hist_entry__sort_list))
if (!strcmp(sd->name, "pid")) sort__first_dimension = i;
sort__first_dimension = SORT_PID;
else if (!strcmp(sd->name, "comm")) list_add_tail(&sd->entry->list, &hist_entry__sort_list);
sort__first_dimension = SORT_COMM; sd->taken = 1;
else if (!strcmp(sd->name, "dso"))
sort__first_dimension = SORT_DSO; return 0;
else if (!strcmp(sd->name, "symbol"))
sort__first_dimension = SORT_SYM;
else if (!strcmp(sd->name, "parent"))
sort__first_dimension = SORT_PARENT;
else if (!strcmp(sd->name, "cpu"))
sort__first_dimension = SORT_CPU;
else if (!strcmp(sd->name, "symbol_from"))
sort__first_dimension = SORT_SYM_FROM;
else if (!strcmp(sd->name, "symbol_to"))
sort__first_dimension = SORT_SYM_TO;
else if (!strcmp(sd->name, "dso_from"))
sort__first_dimension = SORT_DSO_FROM;
else if (!strcmp(sd->name, "dso_to"))
sort__first_dimension = SORT_DSO_TO;
else if (!strcmp(sd->name, "mispredict"))
sort__first_dimension = SORT_MISPREDICT;
} }
for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
struct sort_dimension *sd = &bstack_sort_dimensions[i];
if (strncasecmp(tok, sd->name, strlen(tok)))
continue;
if (sort__branch_mode != 1)
return -EINVAL;
if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
sort__has_sym = 1;
if (sd->taken)
return 0;
if (sd->entry->se_collapse)
sort__need_collapse = 1;
if (list_empty(&hist_entry__sort_list))
sort__first_dimension = i + __SORT_BRANCH_STACK;
list_add_tail(&sd->entry->list, &hist_entry__sort_list); list_add_tail(&sd->entry->list, &hist_entry__sort_list);
sd->taken = 1; sd->taken = 1;
return 0; return 0;
} }
return -ESRCH; return -ESRCH;
} }
...@@ -569,7 +579,11 @@ void setup_sorting(const char * const usagestr[], const struct option *opts) ...@@ -569,7 +579,11 @@ void setup_sorting(const char * const usagestr[], const struct option *opts)
for (tok = strtok_r(str, ", ", &tmp); for (tok = strtok_r(str, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) { tok; tok = strtok_r(NULL, ", ", &tmp)) {
if (sort_dimension__add(tok) < 0) { int ret = sort_dimension__add(tok);
if (ret == -EINVAL) {
error("Invalid --sort key: `%s'", tok);
usage_with_options(usagestr, opts);
} else if (ret == -ESRCH) {
error("Unknown --sort key: `%s'", tok); error("Unknown --sort key: `%s'", tok);
usage_with_options(usagestr, opts); usage_with_options(usagestr, opts);
} }
......
...@@ -122,18 +122,22 @@ static inline void hist_entry__add_pair(struct hist_entry *he, ...@@ -122,18 +122,22 @@ static inline void hist_entry__add_pair(struct hist_entry *he,
} }
enum sort_type { enum sort_type {
/* common sort keys */
SORT_PID, SORT_PID,
SORT_COMM, SORT_COMM,
SORT_DSO, SORT_DSO,
SORT_SYM, SORT_SYM,
SORT_PARENT, SORT_PARENT,
SORT_CPU, SORT_CPU,
SORT_DSO_FROM, SORT_SRCLINE,
/* branch stack specific sort keys */
__SORT_BRANCH_STACK,
SORT_DSO_FROM = __SORT_BRANCH_STACK,
SORT_DSO_TO, SORT_DSO_TO,
SORT_SYM_FROM, SORT_SYM_FROM,
SORT_SYM_TO, SORT_SYM_TO,
SORT_MISPREDICT, SORT_MISPREDICT,
SORT_SRCLINE,
}; };
/* /*
......
...@@ -331,6 +331,24 @@ char *strxfrchar(char *s, char from, char to) ...@@ -331,6 +331,24 @@ char *strxfrchar(char *s, char from, char to)
return s; return s;
} }
/**
* ltrim - Removes leading whitespace from @s.
* @s: The string to be stripped.
*
* Return pointer to the first non-whitespace character in @s.
*/
char *ltrim(char *s)
{
int len = strlen(s);
while (len && isspace(*s)) {
len--;
s++;
}
return s;
}
/** /**
* rtrim - Removes trailing whitespace from @s. * rtrim - Removes trailing whitespace from @s.
* @s: The string to be stripped. * @s: The string to be stripped.
......
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
......
#include "symbol.h" #include "symbol.h"
#include <elf.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
......
此差异已折叠。
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
#ifdef LIBELF_SUPPORT #ifdef LIBELF_SUPPORT
#include <libelf.h> #include <libelf.h>
#include <gelf.h> #include <gelf.h>
#include <elf.h>
#endif #endif
#include <elf.h>
#include "dso.h" #include "dso.h"
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册