perf/core improvements and fixes:
. Allow specifying syscalls in 'perf trace', a la strace. . Simplify symbol filtering by doing it at machine class level, from Adrian Hunter. . Add option to 'perf kvm' to print only events that exceed a specified time duration, from David Ahern. . 'perf sched' improvements, including removing some tracepoints that provide the same information as the PERF_RECORD_{FORK,EXIT} events. . Improve stack trace printing, from David Ahern. . Update documentation with live command, from David Ahern . Fix 'perf test' compile failure on do_sort_something, from David Ahern. . Improve robustness of topology parsing code, from Stephane Eranian. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJSC8hhAAoJENZQFvNTUqpA4pEP/1ZRDZBAH5LoWyAYThJFb5ko cHVEA1cy7rf6G1z7AWNLu50kGiK/VYVgf/C/qzvL10WfUlrZFSsAGovaGHHzLBSd VbW0u0/jj95X2JWnSBZsqKylWuRchBqSbYBQ2zNrddIev7G4/64JhA648Xg4rHP8 NvZj0UhXIZ/mzpQbIvTlUYJBRDdZOo+lVSPtL794fN57JBozTTLWsaJAka8BQ4NW czr2A98pYmmkhd9qKtHGS+10e9p/m7zCpshqj6yb55JMkn7ZOMQ757F35piXaa0G Yn0rl//+rq9kojfE+zft3BdoVsfF0ldEyl8TcLA1itv2+urockqehT7FwaXh2iAa /ywCps1rv5nwLB/PneX6kK3I0WjHBBUQwQniZvxUvah5KowWb/M7uV4ndbStFyXF cZ1/8WgmWnImARo3cQD7HiD6RVCq7kP5M6tvw8mDU/3JvTIWRbMnNuabwRV7PTkI OJaQVwZeYsO1+8H+ByptVPnSfe7pBHR2Cij2napI+UGfXjq4KF15OFjTyXyShoKP 4phft8RHiFnYI7IyiihXswmZWjCcTAJElUiT5jUZLFcDHmQqIsHHZPoO/VEWhF5v tO7q/3jmFb+tCSEQz5OhtxN1mD3opcA/F36Ejrc7Pbd4d+ZosKU2kC3u7u1F73k3 y9auGe0pBEi1Jc60ONGu =9398 -----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 specifying syscalls in 'perf trace', a la strace. * Simplify symbol filtering by doing it at machine class level, from Adrian Hunter. * Add option to 'perf kvm' to print only events that exceed a specified time duration, from David Ahern. * 'perf sched' improvements, including removing some tracepoints that provide the same information as the PERF_RECORD_{FORK,EXIT} events. * Improve stack trace printing, from David Ahern. * Update documentation with live command, from David Ahern * Fix 'perf test' compile failure on do_sort_something, from David Ahern. * Improve robustness of topology parsing code, from Stephane Eranian. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
58cea30743
@ -13,6 +13,7 @@ SYNOPSIS
|
||||
{top|record|report|diff|buildid-list}
|
||||
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
|
||||
| --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat}
|
||||
'perf kvm stat [record|report|live] [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -50,6 +51,10 @@ There are a couple of variants of perf kvm:
|
||||
'perf kvm stat report' reports statistical data which includes events
|
||||
handled time, samples, and so on.
|
||||
|
||||
'perf kvm stat live' reports statistical data in a live mode (similar to
|
||||
record + report but with statistical data updated live at a given display
|
||||
rate).
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-i::
|
||||
@ -85,13 +90,50 @@ STAT REPORT OPTIONS
|
||||
--vcpu=<value>::
|
||||
analyze events which occures on this vcpu. (default: all vcpus)
|
||||
|
||||
--events=<value>::
|
||||
events to be analyzed. Possible values: vmexit, mmio, ioport.
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit, mmio, ioport.
|
||||
(default: vmexit)
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
number), time (sort by average time).
|
||||
-p::
|
||||
--pid=::
|
||||
Analyze events only for given process ID(s) (comma separated list).
|
||||
|
||||
STAT LIVE OPTIONS
|
||||
-----------------
|
||||
-d::
|
||||
--display::
|
||||
Time in seconds between display updates
|
||||
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages. Must be a power of two.
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
System-wide collection from all CPUs.
|
||||
|
||||
-p::
|
||||
--pid=::
|
||||
Analyze events only for given process ID(s) (comma separated list).
|
||||
|
||||
--vcpu=<value>::
|
||||
analyze events which occures on this vcpu. (default: all vcpus)
|
||||
|
||||
|
||||
--event=<value>::
|
||||
event to be analyzed. Possible values: vmexit, mmio, ioport.
|
||||
(default: vmexit)
|
||||
|
||||
-k::
|
||||
--key=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
number), time (sort by average time).
|
||||
|
||||
--duration=<value>::
|
||||
Show events other than HLT that take longer than duration usecs.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
@ -26,6 +26,10 @@ OPTIONS
|
||||
--all-cpus::
|
||||
System-wide collection from all CPUs.
|
||||
|
||||
-e::
|
||||
--expr::
|
||||
List of events to show, currently only syscall names.
|
||||
|
||||
-p::
|
||||
--pid=::
|
||||
Record events on existing process ID (comma separated list).
|
||||
|
@ -90,8 +90,7 @@ static int process_sample_event(struct perf_tool *tool,
|
||||
struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
symbol__annotate_init) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
pr_warning("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
@ -195,6 +194,8 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
machines__set_symbol_filter(&session->machines, symbol__annotate_init);
|
||||
|
||||
if (ann->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, ann->cpu_list,
|
||||
ann->cpu_bitmap);
|
||||
|
@ -319,7 +319,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
{
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
pr_warning("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
|
@ -206,7 +206,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al, NULL);
|
||||
event->ip.ip, &al);
|
||||
|
||||
if (al.map != NULL) {
|
||||
if (!al.map->dso->hit) {
|
||||
|
@ -106,6 +106,7 @@ struct perf_kvm_stat {
|
||||
u64 total_time;
|
||||
u64 total_count;
|
||||
u64 lost_events;
|
||||
u64 duration;
|
||||
|
||||
const char *pid_str;
|
||||
struct intlist *pid_list;
|
||||
@ -473,7 +474,7 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
|
||||
static bool handle_end_event(struct perf_kvm_stat *kvm,
|
||||
struct vcpu_event_record *vcpu_record,
|
||||
struct event_key *key,
|
||||
u64 timestamp)
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct kvm_event *event;
|
||||
u64 time_begin, time_diff;
|
||||
@ -510,12 +511,24 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
|
||||
vcpu_record->start_time = 0;
|
||||
|
||||
/* seems to happen once in a while during live mode */
|
||||
if (timestamp < time_begin) {
|
||||
if (sample->time < time_begin) {
|
||||
pr_debug("End time before begin time; skipping event.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
time_diff = timestamp - time_begin;
|
||||
time_diff = sample->time - time_begin;
|
||||
|
||||
if (kvm->duration && time_diff > kvm->duration) {
|
||||
char decode[32];
|
||||
|
||||
kvm->events_ops->decode_key(kvm, &event->key, decode);
|
||||
if (strcmp(decode, "HLT")) {
|
||||
pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n",
|
||||
sample->time, sample->pid, vcpu_record->vcpu_id,
|
||||
decode, time_diff/1000);
|
||||
}
|
||||
}
|
||||
|
||||
return update_kvm_event(event, vcpu, time_diff);
|
||||
}
|
||||
|
||||
@ -562,7 +575,7 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
|
||||
return handle_begin_event(kvm, vcpu_record, &key, sample->time);
|
||||
|
||||
if (kvm->events_ops->is_end_event(evsel, sample, &key))
|
||||
return handle_end_event(kvm, vcpu_record, &key, sample->time);
|
||||
return handle_end_event(kvm, vcpu_record, &key, sample);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1429,6 +1442,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
|
||||
"key for sorting: sample(sort by samples number)"
|
||||
" time (sort by avg time)"),
|
||||
OPT_U64(0, "duration", &kvm->duration,
|
||||
"show events other than HALT that take longer than duration usecs"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const live_usage[] = {
|
||||
@ -1467,6 +1482,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
usage_with_options(live_usage, live_options);
|
||||
}
|
||||
|
||||
kvm->duration *= NSEC_PER_USEC; /* convert usec to nsec */
|
||||
|
||||
/*
|
||||
* target related setups
|
||||
*/
|
||||
|
@ -14,7 +14,6 @@ static const char *mem_operation = MEM_OPERATION_LOAD;
|
||||
struct perf_mem {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
symbol_filter_t annotate_init;
|
||||
bool hide_unresolved;
|
||||
bool dump_raw;
|
||||
const char *cpu_list;
|
||||
@ -69,8 +68,7 @@ dump_raw_samples(struct perf_tool *tool,
|
||||
struct addr_location al;
|
||||
const char *fmt;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
mem->annotate_init) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
fprintf(stderr, "problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
|
@ -49,7 +49,6 @@ struct perf_report {
|
||||
bool mem_mode;
|
||||
struct perf_read_values show_threads_values;
|
||||
const char *pretty_printing_style;
|
||||
symbol_filter_t annotate_init;
|
||||
const char *cpu_list;
|
||||
const char *symbol_filter_str;
|
||||
float min_percent;
|
||||
@ -305,8 +304,7 @@ static int process_sample_event(struct perf_tool *tool,
|
||||
struct addr_location al;
|
||||
int ret;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
rep->annotate_init) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
fprintf(stderr, "problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
@ -924,7 +922,8 @@ repeat:
|
||||
*/
|
||||
if (use_browser == 1 && sort__has_sym) {
|
||||
symbol_conf.priv_size = sizeof(struct annotation);
|
||||
report.annotate_init = symbol__annotate_init;
|
||||
machines__set_symbol_filter(&session->machines,
|
||||
symbol__annotate_init);
|
||||
/*
|
||||
* For searching by name on the "Browse map details".
|
||||
* providing it only in verbose mode not to bloat too
|
||||
|
@ -109,8 +109,9 @@ struct trace_sched_handler {
|
||||
int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
|
||||
int (*fork_event)(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
/* PERF_RECORD_FORK event, not sched_process_fork tracepoint */
|
||||
int (*fork_event)(struct perf_sched *sched, union perf_event *event,
|
||||
struct machine *machine);
|
||||
|
||||
int (*migrate_task_event)(struct perf_sched *sched,
|
||||
struct perf_evsel *evsel,
|
||||
@ -717,22 +718,29 @@ static int replay_switch_event(struct perf_sched *sched,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int replay_fork_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
static int replay_fork_event(struct perf_sched *sched,
|
||||
union perf_event *event,
|
||||
struct machine *machine)
|
||||
{
|
||||
const char *parent_comm = perf_evsel__strval(evsel, sample, "parent_comm"),
|
||||
*child_comm = perf_evsel__strval(evsel, sample, "child_comm");
|
||||
const u32 parent_pid = perf_evsel__intval(evsel, sample, "parent_pid"),
|
||||
child_pid = perf_evsel__intval(evsel, sample, "child_pid");
|
||||
struct thread *child, *parent;
|
||||
|
||||
if (verbose) {
|
||||
printf("sched_fork event %p\n", evsel);
|
||||
printf("... parent: %s/%d\n", parent_comm, parent_pid);
|
||||
printf("... child: %s/%d\n", child_comm, child_pid);
|
||||
child = machine__findnew_thread(machine, event->fork.tid);
|
||||
parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
|
||||
if (child == NULL || parent == NULL) {
|
||||
pr_debug("thread does not exist on fork event: child %p, parent %p\n",
|
||||
child, parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
register_pid(sched, parent_pid, parent_comm);
|
||||
register_pid(sched, child_pid, child_comm);
|
||||
if (verbose) {
|
||||
printf("fork event\n");
|
||||
printf("... parent: %s/%d\n", parent->comm, parent->tid);
|
||||
printf("... child: %s/%d\n", child->comm, child->tid);
|
||||
}
|
||||
|
||||
register_pid(sched, parent->tid, parent->comm);
|
||||
register_pid(sched, child->tid, child->comm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -824,14 +832,6 @@ static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int latency_fork_event(struct perf_sched *sched __maybe_unused,
|
||||
struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused)
|
||||
{
|
||||
/* should insert the newcomer */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char sched_out_state(u64 prev_state)
|
||||
{
|
||||
const char *str = TASK_STATE_TO_CHAR_STR;
|
||||
@ -1379,28 +1379,23 @@ static int process_sched_runtime_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_fork_event(struct perf_tool *tool,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine __maybe_unused)
|
||||
static int perf_sched__process_fork_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
|
||||
|
||||
/* run the fork event through the perf machineruy */
|
||||
perf_event__process_fork(tool, event, sample, machine);
|
||||
|
||||
/* and then run additional processing needed for this command */
|
||||
if (sched->tp_handler->fork_event)
|
||||
return sched->tp_handler->fork_event(sched, evsel, sample);
|
||||
return sched->tp_handler->fork_event(sched, event, machine);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_exit_event(struct perf_tool *tool __maybe_unused,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
pr_debug("sched_exit event %p\n", evsel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_migrate_task_event(struct perf_tool *tool,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@ -1425,15 +1420,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->tid);
|
||||
int err = 0;
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %s event, skipping it.\n",
|
||||
perf_evsel__name(evsel));
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->hists.stats.total_period += sample->period;
|
||||
hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
|
||||
|
||||
@ -1445,7 +1433,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
static int perf_sched__read_events(struct perf_sched *sched,
|
||||
struct perf_session **psession)
|
||||
{
|
||||
const struct perf_evsel_str_handler handlers[] = {
|
||||
@ -1453,8 +1441,6 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
{ "sched:sched_stat_runtime", process_sched_runtime_event, },
|
||||
{ "sched:sched_wakeup", process_sched_wakeup_event, },
|
||||
{ "sched:sched_wakeup_new", process_sched_wakeup_event, },
|
||||
{ "sched:sched_process_fork", process_sched_fork_event, },
|
||||
{ "sched:sched_process_exit", process_sched_exit_event, },
|
||||
{ "sched:sched_migrate_task", process_sched_migrate_task_event, },
|
||||
};
|
||||
struct perf_session *session;
|
||||
@ -1480,11 +1466,10 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
|
||||
sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST];
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
perf_session__delete(session);
|
||||
|
||||
if (psession)
|
||||
*psession = session;
|
||||
else
|
||||
perf_session__delete(session);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1529,8 +1514,11 @@ static int perf_sched__lat(struct perf_sched *sched)
|
||||
struct perf_session *session;
|
||||
|
||||
setup_pager();
|
||||
if (perf_sched__read_events(sched, false, &session))
|
||||
|
||||
/* save session -- references to threads are held in work_list */
|
||||
if (perf_sched__read_events(sched, &session))
|
||||
return -1;
|
||||
|
||||
perf_sched__sort_lat(sched);
|
||||
|
||||
printf("\n ---------------------------------------------------------------------------------------------------------------\n");
|
||||
@ -1565,7 +1553,7 @@ static int perf_sched__map(struct perf_sched *sched)
|
||||
sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||
|
||||
setup_pager();
|
||||
if (perf_sched__read_events(sched, true, NULL))
|
||||
if (perf_sched__read_events(sched, NULL))
|
||||
return -1;
|
||||
print_bad_events(sched);
|
||||
return 0;
|
||||
@ -1580,7 +1568,7 @@ static int perf_sched__replay(struct perf_sched *sched)
|
||||
|
||||
test_calibrations(sched);
|
||||
|
||||
if (perf_sched__read_events(sched, true, NULL))
|
||||
if (perf_sched__read_events(sched, NULL))
|
||||
return -1;
|
||||
|
||||
printf("nr_run_events: %ld\n", sched->nr_run_events);
|
||||
@ -1639,7 +1627,6 @@ static int __cmd_record(int argc, const char **argv)
|
||||
"-e", "sched:sched_stat_sleep",
|
||||
"-e", "sched:sched_stat_iowait",
|
||||
"-e", "sched:sched_stat_runtime",
|
||||
"-e", "sched:sched_process_exit",
|
||||
"-e", "sched:sched_process_fork",
|
||||
"-e", "sched:sched_wakeup",
|
||||
"-e", "sched:sched_migrate_task",
|
||||
@ -1668,7 +1655,7 @@ static struct perf_sched sched = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.fork = perf_sched__process_fork_event,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
@ -1730,7 +1717,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.wakeup_event = latency_wakeup_event,
|
||||
.switch_event = latency_switch_event,
|
||||
.runtime_event = latency_runtime_event,
|
||||
.fork_event = latency_fork_event,
|
||||
.migrate_task_event = latency_migrate_task_event,
|
||||
};
|
||||
struct trace_sched_handler map_ops = {
|
||||
|
@ -66,6 +66,7 @@ struct output_option {
|
||||
static struct {
|
||||
bool user_set;
|
||||
bool wildcard_set;
|
||||
unsigned int print_ip_opts;
|
||||
u64 fields;
|
||||
u64 invalid_fields;
|
||||
} output[PERF_TYPE_MAX] = {
|
||||
@ -235,6 +236,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
{
|
||||
int j;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr *attr;
|
||||
|
||||
for (j = 0; j < PERF_TYPE_MAX; ++j) {
|
||||
evsel = perf_session__find_first_evtype(session, j);
|
||||
@ -253,6 +255,24 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||
if (evsel && output[j].fields &&
|
||||
perf_evsel__check_attr(evsel, session))
|
||||
return -1;
|
||||
|
||||
if (evsel == NULL)
|
||||
continue;
|
||||
|
||||
attr = &evsel->attr;
|
||||
|
||||
output[j].print_ip_opts = 0;
|
||||
if (PRINT_FIELD(IP))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_IP;
|
||||
|
||||
if (PRINT_FIELD(SYM))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_SYM;
|
||||
|
||||
if (PRINT_FIELD(DSO))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_DSO;
|
||||
|
||||
if (PRINT_FIELD(SYMOFFSET))
|
||||
output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -341,10 +361,10 @@ static void print_sample_addr(union perf_event *event,
|
||||
return;
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
sample->addr, &al, NULL);
|
||||
sample->addr, &al);
|
||||
if (!al.map)
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
|
||||
sample->addr, &al, NULL);
|
||||
sample->addr, &al);
|
||||
|
||||
al.cpu = sample->cpu;
|
||||
al.sym = NULL;
|
||||
@ -382,8 +402,8 @@ static void print_sample_bts(union perf_event *event,
|
||||
else
|
||||
printf("\n");
|
||||
perf_evsel__print_ip(evsel, event, sample, machine,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
|
||||
PRINT_FIELD(SYMOFFSET));
|
||||
output[attr->type].print_ip_opts,
|
||||
PERF_MAX_STACK_DEPTH);
|
||||
}
|
||||
|
||||
printf(" => ");
|
||||
@ -423,9 +443,10 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
perf_evsel__print_ip(evsel, event, sample, machine,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
|
||||
PRINT_FIELD(SYMOFFSET));
|
||||
output[attr->type].print_ip_opts,
|
||||
PERF_MAX_STACK_DEPTH);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
@ -499,7 +520,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample, 0) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
pr_err("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
|
@ -716,8 +716,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
||||
if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
|
||||
top->exact_samples++;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
symbol_filter) < 0 ||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 ||
|
||||
al.filtered)
|
||||
return;
|
||||
|
||||
@ -938,6 +937,8 @@ static int __cmd_top(struct perf_top *top)
|
||||
if (top->session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
machines__set_symbol_filter(&top->session->machines, symbol_filter);
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&top->session->header.env);
|
||||
if (ret)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "util/machine.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/strlist.h"
|
||||
#include "util/thread_map.h"
|
||||
|
||||
#include <libaudit.h>
|
||||
@ -47,6 +48,7 @@ static struct syscall_fmt *syscall_fmt__find(const char *name)
|
||||
struct syscall {
|
||||
struct event_format *tp_format;
|
||||
const char *name;
|
||||
bool filtered;
|
||||
struct syscall_fmt *fmt;
|
||||
};
|
||||
|
||||
@ -110,6 +112,7 @@ struct trace {
|
||||
struct perf_record_opts opts;
|
||||
struct machine host;
|
||||
u64 base_time;
|
||||
struct strlist *ev_qualifier;
|
||||
unsigned long nr_events;
|
||||
bool sched;
|
||||
bool multiple_threads;
|
||||
@ -226,6 +229,16 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
|
||||
sc = trace->syscalls.table + id;
|
||||
sc->name = name;
|
||||
|
||||
if (trace->ev_qualifier && !strlist__find(trace->ev_qualifier, name)) {
|
||||
sc->filtered = true;
|
||||
/*
|
||||
* No need to do read tracepoint information since this will be
|
||||
* filtered out.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
sc->fmt = syscall_fmt__find(sc->name);
|
||||
|
||||
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
|
||||
@ -302,11 +315,19 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
char *msg;
|
||||
void *args;
|
||||
size_t printed = 0;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct thread *thread;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
struct thread_trace *ttrace = thread__trace(thread);
|
||||
struct thread_trace *ttrace;
|
||||
|
||||
if (ttrace == NULL || sc == NULL)
|
||||
if (sc == NULL)
|
||||
return -1;
|
||||
|
||||
if (sc->filtered)
|
||||
return 0;
|
||||
|
||||
thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
ttrace = thread__trace(thread);
|
||||
if (ttrace == NULL)
|
||||
return -1;
|
||||
|
||||
args = perf_evsel__rawptr(evsel, sample, "args");
|
||||
@ -345,11 +366,19 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
{
|
||||
int ret;
|
||||
u64 duration = 0;
|
||||
struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
struct thread_trace *ttrace = thread__trace(thread);
|
||||
struct thread *thread;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, sample);
|
||||
struct thread_trace *ttrace;
|
||||
|
||||
if (ttrace == NULL || sc == NULL)
|
||||
if (sc == NULL)
|
||||
return -1;
|
||||
|
||||
if (sc->filtered)
|
||||
return 0;
|
||||
|
||||
thread = machine__findnew_thread(&trace->host, sample->tid);
|
||||
ttrace = thread__trace(thread);
|
||||
if (ttrace == NULL)
|
||||
return -1;
|
||||
|
||||
ret = perf_evsel__intval(evsel, sample, "ret");
|
||||
@ -634,7 +663,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.mmap_pages = 1024,
|
||||
},
|
||||
};
|
||||
const char *ev_qualifier_str = NULL;
|
||||
const struct option trace_options[] = {
|
||||
OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
|
||||
"list of events to trace"),
|
||||
OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
|
||||
"trace events on existing process id"),
|
||||
OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
|
||||
@ -660,6 +692,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
argc = parse_options(argc, argv, trace_options, trace_usage, 0);
|
||||
|
||||
if (ev_qualifier_str != NULL) {
|
||||
trace.ev_qualifier = strlist__new(true, ev_qualifier_str);
|
||||
if (trace.ev_qualifier == NULL) {
|
||||
puts("Not enough memory to parse event qualifier");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
err = perf_target__validate(&trace.opts.target);
|
||||
if (err) {
|
||||
perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
|
||||
|
@ -125,6 +125,9 @@
|
||||
#ifndef NSEC_PER_SEC
|
||||
# define NSEC_PER_SEC 1000000000ULL
|
||||
#endif
|
||||
#ifndef NSEC_PER_USEC
|
||||
# define NSEC_PER_USEC 1000ULL
|
||||
#endif
|
||||
|
||||
static inline unsigned long long rdclock(void)
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
|
||||
pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr,
|
||||
&al, NULL);
|
||||
&al);
|
||||
if (!al.map || !al.map->dso) {
|
||||
pr_debug("thread__find_addr_map failed\n");
|
||||
return -1;
|
||||
@ -304,15 +304,14 @@ static int comp(const void *a, const void *b)
|
||||
|
||||
static void do_sort_something(void)
|
||||
{
|
||||
size_t sz = 40960;
|
||||
int buf[sz], i;
|
||||
int buf[40960], i;
|
||||
|
||||
for (i = 0; i < (int)sz; i++)
|
||||
buf[i] = sz - i - 1;
|
||||
for (i = 0; i < (int)ARRAY_SIZE(buf); i++)
|
||||
buf[i] = ARRAY_SIZE(buf) - i - 1;
|
||||
|
||||
qsort(buf, sz, sizeof(int), comp);
|
||||
qsort(buf, ARRAY_SIZE(buf), sizeof(int), comp);
|
||||
|
||||
for (i = 0; i < (int)sz; i++) {
|
||||
for (i = 0; i < (int)ARRAY_SIZE(buf); i++) {
|
||||
if (buf[i] != i) {
|
||||
pr_debug("qsort failed\n");
|
||||
break;
|
||||
|
@ -220,7 +220,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
||||
};
|
||||
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample, 0) < 0)
|
||||
&sample) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
|
||||
@ -244,7 +244,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
||||
};
|
||||
|
||||
if (perf_event__preprocess_sample(&event, machine, &al,
|
||||
&sample, 0) < 0)
|
||||
&sample) < 0)
|
||||
goto out;
|
||||
|
||||
he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
|
||||
|
@ -33,7 +33,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al, NULL);
|
||||
event->ip.ip, &al);
|
||||
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
|
@ -592,7 +592,7 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,
|
||||
void thread__find_addr_map(struct thread *self,
|
||||
struct machine *machine, u8 cpumode,
|
||||
enum map_type type, u64 addr,
|
||||
struct addr_location *al, symbol_filter_t filter)
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct map_groups *mg = &self->mg;
|
||||
bool load_map = false;
|
||||
@ -663,19 +663,19 @@ try_again:
|
||||
* must be done prior to using kernel maps.
|
||||
*/
|
||||
if (load_map)
|
||||
map__load(al->map, filter);
|
||||
map__load(al->map, machine->symbol_filter);
|
||||
al->addr = al->map->map_ip(al->map, al->addr);
|
||||
}
|
||||
}
|
||||
|
||||
void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al,
|
||||
symbol_filter_t filter)
|
||||
struct addr_location *al)
|
||||
{
|
||||
thread__find_addr_map(thread, machine, cpumode, type, addr, al, filter);
|
||||
thread__find_addr_map(thread, machine, cpumode, type, addr, al);
|
||||
if (al->map != NULL)
|
||||
al->sym = map__find_symbol(al->map, al->addr, filter);
|
||||
al->sym = map__find_symbol(al->map, al->addr,
|
||||
machine->symbol_filter);
|
||||
else
|
||||
al->sym = NULL;
|
||||
}
|
||||
@ -683,8 +683,7 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
||||
int perf_event__preprocess_sample(const union perf_event *event,
|
||||
struct machine *machine,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
symbol_filter_t filter)
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
@ -709,7 +708,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
machine__create_kernel_maps(machine);
|
||||
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, al, filter);
|
||||
event->ip.ip, al);
|
||||
dump_printf(" ...... dso: %s\n",
|
||||
al->map ? al->map->dso->long_name :
|
||||
al->level == 'H' ? "[hypervisor]" : "<not found>");
|
||||
@ -727,7 +726,8 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
dso->long_name)))))
|
||||
goto out_filtered;
|
||||
|
||||
al->sym = map__find_symbol(al->map, al->addr, filter);
|
||||
al->sym = map__find_symbol(al->map, al->addr,
|
||||
machine->symbol_filter);
|
||||
}
|
||||
|
||||
if (symbol_conf.sym_list &&
|
||||
|
@ -234,8 +234,7 @@ struct addr_location;
|
||||
int perf_event__preprocess_sample(const union perf_event *self,
|
||||
struct machine *machine,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
symbol_filter_t filter);
|
||||
struct perf_sample *sample);
|
||||
|
||||
const char *perf_event__name(unsigned int id);
|
||||
|
||||
|
@ -716,18 +716,19 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu)
|
||||
char filename[MAXPATHLEN];
|
||||
char *buf = NULL, *p;
|
||||
size_t len = 0;
|
||||
ssize_t sret;
|
||||
u32 i = 0;
|
||||
int ret = -1;
|
||||
|
||||
sprintf(filename, CORE_SIB_FMT, cpu);
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (getline(&buf, &len, fp) <= 0)
|
||||
goto done;
|
||||
goto try_threads;
|
||||
|
||||
sret = getline(&buf, &len, fp);
|
||||
fclose(fp);
|
||||
if (sret <= 0)
|
||||
goto try_threads;
|
||||
|
||||
p = strchr(buf, '\n');
|
||||
if (p)
|
||||
@ -743,7 +744,9 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu)
|
||||
buf = NULL;
|
||||
len = 0;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
try_threads:
|
||||
sprintf(filename, THRD_SIB_FMT, cpu);
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
|
@ -25,6 +25,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
|
||||
machine->kmaps.machine = machine;
|
||||
machine->pid = pid;
|
||||
|
||||
machine->symbol_filter = NULL;
|
||||
|
||||
machine->root_dir = strdup(root_dir);
|
||||
if (machine->root_dir == NULL)
|
||||
return -ENOMEM;
|
||||
@ -95,6 +97,7 @@ void machines__init(struct machines *machines)
|
||||
{
|
||||
machine__init(&machines->host, "", HOST_KERNEL_ID);
|
||||
machines->guests = RB_ROOT;
|
||||
machines->symbol_filter = NULL;
|
||||
}
|
||||
|
||||
void machines__exit(struct machines *machines)
|
||||
@ -118,6 +121,8 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
machine->symbol_filter = machines->symbol_filter;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
pos = rb_entry(parent, struct machine, rb_node);
|
||||
@ -133,6 +138,21 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
|
||||
return machine;
|
||||
}
|
||||
|
||||
void machines__set_symbol_filter(struct machines *machines,
|
||||
symbol_filter_t symbol_filter)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
machines->symbol_filter = symbol_filter;
|
||||
machines->host.symbol_filter = symbol_filter;
|
||||
|
||||
for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
|
||||
struct machine *machine = rb_entry(nd, struct machine, rb_node);
|
||||
|
||||
machine->symbol_filter = symbol_filter;
|
||||
}
|
||||
}
|
||||
|
||||
struct machine *machines__find(struct machines *machines, pid_t pid)
|
||||
{
|
||||
struct rb_node **p = &machines->guests.rb_node;
|
||||
@ -1110,7 +1130,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
|
||||
* or else, the symbol is unknown
|
||||
*/
|
||||
thread__find_addr_location(thread, machine, m, MAP__FUNCTION,
|
||||
ip, &al, NULL);
|
||||
ip, &al);
|
||||
if (al.sym)
|
||||
goto found;
|
||||
}
|
||||
@ -1128,8 +1148,8 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,
|
||||
|
||||
memset(&al, 0, sizeof(al));
|
||||
|
||||
thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al,
|
||||
NULL);
|
||||
thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
|
||||
&al);
|
||||
ams->addr = addr;
|
||||
ams->al_addr = al.addr;
|
||||
ams->sym = al.sym;
|
||||
@ -1224,7 +1244,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,
|
||||
|
||||
al.filtered = false;
|
||||
thread__find_addr_location(thread, machine, cpumode,
|
||||
MAP__FUNCTION, ip, &al, NULL);
|
||||
MAP__FUNCTION, ip, &al);
|
||||
if (al.sym != NULL) {
|
||||
if (sort__has_parent && !*parent &&
|
||||
symbol__match_regex(al.sym, &parent_regex))
|
||||
|
@ -29,6 +29,7 @@ struct machine {
|
||||
struct list_head kernel_dsos;
|
||||
struct map_groups kmaps;
|
||||
struct map *vmlinux_maps[MAP__NR_TYPES];
|
||||
symbol_filter_t symbol_filter;
|
||||
};
|
||||
|
||||
static inline
|
||||
@ -51,6 +52,7 @@ typedef void (*machine__process_t)(struct machine *machine, void *data);
|
||||
struct machines {
|
||||
struct machine host;
|
||||
struct rb_root guests;
|
||||
symbol_filter_t symbol_filter;
|
||||
};
|
||||
|
||||
void machines__init(struct machines *machines);
|
||||
@ -68,6 +70,9 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid);
|
||||
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);
|
||||
|
||||
void machines__set_symbol_filter(struct machines *machines,
|
||||
symbol_filter_t symbol_filter);
|
||||
|
||||
int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
|
||||
void machine__exit(struct machine *machine);
|
||||
void machine__delete_dead_threads(struct machine *machine);
|
||||
|
@ -1401,12 +1401,15 @@ int perf_session__process_events(struct perf_session *self,
|
||||
|
||||
bool perf_session__has_traces(struct perf_session *session, const char *msg)
|
||||
{
|
||||
if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) {
|
||||
pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
|
||||
return false;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &session->evlist->entries, node) {
|
||||
if (evsel->attr.type == PERF_TYPE_TRACEPOINT)
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
|
||||
@ -1489,13 +1492,18 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
|
||||
void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine,
|
||||
int print_sym, int print_dso, int print_symoffset)
|
||||
unsigned int print_opts, unsigned int stack_depth)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct callchain_cursor_node *node;
|
||||
int print_ip = print_opts & PRINT_IP_OPT_IP;
|
||||
int print_sym = print_opts & PRINT_IP_OPT_SYM;
|
||||
int print_dso = print_opts & PRINT_IP_OPT_DSO;
|
||||
int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;
|
||||
int print_oneline = print_opts & PRINT_IP_OPT_ONELINE;
|
||||
char s = print_oneline ? ' ' : '\t';
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
NULL) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||
error("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return;
|
||||
@ -1511,12 +1519,14 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||
}
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
|
||||
while (1) {
|
||||
while (stack_depth) {
|
||||
node = callchain_cursor_current(&callchain_cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
printf("\t%16" PRIx64, node->ip);
|
||||
if (print_ip)
|
||||
printf("%c%16" PRIx64, s, node->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printf(" ");
|
||||
if (print_symoffset) {
|
||||
@ -1531,13 +1541,19 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||
map__fprintf_dsoname(node->map, stdout);
|
||||
printf(")");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (!print_oneline)
|
||||
printf("\n");
|
||||
|
||||
callchain_cursor_advance(&callchain_cursor);
|
||||
|
||||
stack_depth--;
|
||||
}
|
||||
|
||||
} else {
|
||||
printf("%16" PRIx64, sample->ip);
|
||||
if (print_ip)
|
||||
printf("%16" PRIx64, sample->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printf(" ");
|
||||
if (print_symoffset)
|
||||
|
@ -41,6 +41,12 @@ struct perf_session {
|
||||
char filename[1];
|
||||
};
|
||||
|
||||
#define PRINT_IP_OPT_IP (1<<0)
|
||||
#define PRINT_IP_OPT_SYM (1<<1)
|
||||
#define PRINT_IP_OPT_DSO (1<<2)
|
||||
#define PRINT_IP_OPT_SYMOFFSET (1<<3)
|
||||
#define PRINT_IP_OPT_ONELINE (1<<4)
|
||||
|
||||
struct perf_tool;
|
||||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
@ -103,7 +109,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
|
||||
void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine,
|
||||
int print_sym, int print_dso, int print_symoffset);
|
||||
unsigned int print_opts, unsigned int stack_depth);
|
||||
|
||||
int perf_session__cpu_bitmap(struct perf_session *session,
|
||||
const char *cpu_list, unsigned long *cpu_bitmap);
|
||||
|
@ -41,12 +41,11 @@ static inline struct map *thread__find_map(struct thread *self,
|
||||
|
||||
void thread__find_addr_map(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al, symbol_filter_t filter);
|
||||
struct addr_location *al);
|
||||
|
||||
void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al,
|
||||
symbol_filter_t filter);
|
||||
struct addr_location *al);
|
||||
|
||||
static inline void *thread__priv(struct thread *thread)
|
||||
{
|
||||
|
@ -272,7 +272,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
||||
struct addr_location al;
|
||||
|
||||
thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
|
||||
MAP__FUNCTION, ip, &al, NULL);
|
||||
MAP__FUNCTION, ip, &al);
|
||||
return al.map;
|
||||
}
|
||||
|
||||
@ -349,7 +349,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
|
||||
ssize_t size;
|
||||
|
||||
thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
|
||||
MAP__FUNCTION, addr, &al, NULL);
|
||||
MAP__FUNCTION, addr, &al);
|
||||
if (!al.map) {
|
||||
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
|
||||
return -1;
|
||||
@ -473,7 +473,7 @@ static int entry(u64 ip, struct thread *thread, struct machine *machine,
|
||||
|
||||
thread__find_addr_location(thread, machine,
|
||||
PERF_RECORD_MISC_USER,
|
||||
MAP__FUNCTION, ip, &al, NULL);
|
||||
MAP__FUNCTION, ip, &al);
|
||||
|
||||
e.ip = ip;
|
||||
e.map = al.map;
|
||||
|
Loading…
Reference in New Issue
Block a user