perf/core improvements and fixes:
. Make per-cpu mmaps the default in 'perf record', from Adrian Hunter. . Default -t/--thread 'perf record' option to no inheritance, from Adrian Hunter. . Make 'perf top -g' refer to callchains, for consistency with other tools, from David Ahern. . Skip ignored symbols while printing callchain, from David Ahern. . Print callchains and symbols if they exist in 'perf script', from David Ahern. . Remove thread summary coloring in 'perf trace', from Pekka Enberg. . zsh completion support, from Ramkumar Ramachandra. . 'perf timechart' improvements, including backtrace support, from Stanislav Fomichev. . Fix using kcore files stored in the buildid cache when doing report/annotate in non-live sessions, from Adrian Hunter . Minor 'timechart' cleanups. . Fix tags/TAGS targets rebuilding, from Jiri Olsa. . Add options to show comm, fork, exit and mmap PERF_RECORD_ events in 'perf script', from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJSl5snAAoJENZQFvNTUqpA/R0P/iuIYsOYQq7NIOtgP3AhzUCa 5uUKtyUdekMtpWHtugJ9J2MdjB1jAdHmtHeReboPpK5B0VFrOUH44QwJluPAxFwn pqHaqc38BeqWvONO7B43vOaw/T+QqESqtWqiGhOhRX4+w0s4y7zEYxlHWy/uWvU2 Qf2FhDRz7QxhQhLNvHyU7CkR12d/lsIiGF8ekSCMJn7kVKdMwGcs22bH5P9Hzs8b kLl9VnE7xx4zKEV/IF0P4IeVkVIUNvlLHszoALYU+l/Ifqhz/Bq2fmA1Y/IJp5lS wsyK8TnvNdVi/3JAKM7WwrHg/axodCmdLoXwNhsBbmQ+fWHpPZJqiJJXy08N+tMJ MswG59lvCoZz5Q953hMrWS9Js2NWGlJdoZ9F135u0nBLfwMQTtQXOyJWR/0CV8sl pGY/B1uIrmqfKd7qmP1WYBobBDthxbw8xwBN3sRtZd9ts9dT0J58YfKwsp+c3y2+ aj9WGFtbSvmNPyRXmBT0/hGKc6nE7/cIfYGWdFkzEC8Z6pDX8/mVJXYIghgHpOBj MRCjDtGp9w9aRFtK5opVFNOzlCHbT9b2+HJf3zHAOGBBH8P/nvOZpACV+JtgrG2P woseZGqm9B41ynPZhFffFCuMDfLELaQj2fO8A2mEPUh0HPaCRs8VdJZcCaB40CKt pqt+dm0FEZeEo40niVpy =lc6f -----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: * Make per-cpu mmaps the default in 'perf record', from Adrian Hunter. * Default -t/--thread 'perf record' option to no inheritance, from Adrian Hunter. * Make 'perf top -g' refer to callchains, for consistency with other tools, from David Ahern. * Skip ignored symbols while printing callchain, from David Ahern. * Print callchains and symbols if they exist in 'perf script', from David Ahern. * Remove thread summary coloring in 'perf trace', from Pekka Enberg. * zsh completion support, from Ramkumar Ramachandra. * 'perf timechart' improvements, including backtrace support, from Stanislav Fomichev. * Fix using kcore files stored in the buildid cache when doing report/annotate in non-live sessions, from Adrian Hunter * Minor 'timechart' cleanups. * Fix tags/TAGS targets rebuilding, from Jiri Olsa. * Add options to show comm, fork, exit and mmap PERF_RECORD_ events in 'perf script', from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
0ed1e0bee0
28 changed files with 831 additions and 234 deletions
|
@ -4099,6 +4099,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
struct func_map *func;
|
struct func_map *func;
|
||||||
const char *saveptr;
|
const char *saveptr;
|
||||||
|
struct trace_seq p;
|
||||||
char *bprint_fmt = NULL;
|
char *bprint_fmt = NULL;
|
||||||
char format[32];
|
char format[32];
|
||||||
int show_func;
|
int show_func;
|
||||||
|
@ -4306,8 +4307,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
||||||
format[len] = 0;
|
format[len] = 0;
|
||||||
if (!len_as_arg)
|
if (!len_as_arg)
|
||||||
len_arg = -1;
|
len_arg = -1;
|
||||||
print_str_arg(s, data, size, event,
|
/* Use helper trace_seq */
|
||||||
|
trace_seq_init(&p);
|
||||||
|
print_str_arg(&p, data, size, event,
|
||||||
format, len_arg, arg);
|
format, len_arg, arg);
|
||||||
|
trace_seq_terminate(&p);
|
||||||
|
trace_seq_puts(s, p.buffer);
|
||||||
arg = arg->next;
|
arg = arg->next;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -57,6 +57,8 @@ OPTIONS
|
||||||
-t::
|
-t::
|
||||||
--tid=::
|
--tid=::
|
||||||
Record events on existing thread ID (comma separated list).
|
Record events on existing thread ID (comma separated list).
|
||||||
|
This option also disables inheritance by default. Enable it by adding
|
||||||
|
--inherit.
|
||||||
|
|
||||||
-u::
|
-u::
|
||||||
--uid=::
|
--uid=::
|
||||||
|
@ -201,11 +203,11 @@ abort events and some memory events in precise mode on modern Intel CPUs.
|
||||||
--transaction::
|
--transaction::
|
||||||
Record transaction flags for transaction related events.
|
Record transaction flags for transaction related events.
|
||||||
|
|
||||||
--force-per-cpu::
|
--per-thread::
|
||||||
Force the use of per-cpu mmaps. By default, when tasks are specified (i.e. -p,
|
Use per-thread mmaps. By default per-cpu mmaps are created. This option
|
||||||
-t or -u options) per-thread mmaps are created. This option overrides that and
|
overrides that and uses per-thread mmaps. A side-effect of that is that
|
||||||
forces per-cpu mmaps. A side-effect of that is that inheritance is
|
inheritance is automatically disabled. --per-thread is ignored with a warning
|
||||||
automatically enabled. Add the -i option also to disable inheritance.
|
if combined with -a or -C options.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -203,6 +203,12 @@ OPTIONS
|
||||||
--show-kernel-path::
|
--show-kernel-path::
|
||||||
Try to resolve the path of [kernel.kallsyms]
|
Try to resolve the path of [kernel.kallsyms]
|
||||||
|
|
||||||
|
--show-task-events
|
||||||
|
Display task related events (e.g. FORK, COMM, EXIT).
|
||||||
|
|
||||||
|
--show-mmap-events
|
||||||
|
Display mmap related events (e.g. MMAP, MMAP2).
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||||
|
|
|
@ -8,8 +8,7 @@ perf-timechart - Tool to visualize total system behavior during a workload
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'perf timechart' record <command>
|
'perf timechart' [<timechart options>] {record} [<record options>]
|
||||||
'perf timechart' [<options>]
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -21,8 +20,8 @@ There are two variants of perf timechart:
|
||||||
'perf timechart' to turn a trace into a Scalable Vector Graphics file,
|
'perf timechart' to turn a trace into a Scalable Vector Graphics file,
|
||||||
that can be viewed with popular SVG viewers such as 'Inkscape'.
|
that can be viewed with popular SVG viewers such as 'Inkscape'.
|
||||||
|
|
||||||
OPTIONS
|
TIMECHART OPTIONS
|
||||||
-------
|
-----------------
|
||||||
-o::
|
-o::
|
||||||
--output=::
|
--output=::
|
||||||
Select the output file (default: output.svg)
|
Select the output file (default: output.svg)
|
||||||
|
@ -35,6 +34,9 @@ OPTIONS
|
||||||
-P::
|
-P::
|
||||||
--power-only::
|
--power-only::
|
||||||
Only output the CPU power section of the diagram
|
Only output the CPU power section of the diagram
|
||||||
|
-T::
|
||||||
|
--tasks-only::
|
||||||
|
Don't output processor state transitions
|
||||||
-p::
|
-p::
|
||||||
--process::
|
--process::
|
||||||
Select the processes to display, by name or PID
|
Select the processes to display, by name or PID
|
||||||
|
@ -54,6 +56,22 @@ $ perf timechart
|
||||||
|
|
||||||
Written 10.2 seconds of trace to output.svg.
|
Written 10.2 seconds of trace to output.svg.
|
||||||
|
|
||||||
|
-n::
|
||||||
|
--proc-num::
|
||||||
|
Print task info for at least given number of tasks.
|
||||||
|
|
||||||
|
RECORD OPTIONS
|
||||||
|
--------------
|
||||||
|
-P::
|
||||||
|
--power-only::
|
||||||
|
Record only power-related events
|
||||||
|
-T::
|
||||||
|
--tasks-only::
|
||||||
|
Record only tasks-related events
|
||||||
|
-g::
|
||||||
|
--callchain::
|
||||||
|
Do call-graph (stack chain/backtrace) recording
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
linkperf:perf-record[1]
|
linkperf:perf-record[1]
|
||||||
|
|
|
@ -50,7 +50,6 @@ Default is to monitor all CPUS.
|
||||||
--count-filter=<count>::
|
--count-filter=<count>::
|
||||||
Only display functions with more events than this.
|
Only display functions with more events than this.
|
||||||
|
|
||||||
-g::
|
|
||||||
--group::
|
--group::
|
||||||
Put the counters into a counter group.
|
Put the counters into a counter group.
|
||||||
|
|
||||||
|
@ -143,12 +142,12 @@ Default is to monitor all CPUS.
|
||||||
--asm-raw::
|
--asm-raw::
|
||||||
Show raw instruction encoding of assembly instructions.
|
Show raw instruction encoding of assembly instructions.
|
||||||
|
|
||||||
-G::
|
-g::
|
||||||
Enables call-graph (stack chain/backtrace) recording.
|
Enables call-graph (stack chain/backtrace) recording.
|
||||||
|
|
||||||
--call-graph::
|
--call-graph::
|
||||||
Setup and enable call-graph (stack chain/backtrace) recording,
|
Setup and enable call-graph (stack chain/backtrace) recording,
|
||||||
implies -G.
|
implies -g.
|
||||||
|
|
||||||
--max-stack::
|
--max-stack::
|
||||||
Set the stack depth limit when parsing the callchain, anything
|
Set the stack depth limit when parsing the callchain, anything
|
||||||
|
|
|
@ -60,8 +60,11 @@ endef
|
||||||
|
|
||||||
#
|
#
|
||||||
# Needed if no target specified:
|
# Needed if no target specified:
|
||||||
|
# (Except for tags and TAGS targets. The reason is that the
|
||||||
|
# Makefile does not treat tags/TAGS as targets but as files
|
||||||
|
# and thus won't rebuilt them once they are in place.)
|
||||||
#
|
#
|
||||||
all:
|
all tags TAGS:
|
||||||
$(print_msg)
|
$(print_msg)
|
||||||
$(make)
|
$(make)
|
||||||
|
|
||||||
|
@ -77,3 +80,5 @@ clean:
|
||||||
%:
|
%:
|
||||||
$(print_msg)
|
$(print_msg)
|
||||||
$(make)
|
$(make)
|
||||||
|
|
||||||
|
.PHONY: tags TAGS
|
||||||
|
|
|
@ -840,9 +840,9 @@ ifndef NO_LIBPYTHON
|
||||||
$(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \
|
$(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \
|
||||||
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
|
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
|
||||||
endif
|
endif
|
||||||
$(call QUIET_INSTALL, bash_completion-script) \
|
$(call QUIET_INSTALL, perf_completion-script) \
|
||||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \
|
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \
|
||||||
$(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
|
$(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
|
||||||
$(call QUIET_INSTALL, tests) \
|
$(call QUIET_INSTALL, tests) \
|
||||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||||
$(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
$(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
|
||||||
|
|
|
@ -800,6 +800,7 @@ static struct perf_record record = {
|
||||||
.freq = 4000,
|
.freq = 4000,
|
||||||
.target = {
|
.target = {
|
||||||
.uses_mmap = true,
|
.uses_mmap = true,
|
||||||
|
.default_per_cpu = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -842,8 +843,9 @@ const struct option record_options[] = {
|
||||||
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
|
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
|
||||||
OPT_STRING('o', "output", &record.file.path, "file",
|
OPT_STRING('o', "output", &record.file.path, "file",
|
||||||
"output file name"),
|
"output file name"),
|
||||||
OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
|
OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit,
|
||||||
"child tasks do not inherit counters"),
|
&record.opts.no_inherit_set,
|
||||||
|
"child tasks do not inherit counters"),
|
||||||
OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
|
OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
|
||||||
OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
|
OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
|
||||||
"number of mmap data pages",
|
"number of mmap data pages",
|
||||||
|
@ -888,8 +890,8 @@ const struct option record_options[] = {
|
||||||
"sample by weight (on special events only)"),
|
"sample by weight (on special events only)"),
|
||||||
OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction,
|
OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction,
|
||||||
"sample transaction flags (special events only)"),
|
"sample transaction flags (special events only)"),
|
||||||
OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu,
|
OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread,
|
||||||
"force the use of per-cpu mmaps"),
|
"use per-thread mmaps"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -938,6 +940,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
goto out_symbol_exit;
|
goto out_symbol_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rec->opts.target.tid && !rec->opts.no_inherit_set)
|
||||||
|
rec->opts.no_inherit = true;
|
||||||
|
|
||||||
err = target__validate(&rec->opts.target);
|
err = target__validate(&rec->opts.target);
|
||||||
if (err) {
|
if (err) {
|
||||||
target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
|
target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
|
||||||
|
|
|
@ -280,6 +280,30 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
||||||
set_print_ip_opts(&evsel->attr);
|
set_print_ip_opts(&evsel->attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set default for tracepoints to print symbols only
|
||||||
|
* if callchains are present
|
||||||
|
*/
|
||||||
|
if (symbol_conf.use_callchain &&
|
||||||
|
!output[PERF_TYPE_TRACEPOINT].user_set) {
|
||||||
|
struct perf_event_attr *attr;
|
||||||
|
|
||||||
|
j = PERF_TYPE_TRACEPOINT;
|
||||||
|
evsel = perf_session__find_first_evtype(session, j);
|
||||||
|
if (evsel == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
attr = &evsel->attr;
|
||||||
|
|
||||||
|
if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
|
||||||
|
output[j].fields |= PERF_OUTPUT_IP;
|
||||||
|
output[j].fields |= PERF_OUTPUT_SYM;
|
||||||
|
output[j].fields |= PERF_OUTPUT_DSO;
|
||||||
|
set_print_ip_opts(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +312,6 @@ static void print_sample_start(struct perf_sample *sample,
|
||||||
struct perf_evsel *evsel)
|
struct perf_evsel *evsel)
|
||||||
{
|
{
|
||||||
struct perf_event_attr *attr = &evsel->attr;
|
struct perf_event_attr *attr = &evsel->attr;
|
||||||
const char *evname = NULL;
|
|
||||||
unsigned long secs;
|
unsigned long secs;
|
||||||
unsigned long usecs;
|
unsigned long usecs;
|
||||||
unsigned long long nsecs;
|
unsigned long long nsecs;
|
||||||
|
@ -323,11 +346,6 @@ static void print_sample_start(struct perf_sample *sample,
|
||||||
usecs = nsecs / NSECS_PER_USEC;
|
usecs = nsecs / NSECS_PER_USEC;
|
||||||
printf("%5lu.%06lu: ", secs, usecs);
|
printf("%5lu.%06lu: ", secs, usecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PRINT_FIELD(EVNAME)) {
|
|
||||||
evname = perf_evsel__name(evsel);
|
|
||||||
printf("%s: ", evname ? evname : "[unknown]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_bts_event(struct perf_event_attr *attr)
|
static bool is_bts_event(struct perf_event_attr *attr)
|
||||||
|
@ -434,6 +452,11 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
|
||||||
|
|
||||||
print_sample_start(sample, thread, evsel);
|
print_sample_start(sample, thread, evsel);
|
||||||
|
|
||||||
|
if (PRINT_FIELD(EVNAME)) {
|
||||||
|
const char *evname = perf_evsel__name(evsel);
|
||||||
|
printf("%s: ", evname ? evname : "[unknown]");
|
||||||
|
}
|
||||||
|
|
||||||
if (is_bts_event(attr)) {
|
if (is_bts_event(attr)) {
|
||||||
print_sample_bts(event, sample, evsel, machine, thread);
|
print_sample_bts(event, sample, evsel, machine, thread);
|
||||||
return;
|
return;
|
||||||
|
@ -549,6 +572,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
struct perf_script {
|
struct perf_script {
|
||||||
struct perf_tool tool;
|
struct perf_tool tool;
|
||||||
struct perf_session *session;
|
struct perf_session *session;
|
||||||
|
bool show_task_events;
|
||||||
|
bool show_mmap_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int process_attr(struct perf_tool *tool, union perf_event *event,
|
static int process_attr(struct perf_tool *tool, union perf_event *event,
|
||||||
|
@ -579,6 +604,163 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
||||||
return perf_evsel__check_attr(evsel, scr->session);
|
return perf_evsel__check_attr(evsel, scr->session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int process_comm_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||||
|
struct perf_session *session = script->session;
|
||||||
|
struct perf_evsel *evsel = perf_evlist__first(session->evlist);
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid);
|
||||||
|
if (thread == NULL) {
|
||||||
|
pr_debug("problem processing COMM event, skipping it.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perf_event__process_comm(tool, event, sample, machine) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!evsel->attr.sample_id_all) {
|
||||||
|
sample->cpu = 0;
|
||||||
|
sample->time = 0;
|
||||||
|
sample->tid = event->comm.tid;
|
||||||
|
sample->pid = event->comm.pid;
|
||||||
|
}
|
||||||
|
print_sample_start(sample, thread, evsel);
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_fork_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||||
|
struct perf_session *session = script->session;
|
||||||
|
struct perf_evsel *evsel = perf_evlist__first(session->evlist);
|
||||||
|
|
||||||
|
if (perf_event__process_fork(tool, event, sample, machine) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
|
||||||
|
if (thread == NULL) {
|
||||||
|
pr_debug("problem processing FORK event, skipping it.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evsel->attr.sample_id_all) {
|
||||||
|
sample->cpu = 0;
|
||||||
|
sample->time = event->fork.time;
|
||||||
|
sample->tid = event->fork.tid;
|
||||||
|
sample->pid = event->fork.pid;
|
||||||
|
}
|
||||||
|
print_sample_start(sample, thread, evsel);
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int process_exit_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||||
|
struct perf_session *session = script->session;
|
||||||
|
struct perf_evsel *evsel = perf_evlist__first(session->evlist);
|
||||||
|
|
||||||
|
thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
|
||||||
|
if (thread == NULL) {
|
||||||
|
pr_debug("problem processing EXIT event, skipping it.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evsel->attr.sample_id_all) {
|
||||||
|
sample->cpu = 0;
|
||||||
|
sample->time = 0;
|
||||||
|
sample->tid = event->comm.tid;
|
||||||
|
sample->pid = event->comm.pid;
|
||||||
|
}
|
||||||
|
print_sample_start(sample, thread, evsel);
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
|
||||||
|
if (perf_event__process_exit(tool, event, sample, machine) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_mmap_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||||
|
struct perf_session *session = script->session;
|
||||||
|
struct perf_evsel *evsel = perf_evlist__first(session->evlist);
|
||||||
|
|
||||||
|
if (perf_event__process_mmap(tool, event, sample, machine) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid);
|
||||||
|
if (thread == NULL) {
|
||||||
|
pr_debug("problem processing MMAP event, skipping it.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evsel->attr.sample_id_all) {
|
||||||
|
sample->cpu = 0;
|
||||||
|
sample->time = 0;
|
||||||
|
sample->tid = event->mmap.tid;
|
||||||
|
sample->pid = event->mmap.pid;
|
||||||
|
}
|
||||||
|
print_sample_start(sample, thread, evsel);
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_mmap2_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||||
|
struct perf_session *session = script->session;
|
||||||
|
struct perf_evsel *evsel = perf_evlist__first(session->evlist);
|
||||||
|
|
||||||
|
if (perf_event__process_mmap2(tool, event, sample, machine) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid);
|
||||||
|
if (thread == NULL) {
|
||||||
|
pr_debug("problem processing MMAP2 event, skipping it.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evsel->attr.sample_id_all) {
|
||||||
|
sample->cpu = 0;
|
||||||
|
sample->time = 0;
|
||||||
|
sample->tid = event->mmap2.tid;
|
||||||
|
sample->pid = event->mmap2.pid;
|
||||||
|
}
|
||||||
|
print_sample_start(sample, thread, evsel);
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void sig_handler(int sig __maybe_unused)
|
static void sig_handler(int sig __maybe_unused)
|
||||||
{
|
{
|
||||||
session_done = 1;
|
session_done = 1;
|
||||||
|
@ -590,6 +772,17 @@ static int __cmd_script(struct perf_script *script)
|
||||||
|
|
||||||
signal(SIGINT, sig_handler);
|
signal(SIGINT, sig_handler);
|
||||||
|
|
||||||
|
/* override event processing functions */
|
||||||
|
if (script->show_task_events) {
|
||||||
|
script->tool.comm = process_comm_event;
|
||||||
|
script->tool.fork = process_fork_event;
|
||||||
|
script->tool.exit = process_exit_event;
|
||||||
|
}
|
||||||
|
if (script->show_mmap_events) {
|
||||||
|
script->tool.mmap = process_mmap_event;
|
||||||
|
script->tool.mmap2 = process_mmap2_event;
|
||||||
|
}
|
||||||
|
|
||||||
ret = perf_session__process_events(script->session, &script->tool);
|
ret = perf_session__process_events(script->session, &script->tool);
|
||||||
|
|
||||||
if (debug_mode)
|
if (debug_mode)
|
||||||
|
@ -1352,6 +1545,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
"display extended information from perf.data file"),
|
"display extended information from perf.data file"),
|
||||||
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
||||||
"Show the path of [kernel.kallsyms]"),
|
"Show the path of [kernel.kallsyms]"),
|
||||||
|
OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events,
|
||||||
|
"Show the fork/comm/exit events"),
|
||||||
|
OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events,
|
||||||
|
"Show the mmap events"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
const char * const script_usage[] = {
|
const char * const script_usage[] = {
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#define SUPPORT_OLD_POWER_EVENTS 1
|
#define SUPPORT_OLD_POWER_EVENTS 1
|
||||||
#define PWR_EVENT_EXIT -1
|
#define PWR_EVENT_EXIT -1
|
||||||
|
|
||||||
|
static int proc_num = 15;
|
||||||
|
|
||||||
static unsigned int numcpus;
|
static unsigned int numcpus;
|
||||||
static u64 min_freq; /* Lowest CPU frequency seen */
|
static u64 min_freq; /* Lowest CPU frequency seen */
|
||||||
|
@ -50,16 +51,12 @@ static u64 turbo_frequency;
|
||||||
static u64 first_time, last_time;
|
static u64 first_time, last_time;
|
||||||
|
|
||||||
static bool power_only;
|
static bool power_only;
|
||||||
|
static bool tasks_only;
|
||||||
|
static bool with_backtrace;
|
||||||
|
|
||||||
|
|
||||||
struct per_pid;
|
|
||||||
struct per_pidcomm;
|
struct per_pidcomm;
|
||||||
|
|
||||||
struct cpu_sample;
|
struct cpu_sample;
|
||||||
struct power_event;
|
|
||||||
struct wake_event;
|
|
||||||
|
|
||||||
struct sample_wrapper;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Datastructure layout:
|
* Datastructure layout:
|
||||||
|
@ -124,6 +121,7 @@ struct cpu_sample {
|
||||||
u64 end_time;
|
u64 end_time;
|
||||||
int type;
|
int type;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
const char *backtrace;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct per_pid *all_data;
|
static struct per_pid *all_data;
|
||||||
|
@ -145,12 +143,12 @@ struct wake_event {
|
||||||
int waker;
|
int waker;
|
||||||
int wakee;
|
int wakee;
|
||||||
u64 time;
|
u64 time;
|
||||||
|
const char *backtrace;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct power_event *power_events;
|
static struct power_event *power_events;
|
||||||
static struct wake_event *wake_events;
|
static struct wake_event *wake_events;
|
||||||
|
|
||||||
struct process_filter;
|
|
||||||
struct process_filter {
|
struct process_filter {
|
||||||
char *name;
|
char *name;
|
||||||
int pid;
|
int pid;
|
||||||
|
@ -229,7 +227,8 @@ static void pid_exit(int pid, u64 timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
|
pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end,
|
||||||
|
const char *backtrace)
|
||||||
{
|
{
|
||||||
struct per_pid *p;
|
struct per_pid *p;
|
||||||
struct per_pidcomm *c;
|
struct per_pidcomm *c;
|
||||||
|
@ -252,6 +251,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
|
||||||
sample->type = type;
|
sample->type = type;
|
||||||
sample->next = c->samples;
|
sample->next = c->samples;
|
||||||
sample->cpu = cpu;
|
sample->cpu = cpu;
|
||||||
|
sample->backtrace = backtrace;
|
||||||
c->samples = sample;
|
c->samples = sample;
|
||||||
|
|
||||||
if (sample->type == TYPE_RUNNING && end > start && start > 0) {
|
if (sample->type == TYPE_RUNNING && end > start && start > 0) {
|
||||||
|
@ -299,50 +299,10 @@ static int process_exit_event(struct perf_tool *tool __maybe_unused,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct trace_entry {
|
|
||||||
unsigned short type;
|
|
||||||
unsigned char flags;
|
|
||||||
unsigned char preempt_count;
|
|
||||||
int pid;
|
|
||||||
int lock_depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||||
static int use_old_power_events;
|
static int use_old_power_events;
|
||||||
struct power_entry_old {
|
|
||||||
struct trace_entry te;
|
|
||||||
u64 type;
|
|
||||||
u64 value;
|
|
||||||
u64 cpu_id;
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct power_processor_entry {
|
|
||||||
struct trace_entry te;
|
|
||||||
u32 state;
|
|
||||||
u32 cpu_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define TASK_COMM_LEN 16
|
|
||||||
struct wakeup_entry {
|
|
||||||
struct trace_entry te;
|
|
||||||
char comm[TASK_COMM_LEN];
|
|
||||||
int pid;
|
|
||||||
int prio;
|
|
||||||
int success;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sched_switch {
|
|
||||||
struct trace_entry te;
|
|
||||||
char prev_comm[TASK_COMM_LEN];
|
|
||||||
int prev_pid;
|
|
||||||
int prev_prio;
|
|
||||||
long prev_state; /* Arjan weeps. */
|
|
||||||
char next_comm[TASK_COMM_LEN];
|
|
||||||
int next_pid;
|
|
||||||
int next_prio;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void c_state_start(int cpu, u64 timestamp, int state)
|
static void c_state_start(int cpu, u64 timestamp, int state)
|
||||||
{
|
{
|
||||||
cpus_cstate_start_times[cpu] = timestamp;
|
cpus_cstate_start_times[cpu] = timestamp;
|
||||||
|
@ -402,23 +362,23 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
|
||||||
turbo_frequency = max_freq;
|
turbo_frequency = max_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void sched_wakeup(int cpu, u64 timestamp, int waker, int wakee,
|
||||||
sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
|
u8 flags, const char *backtrace)
|
||||||
{
|
{
|
||||||
struct per_pid *p;
|
struct per_pid *p;
|
||||||
struct wakeup_entry *wake = (void *)te;
|
|
||||||
struct wake_event *we = zalloc(sizeof(*we));
|
struct wake_event *we = zalloc(sizeof(*we));
|
||||||
|
|
||||||
if (!we)
|
if (!we)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
we->time = timestamp;
|
we->time = timestamp;
|
||||||
we->waker = pid;
|
we->waker = waker;
|
||||||
|
we->backtrace = backtrace;
|
||||||
|
|
||||||
if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
|
if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
|
||||||
we->waker = -1;
|
we->waker = -1;
|
||||||
|
|
||||||
we->wakee = wake->pid;
|
we->wakee = wakee;
|
||||||
we->next = wake_events;
|
we->next = wake_events;
|
||||||
wake_events = we;
|
wake_events = we;
|
||||||
p = find_create_pid(we->wakee);
|
p = find_create_pid(we->wakee);
|
||||||
|
@ -428,27 +388,31 @@ sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
|
||||||
p->current->state = TYPE_WAITING;
|
p->current->state = TYPE_WAITING;
|
||||||
}
|
}
|
||||||
if (p && p->current && p->current->state == TYPE_BLOCKED) {
|
if (p && p->current && p->current->state == TYPE_BLOCKED) {
|
||||||
pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
|
pid_put_sample(p->pid, p->current->state, cpu,
|
||||||
|
p->current->state_since, timestamp, NULL);
|
||||||
p->current->state_since = timestamp;
|
p->current->state_since = timestamp;
|
||||||
p->current->state = TYPE_WAITING;
|
p->current->state = TYPE_WAITING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
|
static void sched_switch(int cpu, u64 timestamp, int prev_pid, int next_pid,
|
||||||
|
u64 prev_state, const char *backtrace)
|
||||||
{
|
{
|
||||||
struct per_pid *p = NULL, *prev_p;
|
struct per_pid *p = NULL, *prev_p;
|
||||||
struct sched_switch *sw = (void *)te;
|
|
||||||
|
|
||||||
|
prev_p = find_create_pid(prev_pid);
|
||||||
|
|
||||||
prev_p = find_create_pid(sw->prev_pid);
|
p = find_create_pid(next_pid);
|
||||||
|
|
||||||
p = find_create_pid(sw->next_pid);
|
|
||||||
|
|
||||||
if (prev_p->current && prev_p->current->state != TYPE_NONE)
|
if (prev_p->current && prev_p->current->state != TYPE_NONE)
|
||||||
pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
|
pid_put_sample(prev_pid, TYPE_RUNNING, cpu,
|
||||||
|
prev_p->current->state_since, timestamp,
|
||||||
|
backtrace);
|
||||||
if (p && p->current) {
|
if (p && p->current) {
|
||||||
if (p->current->state != TYPE_NONE)
|
if (p->current->state != TYPE_NONE)
|
||||||
pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
|
pid_put_sample(next_pid, p->current->state, cpu,
|
||||||
|
p->current->state_since, timestamp,
|
||||||
|
backtrace);
|
||||||
|
|
||||||
p->current->state_since = timestamp;
|
p->current->state_since = timestamp;
|
||||||
p->current->state = TYPE_RUNNING;
|
p->current->state = TYPE_RUNNING;
|
||||||
|
@ -457,18 +421,97 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
|
||||||
if (prev_p->current) {
|
if (prev_p->current) {
|
||||||
prev_p->current->state = TYPE_NONE;
|
prev_p->current->state = TYPE_NONE;
|
||||||
prev_p->current->state_since = timestamp;
|
prev_p->current->state_since = timestamp;
|
||||||
if (sw->prev_state & 2)
|
if (prev_state & 2)
|
||||||
prev_p->current->state = TYPE_BLOCKED;
|
prev_p->current->state = TYPE_BLOCKED;
|
||||||
if (sw->prev_state == 0)
|
if (prev_state == 0)
|
||||||
prev_p->current->state = TYPE_WAITING;
|
prev_p->current->state = TYPE_WAITING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *cat_backtrace(union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct addr_location al;
|
||||||
|
unsigned int i;
|
||||||
|
char *p = NULL;
|
||||||
|
size_t p_len;
|
||||||
|
u8 cpumode = PERF_RECORD_MISC_USER;
|
||||||
|
struct addr_location tal;
|
||||||
|
struct ip_callchain *chain = sample->callchain;
|
||||||
|
FILE *f = open_memstream(&p, &p_len);
|
||||||
|
|
||||||
|
if (!f) {
|
||||||
|
perror("open_memstream error");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chain)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
|
||||||
|
fprintf(stderr, "problem processing %d event, skipping it.\n",
|
||||||
|
event->header.type);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < chain->nr; i++) {
|
||||||
|
u64 ip;
|
||||||
|
|
||||||
|
if (callchain_param.order == ORDER_CALLEE)
|
||||||
|
ip = chain->ips[i];
|
||||||
|
else
|
||||||
|
ip = chain->ips[chain->nr - i - 1];
|
||||||
|
|
||||||
|
if (ip >= PERF_CONTEXT_MAX) {
|
||||||
|
switch (ip) {
|
||||||
|
case PERF_CONTEXT_HV:
|
||||||
|
cpumode = PERF_RECORD_MISC_HYPERVISOR;
|
||||||
|
break;
|
||||||
|
case PERF_CONTEXT_KERNEL:
|
||||||
|
cpumode = PERF_RECORD_MISC_KERNEL;
|
||||||
|
break;
|
||||||
|
case PERF_CONTEXT_USER:
|
||||||
|
cpumode = PERF_RECORD_MISC_USER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_debug("invalid callchain context: "
|
||||||
|
"%"PRId64"\n", (s64) ip);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It seems the callchain is corrupted.
|
||||||
|
* Discard all.
|
||||||
|
*/
|
||||||
|
free(p);
|
||||||
|
p = NULL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tal.filtered = false;
|
||||||
|
thread__find_addr_location(al.thread, machine, cpumode,
|
||||||
|
MAP__FUNCTION, ip, &tal);
|
||||||
|
|
||||||
|
if (tal.sym)
|
||||||
|
fprintf(f, "..... %016" PRIx64 " %s\n", ip,
|
||||||
|
tal.sym->name);
|
||||||
|
else
|
||||||
|
fprintf(f, "..... %016" PRIx64 "\n", ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
|
typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample);
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace);
|
||||||
|
|
||||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
union perf_event *event __maybe_unused,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
struct perf_evsel *evsel,
|
struct perf_evsel *evsel,
|
||||||
struct machine *machine __maybe_unused)
|
struct machine *machine __maybe_unused)
|
||||||
|
@ -485,81 +528,97 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
|
|
||||||
if (evsel->handler != NULL) {
|
if (evsel->handler != NULL) {
|
||||||
tracepoint_handler f = evsel->handler;
|
tracepoint_handler f = evsel->handler;
|
||||||
return f(evsel, sample);
|
return f(evsel, sample, cat_backtrace(event, sample, machine));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
|
process_sample_cpu_idle(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace __maybe_unused)
|
||||||
{
|
{
|
||||||
struct power_processor_entry *ppe = sample->raw_data;
|
u32 state = perf_evsel__intval(evsel, sample, "state");
|
||||||
|
u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
|
||||||
|
|
||||||
if (ppe->state == (u32) PWR_EVENT_EXIT)
|
if (state == (u32)PWR_EVENT_EXIT)
|
||||||
c_state_end(ppe->cpu_id, sample->time);
|
c_state_end(cpu_id, sample->time);
|
||||||
else
|
else
|
||||||
c_state_start(ppe->cpu_id, sample->time, ppe->state);
|
c_state_start(cpu_id, sample->time, state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
|
process_sample_cpu_frequency(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace __maybe_unused)
|
||||||
{
|
{
|
||||||
struct power_processor_entry *ppe = sample->raw_data;
|
u32 state = perf_evsel__intval(evsel, sample, "state");
|
||||||
|
u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
|
||||||
|
|
||||||
p_state_change(ppe->cpu_id, sample->time, ppe->state);
|
p_state_change(cpu_id, sample->time, state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused,
|
process_sample_sched_wakeup(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace)
|
||||||
{
|
{
|
||||||
struct trace_entry *te = sample->raw_data;
|
u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
|
||||||
|
int waker = perf_evsel__intval(evsel, sample, "common_pid");
|
||||||
|
int wakee = perf_evsel__intval(evsel, sample, "pid");
|
||||||
|
|
||||||
sched_wakeup(sample->cpu, sample->time, sample->pid, te);
|
sched_wakeup(sample->cpu, sample->time, waker, wakee, flags, backtrace);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
|
process_sample_sched_switch(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace)
|
||||||
{
|
{
|
||||||
struct trace_entry *te = sample->raw_data;
|
int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
|
||||||
|
int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
|
||||||
|
u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
|
||||||
|
|
||||||
sched_switch(sample->cpu, sample->time, te);
|
sched_switch(sample->cpu, sample->time, prev_pid, next_pid, prev_state,
|
||||||
|
backtrace);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||||
static int
|
static int
|
||||||
process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
|
process_sample_power_start(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace __maybe_unused)
|
||||||
{
|
{
|
||||||
struct power_entry_old *peo = sample->raw_data;
|
u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
|
||||||
|
u64 value = perf_evsel__intval(evsel, sample, "value");
|
||||||
|
|
||||||
c_state_start(peo->cpu_id, sample->time, peo->value);
|
c_state_start(cpu_id, sample->time, value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
|
process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace __maybe_unused)
|
||||||
{
|
{
|
||||||
c_state_end(sample->cpu, sample->time);
|
c_state_end(sample->cpu, sample->time);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,
|
process_sample_power_frequency(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample,
|
||||||
|
const char *backtrace __maybe_unused)
|
||||||
{
|
{
|
||||||
struct power_entry_old *peo = sample->raw_data;
|
u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
|
||||||
|
u64 value = perf_evsel__intval(evsel, sample, "value");
|
||||||
|
|
||||||
p_state_change(peo->cpu_id, sample->time, peo->value);
|
p_state_change(cpu_id, sample->time, value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* SUPPORT_OLD_POWER_EVENTS */
|
#endif /* SUPPORT_OLD_POWER_EVENTS */
|
||||||
|
@ -739,11 +798,12 @@ static void draw_wakeups(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (we->waker == -1)
|
if (we->waker == -1)
|
||||||
svg_interrupt(we->time, to);
|
svg_interrupt(we->time, to, we->backtrace);
|
||||||
else if (from && to && abs(from - to) == 1)
|
else if (from && to && abs(from - to) == 1)
|
||||||
svg_wakeline(we->time, from, to);
|
svg_wakeline(we->time, from, to, we->backtrace);
|
||||||
else
|
else
|
||||||
svg_partial_wakeline(we->time, from, task_from, to, task_to);
|
svg_partial_wakeline(we->time, from, task_from, to,
|
||||||
|
task_to, we->backtrace);
|
||||||
we = we->next;
|
we = we->next;
|
||||||
|
|
||||||
free(task_from);
|
free(task_from);
|
||||||
|
@ -796,11 +856,20 @@ static void draw_process_bars(void)
|
||||||
sample = c->samples;
|
sample = c->samples;
|
||||||
while (sample) {
|
while (sample) {
|
||||||
if (sample->type == TYPE_RUNNING)
|
if (sample->type == TYPE_RUNNING)
|
||||||
svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
|
svg_running(Y, sample->cpu,
|
||||||
|
sample->start_time,
|
||||||
|
sample->end_time,
|
||||||
|
sample->backtrace);
|
||||||
if (sample->type == TYPE_BLOCKED)
|
if (sample->type == TYPE_BLOCKED)
|
||||||
svg_box(Y, sample->start_time, sample->end_time, "blocked");
|
svg_blocked(Y, sample->cpu,
|
||||||
|
sample->start_time,
|
||||||
|
sample->end_time,
|
||||||
|
sample->backtrace);
|
||||||
if (sample->type == TYPE_WAITING)
|
if (sample->type == TYPE_WAITING)
|
||||||
svg_waiting(Y, sample->start_time, sample->end_time);
|
svg_waiting(Y, sample->cpu,
|
||||||
|
sample->start_time,
|
||||||
|
sample->end_time,
|
||||||
|
sample->backtrace);
|
||||||
sample = sample->next;
|
sample = sample->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -911,7 +980,7 @@ static int determine_display_tasks(u64 threshold)
|
||||||
/* no exit marker, task kept running to the end */
|
/* no exit marker, task kept running to the end */
|
||||||
if (p->end_time == 0)
|
if (p->end_time == 0)
|
||||||
p->end_time = last_time;
|
p->end_time = last_time;
|
||||||
if (p->total_time >= threshold && !power_only)
|
if (p->total_time >= threshold)
|
||||||
p->display = 1;
|
p->display = 1;
|
||||||
|
|
||||||
c = p->all;
|
c = p->all;
|
||||||
|
@ -922,7 +991,7 @@ static int determine_display_tasks(u64 threshold)
|
||||||
if (c->start_time == 1)
|
if (c->start_time == 1)
|
||||||
c->start_time = first_time;
|
c->start_time = first_time;
|
||||||
|
|
||||||
if (c->total_time >= threshold && !power_only) {
|
if (c->total_time >= threshold) {
|
||||||
c->display = 1;
|
c->display = 1;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
@ -945,15 +1014,19 @@ static void write_svg_file(const char *filename)
|
||||||
{
|
{
|
||||||
u64 i;
|
u64 i;
|
||||||
int count;
|
int count;
|
||||||
|
int thresh = TIME_THRESH;
|
||||||
|
|
||||||
numcpus++;
|
numcpus++;
|
||||||
|
|
||||||
|
if (power_only)
|
||||||
|
proc_num = 0;
|
||||||
|
|
||||||
count = determine_display_tasks(TIME_THRESH);
|
/* We'd like to show at least proc_num tasks;
|
||||||
|
* be less picky if we have fewer */
|
||||||
/* We'd like to show at least 15 tasks; be less picky if we have fewer */
|
do {
|
||||||
if (count < 15)
|
count = determine_display_tasks(thresh);
|
||||||
count = determine_display_tasks(TIME_THRESH / 10);
|
thresh /= 10;
|
||||||
|
} while (!process_filter && thresh && count < proc_num);
|
||||||
|
|
||||||
open_svg(filename, numcpus, count, first_time, last_time);
|
open_svg(filename, numcpus, count, first_time, last_time);
|
||||||
|
|
||||||
|
@ -964,9 +1037,12 @@ static void write_svg_file(const char *filename)
|
||||||
svg_cpu_box(i, max_freq, turbo_frequency);
|
svg_cpu_box(i, max_freq, turbo_frequency);
|
||||||
|
|
||||||
draw_cpu_usage();
|
draw_cpu_usage();
|
||||||
draw_process_bars();
|
if (proc_num)
|
||||||
draw_c_p_states();
|
draw_process_bars();
|
||||||
draw_wakeups();
|
if (!tasks_only)
|
||||||
|
draw_c_p_states();
|
||||||
|
if (proc_num)
|
||||||
|
draw_wakeups();
|
||||||
|
|
||||||
svg_close();
|
svg_close();
|
||||||
}
|
}
|
||||||
|
@ -1031,50 +1107,92 @@ out_delete:
|
||||||
|
|
||||||
static int __cmd_record(int argc, const char **argv)
|
static int __cmd_record(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
unsigned int rec_argc, i, j;
|
||||||
const char * const record_old_args[] = {
|
const char **rec_argv;
|
||||||
|
const char **p;
|
||||||
|
unsigned int record_elems;
|
||||||
|
|
||||||
|
const char * const common_args[] = {
|
||||||
"record", "-a", "-R", "-c", "1",
|
"record", "-a", "-R", "-c", "1",
|
||||||
|
};
|
||||||
|
unsigned int common_args_nr = ARRAY_SIZE(common_args);
|
||||||
|
|
||||||
|
const char * const backtrace_args[] = {
|
||||||
|
"-g",
|
||||||
|
};
|
||||||
|
unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
|
||||||
|
|
||||||
|
const char * const power_args[] = {
|
||||||
|
"-e", "power:cpu_frequency",
|
||||||
|
"-e", "power:cpu_idle",
|
||||||
|
};
|
||||||
|
unsigned int power_args_nr = ARRAY_SIZE(power_args);
|
||||||
|
|
||||||
|
const char * const old_power_args[] = {
|
||||||
|
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||||
"-e", "power:power_start",
|
"-e", "power:power_start",
|
||||||
"-e", "power:power_end",
|
"-e", "power:power_end",
|
||||||
"-e", "power:power_frequency",
|
"-e", "power:power_frequency",
|
||||||
"-e", "sched:sched_wakeup",
|
|
||||||
"-e", "sched:sched_switch",
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
const char * const record_new_args[] = {
|
};
|
||||||
"record", "-a", "-R", "-c", "1",
|
unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
|
||||||
"-e", "power:cpu_frequency",
|
|
||||||
"-e", "power:cpu_idle",
|
const char * const tasks_args[] = {
|
||||||
"-e", "sched:sched_wakeup",
|
"-e", "sched:sched_wakeup",
|
||||||
"-e", "sched:sched_switch",
|
"-e", "sched:sched_switch",
|
||||||
};
|
};
|
||||||
unsigned int rec_argc, i, j;
|
unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
|
||||||
const char **rec_argv;
|
|
||||||
const char * const *record_args = record_new_args;
|
|
||||||
unsigned int record_elems = ARRAY_SIZE(record_new_args);
|
|
||||||
|
|
||||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||||
if (!is_valid_tracepoint("power:cpu_idle") &&
|
if (!is_valid_tracepoint("power:cpu_idle") &&
|
||||||
is_valid_tracepoint("power:power_start")) {
|
is_valid_tracepoint("power:power_start")) {
|
||||||
use_old_power_events = 1;
|
use_old_power_events = 1;
|
||||||
record_args = record_old_args;
|
power_args_nr = 0;
|
||||||
record_elems = ARRAY_SIZE(record_old_args);
|
} else {
|
||||||
|
old_power_args_nr = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rec_argc = record_elems + argc - 1;
|
if (power_only)
|
||||||
|
tasks_args_nr = 0;
|
||||||
|
|
||||||
|
if (tasks_only) {
|
||||||
|
power_args_nr = 0;
|
||||||
|
old_power_args_nr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!with_backtrace)
|
||||||
|
backtrace_args_no = 0;
|
||||||
|
|
||||||
|
record_elems = common_args_nr + tasks_args_nr +
|
||||||
|
power_args_nr + old_power_args_nr + backtrace_args_no;
|
||||||
|
|
||||||
|
rec_argc = record_elems + argc;
|
||||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||||
|
|
||||||
if (rec_argv == NULL)
|
if (rec_argv == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < record_elems; i++)
|
p = rec_argv;
|
||||||
rec_argv[i] = strdup(record_args[i]);
|
for (i = 0; i < common_args_nr; i++)
|
||||||
|
*p++ = strdup(common_args[i]);
|
||||||
|
|
||||||
for (j = 1; j < (unsigned int)argc; j++, i++)
|
for (i = 0; i < backtrace_args_no; i++)
|
||||||
rec_argv[i] = argv[j];
|
*p++ = strdup(backtrace_args[i]);
|
||||||
|
|
||||||
return cmd_record(i, rec_argv, NULL);
|
for (i = 0; i < tasks_args_nr; i++)
|
||||||
|
*p++ = strdup(tasks_args[i]);
|
||||||
|
|
||||||
|
for (i = 0; i < power_args_nr; i++)
|
||||||
|
*p++ = strdup(power_args[i]);
|
||||||
|
|
||||||
|
for (i = 0; i < old_power_args_nr; i++)
|
||||||
|
*p++ = strdup(old_power_args[i]);
|
||||||
|
|
||||||
|
for (j = 1; j < (unsigned int)argc; j++)
|
||||||
|
*p++ = argv[j];
|
||||||
|
|
||||||
|
return cmd_record(rec_argc, rec_argv, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1090,16 +1208,20 @@ int cmd_timechart(int argc, const char **argv,
|
||||||
const char *prefix __maybe_unused)
|
const char *prefix __maybe_unused)
|
||||||
{
|
{
|
||||||
const char *output_name = "output.svg";
|
const char *output_name = "output.svg";
|
||||||
const struct option options[] = {
|
const struct option timechart_options[] = {
|
||||||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||||
OPT_STRING('o', "output", &output_name, "file", "output file name"),
|
OPT_STRING('o', "output", &output_name, "file", "output file name"),
|
||||||
OPT_INTEGER('w', "width", &svg_page_width, "page width"),
|
OPT_INTEGER('w', "width", &svg_page_width, "page width"),
|
||||||
OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
|
OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
|
||||||
|
OPT_BOOLEAN('T', "tasks-only", &tasks_only,
|
||||||
|
"output processes data only"),
|
||||||
OPT_CALLBACK('p', "process", NULL, "process",
|
OPT_CALLBACK('p', "process", NULL, "process",
|
||||||
"process selector. Pass a pid or process name.",
|
"process selector. Pass a pid or process name.",
|
||||||
parse_process),
|
parse_process),
|
||||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||||
"Look for files with symbols relative to this directory"),
|
"Look for files with symbols relative to this directory"),
|
||||||
|
OPT_INTEGER('n', "proc-num", &proc_num,
|
||||||
|
"min. number of tasks to print"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
const char * const timechart_usage[] = {
|
const char * const timechart_usage[] = {
|
||||||
|
@ -1107,15 +1229,39 @@ int cmd_timechart(int argc, const char **argv,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
argc = parse_options(argc, argv, options, timechart_usage,
|
const struct option record_options[] = {
|
||||||
|
OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
|
||||||
|
OPT_BOOLEAN('T', "tasks-only", &tasks_only,
|
||||||
|
"output processes data only"),
|
||||||
|
OPT_BOOLEAN('g', "callchain", &with_backtrace, "record callchain"),
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
const char * const record_usage[] = {
|
||||||
|
"perf timechart record [<options>]",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
argc = parse_options(argc, argv, timechart_options, timechart_usage,
|
||||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
|
|
||||||
|
if (power_only && tasks_only) {
|
||||||
|
pr_err("-P and -T options cannot be used at the same time.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
symbol__init();
|
symbol__init();
|
||||||
|
|
||||||
if (argc && !strncmp(argv[0], "rec", 3))
|
if (argc && !strncmp(argv[0], "rec", 3)) {
|
||||||
|
argc = parse_options(argc, argv, record_options, record_usage,
|
||||||
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
|
|
||||||
|
if (power_only && tasks_only) {
|
||||||
|
pr_err("-P and -T options cannot be used at the same time.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return __cmd_record(argc, argv);
|
return __cmd_record(argc, argv);
|
||||||
else if (argc)
|
} else if (argc)
|
||||||
usage_with_options(timechart_usage, options);
|
usage_with_options(timechart_usage, timechart_options);
|
||||||
|
|
||||||
setup_pager();
|
setup_pager();
|
||||||
|
|
||||||
|
|
|
@ -634,26 +634,9 @@ repeat:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tag samples to be skipped. */
|
|
||||||
static const char *skip_symbols[] = {
|
|
||||||
"intel_idle",
|
|
||||||
"default_idle",
|
|
||||||
"native_safe_halt",
|
|
||||||
"cpu_idle",
|
|
||||||
"enter_idle",
|
|
||||||
"exit_idle",
|
|
||||||
"mwait_idle",
|
|
||||||
"mwait_idle_with_hints",
|
|
||||||
"poll_idle",
|
|
||||||
"ppc64_runlatch_off",
|
|
||||||
"pseries_dedicated_idle_sleep",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
|
static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
|
||||||
{
|
{
|
||||||
const char *name = sym->name;
|
const char *name = sym->name;
|
||||||
int i;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ppc64 uses function descriptors and appends a '.' to the
|
* ppc64 uses function descriptors and appends a '.' to the
|
||||||
|
@ -671,12 +654,8 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
|
||||||
strstr(name, "_text_end"))
|
strstr(name, "_text_end"))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
for (i = 0; skip_symbols[i]; i++) {
|
if (symbol__is_idle(sym))
|
||||||
if (!strcmp(skip_symbols[i], name)) {
|
sym->ignore = true;
|
||||||
sym->ignore = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1084,7 +1063,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
"dump the symbol table used for profiling"),
|
"dump the symbol table used for profiling"),
|
||||||
OPT_INTEGER('f', "count-filter", &top.count_filter,
|
OPT_INTEGER('f', "count-filter", &top.count_filter,
|
||||||
"only display functions with more events than this"),
|
"only display functions with more events than this"),
|
||||||
OPT_BOOLEAN('g', "group", &opts->group,
|
OPT_BOOLEAN(0, "group", &opts->group,
|
||||||
"put the counters into a counter group"),
|
"put the counters into a counter group"),
|
||||||
OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit,
|
OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit,
|
||||||
"child tasks do not inherit counters"),
|
"child tasks do not inherit counters"),
|
||||||
|
@ -1105,7 +1084,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||||
" abort, in_tx, transaction"),
|
" abort, in_tx, transaction"),
|
||||||
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
||||||
"Show a column with the number of samples"),
|
"Show a column with the number of samples"),
|
||||||
OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts,
|
OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
|
||||||
NULL, "enables call-graph recording",
|
NULL, "enables call-graph recording",
|
||||||
&callchain_opt),
|
&callchain_opt),
|
||||||
OPT_CALLBACK(0, "call-graph", &top.record_opts,
|
OPT_CALLBACK(0, "call-graph", &top.record_opts,
|
||||||
|
|
|
@ -2158,7 +2158,6 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
|
||||||
size_t printed = data->printed;
|
size_t printed = data->printed;
|
||||||
struct trace *trace = data->trace;
|
struct trace *trace = data->trace;
|
||||||
struct thread_trace *ttrace = thread->priv;
|
struct thread_trace *ttrace = thread->priv;
|
||||||
const char *color;
|
|
||||||
double ratio;
|
double ratio;
|
||||||
|
|
||||||
if (ttrace == NULL)
|
if (ttrace == NULL)
|
||||||
|
@ -2166,17 +2165,9 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
|
||||||
|
|
||||||
ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;
|
ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;
|
||||||
|
|
||||||
color = PERF_COLOR_NORMAL;
|
printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid);
|
||||||
if (ratio > 50.0)
|
|
||||||
color = PERF_COLOR_RED;
|
|
||||||
else if (ratio > 25.0)
|
|
||||||
color = PERF_COLOR_GREEN;
|
|
||||||
else if (ratio > 5.0)
|
|
||||||
color = PERF_COLOR_YELLOW;
|
|
||||||
|
|
||||||
printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid);
|
|
||||||
printed += fprintf(fp, "%lu events, ", ttrace->nr_events);
|
printed += fprintf(fp, "%lu events, ", ttrace->nr_events);
|
||||||
printed += color_fprintf(fp, color, "%.1f%%", ratio);
|
printed += fprintf(fp, "%.1f%%", ratio);
|
||||||
printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
|
printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
|
||||||
printed += thread__dump_stats(ttrace, trace, fp);
|
printed += thread__dump_stats(ttrace, trace, fp);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# perf completion
|
# perf bash and zsh completion
|
||||||
|
|
||||||
# Taken from git.git's completion script.
|
# Taken from git.git's completion script.
|
||||||
__my_reassemble_comp_words_by_ref()
|
__my_reassemble_comp_words_by_ref()
|
||||||
|
@ -89,37 +89,113 @@ __ltrim_colon_completions()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
type perf &>/dev/null &&
|
__perfcomp ()
|
||||||
_perf()
|
|
||||||
{
|
{
|
||||||
local cur words cword prev cmd
|
COMPREPLY=( $( compgen -W "$1" -- "$2" ) )
|
||||||
|
}
|
||||||
|
|
||||||
COMPREPLY=()
|
__perfcomp_colon ()
|
||||||
_get_comp_words_by_ref -n =: cur words cword prev
|
{
|
||||||
|
__perfcomp "$1" "$2"
|
||||||
|
__ltrim_colon_completions $cur
|
||||||
|
}
|
||||||
|
|
||||||
|
__perf_main ()
|
||||||
|
{
|
||||||
|
local cmd
|
||||||
|
|
||||||
cmd=${words[0]}
|
cmd=${words[0]}
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
# List perf subcommands or long options
|
# List perf subcommands or long options
|
||||||
if [ $cword -eq 1 ]; then
|
if [ $cword -eq 1 ]; then
|
||||||
if [[ $cur == --* ]]; then
|
if [[ $cur == --* ]]; then
|
||||||
COMPREPLY=( $( compgen -W '--help --version \
|
__perfcomp '--help --version \
|
||||||
--exec-path --html-path --paginate --no-pager \
|
--exec-path --html-path --paginate --no-pager \
|
||||||
--perf-dir --work-tree --debugfs-dir' -- "$cur" ) )
|
--perf-dir --work-tree --debugfs-dir' -- "$cur"
|
||||||
else
|
else
|
||||||
cmds=$($cmd --list-cmds)
|
cmds=$($cmd --list-cmds)
|
||||||
COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
|
__perfcomp "$cmds" "$cur"
|
||||||
fi
|
fi
|
||||||
# List possible events for -e option
|
# List possible events for -e option
|
||||||
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
|
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
|
||||||
evts=$($cmd list --raw-dump)
|
evts=$($cmd list --raw-dump)
|
||||||
COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) )
|
__perfcomp_colon "$evts" "$cur"
|
||||||
__ltrim_colon_completions $cur
|
|
||||||
# List long option names
|
# List long option names
|
||||||
elif [[ $cur == --* ]]; then
|
elif [[ $cur == --* ]]; then
|
||||||
subcmd=${words[1]}
|
subcmd=${words[1]}
|
||||||
opts=$($cmd $subcmd --list-opts)
|
opts=$($cmd $subcmd --list-opts)
|
||||||
COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) )
|
__perfcomp "$opts" "$cur"
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n ${ZSH_VERSION-} ]]; then
|
||||||
|
autoload -U +X compinit && compinit
|
||||||
|
|
||||||
|
__perfcomp ()
|
||||||
|
{
|
||||||
|
emulate -L zsh
|
||||||
|
|
||||||
|
local c IFS=$' \t\n'
|
||||||
|
local -a array
|
||||||
|
|
||||||
|
for c in ${=1}; do
|
||||||
|
case $c in
|
||||||
|
--*=*|*.) ;;
|
||||||
|
*) c="$c " ;;
|
||||||
|
esac
|
||||||
|
array[${#array[@]}+1]="$c"
|
||||||
|
done
|
||||||
|
|
||||||
|
compset -P '*[=:]'
|
||||||
|
compadd -Q -S '' -a -- array && _ret=0
|
||||||
|
}
|
||||||
|
|
||||||
|
__perfcomp_colon ()
|
||||||
|
{
|
||||||
|
emulate -L zsh
|
||||||
|
|
||||||
|
local cur_="${2-$cur}"
|
||||||
|
local c IFS=$' \t\n'
|
||||||
|
local -a array
|
||||||
|
|
||||||
|
if [[ "$cur_" == *:* ]]; then
|
||||||
|
local colon_word=${cur_%"${cur_##*:}"}
|
||||||
|
fi
|
||||||
|
|
||||||
|
for c in ${=1}; do
|
||||||
|
case $c in
|
||||||
|
--*=*|*.) ;;
|
||||||
|
*) c="$c " ;;
|
||||||
|
esac
|
||||||
|
array[$#array+1]=${c#"$colon_word"}
|
||||||
|
done
|
||||||
|
|
||||||
|
compset -P '*[=:]'
|
||||||
|
compadd -Q -S '' -a -- array && _ret=0
|
||||||
|
}
|
||||||
|
|
||||||
|
_perf ()
|
||||||
|
{
|
||||||
|
local _ret=1 cur cword prev
|
||||||
|
cur=${words[CURRENT]}
|
||||||
|
prev=${words[CURRENT-1]}
|
||||||
|
let cword=CURRENT-1
|
||||||
|
emulate ksh -c __perf_main
|
||||||
|
let _ret && _default && _ret=0
|
||||||
|
return _ret
|
||||||
|
}
|
||||||
|
|
||||||
|
compdef _perf perf
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
type perf &>/dev/null &&
|
||||||
|
_perf()
|
||||||
|
{
|
||||||
|
local cur words cword prev
|
||||||
|
_get_comp_words_by_ref -n =: cur words cword prev
|
||||||
|
__perf_main
|
||||||
} &&
|
} &&
|
||||||
|
|
||||||
complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \
|
complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \
|
|
@ -254,6 +254,7 @@ struct perf_record_opts {
|
||||||
bool inherit_stat;
|
bool inherit_stat;
|
||||||
bool no_delay;
|
bool no_delay;
|
||||||
bool no_inherit;
|
bool no_inherit;
|
||||||
|
bool no_inherit_set;
|
||||||
bool no_samples;
|
bool no_samples;
|
||||||
bool raw_samples;
|
bool raw_samples;
|
||||||
bool sample_address;
|
bool sample_address;
|
||||||
|
|
|
@ -3,5 +3,5 @@ command = record
|
||||||
args = -i kill >/dev/null 2>&1
|
args = -i kill >/dev/null 2>&1
|
||||||
|
|
||||||
[event:base-record]
|
[event:base-record]
|
||||||
sample_type=259
|
sample_type=263
|
||||||
inherit=0
|
inherit=0
|
||||||
|
|
|
@ -732,8 +732,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (symbol_conf.comm_list &&
|
if (thread__is_filtered(thread))
|
||||||
!strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread)))
|
|
||||||
goto out_filtered;
|
goto out_filtered;
|
||||||
|
|
||||||
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
|
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
|
||||||
|
|
|
@ -819,8 +819,10 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
|
||||||
if (evlist->threads == NULL)
|
if (evlist->threads == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (target->force_per_cpu)
|
if (target->default_per_cpu)
|
||||||
evlist->cpus = cpu_map__new(target->cpu_list);
|
evlist->cpus = target->per_thread ?
|
||||||
|
cpu_map__dummy_new() :
|
||||||
|
cpu_map__new(target->cpu_list);
|
||||||
else if (target__has_task(target))
|
else if (target__has_task(target))
|
||||||
evlist->cpus = cpu_map__dummy_new();
|
evlist->cpus = cpu_map__dummy_new();
|
||||||
else if (!target__has_cpu(target) && !target->uses_mmap)
|
else if (!target__has_cpu(target) && !target->uses_mmap)
|
||||||
|
|
|
@ -574,6 +574,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||||
struct perf_evsel *leader = evsel->leader;
|
struct perf_evsel *leader = evsel->leader;
|
||||||
struct perf_event_attr *attr = &evsel->attr;
|
struct perf_event_attr *attr = &evsel->attr;
|
||||||
int track = !evsel->idx; /* only the first counter needs these */
|
int track = !evsel->idx; /* only the first counter needs these */
|
||||||
|
bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;
|
||||||
|
|
||||||
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
|
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
|
||||||
attr->inherit = !opts->no_inherit;
|
attr->inherit = !opts->no_inherit;
|
||||||
|
@ -647,7 +648,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target__has_cpu(&opts->target) || opts->target.force_per_cpu)
|
if (target__has_cpu(&opts->target))
|
||||||
perf_evsel__set_sample_bit(evsel, CPU);
|
perf_evsel__set_sample_bit(evsel, CPU);
|
||||||
|
|
||||||
if (opts->period)
|
if (opts->period)
|
||||||
|
@ -655,7 +656,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
||||||
|
|
||||||
if (!perf_missing_features.sample_id_all &&
|
if (!perf_missing_features.sample_id_all &&
|
||||||
(opts->sample_time || !opts->no_inherit ||
|
(opts->sample_time || !opts->no_inherit ||
|
||||||
target__has_cpu(&opts->target) || opts->target.force_per_cpu))
|
target__has_cpu(&opts->target) || per_cpu))
|
||||||
perf_evsel__set_sample_bit(evsel, TIME);
|
perf_evsel__set_sample_bit(evsel, TIME);
|
||||||
|
|
||||||
if (opts->raw_samples) {
|
if (opts->raw_samples) {
|
||||||
|
|
|
@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||||
|
|
||||||
case OPTION_BOOLEAN:
|
case OPTION_BOOLEAN:
|
||||||
*(bool *)opt->value = unset ? false : true;
|
*(bool *)opt->value = unset ? false : true;
|
||||||
|
if (opt->set)
|
||||||
|
*(bool *)opt->set = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case OPTION_INCR:
|
case OPTION_INCR:
|
||||||
|
@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!rest) {
|
if (!rest) {
|
||||||
|
if (!prefixcmp(options->long_name, "no-")) {
|
||||||
|
/*
|
||||||
|
* The long name itself starts with "no-", so
|
||||||
|
* accept the option without "no-" so that users
|
||||||
|
* do not have to enter "no-no-" to get the
|
||||||
|
* negation.
|
||||||
|
*/
|
||||||
|
rest = skip_prefix(arg, options->long_name + 3);
|
||||||
|
if (rest) {
|
||||||
|
flags |= OPT_UNSET;
|
||||||
|
goto match;
|
||||||
|
}
|
||||||
|
/* Abbreviated case */
|
||||||
|
if (!prefixcmp(options->long_name + 3, arg)) {
|
||||||
|
flags |= OPT_UNSET;
|
||||||
|
goto is_abbreviated;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* abbreviated? */
|
/* abbreviated? */
|
||||||
if (!strncmp(options->long_name, arg, arg_end - arg)) {
|
if (!strncmp(options->long_name, arg, arg_end - arg)) {
|
||||||
is_abbreviated:
|
is_abbreviated:
|
||||||
|
@ -259,6 +279,7 @@ is_abbreviated:
|
||||||
if (!rest)
|
if (!rest)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
match:
|
||||||
if (*rest) {
|
if (*rest) {
|
||||||
if (*rest != '=')
|
if (*rest != '=')
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
|
||||||
* OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
|
* OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
|
||||||
* the value when met.
|
* the value when met.
|
||||||
* CALLBACKS can use it like they want.
|
* CALLBACKS can use it like they want.
|
||||||
|
*
|
||||||
|
* `set`::
|
||||||
|
* whether an option was set by the user
|
||||||
*/
|
*/
|
||||||
struct option {
|
struct option {
|
||||||
enum parse_opt_type type;
|
enum parse_opt_type type;
|
||||||
|
@ -94,6 +97,7 @@ struct option {
|
||||||
int flags;
|
int flags;
|
||||||
parse_opt_cb *callback;
|
parse_opt_cb *callback;
|
||||||
intptr_t defval;
|
intptr_t defval;
|
||||||
|
bool *set;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
|
#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
|
||||||
|
@ -103,6 +107,10 @@ struct option {
|
||||||
#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
|
#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
|
||||||
#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
|
#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
|
||||||
#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
|
#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
|
||||||
|
#define OPT_BOOLEAN_SET(s, l, v, os, h) \
|
||||||
|
{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
|
||||||
|
.value = check_vtype(v, bool *), .help = (h), \
|
||||||
|
.set = check_vtype(os, bool *)}
|
||||||
#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
|
#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
|
||||||
#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
|
#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
|
||||||
#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
|
#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
|
||||||
|
|
|
@ -1522,6 +1522,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||||
if (!node)
|
if (!node)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (node->sym && node->sym->ignore)
|
||||||
|
goto next;
|
||||||
|
|
||||||
if (print_ip)
|
if (print_ip)
|
||||||
printf("%c%16" PRIx64, s, node->ip);
|
printf("%c%16" PRIx64, s, node->ip);
|
||||||
|
|
||||||
|
@ -1544,12 +1547,15 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
||||||
if (!print_oneline)
|
if (!print_oneline)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
|
||||||
|
|
||||||
stack_depth--;
|
stack_depth--;
|
||||||
|
next:
|
||||||
|
callchain_cursor_advance(&callchain_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (al.sym && al.sym->ignore)
|
||||||
|
return;
|
||||||
|
|
||||||
if (print_ip)
|
if (print_ip)
|
||||||
printf("%16" PRIx64, sample->ip);
|
printf("%16" PRIx64, sample->ip);
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
|
||||||
|
|
||||||
total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
|
total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
|
||||||
fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
|
fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
|
||||||
|
fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
|
||||||
fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);
|
fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);
|
||||||
|
|
||||||
fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
|
fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
|
||||||
|
@ -128,12 +129,33 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type)
|
||||||
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
|
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_sample(int Yslot, int cpu, u64 start, u64 end)
|
static char *time_to_string(u64 duration);
|
||||||
|
void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
|
||||||
|
{
|
||||||
|
if (!svgfile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu,
|
||||||
|
time_to_string(end - start));
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace);
|
||||||
|
svg_box(Yslot, start, end, "blocked");
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
|
||||||
{
|
{
|
||||||
double text_size;
|
double text_size;
|
||||||
if (!svgfile)
|
if (!svgfile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
|
fprintf(svgfile, "<title>#%d running %s</title>\n",
|
||||||
|
cpu, time_to_string(end - start));
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace);
|
||||||
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
|
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
|
||||||
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
|
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
|
||||||
|
|
||||||
|
@ -148,6 +170,7 @@ void svg_sample(int Yslot, int cpu, u64 start, u64 end)
|
||||||
fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",
|
fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",
|
||||||
time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1);
|
time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1);
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *time_to_string(u64 duration)
|
static char *time_to_string(u64 duration)
|
||||||
|
@ -168,7 +191,7 @@ static char *time_to_string(u64 duration)
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_waiting(int Yslot, u64 start, u64 end)
|
void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
|
||||||
{
|
{
|
||||||
char *text;
|
char *text;
|
||||||
const char *style;
|
const char *style;
|
||||||
|
@ -192,6 +215,9 @@ void svg_waiting(int Yslot, u64 start, u64 end)
|
||||||
font_size = round_text_size(font_size);
|
font_size = round_text_size(font_size);
|
||||||
|
|
||||||
fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
|
fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
|
||||||
|
fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start));
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace);
|
||||||
fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
|
fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
|
||||||
time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
|
time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
|
||||||
if (font_size > MIN_TEXT_SIZE)
|
if (font_size > MIN_TEXT_SIZE)
|
||||||
|
@ -242,6 +268,8 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
|
||||||
max_freq = __max_freq;
|
max_freq = __max_freq;
|
||||||
turbo_frequency = __turbo_freq;
|
turbo_frequency = __turbo_freq;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
|
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
|
||||||
time2pixels(first_time),
|
time2pixels(first_time),
|
||||||
time2pixels(last_time)-time2pixels(first_time),
|
time2pixels(last_time)-time2pixels(first_time),
|
||||||
|
@ -253,6 +281,8 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
|
||||||
|
|
||||||
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
|
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
|
||||||
10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
|
10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
|
void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
|
||||||
|
@ -264,6 +294,7 @@ void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name
|
||||||
|
|
||||||
|
|
||||||
fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu));
|
fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu));
|
||||||
|
fprintf(svgfile, "<title>%s %s</title>\n", name, time_to_string(end - start));
|
||||||
fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
|
fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
|
||||||
time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);
|
time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);
|
||||||
width = time2pixels(end)-time2pixels(start);
|
width = time2pixels(end)-time2pixels(start);
|
||||||
|
@ -288,6 +319,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
if (type > 6)
|
if (type > 6)
|
||||||
type = 6;
|
type = 6;
|
||||||
sprintf(style, "c%i", type);
|
sprintf(style, "c%i", type);
|
||||||
|
@ -306,6 +339,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)
|
||||||
if (width > MIN_TEXT_SIZE)
|
if (width > MIN_TEXT_SIZE)
|
||||||
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",
|
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",
|
||||||
time2pixels(start), cpu2y(cpu)+width, width, type);
|
time2pixels(start), cpu2y(cpu)+width, width, type);
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *HzToHuman(unsigned long hz)
|
static char *HzToHuman(unsigned long hz)
|
||||||
|
@ -339,6 +374,8 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
|
||||||
if (!svgfile)
|
if (!svgfile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
if (max_freq)
|
if (max_freq)
|
||||||
height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
|
height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
|
||||||
height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
|
height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
|
||||||
|
@ -347,10 +384,11 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
|
||||||
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",
|
fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",
|
||||||
time2pixels(start), height+0.9, HzToHuman(freq));
|
time2pixels(start), height+0.9, HzToHuman(freq));
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2)
|
void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)
|
||||||
{
|
{
|
||||||
double height;
|
double height;
|
||||||
|
|
||||||
|
@ -358,6 +396,15 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
|
fprintf(svgfile, "<title>%s wakes up %s</title>\n",
|
||||||
|
desc1 ? desc1 : "?",
|
||||||
|
desc2 ? desc2 : "?");
|
||||||
|
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
|
||||||
|
|
||||||
if (row1 < row2) {
|
if (row1 < row2) {
|
||||||
if (row1) {
|
if (row1) {
|
||||||
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
|
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
|
||||||
|
@ -395,9 +442,11 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
|
||||||
if (row1)
|
if (row1)
|
||||||
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
|
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
|
||||||
time2pixels(start), height);
|
time2pixels(start), height);
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_wakeline(u64 start, int row1, int row2)
|
void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)
|
||||||
{
|
{
|
||||||
double height;
|
double height;
|
||||||
|
|
||||||
|
@ -405,6 +454,11 @@ void svg_wakeline(u64 start, int row1, int row2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
|
||||||
|
|
||||||
if (row1 < row2)
|
if (row1 < row2)
|
||||||
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
|
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
|
||||||
time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
|
time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
|
||||||
|
@ -417,17 +471,28 @@ void svg_wakeline(u64 start, int row1, int row2)
|
||||||
height += SLOT_HEIGHT;
|
height += SLOT_HEIGHT;
|
||||||
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
|
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
|
||||||
time2pixels(start), height);
|
time2pixels(start), height);
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_interrupt(u64 start, int row)
|
void svg_interrupt(u64 start, int row, const char *backtrace)
|
||||||
{
|
{
|
||||||
if (!svgfile)
|
if (!svgfile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
|
|
||||||
|
fprintf(svgfile, "<title>Wakeup from interrupt</title>\n");
|
||||||
|
|
||||||
|
if (backtrace)
|
||||||
|
fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
|
||||||
|
|
||||||
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
|
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
|
||||||
time2pixels(start), row * SLOT_MULT);
|
time2pixels(start), row * SLOT_MULT);
|
||||||
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
|
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
|
||||||
time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
|
time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
|
||||||
|
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_text(int Yslot, u64 start, const char *text)
|
void svg_text(int Yslot, u64 start, const char *text)
|
||||||
|
@ -455,6 +520,7 @@ void svg_legenda(void)
|
||||||
if (!svgfile)
|
if (!svgfile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fprintf(svgfile, "<g>\n");
|
||||||
svg_legenda_box(0, "Running", "sample");
|
svg_legenda_box(0, "Running", "sample");
|
||||||
svg_legenda_box(100, "Idle","c1");
|
svg_legenda_box(100, "Idle","c1");
|
||||||
svg_legenda_box(200, "Deeper Idle", "c3");
|
svg_legenda_box(200, "Deeper Idle", "c3");
|
||||||
|
@ -462,6 +528,7 @@ void svg_legenda(void)
|
||||||
svg_legenda_box(550, "Sleeping", "process2");
|
svg_legenda_box(550, "Sleeping", "process2");
|
||||||
svg_legenda_box(650, "Waiting for cpu", "waiting");
|
svg_legenda_box(650, "Waiting for cpu", "waiting");
|
||||||
svg_legenda_box(800, "Blocked on IO", "blocked");
|
svg_legenda_box(800, "Blocked on IO", "blocked");
|
||||||
|
fprintf(svgfile, "</g>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void svg_time_grid(void)
|
void svg_time_grid(void)
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
|
|
||||||
extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
|
extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
|
||||||
extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
|
extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
|
||||||
extern void svg_sample(int Yslot, int cpu, u64 start, u64 end);
|
extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
|
||||||
extern void svg_waiting(int Yslot, u64 start, u64 end);
|
extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
|
||||||
|
extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
|
||||||
extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
|
extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,9 +18,9 @@ extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
|
||||||
|
|
||||||
extern void svg_time_grid(void);
|
extern void svg_time_grid(void);
|
||||||
extern void svg_legenda(void);
|
extern void svg_legenda(void);
|
||||||
extern void svg_wakeline(u64 start, int row1, int row2);
|
extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace);
|
||||||
extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2);
|
extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace);
|
||||||
extern void svg_interrupt(u64 start, int row);
|
extern void svg_interrupt(u64 start, int row, const char *backtrace);
|
||||||
extern void svg_text(int Yslot, u64 start, const char *text);
|
extern void svg_text(int Yslot, u64 start, const char *text);
|
||||||
extern void svg_close(void);
|
extern void svg_close(void);
|
||||||
|
|
||||||
|
|
|
@ -573,6 +573,36 @@ static u8 kallsyms2elf_type(char type)
|
||||||
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
|
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool symbol__is_idle(struct symbol *sym)
|
||||||
|
{
|
||||||
|
const char * const idle_symbols[] = {
|
||||||
|
"cpu_idle",
|
||||||
|
"intel_idle",
|
||||||
|
"default_idle",
|
||||||
|
"native_safe_halt",
|
||||||
|
"enter_idle",
|
||||||
|
"exit_idle",
|
||||||
|
"mwait_idle",
|
||||||
|
"mwait_idle_with_hints",
|
||||||
|
"poll_idle",
|
||||||
|
"ppc64_runlatch_off",
|
||||||
|
"pseries_dedicated_idle_sleep",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!sym)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (i = 0; idle_symbols[i]; i++) {
|
||||||
|
if (!strcmp(idle_symbols[i], sym->name))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int map__process_kallsym_symbol(void *arg, const char *name,
|
static int map__process_kallsym_symbol(void *arg, const char *name,
|
||||||
char type, u64 start)
|
char type, u64 start)
|
||||||
{
|
{
|
||||||
|
@ -1496,14 +1526,15 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
|
||||||
|
|
||||||
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
|
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
|
||||||
|
|
||||||
|
scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir,
|
||||||
|
sbuild_id);
|
||||||
|
|
||||||
/* Use /proc/kallsyms if possible */
|
/* Use /proc/kallsyms if possible */
|
||||||
if (is_host) {
|
if (is_host) {
|
||||||
DIR *d;
|
DIR *d;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
/* If no cached kcore go with /proc/kallsyms */
|
/* If no cached kcore go with /proc/kallsyms */
|
||||||
scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s",
|
|
||||||
buildid_dir, sbuild_id);
|
|
||||||
d = opendir(path);
|
d = opendir(path);
|
||||||
if (!d)
|
if (!d)
|
||||||
goto proc_kallsyms;
|
goto proc_kallsyms;
|
||||||
|
@ -1528,6 +1559,10 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
|
||||||
goto proc_kallsyms;
|
goto proc_kallsyms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find kallsyms in build-id cache with kcore */
|
||||||
|
if (!find_matching_kcore(map, path, sizeof(path)))
|
||||||
|
return strdup(path);
|
||||||
|
|
||||||
scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s",
|
scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s",
|
||||||
buildid_dir, sbuild_id);
|
buildid_dir, sbuild_id);
|
||||||
|
|
||||||
|
@ -1719,7 +1754,7 @@ out_fail:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_list(struct strlist **list, const char *list_str,
|
int setup_list(struct strlist **list, const char *list_str,
|
||||||
const char *list_name)
|
const char *list_name)
|
||||||
{
|
{
|
||||||
if (list_str == NULL)
|
if (list_str == NULL)
|
||||||
|
|
|
@ -240,6 +240,7 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp);
|
||||||
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
|
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
|
||||||
bool symbol__restricted_filename(const char *filename,
|
bool symbol__restricted_filename(const char *filename,
|
||||||
const char *restricted_filename);
|
const char *restricted_filename);
|
||||||
|
bool symbol__is_idle(struct symbol *sym);
|
||||||
|
|
||||||
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
|
||||||
struct symsrc *runtime_ss, symbol_filter_t filter,
|
struct symsrc *runtime_ss, symbol_filter_t filter,
|
||||||
|
@ -273,4 +274,7 @@ void kcore_extract__delete(struct kcore_extract *kce);
|
||||||
int kcore_copy(const char *from_dir, const char *to_dir);
|
int kcore_copy(const char *from_dir, const char *to_dir);
|
||||||
int compare_proc_modules(const char *from, const char *to);
|
int compare_proc_modules(const char *from, const char *to);
|
||||||
|
|
||||||
|
int setup_list(struct strlist **list, const char *list_str,
|
||||||
|
const char *list_name);
|
||||||
|
|
||||||
#endif /* __PERF_SYMBOL */
|
#endif /* __PERF_SYMBOL */
|
||||||
|
|
|
@ -55,6 +55,13 @@ enum target_errno target__validate(struct target *target)
|
||||||
ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM;
|
ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* THREAD and SYSTEM/CPU are mutually exclusive */
|
||||||
|
if (target->per_thread && (target->system_wide || target->cpu_list)) {
|
||||||
|
target->per_thread = false;
|
||||||
|
if (ret == TARGET_ERRNO__SUCCESS)
|
||||||
|
ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +107,7 @@ static const char *target__error_str[] = {
|
||||||
"UID switch overriding CPU",
|
"UID switch overriding CPU",
|
||||||
"PID/TID switch overriding SYSTEM",
|
"PID/TID switch overriding SYSTEM",
|
||||||
"UID switch overriding SYSTEM",
|
"UID switch overriding SYSTEM",
|
||||||
|
"SYSTEM/CPU switch overriding PER-THREAD",
|
||||||
"Invalid User: %s",
|
"Invalid User: %s",
|
||||||
"Problems obtaining information for user %s",
|
"Problems obtaining information for user %s",
|
||||||
};
|
};
|
||||||
|
@ -131,7 +139,8 @@ int target__strerror(struct target *target, int errnum,
|
||||||
msg = target__error_str[idx];
|
msg = target__error_str[idx];
|
||||||
|
|
||||||
switch (errnum) {
|
switch (errnum) {
|
||||||
case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM:
|
case TARGET_ERRNO__PID_OVERRIDE_CPU ...
|
||||||
|
TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD:
|
||||||
snprintf(buf, buflen, "%s", msg);
|
snprintf(buf, buflen, "%s", msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ struct target {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
bool system_wide;
|
bool system_wide;
|
||||||
bool uses_mmap;
|
bool uses_mmap;
|
||||||
bool force_per_cpu;
|
bool default_per_cpu;
|
||||||
|
bool per_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum target_errno {
|
enum target_errno {
|
||||||
|
@ -33,6 +34,7 @@ enum target_errno {
|
||||||
TARGET_ERRNO__UID_OVERRIDE_CPU,
|
TARGET_ERRNO__UID_OVERRIDE_CPU,
|
||||||
TARGET_ERRNO__PID_OVERRIDE_SYSTEM,
|
TARGET_ERRNO__PID_OVERRIDE_SYSTEM,
|
||||||
TARGET_ERRNO__UID_OVERRIDE_SYSTEM,
|
TARGET_ERRNO__UID_OVERRIDE_SYSTEM,
|
||||||
|
TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD,
|
||||||
|
|
||||||
/* for target__parse_uid() */
|
/* for target__parse_uid() */
|
||||||
TARGET_ERRNO__INVALID_UID,
|
TARGET_ERRNO__INVALID_UID,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
|
#include <strlist.h>
|
||||||
|
|
||||||
struct thread {
|
struct thread {
|
||||||
union {
|
union {
|
||||||
|
@ -66,4 +67,15 @@ static inline void thread__set_priv(struct thread *thread, void *p)
|
||||||
{
|
{
|
||||||
thread->priv = p;
|
thread->priv = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool thread__is_filtered(struct thread *thread)
|
||||||
|
{
|
||||||
|
if (symbol_conf.comm_list &&
|
||||||
|
!strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __PERF_THREAD_H */
|
#endif /* __PERF_THREAD_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue