1
0
Fork 0

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>
hifive-unleashed-5.1
Ingo Molnar 2013-01-25 11:33:41 +01:00
commit a2d28d0c19
81 changed files with 1817 additions and 1203 deletions

View File

@ -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

View File

@ -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 *

View File

@ -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;

View File

@ -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>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

View File

@ -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>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

View File

@ -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>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

View File

@ -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>

View File

@ -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>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

View File

@ -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.

View File

@ -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

View File

@ -23,6 +23,10 @@ from 'perf test list'.
OPTIONS
-------
-s::
--skip::
Tests to skip (comma separater numeric list).
-v::
--verbose::
Be more verbose.

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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"),

View File

@ -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,

View File

@ -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",

View File

@ -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)

View File

@ -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;

View File

@ -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 @@ retry:
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;

View File

@ -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);

View File

@ -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))

View File

@ -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;
}

View File

@ -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;

View File

@ -1,2 +0,0 @@
#!/bin/bash
perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@

View File

@ -1,3 +0,0 @@
#!/bin/bash
# description: workqueue stats (ins/exe/create/destroy)
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl

View File

@ -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}++;
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View 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;
}

View File

@ -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, ".") ||

View File

@ -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();

View 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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 @@ out_free_offsets:
#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;

View File

@ -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;
}

View File

@ -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;

View 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;
}

View File

@ -459,7 +459,7 @@ out:
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;

View File

@ -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

View File

@ -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
}

View File

@ -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 */

View File

@ -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));
}

View File

@ -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 */

View File

@ -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);

View File

@ -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 @@ out:
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);

View File

@ -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);

View File

@ -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)); \

View File

@ -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)

View File

@ -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);

View File

@ -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 @@ out:
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 @@ failure:
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);

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 */

View File

@ -1045,3 +1045,12 @@ error:
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)
{
}

View File

@ -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;

View File

@ -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);

View File

@ -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 @@ out_delete:
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,

View File

@ -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);

View File

@ -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);
}

View File

@ -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,
};
/*

View File

@ -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.

View File

@ -1,6 +1,3 @@
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

View File

@ -1,6 +1,5 @@
#include "symbol.h"
#include <elf.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

View File

@ -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;

View File

@ -16,8 +16,8 @@
#ifdef LIBELF_SUPPORT
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
#endif
#include <elf.h>
#include "dso.h"

View File

@ -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)
{

View File

@ -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;

View File

@ -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;
}

View File

@ -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