diff options
author | David Ahern <dsahern@gmail.com> | 2016-11-16 01:06:32 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-11-23 08:44:09 -0500 |
commit | 6c973c90852c3fa4b4e76a061ce89dcd373efccc (patch) | |
tree | 72efae60d507320855c1a1844b7d112d54411c44 | |
parent | fc1469f1b20777929a47f6f8e55bca12c6f142ac (diff) |
perf sched timehist: Add call graph options
If callchains were recorded they are appended to the line with a default stack depth of 5:
1.874569 [0011] gcc[31949] 0.014 0.000 1.148 wait_for_completion_killable <- do_fork <- sys_vfork <- stub_vfork <- __vfork
1.874591 [0010] gcc[31951] 0.000 0.000 0.024 __cond_resched <- _cond_resched <- wait_for_completion <- stop_one_cpu <- sched_exec
1.874603 [0010] migration/10[59] 3.350 0.004 0.011 smpboot_thread_fn <- kthread <- ret_from_fork
1.874604 [0011] <idle> 1.148 0.000 0.035 cpu_startup_entry <- start_secondary
1.874723 [0005] <idle> 0.016 0.000 1.383 cpu_startup_entry <- start_secondary
1.874746 [0005] gcc[31949] 0.153 0.078 0.022 do_wait sys_wait4 <- system_call_fastpath <- __GI___waitpid
--no-call-graph can be used to not show the callchains. --max-stack is used
to control the number of frames shown (default of 5). -x/--excl options can
be used to collapse redundant callchains to get more relevant data on screen.
Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/20161116060634.28477-7-namhyung@kernel.org
[ Add documentation based on above commit message ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Documentation/perf-sched.txt | 7 | ||||
-rw-r--r-- | tools/perf/builtin-sched.c | 88 |
2 files changed, 89 insertions, 6 deletions
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 9a77bc73e8a3..83452088727d 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt | |||
@@ -99,6 +99,13 @@ OPTIONS for 'perf sched timehist' | |||
99 | --kallsyms=<file>:: | 99 | --kallsyms=<file>:: |
100 | kallsyms pathname | 100 | kallsyms pathname |
101 | 101 | ||
102 | -g:: | ||
103 | --no-call-graph:: | ||
104 | Do not display call chains if present. | ||
105 | |||
106 | --max-stack:: | ||
107 | Maximum number of functions to display in backtrace, default 5. | ||
108 | |||
102 | -s:: | 109 | -s:: |
103 | --summary:: | 110 | --summary:: |
104 | Show only a summary of scheduling by thread with min, max, and average | 111 | Show only a summary of scheduling by thread with min, max, and average |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 8fb7bcc2cb76..1f8731640809 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util/thread_map.h" | 14 | #include "util/thread_map.h" |
15 | #include "util/color.h" | 15 | #include "util/color.h" |
16 | #include "util/stat.h" | 16 | #include "util/stat.h" |
17 | #include "util/callchain.h" | ||
17 | 18 | ||
18 | #include <subcmd/parse-options.h> | 19 | #include <subcmd/parse-options.h> |
19 | #include "util/trace-event.h" | 20 | #include "util/trace-event.h" |
@@ -198,6 +199,8 @@ struct perf_sched { | |||
198 | /* options for timehist command */ | 199 | /* options for timehist command */ |
199 | bool summary; | 200 | bool summary; |
200 | bool summary_only; | 201 | bool summary_only; |
202 | bool show_callchain; | ||
203 | unsigned int max_stack; | ||
201 | bool show_wakeups; | 204 | bool show_wakeups; |
202 | u64 skipped_samples; | 205 | u64 skipped_samples; |
203 | }; | 206 | }; |
@@ -1810,6 +1813,7 @@ static void timehist_header(void) | |||
1810 | 1813 | ||
1811 | static void timehist_print_sample(struct perf_sched *sched, | 1814 | static void timehist_print_sample(struct perf_sched *sched, |
1812 | struct perf_sample *sample, | 1815 | struct perf_sample *sample, |
1816 | struct addr_location *al, | ||
1813 | struct thread *thread) | 1817 | struct thread *thread) |
1814 | { | 1818 | { |
1815 | struct thread_runtime *tr = thread__priv(thread); | 1819 | struct thread_runtime *tr = thread__priv(thread); |
@@ -1827,6 +1831,18 @@ static void timehist_print_sample(struct perf_sched *sched, | |||
1827 | if (sched->show_wakeups) | 1831 | if (sched->show_wakeups) |
1828 | printf(" %-*s", comm_width, ""); | 1832 | printf(" %-*s", comm_width, ""); |
1829 | 1833 | ||
1834 | if (thread->tid == 0) | ||
1835 | goto out; | ||
1836 | |||
1837 | if (sched->show_callchain) | ||
1838 | printf(" "); | ||
1839 | |||
1840 | sample__fprintf_sym(sample, al, 0, | ||
1841 | EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE | | ||
1842 | EVSEL__PRINT_CALLCHAIN_ARROW, | ||
1843 | &callchain_cursor, stdout); | ||
1844 | |||
1845 | out: | ||
1830 | printf("\n"); | 1846 | printf("\n"); |
1831 | } | 1847 | } |
1832 | 1848 | ||
@@ -1878,9 +1894,14 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, | |||
1878 | r->total_run_time += r->dt_run; | 1894 | r->total_run_time += r->dt_run; |
1879 | } | 1895 | } |
1880 | 1896 | ||
1881 | static bool is_idle_sample(struct perf_sample *sample, | 1897 | static bool is_idle_sample(struct perf_sched *sched, |
1882 | struct perf_evsel *evsel) | 1898 | struct perf_sample *sample, |
1899 | struct perf_evsel *evsel, | ||
1900 | struct machine *machine) | ||
1883 | { | 1901 | { |
1902 | struct thread *thread; | ||
1903 | struct callchain_cursor *cursor = &callchain_cursor; | ||
1904 | |||
1884 | /* pid 0 == swapper == idle task */ | 1905 | /* pid 0 == swapper == idle task */ |
1885 | if (sample->pid == 0) | 1906 | if (sample->pid == 0) |
1886 | return true; | 1907 | return true; |
@@ -1889,6 +1910,25 @@ static bool is_idle_sample(struct perf_sample *sample, | |||
1889 | if (perf_evsel__intval(evsel, sample, "prev_pid") == 0) | 1910 | if (perf_evsel__intval(evsel, sample, "prev_pid") == 0) |
1890 | return true; | 1911 | return true; |
1891 | } | 1912 | } |
1913 | |||
1914 | /* want main thread for process - has maps */ | ||
1915 | thread = machine__findnew_thread(machine, sample->pid, sample->pid); | ||
1916 | if (thread == NULL) { | ||
1917 | pr_debug("Failed to get thread for pid %d.\n", sample->pid); | ||
1918 | return false; | ||
1919 | } | ||
1920 | |||
1921 | if (!symbol_conf.use_callchain || sample->callchain == NULL) | ||
1922 | return false; | ||
1923 | |||
1924 | if (thread__resolve_callchain(thread, cursor, evsel, sample, | ||
1925 | NULL, NULL, sched->max_stack) != 0) { | ||
1926 | if (verbose) | ||
1927 | error("Failed to resolve callchain. Skipping\n"); | ||
1928 | |||
1929 | return false; | ||
1930 | } | ||
1931 | callchain_cursor_commit(cursor); | ||
1892 | return false; | 1932 | return false; |
1893 | } | 1933 | } |
1894 | 1934 | ||
@@ -1999,13 +2039,14 @@ static struct thread_runtime *thread__get_runtime(struct thread *thread) | |||
1999 | return tr; | 2039 | return tr; |
2000 | } | 2040 | } |
2001 | 2041 | ||
2002 | static struct thread *timehist_get_thread(struct perf_sample *sample, | 2042 | static struct thread *timehist_get_thread(struct perf_sched *sched, |
2043 | struct perf_sample *sample, | ||
2003 | struct machine *machine, | 2044 | struct machine *machine, |
2004 | struct perf_evsel *evsel) | 2045 | struct perf_evsel *evsel) |
2005 | { | 2046 | { |
2006 | struct thread *thread; | 2047 | struct thread *thread; |
2007 | 2048 | ||
2008 | if (is_idle_sample(sample, evsel)) { | 2049 | if (is_idle_sample(sched, sample, evsel, machine)) { |
2009 | thread = get_idle_thread(sample->cpu); | 2050 | thread = get_idle_thread(sample->cpu); |
2010 | if (thread == NULL) | 2051 | if (thread == NULL) |
2011 | pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu); | 2052 | pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu); |
@@ -2115,7 +2156,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, | |||
2115 | goto out; | 2156 | goto out; |
2116 | } | 2157 | } |
2117 | 2158 | ||
2118 | thread = timehist_get_thread(sample, machine, evsel); | 2159 | thread = timehist_get_thread(sched, sample, machine, evsel); |
2119 | if (thread == NULL) { | 2160 | if (thread == NULL) { |
2120 | rc = -1; | 2161 | rc = -1; |
2121 | goto out; | 2162 | goto out; |
@@ -2134,7 +2175,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, | |||
2134 | 2175 | ||
2135 | timehist_update_runtime_stats(tr, sample->time, tprev); | 2176 | timehist_update_runtime_stats(tr, sample->time, tprev); |
2136 | if (!sched->summary_only) | 2177 | if (!sched->summary_only) |
2137 | timehist_print_sample(sched, sample, thread); | 2178 | timehist_print_sample(sched, sample, &al, thread); |
2138 | 2179 | ||
2139 | out: | 2180 | out: |
2140 | if (tr) { | 2181 | if (tr) { |
@@ -2327,6 +2368,30 @@ static int perf_timehist__process_sample(struct perf_tool *tool, | |||
2327 | return err; | 2368 | return err; |
2328 | } | 2369 | } |
2329 | 2370 | ||
2371 | static int timehist_check_attr(struct perf_sched *sched, | ||
2372 | struct perf_evlist *evlist) | ||
2373 | { | ||
2374 | struct perf_evsel *evsel; | ||
2375 | struct evsel_runtime *er; | ||
2376 | |||
2377 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
2378 | er = perf_evsel__get_runtime(evsel); | ||
2379 | if (er == NULL) { | ||
2380 | pr_err("Failed to allocate memory for evsel runtime data\n"); | ||
2381 | return -1; | ||
2382 | } | ||
2383 | |||
2384 | if (sched->show_callchain && | ||
2385 | !(evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN)) { | ||
2386 | pr_info("Samples do not have callchains.\n"); | ||
2387 | sched->show_callchain = 0; | ||
2388 | symbol_conf.use_callchain = 0; | ||
2389 | } | ||
2390 | } | ||
2391 | |||
2392 | return 0; | ||
2393 | } | ||
2394 | |||
2330 | static int perf_sched__timehist(struct perf_sched *sched) | 2395 | static int perf_sched__timehist(struct perf_sched *sched) |
2331 | { | 2396 | { |
2332 | const struct perf_evsel_str_handler handlers[] = { | 2397 | const struct perf_evsel_str_handler handlers[] = { |
@@ -2359,6 +2424,8 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2359 | sched->tool.ordered_events = true; | 2424 | sched->tool.ordered_events = true; |
2360 | sched->tool.ordering_requires_timestamps = true; | 2425 | sched->tool.ordering_requires_timestamps = true; |
2361 | 2426 | ||
2427 | symbol_conf.use_callchain = sched->show_callchain; | ||
2428 | |||
2362 | session = perf_session__new(&file, false, &sched->tool); | 2429 | session = perf_session__new(&file, false, &sched->tool); |
2363 | if (session == NULL) | 2430 | if (session == NULL) |
2364 | return -ENOMEM; | 2431 | return -ENOMEM; |
@@ -2367,6 +2434,9 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2367 | 2434 | ||
2368 | symbol__init(&session->header.env); | 2435 | symbol__init(&session->header.env); |
2369 | 2436 | ||
2437 | if (timehist_check_attr(sched, evlist) != 0) | ||
2438 | goto out; | ||
2439 | |||
2370 | setup_pager(); | 2440 | setup_pager(); |
2371 | 2441 | ||
2372 | /* setup per-evsel handlers */ | 2442 | /* setup per-evsel handlers */ |
@@ -2714,6 +2784,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2714 | .next_shortname1 = 'A', | 2784 | .next_shortname1 = 'A', |
2715 | .next_shortname2 = '0', | 2785 | .next_shortname2 = '0', |
2716 | .skip_merge = 0, | 2786 | .skip_merge = 0, |
2787 | .show_callchain = 1, | ||
2788 | .max_stack = 5, | ||
2717 | }; | 2789 | }; |
2718 | const struct option sched_options[] = { | 2790 | const struct option sched_options[] = { |
2719 | OPT_STRING('i', "input", &input_name, "file", | 2791 | OPT_STRING('i', "input", &input_name, "file", |
@@ -2759,6 +2831,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2759 | "file", "vmlinux pathname"), | 2831 | "file", "vmlinux pathname"), |
2760 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, | 2832 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, |
2761 | "file", "kallsyms pathname"), | 2833 | "file", "kallsyms pathname"), |
2834 | OPT_BOOLEAN('g', "call-graph", &sched.show_callchain, | ||
2835 | "Display call chains if present (default on)"), | ||
2836 | OPT_UINTEGER(0, "max-stack", &sched.max_stack, | ||
2837 | "Maximum number of functions to display backtrace."), | ||
2762 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 2838 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
2763 | "Look for files with symbols relative to this directory"), | 2839 | "Look for files with symbols relative to this directory"), |
2764 | OPT_BOOLEAN('s', "summary", &sched.summary_only, | 2840 | OPT_BOOLEAN('s', "summary", &sched.summary_only, |