mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-12 21:36:15 +07:00
perf/core improvements and fixes
. 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJRAZIRAAoJENZQFvNTUqpAA6AP+wTFrshPRkrjMcySBxYa5qdS Dg+2VujN4Ticz+SsPhqLNN/uISuq3I1B2BuLQ6UHxioIDnA2BIWmoOm1ijPspmyZ fdT/AyyiyYoV2V7N7X3HORrtnYMP/2RCccotJOL16YY2XyL1W/rcKohxZipcHL19 j3DtlqHAWcu8u/b7powrMB/JiVO5msGUn+aOm1E7jUC41H7vixKAFAPk1ERRz3+/ dPMskpTLR1UsuEbyt5BwOY1XHPurkNCDJJvTeO7gEc++pLcIARJjhgtOCKdAa9ak IOJl343i5/fXWwep0bUOEYbpi5WVhYBEp+7m3jOe9Fj4nMQppXNha894SegIQA3H c9H2sGgNyrlgrhHdfCrBrFhC8wfHZZoUJ5HQP0Jyqn3tszMpWK+XUp650QYaavGx k3AEiYA2SFTdx2WUy6IYse44VyDkDaAQanTocbUiD0M+2alvop0yhjVp0QW3s4Ne 65frbUS6MaMTWlYf8OiazFMZMc0pz3zr3RQgN23nAG2+nDpURrtIO6zLXEV02rV3 FEvDFFgzg8NZLvk8LqaV9hpDRUUSfsK2dhrimPZgh26XHGx1tn1dlfxNgpFl+d1J iW0LPkB7st8wmQdIvtaFUGfjGtEgELKiH7xPU5k4RzALVK8xajdPzPffYbvCKlaZ ag9GjAe+x93Hd4umJtOH =FJ2l -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: . 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
a2d28d0c19
@ -579,7 +579,8 @@ enum perf_event_type {
|
||||
* { u32 size;
|
||||
* char data[size];}&& PERF_SAMPLE_RAW
|
||||
*
|
||||
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
|
||||
* { u64 nr;
|
||||
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
|
||||
*
|
||||
* { u64 abi; # enum perf_sample_regs_abi
|
||||
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
|
||||
|
@ -901,8 +901,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume
|
||||
}
|
||||
|
||||
mutex_unlock(uprobes_hash(inode));
|
||||
if (uprobe)
|
||||
put_uprobe(uprobe);
|
||||
put_uprobe(uprobe);
|
||||
}
|
||||
|
||||
static struct rb_node *
|
||||
|
@ -13,8 +13,7 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
@ -1463,7 +1462,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
||||
if (read_expect_type(EVENT_ITEM, &token))
|
||||
goto fail;
|
||||
|
||||
/* add signed type */
|
||||
if (strtoul(token, NULL, 0))
|
||||
field->flags |= FIELD_IS_SIGNED;
|
||||
|
||||
free_token(token);
|
||||
if (read_expected(EVENT_OP, ";") < 0)
|
||||
@ -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) {
|
||||
|
||||
@ -2481,7 +2483,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
|
||||
|
||||
free_token(token);
|
||||
arg = alloc_arg();
|
||||
if (!field) {
|
||||
if (!arg) {
|
||||
do_warning("%s: not enough memory!", __func__);
|
||||
*tok = NULL;
|
||||
return EVENT_ERROR;
|
||||
|
@ -13,8 +13,7 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@ -13,8 +13,7 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@ -13,8 +13,7 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@ -1,3 +1,22 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -13,8 +13,7 @@
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
@ -57,11 +57,44 @@ OPTIONS
|
||||
|
||||
-s::
|
||||
--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::
|
||||
--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::
|
||||
--exclude-other::
|
||||
@ -74,7 +107,6 @@ OPTIONS
|
||||
|
||||
-t::
|
||||
--field-separator=::
|
||||
|
||||
Use a special separator character and don't pad with spaces, replacing
|
||||
all occurrences of this separator in symbol names (and other output)
|
||||
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.:
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
@ -402,7 +401,6 @@ should show a new entry for your script:
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
|
@ -23,6 +23,10 @@ from 'perf test list'.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-s::
|
||||
--skip::
|
||||
Tests to skip (comma separater numeric list).
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
@ -50,7 +50,6 @@ include config/utilities.mak
|
||||
|
||||
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
|
||||
-include $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
uname_M := $(shell uname -m 2>/dev/null || echo not)
|
||||
|
||||
@ -487,6 +486,8 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
|
||||
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.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-bench.o
|
||||
@ -532,9 +533,6 @@ ifneq ($(MAKECMDGOALS),tags)
|
||||
# because maintaining the nesting to match is a pain. If
|
||||
# we had "elif" things would have been much nicer...
|
||||
|
||||
-include config.mak.autogen
|
||||
-include config.mak
|
||||
|
||||
ifdef NO_LIBELF
|
||||
NO_DWARF := 1
|
||||
NO_DEMANGLE := 1
|
||||
@ -686,6 +684,7 @@ ifndef NO_GTK2
|
||||
BASIC_CFLAGS += $(shell pkg-config --cflags 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/hists.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
|
||||
@ -887,7 +886,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf
|
||||
|
||||
$(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)"' \
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
|
||||
|
||||
@ -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
|
||||
$(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
|
||||
|
@ -159,6 +159,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */
|
||||
printf("# Running %s/%s benchmark...\n",
|
||||
subsys->name,
|
||||
suites[i].name);
|
||||
fflush(stdout);
|
||||
|
||||
argv[1] = suites[i].name;
|
||||
suites[i].fn(1, argv, NULL);
|
||||
@ -225,6 +226,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
printf("# Running %s/%s benchmark...\n",
|
||||
subsystems[i].name,
|
||||
subsystems[i].suites[j].name);
|
||||
fflush(stdout);
|
||||
status = subsystems[i].suites[j].fn(argc - 1,
|
||||
argv + 1, prefix);
|
||||
goto end;
|
||||
|
@ -275,43 +275,6 @@ static struct perf_tool tool = {
|
||||
.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,
|
||||
struct perf_evlist *evlist)
|
||||
{
|
||||
@ -324,30 +287,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
|
||||
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;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
struct hists *hists = &evsel->hists;
|
||||
|
||||
hists__output_resort(hists);
|
||||
|
||||
if (name)
|
||||
hists__name_resort(hists);
|
||||
hists__collapse_resort(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) {
|
||||
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)) {
|
||||
rb_erase(&he->rb_node, &hists->entries);
|
||||
rb_erase(&he->rb_node_in, root);
|
||||
hist_entry__free(he);
|
||||
}
|
||||
}
|
||||
@ -447,19 +414,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root,
|
||||
|
||||
static void hists__compute_resort(struct hists *hists)
|
||||
{
|
||||
struct rb_root tmp = RB_ROOT;
|
||||
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;
|
||||
|
||||
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) {
|
||||
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(&tmp, he, compute);
|
||||
insert_hist_entry_by_compute(&hists->entries, he, compute);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
}
|
||||
|
||||
hists->entries = tmp;
|
||||
}
|
||||
|
||||
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) {
|
||||
hists__precompute(new);
|
||||
hists__compute_resort(new);
|
||||
} else {
|
||||
hists__output_resort(new);
|
||||
}
|
||||
|
||||
hists__fprintf(new, true, 0, 0, stdout);
|
||||
@ -505,8 +485,8 @@ static int __cmd_diff(void)
|
||||
evlist_old = older->evlist;
|
||||
evlist_new = newer->evlist;
|
||||
|
||||
perf_evlist__resort_hists(evlist_old, true);
|
||||
perf_evlist__resort_hists(evlist_new, false);
|
||||
perf_evlist__collapse_resort(evlist_old);
|
||||
perf_evlist__collapse_resort(evlist_new);
|
||||
|
||||
list_for_each_entry(evsel, &evlist_new->entries, node) {
|
||||
struct perf_evsel *evsel_old;
|
||||
|
@ -340,7 +340,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
|
||||
int n_lines, int is_caller)
|
||||
{
|
||||
struct rb_node *next;
|
||||
struct machine *machine;
|
||||
struct machine *machine = &session->machines.host;
|
||||
|
||||
printf("%.102s\n", graph_dotted_line);
|
||||
printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr");
|
||||
@ -349,11 +349,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
|
||||
|
||||
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--) {
|
||||
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
|
||||
node);
|
||||
@ -614,8 +609,7 @@ static struct sort_dimension *avail_sorts[] = {
|
||||
&pingpong_sort_dimension,
|
||||
};
|
||||
|
||||
#define NUM_AVAIL_SORTS \
|
||||
(int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *))
|
||||
#define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts))
|
||||
|
||||
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)
|
||||
|
||||
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[] = {
|
||||
OPT_STRING('i', "input", &file_name, "file",
|
||||
"Input file name"),
|
||||
|
@ -224,6 +224,7 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
|
||||
|
||||
static int perf_record__open(struct perf_record *rec)
|
||||
{
|
||||
char msg[512];
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evlist *evlist = rec->evlist;
|
||||
struct perf_session *session = rec->session;
|
||||
@ -233,114 +234,18 @@ static int perf_record__open(struct perf_record *rec)
|
||||
perf_evlist__config(evlist, opts);
|
||||
|
||||
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:
|
||||
if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
|
||||
int err = errno;
|
||||
|
||||
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 (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
|
||||
if (verbose)
|
||||
ui__warning("The cycles event is not supported, "
|
||||
"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;
|
||||
}
|
||||
ui__warning("%s\n", msg);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
perf_evsel__name(pos));
|
||||
rc = -err;
|
||||
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;
|
||||
rc = -errno;
|
||||
perf_evsel__open_strerror(pos, &opts->target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -423,10 +328,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
|
||||
{
|
||||
int err;
|
||||
struct perf_tool *tool = data;
|
||||
|
||||
if (machine__is_host(machine))
|
||||
return;
|
||||
|
||||
/*
|
||||
*As for guest kernel when processing subcommand record&report,
|
||||
*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)
|
||||
|
||||
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (!machine) {
|
||||
pr_err("Couldn't find native kernel information.\n");
|
||||
err = -1;
|
||||
goto out_delete_session;
|
||||
}
|
||||
machine = &session->machines.host;
|
||||
|
||||
if (opts->pipe_output) {
|
||||
err = perf_event__synthesize_attrs(tool, session,
|
||||
@ -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"
|
||||
"Check /proc/modules permission or run as root.\n");
|
||||
|
||||
if (perf_guest)
|
||||
perf_session__process_machines(session, tool,
|
||||
perf_event__synthesize_guest_os);
|
||||
if (perf_guest) {
|
||||
machines__process_guests(&session->machines,
|
||||
perf_event__synthesize_guest_os, tool);
|
||||
}
|
||||
|
||||
if (!opts->target.system_wide)
|
||||
err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
|
||||
|
@ -372,7 +372,7 @@ static int __cmd_report(struct perf_report *rep)
|
||||
if (ret)
|
||||
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);
|
||||
if (kernel_map == NULL ||
|
||||
(kernel_map->dso->hit &&
|
||||
@ -595,8 +595,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
|
||||
"Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, dso_to,"
|
||||
" dso_from, symbol_to, symbol_from, mispredict"),
|
||||
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
|
||||
" dso_to, dso_from, symbol_to, symbol_from, mispredict"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes"),
|
||||
OPT_STRING('p', "parent", &parent_pattern, "regex",
|
||||
|
@ -1475,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
sched->nr_events = session->hists.stats.nr_events[0];
|
||||
sched->nr_lost_events = session->hists.stats.total_lost;
|
||||
sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
|
||||
sched->nr_events = session->stats.nr_events[0];
|
||||
sched->nr_lost_events = session->stats.total_lost;
|
||||
sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST];
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
|
@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
|
||||
const char *arg, int unset __maybe_unused)
|
||||
{
|
||||
char *tok;
|
||||
int i, imax = sizeof(all_output_options) / sizeof(struct output_option);
|
||||
int i, imax = ARRAY_SIZE(all_output_options);
|
||||
int j;
|
||||
int rc = 0;
|
||||
char *str = strdup(arg);
|
||||
@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix)
|
||||
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)
|
||||
{
|
||||
char line[BUFSIZ], *p;
|
||||
@ -1487,7 +1475,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return -1;
|
||||
}
|
||||
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
if (!script_name && !generate_script_lang)
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
|
||||
if (!no_callchain)
|
||||
symbol_conf.use_callchain = true;
|
||||
|
@ -132,8 +132,6 @@ static struct stats walltime_nsecs_stats;
|
||||
static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
bool exclude_guest_missing = false;
|
||||
int ret;
|
||||
|
||||
if (scale)
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
@ -141,16 +139,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
|
||||
attr->inherit = !no_inherit;
|
||||
|
||||
retry:
|
||||
if (exclude_guest_missing)
|
||||
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_cpu(&target))
|
||||
return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
|
||||
|
||||
if (!perf_target__has_task(&target) &&
|
||||
perf_evsel__is_group_leader(evsel)) {
|
||||
@ -158,21 +148,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
|
||||
ret = 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;
|
||||
return perf_evsel__open_per_thread(evsel, evsel_list->threads);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -271,6 +247,7 @@ static int read_counter(struct perf_evsel *counter)
|
||||
|
||||
static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
{
|
||||
char msg[512];
|
||||
unsigned long long t0, t1;
|
||||
struct perf_evsel *counter;
|
||||
int status = 0;
|
||||
@ -348,20 +325,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == EPERM || errno == EACCES) {
|
||||
error("You may not have permission to collect %sstats.\n"
|
||||
"\t Consider tweaking"
|
||||
" /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));
|
||||
}
|
||||
perf_evsel__open_strerror(counter, &target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
|
||||
if (child_pid != -1)
|
||||
kill(child_pid, SIGTERM);
|
||||
|
||||
pr_err("Not all events could be opened.\n");
|
||||
return -1;
|
||||
}
|
||||
counter->supported = true;
|
||||
|
@ -68,28 +68,6 @@
|
||||
#include <linux/unistd.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)
|
||||
{
|
||||
if (top->print_entries > 9)
|
||||
@ -716,7 +694,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
static struct intlist *seen;
|
||||
|
||||
if (!seen)
|
||||
seen = intlist__new();
|
||||
seen = intlist__new(NULL);
|
||||
|
||||
if (!intlist__has_entry(seen, event->ip.pid)) {
|
||||
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,
|
||||
|
||||
if (!machine) {
|
||||
pr_err("%u unprocessable samples recorded.\n",
|
||||
top->session->hists.stats.nr_unprocessable_samples++);
|
||||
top->session->stats.nr_unprocessable_samples++);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -847,13 +825,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||
++top->us_samples;
|
||||
if (top->hide_user_symbols)
|
||||
continue;
|
||||
machine = perf_session__find_host_machine(session);
|
||||
machine = &session->machines.host;
|
||||
break;
|
||||
case PERF_RECORD_MISC_KERNEL:
|
||||
++top->kernel_samples;
|
||||
if (top->hide_kernel_symbols)
|
||||
continue;
|
||||
machine = perf_session__find_host_machine(session);
|
||||
machine = &session->machines.host;
|
||||
break;
|
||||
case PERF_RECORD_MISC_GUEST_KERNEL:
|
||||
++top->guest_kernel_samples;
|
||||
@ -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);
|
||||
machine__process_event(machine, event);
|
||||
} 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)
|
||||
|
||||
static void perf_top__start_counters(struct perf_top *top)
|
||||
{
|
||||
char msg[512];
|
||||
struct perf_evsel *counter;
|
||||
struct perf_evlist *evlist = top->evlist;
|
||||
struct perf_record_opts *opts = &top->record_opts;
|
||||
@ -899,77 +878,18 @@ static void perf_top__start_counters(struct perf_top *top)
|
||||
perf_evlist__config(evlist, opts);
|
||||
|
||||
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:
|
||||
if (perf_evsel__open(counter, top->evlist->cpus,
|
||||
top->evlist->threads) < 0) {
|
||||
int err = errno;
|
||||
|
||||
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 (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
|
||||
if (verbose)
|
||||
ui__warning("Cycles event not supported,\n"
|
||||
"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;
|
||||
}
|
||||
ui__warning("%s\n", msg);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
perf_evsel__name(counter));
|
||||
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));
|
||||
perf_evsel__open_strerror(counter, &opts->target,
|
||||
errno, msg, sizeof(msg));
|
||||
ui__error("%s\n", msg);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
@ -1024,10 +944,10 @@ static int __cmd_top(struct perf_top *top)
|
||||
if (perf_target__has_task(&opts->target))
|
||||
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
|
||||
perf_event__process,
|
||||
&top->session->host_machine);
|
||||
&top->session->machines.host);
|
||||
else
|
||||
perf_event__synthesize_threads(&top->tool, perf_event__process,
|
||||
&top->session->host_machine);
|
||||
&top->session->machines.host);
|
||||
perf_top__start_counters(top);
|
||||
top->session->evlist = top->evlist;
|
||||
perf_session__set_id_hdr_size(top->session);
|
||||
|
@ -13,7 +13,7 @@ newline := $(newline)
|
||||
# what should replace a newline when escaping
|
||||
# newlines; the default is a bizarre string.
|
||||
#
|
||||
nl-escape = $(or $(1),m822df3020w6a44id34bt574ctac44eb9f4n)
|
||||
nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n)
|
||||
|
||||
# escape-nl
|
||||
#
|
||||
@ -173,9 +173,9 @@ _ge-abspath = $(if $(is-executable),$(1))
|
||||
# Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,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
|
||||
_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_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)
|
||||
if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
|
||||
return 0;
|
||||
|
||||
status = 1;
|
||||
/* Check for ENOSPC and EIO errors.. */
|
||||
if (fflush(stdout))
|
||||
die("write failure on standard output: %s", strerror(errno));
|
||||
if (ferror(stdout))
|
||||
die("unknown write failure on standard output");
|
||||
if (fclose(stdout))
|
||||
die("close failed on standard output: %s", strerror(errno));
|
||||
return 0;
|
||||
if (fflush(stdout)) {
|
||||
fprintf(stderr, "write failure on standard output: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if (ferror(stdout)) {
|
||||
fprintf(stderr, "unknown write failure on standard output");
|
||||
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)
|
||||
@ -467,7 +476,8 @@ int main(int argc, const char **argv)
|
||||
cmd += 5;
|
||||
argv[0] = cmd;
|
||||
handle_internal_command(argc, argv);
|
||||
die("cannot handle %s internally", cmd);
|
||||
fprintf(stderr, "cannot handle %s internally", cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Look for flags.. */
|
||||
@ -485,7 +495,7 @@ int main(int argc, const char **argv)
|
||||
printf("\n usage: %s\n\n", perf_usage_string);
|
||||
list_common_cmds_help();
|
||||
printf("\n %s\n\n", perf_more_info_string);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
@ -517,7 +527,7 @@ int main(int argc, const char **argv)
|
||||
fprintf(stderr, "Expansion of alias '%s' failed; "
|
||||
"'%s' is not a perf-command\n",
|
||||
cmd, argv[0]);
|
||||
exit(1);
|
||||
goto out;
|
||||
}
|
||||
if (!done_help) {
|
||||
cmd = argv[0] = help_unknown_cmd(cmd);
|
||||
@ -528,6 +538,6 @@ int main(int argc, const char **argv)
|
||||
|
||||
fprintf(stderr, "Failed to run command '%s': %s\n",
|
||||
cmd, strerror(errno));
|
||||
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
#ifndef _PERF_PERF_H
|
||||
#define _PERF_PERF_H
|
||||
|
||||
struct winsize;
|
||||
|
||||
void get_term_dimensions(struct winsize *ws);
|
||||
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#if defined(__i386__)
|
||||
@ -237,8 +233,6 @@ struct perf_record_opts {
|
||||
bool raw_samples;
|
||||
bool sample_address;
|
||||
bool sample_time;
|
||||
bool sample_id_all_missing;
|
||||
bool exclude_guest_missing;
|
||||
bool period;
|
||||
unsigned int freq;
|
||||
unsigned int mmap_pages;
|
||||
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
|
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
# description: workqueue stats (ins/exe/create/destroy)
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl
|
@ -1,129 +0,0 @@
|
||||
#!/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 @@
|
||||
|
||||
extern int verbose;
|
||||
|
||||
bool test_attr__enabled;
|
||||
|
||||
static char *dir;
|
||||
|
||||
void test_attr__init(void)
|
||||
@ -146,7 +144,7 @@ static int run_dir(const char *d, const char *perf)
|
||||
{
|
||||
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" : "");
|
||||
|
||||
return system(cmd);
|
||||
|
@ -68,7 +68,7 @@ class Event(dict):
|
||||
self[key] = val
|
||||
|
||||
def __init__(self, name, data, base):
|
||||
log.info(" Event %s" % name);
|
||||
log.debug(" Event %s" % name);
|
||||
self.name = name;
|
||||
self.group = ''
|
||||
self.add(base)
|
||||
@ -97,6 +97,14 @@ class Event(dict):
|
||||
return False
|
||||
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:
|
||||
# [config]
|
||||
# - just single instance in file
|
||||
@ -113,7 +121,7 @@ class Test(object):
|
||||
parser = ConfigParser.SafeConfigParser()
|
||||
parser.read(path)
|
||||
|
||||
log.warning("running '%s'" % path)
|
||||
log.debug("running '%s'" % path)
|
||||
|
||||
self.path = path
|
||||
self.test_dir = options.test_dir
|
||||
@ -128,7 +136,7 @@ class Test(object):
|
||||
|
||||
self.expect = {}
|
||||
self.result = {}
|
||||
log.info(" loading expected events");
|
||||
log.debug(" loading expected events");
|
||||
self.load_events(path, self.expect)
|
||||
|
||||
def is_event(self, name):
|
||||
@ -164,7 +172,7 @@ class Test(object):
|
||||
self.perf, self.command, tempdir, self.args)
|
||||
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):
|
||||
raise Unsup(self)
|
||||
@ -172,7 +180,7 @@ class Test(object):
|
||||
def compare(self, expect, result):
|
||||
match = {}
|
||||
|
||||
log.info(" compare");
|
||||
log.debug(" compare");
|
||||
|
||||
# For each expected event find all matching
|
||||
# events in result. Fail if there's not any.
|
||||
@ -187,10 +195,11 @@ class Test(object):
|
||||
else:
|
||||
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
|
||||
if (not exp_list):
|
||||
exp_event.diff(res_event)
|
||||
raise Fail(self, 'match failure');
|
||||
|
||||
match[exp_name] = exp_list
|
||||
@ -208,10 +217,10 @@ class Test(object):
|
||||
if res_group not in match[group]:
|
||||
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])))
|
||||
|
||||
log.info(" matched")
|
||||
log.debug(" matched")
|
||||
|
||||
def resolve_groups(self, events):
|
||||
for name, event in events.items():
|
||||
@ -233,7 +242,7 @@ class Test(object):
|
||||
self.run_cmd(tempdir);
|
||||
|
||||
# load events expectation for the test
|
||||
log.info(" loading result events");
|
||||
log.debug(" loading result events");
|
||||
for f in glob.glob(tempdir + '/event*'):
|
||||
self.load_events(f, self.result);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[config]
|
||||
command = record
|
||||
args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1
|
||||
args = -e '{cycles,instructions}' kill >/dev/null 2>&1
|
||||
|
||||
[event-1:base-record]
|
||||
fd=1
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Builtin regression testing command: ever growing number of sanity tests
|
||||
*/
|
||||
#include "builtin.h"
|
||||
#include "intlist.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "color.h"
|
||||
@ -68,6 +69,14 @@ static struct test {
|
||||
.desc = "struct perf_event_attr setup",
|
||||
.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,
|
||||
},
|
||||
@ -97,7 +106,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
|
||||
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 width = 0;
|
||||
@ -118,13 +127,28 @@ static int __cmd_test(int argc, const char *argv[])
|
||||
continue;
|
||||
|
||||
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");
|
||||
err = tests[curr].func();
|
||||
pr_debug("---- end ----\n%s:", tests[curr].desc);
|
||||
if (err)
|
||||
color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
|
||||
else
|
||||
|
||||
switch (err) {
|
||||
case TEST_OK:
|
||||
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;
|
||||
@ -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>]}]",
|
||||
NULL,
|
||||
};
|
||||
const char *skip = NULL;
|
||||
const struct option test_options[] = {
|
||||
OPT_STRING('s', "skip", &skip, "tests", "tests to skip"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
struct intlist *skiplist = NULL;
|
||||
|
||||
argc = parse_options(argc, argv, test_options, test_usage, 0);
|
||||
if (argc >= 1 && !strcmp(argv[0], "list"))
|
||||
@ -169,5 +196,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (symbol__init() < 0)
|
||||
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)
|
||||
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
|
||||
__perf_evsel__hw_cache_type_op_res_name(type, op, i,
|
||||
name, sizeof(name));
|
||||
err = parse_events(evlist, name, 0);
|
||||
err = parse_events(evlist, name);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
@ -70,7 +70,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_names; ++i) {
|
||||
err = parse_events(evlist, names[i], 0);
|
||||
err = parse_events(evlist, names[i]);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
names[i], err);
|
||||
|
499
tools/perf/tests/hists_link.c
Normal file
499
tools/perf/tests/hists_link.c
Normal file
@ -0,0 +1,499 @@
|
||||
#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 @@
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include "tests.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
@ -463,10 +464,10 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
|
||||
|
||||
static int test__checkterms_simple(struct list_head *terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
|
||||
/* 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",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@ -475,7 +476,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* 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",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@ -484,7 +485,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* 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",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@ -493,7 +494,7 @@ static int test__checkterms_simple(struct list_head *terms)
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
|
||||
/* 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",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
@ -782,13 +783,70 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
|
||||
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;
|
||||
__u32 type;
|
||||
int (*check)(struct perf_evlist *evlist);
|
||||
};
|
||||
|
||||
static struct test__event_st test__events[] = {
|
||||
static struct evlist_test test__events[] = {
|
||||
[0] = {
|
||||
.name = "syscalls:sys_enter_open",
|
||||
.check = test__checkevent_tracepoint,
|
||||
@ -921,9 +979,13 @@ static struct test__event_st test__events[] = {
|
||||
.name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",
|
||||
.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] = {
|
||||
.name = "cpu/config=10,config1,config2=3,period=1000/u",
|
||||
.check = test__checkevent_pmu,
|
||||
@ -934,20 +996,20 @@ static struct test__event_st test__events_pmu[] = {
|
||||
},
|
||||
};
|
||||
|
||||
struct test__term {
|
||||
struct terms_test {
|
||||
const char *str;
|
||||
__u32 type;
|
||||
int (*check)(struct list_head *terms);
|
||||
};
|
||||
|
||||
static struct test__term test__terms[] = {
|
||||
static struct terms_test test__terms[] = {
|
||||
[0] = {
|
||||
.str = "config=10,config1,config2=3,umask=1",
|
||||
.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;
|
||||
int ret;
|
||||
@ -956,7 +1018,7 @@ static int test_event(struct test__event_st *e)
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = parse_events(evlist, e->name, 0);
|
||||
ret = parse_events(evlist, e->name);
|
||||
if (ret) {
|
||||
pr_debug("failed to parse event '%s', err %d\n",
|
||||
e->name, ret);
|
||||
@ -969,13 +1031,13 @@ static int test_event(struct test__event_st *e)
|
||||
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;
|
||||
unsigned 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);
|
||||
ret1 = test_event(e);
|
||||
@ -986,7 +1048,7 @@ static int test_events(struct test__event_st *events, unsigned cnt)
|
||||
return ret2;
|
||||
}
|
||||
|
||||
static int test_term(struct test__term *t)
|
||||
static int test_term(struct terms_test *t)
|
||||
{
|
||||
struct list_head *terms;
|
||||
int ret;
|
||||
@ -1010,13 +1072,13 @@ static int test_term(struct test__term *t)
|
||||
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;
|
||||
unsigned 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);
|
||||
ret = test_term(t);
|
||||
@ -1067,7 +1129,7 @@ static int test_pmu_events(void)
|
||||
|
||||
while (!ret && (ent = readdir(dir))) {
|
||||
#define MAX_NAME 100
|
||||
struct test__event_st e;
|
||||
struct evlist_test e;
|
||||
char name[MAX_NAME];
|
||||
|
||||
if (!strcmp(ent->d_name, ".") ||
|
||||
|
@ -19,10 +19,8 @@ static struct test_format {
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
static struct parse_events_term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
@ -78,7 +76,6 @@ static struct parse_events__term test_terms[] = {
|
||||
.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
|
||||
@ -93,7 +90,7 @@ static char *test_format_dir_get(void)
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(test_formats); i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
@ -130,14 +127,12 @@ static struct list_head *test_terms_list(void)
|
||||
static LIST_HEAD(terms);
|
||||
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);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int test__pmu(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
|
23
tools/perf/tests/python-use.c
Normal file
23
tools/perf/tests/python-use.c
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,6 +1,12 @@
|
||||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
enum {
|
||||
TEST_OK = 0,
|
||||
TEST_FAIL = -1,
|
||||
TEST_SKIP = -2,
|
||||
};
|
||||
|
||||
/* Tests */
|
||||
int test__vmlinux_matches_kallsyms(void);
|
||||
int test__open_syscall_event(void);
|
||||
@ -15,5 +21,7 @@ int test__pmu(void);
|
||||
int test__attr(void);
|
||||
int test__dso_data(void);
|
||||
int test__parse_events(void);
|
||||
int test__hists_link(void);
|
||||
int test__python_use(void);
|
||||
|
||||
#endif /* TESTS_H */
|
||||
|
@ -101,7 +101,8 @@ int test__vmlinux_matches_kallsyms(void)
|
||||
*/
|
||||
if (machine__load_vmlinux_path(&vmlinux, type,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -471,7 +471,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
|
||||
return row;
|
||||
}
|
||||
|
||||
static struct ui_browser__colorset {
|
||||
static struct ui_browser_colorset {
|
||||
const char *name, *fg, *bg;
|
||||
int colorset;
|
||||
} ui_browser__colorsets[] = {
|
||||
@ -706,7 +706,7 @@ void ui_browser__init(void)
|
||||
perf_config(ui_browser__color_config, NULL);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -182,6 +182,16 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
|
||||
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)
|
||||
{
|
||||
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)
|
||||
if (strstr(sym->name, "@plt"))
|
||||
return;
|
||||
|
||||
if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
|
||||
!disasm_line__has_offset(cursor))
|
||||
if (!disasm_line__is_valid_jump(cursor, sym))
|
||||
return;
|
||||
|
||||
target = ab->offsets[cursor->ops.target.offset];
|
||||
@ -788,17 +797,9 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
|
||||
struct disasm_line *dl = browser->offsets[offset], *dlt;
|
||||
struct browser_disasm_line *bdlt;
|
||||
|
||||
if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
|
||||
!disasm_line__has_offset(dl))
|
||||
if (!disasm_line__is_valid_jump(dl, sym))
|
||||
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;
|
||||
}
|
||||
|
||||
dlt = browser->offsets[dl->ops.target.offset];
|
||||
/*
|
||||
* FIXME: Oops, no jump target? Buggy disassembler? Or do we
|
||||
@ -921,11 +922,11 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
|
||||
#define ANNOTATE_CFG(n) \
|
||||
{ .name = #n, .value = &annotate_browser__opts.n, }
|
||||
|
||||
|
||||
/*
|
||||
* Keep the entries sorted, they are bsearch'ed
|
||||
*/
|
||||
static struct annotate__config {
|
||||
static struct annotate_config {
|
||||
const char *name;
|
||||
bool *value;
|
||||
} annotate__configs[] = {
|
||||
@ -939,7 +940,7 @@ static struct annotate__config {
|
||||
|
||||
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);
|
||||
}
|
||||
@ -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,
|
||||
void *data __maybe_unused)
|
||||
{
|
||||
struct annotate__config *cfg;
|
||||
struct annotate_config *cfg;
|
||||
const char *name;
|
||||
|
||||
if (prefixcmp(var, "annotate.") != 0)
|
||||
@ -955,7 +956,7 @@ static int annotate__config(const char *var, const char *value,
|
||||
|
||||
name = var + 9;
|
||||
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)
|
||||
return -1;
|
||||
|
@ -8,15 +8,13 @@
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#define MAX_COLUMNS 32
|
||||
|
||||
static void perf_gtk__signal(int sig)
|
||||
void perf_gtk__signal(int sig)
|
||||
{
|
||||
perf_gtk__exit(false);
|
||||
psignal(sig, "perf");
|
||||
}
|
||||
|
||||
static void perf_gtk__resize_window(GtkWidget *window)
|
||||
void perf_gtk__resize_window(GtkWidget *window)
|
||||
{
|
||||
GdkRectangle rect;
|
||||
GdkScreen *screen;
|
||||
@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window)
|
||||
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)
|
||||
return "<span fgcolor='red'>";
|
||||
@ -45,147 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent)
|
||||
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
|
||||
static GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *label;
|
||||
@ -212,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
{
|
||||
GtkWidget *stbar;
|
||||
unsigned ctxid;
|
||||
@ -226,79 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
|
||||
|
||||
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);
|
||||
void perf_gtk__init_progress(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)
|
||||
{
|
||||
return NULL;
|
||||
|
226
tools/perf/ui/gtk/hists.c
Normal file
226
tools/perf/ui/gtk/hists.c
Normal file
@ -0,0 +1,226 @@
|
||||
#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,
|
||||
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;
|
||||
size_t ret = 0;
|
||||
@ -467,7 +467,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
|
||||
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
|
||||
const char *name;
|
||||
|
||||
if (hists->stats.nr_events[i] == 0)
|
||||
if (stats->nr_events[i] == 0)
|
||||
continue;
|
||||
|
||||
name = perf_event__name(i);
|
||||
@ -475,7 +475,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
|
||||
continue;
|
||||
|
||||
ret += fprintf(fp, "%16s events: %10d\n", name,
|
||||
hists->stats.nr_events[i]);
|
||||
stats->nr_events[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -52,17 +52,6 @@ int ui__warning(const char *format, ...)
|
||||
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
|
||||
* @eops: The pointer to error logging function struct
|
||||
|
@ -26,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)')
|
||||
|
||||
if test -r $GVF
|
||||
then
|
||||
VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
|
||||
VC=$(sed -e 's/^#define PERF_VERSION "\(.*\)"/\1/' <$GVF)
|
||||
else
|
||||
VC=unset
|
||||
fi
|
||||
test "$VN" = "$VC" || {
|
||||
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);
|
||||
|
||||
int ui__error(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 */
|
||||
|
@ -22,6 +22,11 @@
|
||||
#include <linux/perf_event.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))
|
||||
|
||||
static int __perf_evsel__sample_size(u64 sample_type)
|
||||
@ -463,7 +468,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
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;
|
||||
|
||||
perf_evsel__set_sample_bit(evsel, IP);
|
||||
@ -513,7 +518,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||
if (opts->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 ||
|
||||
perf_target__has_cpu(&opts->target)))
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
@ -761,6 +766,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
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 (thread = 0; thread < threads->nr; thread++) {
|
||||
@ -777,13 +789,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
|
||||
group_fd, flags);
|
||||
if (FD(evsel, cpu, thread) < 0) {
|
||||
err = -errno;
|
||||
goto out_close;
|
||||
goto try_fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
do {
|
||||
while (--thread >= 0) {
|
||||
@ -1353,3 +1378,80 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
fputc('\n', fp);
|
||||
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 {
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
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 */
|
||||
|
@ -148,7 +148,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
|
||||
u32 len;
|
||||
char *buf;
|
||||
|
||||
sz = read(fd, &len, sizeof(len));
|
||||
sz = readn(fd, &len, sizeof(len));
|
||||
if (sz < (ssize_t)sizeof(len))
|
||||
return NULL;
|
||||
|
||||
@ -159,7 +159,7 @@ static char *do_read_string(int fd, struct perf_header *ph)
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
ret = read(fd, buf, len);
|
||||
ret = readn(fd, buf, len);
|
||||
if (ret == (ssize_t)len) {
|
||||
/*
|
||||
* strings are padded by zeroes
|
||||
@ -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, header);
|
||||
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)
|
||||
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);
|
||||
err = machine__write_buildid_table(pos, fd);
|
||||
if (err)
|
||||
@ -448,9 +448,9 @@ static int perf_session__cache_build_ids(struct perf_session *session)
|
||||
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
|
||||
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);
|
||||
ret |= machine__cache_build_ids(pos, debugdir);
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
struct perf_pmu *pmu = NULL;
|
||||
off_t offset = lseek(fd, 0, SEEK_CUR);
|
||||
__u32 pmu_num = 0;
|
||||
int ret;
|
||||
|
||||
/* 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))) {
|
||||
if (!pmu->name)
|
||||
continue;
|
||||
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)) {
|
||||
@ -1209,14 +1218,14 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
size_t msz;
|
||||
|
||||
/* number of events */
|
||||
ret = read(fd, &nre, sizeof(nre));
|
||||
ret = readn(fd, &nre, sizeof(nre));
|
||||
if (ret != (ssize_t)sizeof(nre))
|
||||
goto error;
|
||||
|
||||
if (ph->needs_swap)
|
||||
nre = bswap_32(nre);
|
||||
|
||||
ret = read(fd, &sz, sizeof(sz));
|
||||
ret = readn(fd, &sz, sizeof(sz));
|
||||
if (ret != (ssize_t)sizeof(sz))
|
||||
goto error;
|
||||
|
||||
@ -1244,7 +1253,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
* must read entire on-file attr struct to
|
||||
* sync up with layout.
|
||||
*/
|
||||
ret = read(fd, buf, sz);
|
||||
ret = readn(fd, buf, sz);
|
||||
if (ret != (ssize_t)sz)
|
||||
goto error;
|
||||
|
||||
@ -1253,7 +1262,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
|
||||
memcpy(&evsel->attr, buf, msz);
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != (ssize_t)sizeof(nr))
|
||||
goto error;
|
||||
|
||||
@ -1274,7 +1283,7 @@ read_event_desc(struct perf_header *ph, int fd)
|
||||
evsel->id = id;
|
||||
|
||||
for (j = 0 ; j < nr; j++) {
|
||||
ret = read(fd, id, sizeof(*id));
|
||||
ret = readn(fd, id, sizeof(*id));
|
||||
if (ret != (ssize_t)sizeof(*id))
|
||||
goto error;
|
||||
if (ph->needs_swap)
|
||||
@ -1506,14 +1515,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
|
||||
while (offset < limit) {
|
||||
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;
|
||||
|
||||
if (header->needs_swap)
|
||||
perf_event_header__bswap(&old_bev.header);
|
||||
|
||||
len = old_bev.header.size - sizeof(old_bev);
|
||||
if (read(input, filename, len) != len)
|
||||
if (readn(input, filename, len) != len)
|
||||
return -1;
|
||||
|
||||
bev.header = old_bev.header;
|
||||
@ -1548,14 +1557,14 @@ static int perf_header__read_build_ids(struct perf_header *header,
|
||||
while (offset < limit) {
|
||||
ssize_t len;
|
||||
|
||||
if (read(input, &bev, sizeof(bev)) != sizeof(bev))
|
||||
if (readn(input, &bev, sizeof(bev)) != sizeof(bev))
|
||||
goto out;
|
||||
|
||||
if (header->needs_swap)
|
||||
perf_event_header__bswap(&bev.header);
|
||||
|
||||
len = bev.header.size - sizeof(bev);
|
||||
if (read(input, filename, len) != len)
|
||||
if (readn(input, filename, len) != len)
|
||||
goto out;
|
||||
/*
|
||||
* The a1645ce1 changeset:
|
||||
@ -1641,7 +1650,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
|
||||
size_t ret;
|
||||
u32 nr;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@ -1650,7 +1659,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused,
|
||||
|
||||
ph->env.nr_cpus_online = nr;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@ -1684,7 +1693,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused,
|
||||
uint64_t mem;
|
||||
size_t ret;
|
||||
|
||||
ret = read(fd, &mem, sizeof(mem));
|
||||
ret = readn(fd, &mem, sizeof(mem));
|
||||
if (ret != sizeof(mem))
|
||||
return -1;
|
||||
|
||||
@ -1756,7 +1765,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused,
|
||||
u32 nr, i;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@ -1792,7 +1801,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
|
||||
char *str;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@ -1813,7 +1822,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused
|
||||
}
|
||||
ph->env.sibling_cores = strbuf_detach(&sb, NULL);
|
||||
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
return -1;
|
||||
|
||||
@ -1850,7 +1859,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
|
||||
struct strbuf sb;
|
||||
|
||||
/* nr nodes */
|
||||
ret = read(fd, &nr, sizeof(nr));
|
||||
ret = readn(fd, &nr, sizeof(nr));
|
||||
if (ret != sizeof(nr))
|
||||
goto error;
|
||||
|
||||
@ -1862,15 +1871,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
/* node number */
|
||||
ret = read(fd, &node, sizeof(node));
|
||||
ret = readn(fd, &node, sizeof(node));
|
||||
if (ret != sizeof(node))
|
||||
goto error;
|
||||
|
||||
ret = read(fd, &mem_total, sizeof(u64));
|
||||
ret = readn(fd, &mem_total, sizeof(u64));
|
||||
if (ret != sizeof(u64))
|
||||
goto error;
|
||||
|
||||
ret = read(fd, &mem_free, sizeof(u64));
|
||||
ret = readn(fd, &mem_free, sizeof(u64));
|
||||
if (ret != sizeof(u64))
|
||||
goto error;
|
||||
|
||||
@ -1909,7 +1918,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
|
||||
u32 type;
|
||||
struct strbuf sb;
|
||||
|
||||
ret = read(fd, &pmu_num, sizeof(pmu_num));
|
||||
ret = readn(fd, &pmu_num, sizeof(pmu_num));
|
||||
if (ret != sizeof(pmu_num))
|
||||
return -1;
|
||||
|
||||
@ -1925,7 +1934,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
|
||||
strbuf_init(&sb, 128);
|
||||
|
||||
while (pmu_num) {
|
||||
if (read(fd, &type, sizeof(type)) != sizeof(type))
|
||||
if (readn(fd, &type, sizeof(type)) != sizeof(type))
|
||||
goto error;
|
||||
if (ph->needs_swap)
|
||||
type = bswap_32(type);
|
||||
@ -2912,7 +2921,7 @@ int perf_event__process_tracing_data(union perf_event *event,
|
||||
session->repipe);
|
||||
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");
|
||||
if (session->repipe) {
|
||||
int retw = write(STDOUT_FILENO, buf, padding);
|
||||
|
@ -82,6 +82,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
||||
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) {
|
||||
int symlen;
|
||||
/*
|
||||
@ -242,6 +245,14 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
|
||||
if (he->ms.map)
|
||||
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)
|
||||
callchain_init(he->callchain);
|
||||
|
||||
@ -251,7 +262,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
|
||||
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) {
|
||||
hists__calc_col_len(hists, h);
|
||||
@ -285,7 +296,13 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
|
||||
parent = *p;
|
||||
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) {
|
||||
he_stat__add_period(&he->stat, period);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
++hists->stats.nr_events[0];
|
||||
++hists->stats.nr_events[type];
|
||||
events_stats__inc(&hists->stats, type);
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
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 hist_entry *he;
|
||||
int cmp;
|
||||
|
||||
if (sort__need_collapse)
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
||||
p = &root->rb_node;
|
||||
|
||||
while (*p != NULL) {
|
||||
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)
|
||||
goto out;
|
||||
@ -744,8 +774,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
||||
if (he) {
|
||||
memset(&he->stat, 0, sizeof(he->stat));
|
||||
he->hists = hists;
|
||||
rb_link_node(&he->rb_node, parent, p);
|
||||
rb_insert_color(&he->rb_node, &hists->entries);
|
||||
rb_link_node(&he->rb_node_in, parent, p);
|
||||
rb_insert_color(&he->rb_node_in, root);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
}
|
||||
out:
|
||||
@ -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,
|
||||
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) {
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
|
||||
int64_t cmp = hist_entry__cmp(he, iter);
|
||||
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in);
|
||||
int64_t cmp = hist_entry__collapse(iter, he);
|
||||
|
||||
if (cmp < 0)
|
||||
n = n->rb_left;
|
||||
@ -777,11 +812,17 @@ static struct hist_entry *hists__find_entry(struct hists *hists,
|
||||
*/
|
||||
void hists__match(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_root *root;
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
if (sort__need_collapse)
|
||||
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);
|
||||
|
||||
if (pair)
|
||||
@ -796,11 +837,17 @@ void hists__match(struct hists *leader, struct hists *other)
|
||||
*/
|
||||
int hists__link(struct hists *leader, struct hists *other)
|
||||
{
|
||||
struct rb_root *root;
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *pos, *pair;
|
||||
|
||||
for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct hist_entry, rb_node);
|
||||
if (sort__need_collapse)
|
||||
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)) {
|
||||
pair = hists__add_dummy_entry(leader, pos);
|
||||
|
@ -96,8 +96,10 @@ void hists__decay_entries_threaded(struct hists *hists, bool zap_user,
|
||||
bool zap_kernel);
|
||||
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);
|
||||
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,
|
||||
int max_cols, FILE *fp);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#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_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) \
|
||||
for ((bit) = find_first_bit((addr), (size)); \
|
||||
|
@ -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 *node = NULL;
|
||||
struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
|
||||
struct int_node *node;
|
||||
struct rb_node *rb_node;
|
||||
|
||||
if (ilist == NULL)
|
||||
return NULL;
|
||||
|
||||
node = NULL;
|
||||
rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
|
||||
if (rb_node)
|
||||
node = container_of(rb_node, struct int_node, rb_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));
|
||||
|
||||
@ -77,9 +101,15 @@ struct intlist *intlist__new(void)
|
||||
ilist->rblist.node_cmp = intlist__node_cmp;
|
||||
ilist->rblist.node_new = intlist__node_new;
|
||||
ilist->rblist.node_delete = intlist__node_delete;
|
||||
|
||||
if (slist && intlist__parse_list(ilist, slist))
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
return ilist;
|
||||
out_delete:
|
||||
intlist__delete(ilist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void intlist__delete(struct intlist *ilist)
|
||||
|
@ -15,7 +15,7 @@ struct intlist {
|
||||
struct rblist rblist;
|
||||
};
|
||||
|
||||
struct intlist *intlist__new(void);
|
||||
struct intlist *intlist__new(const char *slist);
|
||||
void intlist__delete(struct intlist *ilist);
|
||||
|
||||
void intlist__remove(struct intlist *ilist, struct int_node *in);
|
||||
|
@ -91,10 +91,22 @@ void machine__delete(struct machine *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)
|
||||
{
|
||||
struct rb_node **p = &machines->rb_node;
|
||||
struct rb_node **p = &machines->guests.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct machine *pos, *machine = malloc(sizeof(*machine));
|
||||
|
||||
@ -116,18 +128,21 @@ struct machine *machines__add(struct rb_root *machines, pid_t pid,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 machine *machine;
|
||||
struct machine *default_machine = NULL;
|
||||
|
||||
if (pid == HOST_KERNEL_ID)
|
||||
return &machines->host;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
machine = rb_entry(parent, struct machine, rb_node);
|
||||
@ -144,7 +159,7 @@ struct machine *machines__find(struct rb_root *machines, pid_t pid)
|
||||
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];
|
||||
const char *root_dir = "";
|
||||
@ -178,12 +193,12 @@ struct machine *machines__findnew(struct rb_root *machines, pid_t pid)
|
||||
return machine;
|
||||
}
|
||||
|
||||
void machines__process(struct rb_root *machines,
|
||||
machine__process_t process, void *data)
|
||||
void machines__process_guests(struct machines *machines,
|
||||
machine__process_t process, void *data)
|
||||
{
|
||||
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);
|
||||
process(pos, data);
|
||||
}
|
||||
@ -203,12 +218,14 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size)
|
||||
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 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->id_hdr_size = id_hdr_size;
|
||||
}
|
||||
@ -313,12 +330,13 @@ struct map *machine__new_module(struct machine *machine, u64 start,
|
||||
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;
|
||||
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);
|
||||
ret += __dsos__fprintf(&pos->kernel_dsos, fp);
|
||||
ret += __dsos__fprintf(&pos->user_dsos, 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm);
|
||||
}
|
||||
@ -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;
|
||||
struct dirent **namelist = NULL;
|
||||
@ -560,20 +578,22 @@ int machines__create_guest_kernel_maps(struct rb_root *machines)
|
||||
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) {
|
||||
struct machine *pos = rb_entry(next, struct machine, rb_node);
|
||||
|
||||
next = rb_next(&pos->rb_node);
|
||||
rb_erase(&pos->rb_node, machines);
|
||||
rb_erase(&pos->rb_node, &machines->guests);
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
void machines__process(struct rb_root *machines,
|
||||
machine__process_t process, void *data);
|
||||
struct machines {
|
||||
struct machine host;
|
||||
struct rb_root guests;
|
||||
};
|
||||
|
||||
struct machine *machines__add(struct rb_root *machines, pid_t pid,
|
||||
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);
|
||||
|
||||
struct machine *machines__add(struct machines *machines, pid_t pid,
|
||||
const char *root_dir);
|
||||
struct machine *machines__find_host(struct rb_root *machines);
|
||||
struct machine *machines__find(struct rb_root *machines, pid_t pid);
|
||||
struct machine *machines__findnew(struct rb_root *machines, pid_t pid);
|
||||
struct machine *machines__find_host(struct machines *machines);
|
||||
struct machine *machines__find(struct machines *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);
|
||||
|
||||
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,
|
||||
|
||||
size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
|
||||
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_buildid(struct rb_root *machines, FILE *fp,
|
||||
size_t machines__fprintf_dsos(struct machines *machines, FILE *fp);
|
||||
size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
|
||||
bool (skip)(struct dso *dso, int parm), int parm);
|
||||
|
||||
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);
|
||||
|
||||
int machines__create_kernel_maps(struct rb_root *machines, pid_t pid);
|
||||
int machines__create_guest_kernel_maps(struct rb_root *machines);
|
||||
void machines__destroy_guest_kernel_maps(struct rb_root *machines);
|
||||
int machines__create_kernel_maps(struct machines *machines, pid_t pid);
|
||||
int machines__create_guest_kernel_maps(struct machines *machines);
|
||||
void machines__destroy_kernel_maps(struct machines *machines);
|
||||
|
||||
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
|
||||
|
||||
|
@ -19,7 +19,8 @@ const char *map_type__name[MAP__NR_TYPES] = {
|
||||
|
||||
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)
|
||||
|
@ -380,8 +380,8 @@ static int add_tracepoint(struct list_head **listp, int *idx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_tracepoint_multi(struct list_head **list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
static int add_tracepoint_multi_event(struct list_head **list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
char evt_path[MAXPATHLEN];
|
||||
struct dirent *evt_ent;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -420,9 +461,10 @@ int parse_events_add_tracepoint(struct list_head **list, int *idx,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return strpbrk(event, "*?") ?
|
||||
add_tracepoint_multi(list, idx, sys, event) :
|
||||
add_tracepoint(list, idx, sys, event);
|
||||
if (strpbrk(sys, "*?"))
|
||||
return add_tracepoint_multi_sys(list, idx, sys, event);
|
||||
else
|
||||
return add_tracepoint_event(list, idx, sys, event);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -492,7 +534,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
|
||||
}
|
||||
|
||||
static int config_term(struct perf_event_attr *attr,
|
||||
struct parse_events__term *term)
|
||||
struct parse_events_term *term)
|
||||
{
|
||||
#define CHECK_TYPE_VAL(type) \
|
||||
do { \
|
||||
@ -537,7 +579,7 @@ do { \
|
||||
static int config_attr(struct perf_event_attr *attr,
|
||||
struct list_head *head, int fail)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
|
||||
list_for_each_entry(term, head, list)
|
||||
if (config_term(attr, term) && fail)
|
||||
@ -563,14 +605,14 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
if (parse_events__is_name_term(term))
|
||||
@ -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)
|
||||
{
|
||||
struct parse_events_data__terms data = {
|
||||
struct parse_events_terms data = {
|
||||
.terms = NULL,
|
||||
};
|
||||
int ret;
|
||||
@ -830,10 +872,9 @@ int parse_events_terms(struct list_head *terms, const char *str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
int unset __maybe_unused)
|
||||
int parse_events(struct perf_evlist *evlist, const char *str)
|
||||
{
|
||||
struct parse_events_data__events data = {
|
||||
struct parse_events_evlist data = {
|
||||
.list = LIST_HEAD_INIT(data.list),
|
||||
.idx = evlist->nr_entries,
|
||||
};
|
||||
@ -858,7 +899,7 @@ int parse_events_option(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
int ret = parse_events(evlist, str, unset);
|
||||
int ret = parse_events(evlist, str);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
|
||||
@ -1121,16 +1162,16 @@ void print_events(const char *event_glob, bool 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;
|
||||
}
|
||||
|
||||
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,
|
||||
char *str, u64 num)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
|
||||
term = zalloc(sizeof(*term));
|
||||
if (!term)
|
||||
@ -1156,21 +1197,21 @@ static int new_term(struct parse_events__term **_term, int type_val,
|
||||
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)
|
||||
{
|
||||
return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
|
||||
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)
|
||||
{
|
||||
return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term,
|
||||
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)
|
||||
{
|
||||
struct event_symbol *sym;
|
||||
@ -1188,8 +1229,8 @@ int parse_events__term_sym_hw(struct parse_events__term **term,
|
||||
(char *) "event", (char *) sym->symbol, 0);
|
||||
}
|
||||
|
||||
int parse_events__term_clone(struct parse_events__term **new,
|
||||
struct parse_events__term *term)
|
||||
int parse_events_term__clone(struct parse_events_term **new,
|
||||
struct parse_events_term *term)
|
||||
{
|
||||
return new_term(new, term->type_val, term->type_term, term->config,
|
||||
term->val.str, term->val.num);
|
||||
@ -1197,7 +1238,7 @@ int parse_events__term_clone(struct parse_events__term **new,
|
||||
|
||||
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)
|
||||
free(term);
|
||||
|
@ -29,8 +29,7 @@ const char *event_type(int type);
|
||||
|
||||
extern int parse_events_option(const struct option *opt, const char *str,
|
||||
int unset);
|
||||
extern int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
int unset);
|
||||
extern int parse_events(struct perf_evlist *evlist, 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);
|
||||
|
||||
@ -51,7 +50,7 @@ enum {
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
};
|
||||
|
||||
struct parse_events__term {
|
||||
struct parse_events_term {
|
||||
char *config;
|
||||
union {
|
||||
char *str;
|
||||
@ -62,24 +61,24 @@ struct parse_events__term {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct parse_events_data__events {
|
||||
struct parse_events_evlist {
|
||||
struct list_head list;
|
||||
int idx;
|
||||
};
|
||||
|
||||
struct parse_events_data__terms {
|
||||
struct parse_events_terms {
|
||||
struct list_head *terms;
|
||||
};
|
||||
|
||||
int parse_events__is_hardcoded_term(struct parse_events__term *term);
|
||||
int parse_events__term_num(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 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 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);
|
||||
int parse_events__term_clone(struct parse_events__term **new,
|
||||
struct parse_events__term *term);
|
||||
int parse_events_term__clone(struct parse_events_term **new,
|
||||
struct parse_events_term *term);
|
||||
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_group(struct list_head *list, char *event_mod);
|
||||
|
@ -68,7 +68,7 @@ do { \
|
||||
char *str;
|
||||
u64 num;
|
||||
struct list_head *head;
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
}
|
||||
%%
|
||||
|
||||
@ -79,7 +79,7 @@ PE_START_TERMS start_terms
|
||||
|
||||
start_events: groups
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
|
||||
parse_events_update_lists($1, &data->list);
|
||||
}
|
||||
@ -186,7 +186,7 @@ event_def: event_pmu |
|
||||
event_pmu:
|
||||
PE_NAME '/' event_config '/'
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
||||
ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3));
|
||||
@ -202,7 +202,7 @@ PE_VALUE_SYM_SW
|
||||
event_legacy_symbol:
|
||||
value_sym '/' event_config '/'
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
@ -215,7 +215,7 @@ value_sym '/' event_config '/'
|
||||
|
|
||||
value_sym sep_slash_dc
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
@ -228,7 +228,7 @@ value_sym sep_slash_dc
|
||||
event_legacy_cache:
|
||||
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;
|
||||
|
||||
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
|
||||
|
|
||||
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;
|
||||
|
||||
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
|
||||
|
|
||||
PE_NAME_CACHE_TYPE
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
||||
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL));
|
||||
@ -256,7 +256,7 @@ PE_NAME_CACHE_TYPE
|
||||
event_legacy_mem:
|
||||
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;
|
||||
|
||||
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
|
||||
@ -266,7 +266,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP 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;
|
||||
|
||||
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
|
||||
@ -277,7 +277,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
event_legacy_tracepoint:
|
||||
PE_NAME ':' PE_NAME
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
||||
ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3));
|
||||
@ -287,7 +287,7 @@ PE_NAME ':' PE_NAME
|
||||
event_legacy_numeric:
|
||||
PE_VALUE ':' PE_VALUE
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL));
|
||||
@ -297,7 +297,7 @@ PE_VALUE ':' PE_VALUE
|
||||
event_legacy_raw:
|
||||
PE_RAW
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx,
|
||||
@ -307,7 +307,7 @@ PE_RAW
|
||||
|
||||
start_terms: event_config
|
||||
{
|
||||
struct parse_events_data__terms *data = _data;
|
||||
struct parse_events_terms *data = _data;
|
||||
data->terms = $1;
|
||||
}
|
||||
|
||||
@ -315,7 +315,7 @@ event_config:
|
||||
event_config ',' event_term
|
||||
{
|
||||
struct list_head *head = $1;
|
||||
struct parse_events__term *term = $3;
|
||||
struct parse_events_term *term = $3;
|
||||
|
||||
ABORT_ON(!head);
|
||||
list_add_tail(&term->list, head);
|
||||
@ -325,7 +325,7 @@ event_config ',' event_term
|
||||
event_term
|
||||
{
|
||||
struct list_head *head = malloc(sizeof(*head));
|
||||
struct parse_events__term *term = $1;
|
||||
struct parse_events_term *term = $1;
|
||||
|
||||
ABORT_ON(!head);
|
||||
INIT_LIST_HEAD(head);
|
||||
@ -336,70 +336,70 @@ event_term
|
||||
event_term:
|
||||
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));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
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));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_NAME '=' PE_VALUE_SYM_HW
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
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;
|
||||
}
|
||||
|
|
||||
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));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_VALUE_SYM_HW
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
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;
|
||||
}
|
||||
|
|
||||
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;
|
||||
}
|
||||
|
|
||||
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;
|
||||
}
|
||||
|
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -11,6 +10,19 @@
|
||||
#include "parse-events.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/"
|
||||
|
||||
int perf_pmu_parse(struct list_head *list, char *name);
|
||||
@ -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)
|
||||
{
|
||||
struct perf_pmu__alias *alias;
|
||||
struct perf_pmu_alias *alias;
|
||||
char buf[256];
|
||||
int ret;
|
||||
|
||||
@ -172,15 +184,15 @@ static int pmu_aliases(char *name, struct list_head *head)
|
||||
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 parse_events__term *term, *clone;
|
||||
struct parse_events_term *term, *clone;
|
||||
LIST_HEAD(list);
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(term, &alias->terms, list) {
|
||||
ret = parse_events__term_clone(&clone, term);
|
||||
ret = parse_events_term__clone(&clone, term);
|
||||
if (ret) {
|
||||
parse_events__free_terms(&list);
|
||||
return ret;
|
||||
@ -360,10 +372,10 @@ struct perf_pmu *perf_pmu__find(char *name)
|
||||
return pmu_lookup(name);
|
||||
}
|
||||
|
||||
static struct perf_pmu__format*
|
||||
static struct perf_pmu_format *
|
||||
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)
|
||||
if (!strcmp(format->name, name))
|
||||
@ -403,9 +415,9 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)
|
||||
*/
|
||||
static int pmu_config_term(struct list_head *formats,
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -450,7 +462,7 @@ int perf_pmu__config_terms(struct list_head *formats,
|
||||
struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
struct parse_events_term *term;
|
||||
|
||||
list_for_each_entry(term, head_terms, list)
|
||||
if (pmu_config_term(formats, attr, term))
|
||||
@ -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);
|
||||
}
|
||||
|
||||
static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
|
||||
struct parse_events__term *term)
|
||||
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
|
||||
struct parse_events_term *term)
|
||||
{
|
||||
struct perf_pmu__alias *alias;
|
||||
struct perf_pmu_alias *alias;
|
||||
char *name;
|
||||
|
||||
if (parse_events__is_hardcoded_term(term))
|
||||
@ -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)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
struct perf_pmu__alias *alias;
|
||||
struct parse_events_term *term, *h;
|
||||
struct perf_pmu_alias *alias;
|
||||
int ret;
|
||||
|
||||
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)
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
struct perf_pmu_format *format;
|
||||
|
||||
format = zalloc(sizeof(*format));
|
||||
if (!format)
|
||||
@ -548,7 +560,7 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to)
|
||||
if (!to)
|
||||
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++)
|
||||
set_bit(b, bits);
|
||||
}
|
||||
|
@ -12,19 +12,6 @@ enum {
|
||||
|
||||
#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 {
|
||||
char *name;
|
||||
__u32 type;
|
||||
@ -42,7 +29,7 @@ int perf_pmu__config_terms(struct list_head *formats,
|
||||
struct list_head *head_terms);
|
||||
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
|
||||
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
|
||||
struct list_head *head_terms);
|
||||
struct list_head *head_terms);
|
||||
int perf_pmu_wrap(void);
|
||||
void perf_pmu_error(struct list_head *list, char *name, char const *msg);
|
||||
|
||||
|
@ -413,12 +413,12 @@ static int convert_variable_type(Dwarf_Die *vr_die,
|
||||
dwarf_diename(vr_die), dwarf_diename(&type));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (die_get_real_type(&type, &type) == NULL) {
|
||||
pr_warning("Failed to get a type"
|
||||
" information.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (ret == DW_TAG_pointer_type) {
|
||||
if (die_get_real_type(&type, &type) == NULL) {
|
||||
pr_warning("Failed to get a type"
|
||||
" information.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
while (*ref_ptr)
|
||||
ref_ptr = &(*ref_ptr)->next;
|
||||
/* Add new reference with offset +0 */
|
||||
|
@ -1045,3 +1045,12 @@ PyMODINIT_FUNC initperf(void)
|
||||
if (PyErr_Occurred())
|
||||
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,
|
||||
ns = nsecs - s * NSECS_PER_SEC;
|
||||
|
||||
scripting_context->event_data = data;
|
||||
scripting_context->pevent = evsel->tp_format->pevent;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
@ -265,6 +265,7 @@ static void python_process_tracepoint(union perf_event *perf_event
|
||||
ns = nsecs - s * NSECS_PER_SEC;
|
||||
|
||||
scripting_context->event_data = data;
|
||||
scripting_context->pevent = evsel->tp_format->pevent;
|
||||
|
||||
context = PyCObject_FromVoidPtr(scripting_context, NULL);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
session->host_machine.id_hdr_size = id_hdr_size;
|
||||
machines__set_id_hdr_size(&session->machines, id_hdr_size);
|
||||
}
|
||||
|
||||
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)
|
||||
ret = machines__create_guest_kernel_maps(&self->machines);
|
||||
@ -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)
|
||||
{
|
||||
machine__destroy_kernel_maps(&self->host_machine);
|
||||
machines__destroy_guest_kernel_maps(&self->machines);
|
||||
machines__destroy_kernel_maps(&self->machines);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
memcpy(self->filename, filename, len);
|
||||
self->machines = RB_ROOT;
|
||||
self->repipe = repipe;
|
||||
INIT_LIST_HEAD(&self->ordered_samples.samples);
|
||||
INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
|
||||
INIT_LIST_HEAD(&self->ordered_samples.to_free);
|
||||
machine__init(&self->host_machine, "", HOST_KERNEL_ID);
|
||||
hists__init(&self->hists);
|
||||
machines__init(&self->machines);
|
||||
|
||||
if (mode == O_RDONLY) {
|
||||
if (perf_session__open(self, force) < 0)
|
||||
@ -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)
|
||||
{
|
||||
machine__delete_dead_threads(&session->host_machine);
|
||||
machine__delete_dead_threads(&session->machines.host);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -193,7 +189,7 @@ void perf_session__delete(struct perf_session *self)
|
||||
perf_session__delete_dead_threads(self);
|
||||
perf_session__delete_threads(self);
|
||||
perf_session_env__delete(&self->header.env);
|
||||
machine__exit(&self->host_machine);
|
||||
machines__exit(&self->machines);
|
||||
close(self->fd);
|
||||
free(self);
|
||||
vdso__exit();
|
||||
@ -825,7 +821,7 @@ static struct machine *
|
||||
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,
|
||||
@ -863,11 +859,11 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
case PERF_RECORD_SAMPLE:
|
||||
dump_sample(evsel, event, sample);
|
||||
if (evsel == NULL) {
|
||||
++session->hists.stats.nr_unknown_id;
|
||||
++session->stats.nr_unknown_id;
|
||||
return 0;
|
||||
}
|
||||
if (machine == NULL) {
|
||||
++session->hists.stats.nr_unprocessable_samples;
|
||||
++session->stats.nr_unprocessable_samples;
|
||||
return 0;
|
||||
}
|
||||
return tool->sample(tool, event, sample, evsel, machine);
|
||||
@ -881,7 +877,7 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
return tool->exit(tool, event, sample, machine);
|
||||
case PERF_RECORD_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);
|
||||
case PERF_RECORD_READ:
|
||||
return tool->read(tool, event, sample, evsel, machine);
|
||||
@ -890,7 +886,7 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
case PERF_RECORD_UNTHROTTLE:
|
||||
return tool->unthrottle(tool, event, sample, machine);
|
||||
default:
|
||||
++session->hists.stats.nr_unknown_events;
|
||||
++session->stats.nr_unknown_events;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -904,8 +900,8 @@ static int perf_session__preprocess_sample(struct perf_session *session,
|
||||
|
||||
if (!ip_callchain__valid(sample->callchain, event)) {
|
||||
pr_debug("call-chain problem with event, skipping it.\n");
|
||||
++session->hists.stats.nr_invalid_chains;
|
||||
session->hists.stats.total_invalid_chains += sample->period;
|
||||
++session->stats.nr_invalid_chains;
|
||||
session->stats.total_invalid_chains += sample->period;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -963,7 +959,7 @@ static int perf_session__process_event(struct perf_session *session,
|
||||
if (event->header.type >= PERF_RECORD_HEADER_MAX)
|
||||
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)
|
||||
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)
|
||||
|
||||
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)
|
||||
@ -1018,39 +1014,39 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
|
||||
const struct perf_tool *tool)
|
||||
{
|
||||
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"
|
||||
"Check IO/CPU overload!\n\n",
|
||||
session->hists.stats.nr_events[0],
|
||||
session->hists.stats.nr_events[PERF_RECORD_LOST]);
|
||||
session->stats.nr_events[0],
|
||||
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"
|
||||
"Is this an older tool processing a perf.data "
|
||||
"file generated by a more recent tool?\n\n"
|
||||
"If that is not the case, consider "
|
||||
"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",
|
||||
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"
|
||||
"%u out of %u events were discarded for this reason.\n\n"
|
||||
"Consider reporting to linux-kernel@vger.kernel.org.\n\n",
|
||||
session->hists.stats.nr_invalid_chains,
|
||||
session->hists.stats.nr_events[PERF_RECORD_SAMPLE]);
|
||||
session->stats.nr_invalid_chains,
|
||||
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"
|
||||
"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,
|
||||
|
||||
size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
|
||||
{
|
||||
return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
|
||||
__dsos__fprintf(&self->host_machine.user_dsos, fp) +
|
||||
machines__fprintf_dsos(&self->machines, fp);
|
||||
return machines__fprintf_dsos(&self->machines, fp);
|
||||
}
|
||||
|
||||
size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
|
||||
bool (skip)(struct dso *dso, int parm), int parm)
|
||||
{
|
||||
size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, skip, parm);
|
||||
return ret + machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm);
|
||||
return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
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;
|
||||
@ -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
|
||||
* 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,
|
||||
@ -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
|
||||
* 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.
|
||||
*/
|
||||
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,
|
||||
|
@ -30,15 +30,10 @@ struct ordered_samples {
|
||||
struct perf_session {
|
||||
struct perf_header header;
|
||||
unsigned long size;
|
||||
struct machine host_machine;
|
||||
struct rb_root machines;
|
||||
struct machines machines;
|
||||
struct perf_evlist *evlist;
|
||||
struct pevent *pevent;
|
||||
/*
|
||||
* FIXME: Need to split this up further, we need global
|
||||
* stats + per event stats.
|
||||
*/
|
||||
struct hists hists;
|
||||
struct events_stats stats;
|
||||
int fd;
|
||||
bool fd_pipe;
|
||||
bool repipe;
|
||||
@ -53,7 +48,7 @@ struct perf_tool;
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
bool force, bool repipe,
|
||||
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);
|
||||
|
||||
@ -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__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
|
||||
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);
|
||||
}
|
||||
|
||||
static inline
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct dso *dso_l = map_l ? map_l->dso : NULL;
|
||||
@ -117,40 +127,12 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_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
|
||||
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return _sort__dso_cmp(left->ms.map, right->ms.map);
|
||||
}
|
||||
|
||||
|
||||
static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
|
||||
u64 ip_l, u64 ip_r)
|
||||
{
|
||||
if (!sym_l || !sym_r)
|
||||
return cmp_null(sym_l, sym_r);
|
||||
|
||||
if (sym_l == sym_r)
|
||||
return 0;
|
||||
|
||||
if (sym_l)
|
||||
ip_l = sym_l->start;
|
||||
if (sym_r)
|
||||
ip_r = sym_r->start;
|
||||
|
||||
return (int64_t)(ip_r - ip_l);
|
||||
}
|
||||
|
||||
static int _hist_entry__dso_snprintf(struct map *map, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
@ -169,9 +151,53 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
|
||||
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,
|
||||
u64 ip_l, u64 ip_r)
|
||||
{
|
||||
if (!sym_l || !sym_r)
|
||||
return cmp_null(sym_l, sym_r);
|
||||
|
||||
if (sym_l == sym_r)
|
||||
return 0;
|
||||
|
||||
ip_l = sym_l->start;
|
||||
ip_r = sym_r->start;
|
||||
|
||||
return (int64_t)(ip_r - ip_l);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
|
||||
u64 ip, char level, char *bf, size_t size,
|
||||
unsigned int width __maybe_unused)
|
||||
unsigned int width)
|
||||
{
|
||||
size_t ret = 0;
|
||||
|
||||
@ -197,43 +223,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
|
||||
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,
|
||||
size_t size,
|
||||
unsigned int width __maybe_unused)
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
|
||||
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 = {
|
||||
.se_header = "Symbol",
|
||||
.se_cmp = sort__sym_cmp,
|
||||
@ -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,
|
||||
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 = {
|
||||
@ -345,6 +341,8 @@ struct sort_entry sort_cpu = {
|
||||
.se_width_idx = HISTC_CPU,
|
||||
};
|
||||
|
||||
/* sort keys for branch stacks */
|
||||
|
||||
static int64_t
|
||||
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,
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size,
|
||||
unsigned int width __maybe_unused)
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
struct addr_map_symbol *from = &self->branch_info->from;
|
||||
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,
|
||||
}
|
||||
|
||||
static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size,
|
||||
unsigned int width __maybe_unused)
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
struct addr_map_symbol *to = &self->branch_info->to;
|
||||
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,
|
||||
|
||||
}
|
||||
|
||||
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 = {
|
||||
.se_header = "Target Shared Object",
|
||||
.se_cmp = sort__dso_to_cmp,
|
||||
@ -484,30 +480,40 @@ struct sort_dimension {
|
||||
|
||||
#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_COMM, "comm", sort_comm),
|
||||
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_TO, "symbol_from", sort_sym_from),
|
||||
DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
|
||||
DIM(SORT_PARENT, "parent", sort_parent),
|
||||
DIM(SORT_CPU, "cpu", sort_cpu),
|
||||
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
|
||||
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)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
|
||||
struct sort_dimension *sd = &sort_dimensions[i];
|
||||
for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
|
||||
struct sort_dimension *sd = &common_sort_dimensions[i];
|
||||
|
||||
if (strncasecmp(tok, sd->name, strlen(tok)))
|
||||
continue;
|
||||
|
||||
if (sd->entry == &sort_parent) {
|
||||
int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
|
||||
if (ret) {
|
||||
@ -518,9 +524,7 @@ int sort_dimension__add(const char *tok)
|
||||
return -EINVAL;
|
||||
}
|
||||
sort__has_parent = 1;
|
||||
} else if (sd->entry == &sort_sym ||
|
||||
sd->entry == &sort_sym_from ||
|
||||
sd->entry == &sort_sym_to) {
|
||||
} else if (sd->entry == &sort_sym) {
|
||||
sort__has_sym = 1;
|
||||
}
|
||||
|
||||
@ -530,36 +534,42 @@ int sort_dimension__add(const char *tok)
|
||||
if (sd->entry->se_collapse)
|
||||
sort__need_collapse = 1;
|
||||
|
||||
if (list_empty(&hist_entry__sort_list)) {
|
||||
if (!strcmp(sd->name, "pid"))
|
||||
sort__first_dimension = SORT_PID;
|
||||
else if (!strcmp(sd->name, "comm"))
|
||||
sort__first_dimension = SORT_COMM;
|
||||
else if (!strcmp(sd->name, "dso"))
|
||||
sort__first_dimension = SORT_DSO;
|
||||
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;
|
||||
}
|
||||
if (list_empty(&hist_entry__sort_list))
|
||||
sort__first_dimension = i;
|
||||
|
||||
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
|
||||
sd->taken = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
sd->taken = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
@ -569,7 +579,11 @@ void setup_sorting(const char * const usagestr[], const struct option *opts)
|
||||
|
||||
for (tok = strtok_r(str, ", ", &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);
|
||||
usage_with_options(usagestr, opts);
|
||||
}
|
||||
|
@ -122,18 +122,22 @@ static inline void hist_entry__add_pair(struct hist_entry *he,
|
||||
}
|
||||
|
||||
enum sort_type {
|
||||
/* common sort keys */
|
||||
SORT_PID,
|
||||
SORT_COMM,
|
||||
SORT_DSO,
|
||||
SORT_SYM,
|
||||
SORT_PARENT,
|
||||
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_SYM_FROM,
|
||||
SORT_SYM_TO,
|
||||
SORT_MISPREDICT,
|
||||
SORT_SRCLINE,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -331,6 +331,24 @@ char *strxfrchar(char *s, char from, char to)
|
||||
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.
|
||||
* @s: The string to be stripped.
|
||||
|
@ -1,6 +1,3 @@
|
||||
#include <libelf.h>
|
||||
#include <gelf.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "symbol.h"
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
@ -768,10 +768,6 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
|
||||
else
|
||||
machine = NULL;
|
||||
|
||||
name = malloc(PATH_MAX);
|
||||
if (!name)
|
||||
return -1;
|
||||
|
||||
dso->adjust_symbols = 0;
|
||||
|
||||
if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
|
||||
@ -795,6 +791,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
|
||||
if (machine)
|
||||
root_dir = machine->root_dir;
|
||||
|
||||
name = malloc(PATH_MAX);
|
||||
if (!name)
|
||||
return -1;
|
||||
|
||||
/* Iterate over candidate debug images.
|
||||
* Keep track of "interesting" ones (those which have a symtab, dynsym,
|
||||
* and/or opd section) for processing.
|
||||
@ -923,8 +923,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
|
||||
filename = dso__build_id_filename(dso, NULL, 0);
|
||||
if (filename != NULL) {
|
||||
err = dso__load_vmlinux(dso, map, filename, filter);
|
||||
if (err > 0)
|
||||
if (err > 0) {
|
||||
dso->lname_alloc = 1;
|
||||
goto out;
|
||||
}
|
||||
free(filename);
|
||||
}
|
||||
|
||||
@ -932,6 +934,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
|
||||
err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter);
|
||||
if (err > 0) {
|
||||
dso__set_long_name(dso, strdup(vmlinux_path[i]));
|
||||
dso->lname_alloc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -971,6 +974,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
|
||||
if (err > 0) {
|
||||
dso__set_long_name(dso,
|
||||
strdup(symbol_conf.vmlinux_name));
|
||||
dso->lname_alloc = 1;
|
||||
goto out_fixup;
|
||||
}
|
||||
return err;
|
||||
|
@ -16,8 +16,8 @@
|
||||
#ifdef LIBELF_SUPPORT
|
||||
#include <libelf.h>
|
||||
#include <gelf.h>
|
||||
#include <elf.h>
|
||||
#endif
|
||||
#include <elf.h>
|
||||
|
||||
#include "dso.h"
|
||||
|
||||
|
@ -8,7 +8,7 @@ static const char * const sysfs_known_mountpoints[] = {
|
||||
};
|
||||
|
||||
static int sysfs_found;
|
||||
char sysfs_mountpoint[PATH_MAX];
|
||||
char sysfs_mountpoint[PATH_MAX + 1];
|
||||
|
||||
static int sysfs_valid_mountpoint(const char *sysfs)
|
||||
{
|
||||
|
@ -29,8 +29,6 @@ struct perf_top {
|
||||
bool sort_has_symbols;
|
||||
bool kptr_restrict_warned;
|
||||
bool vmlinux_warned;
|
||||
bool sample_id_all_missing;
|
||||
bool exclude_guest_missing;
|
||||
bool dump_symtab;
|
||||
struct hist_entry *sym_filter_entry;
|
||||
struct perf_evsel *sym_evsel;
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
unsigned int page_size;
|
||||
|
||||
bool test_attr__enabled;
|
||||
|
||||
bool perf_host = true;
|
||||
bool perf_guest = false;
|
||||
|
||||
@ -218,3 +220,25 @@ void dump_stack(void)
|
||||
#else
|
||||
void dump_stack(void) {}
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -265,10 +265,14 @@ bool is_power_of_2(unsigned long n)
|
||||
size_t hex_width(u64 v);
|
||||
int hex2u64(const char *ptr, u64 *val);
|
||||
|
||||
char *ltrim(char *s);
|
||||
char *rtrim(char *s);
|
||||
|
||||
void dump_stack(void);
|
||||
|
||||
extern unsigned int page_size;
|
||||
|
||||
struct winsize;
|
||||
void get_term_dimensions(struct winsize *ws);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user