diff options
author | David Ahern <dsahern@gmail.com> | 2016-11-16 01:06:30 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-11-23 08:44:08 -0500 |
commit | 52df138caaa8daaaf4e0dc64428ef06f303a8dbf (patch) | |
tree | d588d597bd8fe1c945c68f2b4b430cb15148dbf8 | |
parent | 49394a2a24c78ce034fe0e732b1b54922c23fd92 (diff) |
perf sched timehist: Add summary options
The -s/--summary option is to show process runtime statistics. And the
-S/--with-summary option is to show the stats with the normal output.
$ perf sched timehist -s
Runtime summary
comm parent sched-in run-time min-run avg-run max-run stddev
(count) (msec) (msec) (msec) (msec) %
---------------------------------------------------------------------------------------------------------
ksoftirqd/0[3] 2 2 0.011 0.004 0.005 0.006 14.87
rcu_preempt[7] 2 11 0.071 0.002 0.006 0.017 20.23
watchdog/0[11] 2 1 0.002 0.002 0.002 0.002 0.00
watchdog/1[12] 2 1 0.004 0.004 0.004 0.004 0.00
...
Terminated tasks:
sleep[7220] 7219 3 0.770 0.087 0.256 0.576 62.28
Idle stats:
CPU 0 idle for 2352.006 msec
CPU 1 idle for 2764.497 msec
CPU 2 idle for 2998.229 msec
CPU 3 idle for 2967.800 msec
Total number of unique tasks: 52
Total number of context switches: 2532
Total run time (msec): 218.036
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-5-namhyung@kernel.org
[ Add documentation from last commit, so that docs comes with the cset that introduces the feature ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/builtin-sched.c | 166 |
1 files changed, 160 insertions, 6 deletions
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c0ac0c9557e8..1e7d81ad5ec6 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -194,6 +194,11 @@ struct perf_sched { | |||
194 | bool force; | 194 | bool force; |
195 | bool skip_merge; | 195 | bool skip_merge; |
196 | struct perf_sched_map map; | 196 | struct perf_sched_map map; |
197 | |||
198 | /* options for timehist command */ | ||
199 | bool summary; | ||
200 | bool summary_only; | ||
201 | u64 skipped_samples; | ||
197 | }; | 202 | }; |
198 | 203 | ||
199 | /* per thread run time data */ | 204 | /* per thread run time data */ |
@@ -2010,12 +2015,15 @@ static struct thread *timehist_get_thread(struct perf_sample *sample, | |||
2010 | return thread; | 2015 | return thread; |
2011 | } | 2016 | } |
2012 | 2017 | ||
2013 | static bool timehist_skip_sample(struct thread *thread) | 2018 | static bool timehist_skip_sample(struct perf_sched *sched, |
2019 | struct thread *thread) | ||
2014 | { | 2020 | { |
2015 | bool rc = false; | 2021 | bool rc = false; |
2016 | 2022 | ||
2017 | if (thread__is_filtered(thread)) | 2023 | if (thread__is_filtered(thread)) { |
2018 | rc = true; | 2024 | rc = true; |
2025 | sched->skipped_samples++; | ||
2026 | } | ||
2019 | 2027 | ||
2020 | return rc; | 2028 | return rc; |
2021 | } | 2029 | } |
@@ -2045,7 +2053,7 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool __maybe_unused, | |||
2045 | return 0; | 2053 | return 0; |
2046 | } | 2054 | } |
2047 | 2055 | ||
2048 | static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused, | 2056 | static int timehist_sched_change_event(struct perf_tool *tool, |
2049 | union perf_event *event, | 2057 | union perf_event *event, |
2050 | struct perf_evsel *evsel, | 2058 | struct perf_evsel *evsel, |
2051 | struct perf_sample *sample, | 2059 | struct perf_sample *sample, |
@@ -2056,6 +2064,7 @@ static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused, | |||
2056 | struct thread_runtime *tr = NULL; | 2064 | struct thread_runtime *tr = NULL; |
2057 | u64 tprev; | 2065 | u64 tprev; |
2058 | int rc = 0; | 2066 | int rc = 0; |
2067 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); | ||
2059 | 2068 | ||
2060 | if (machine__resolve(machine, &al, sample) < 0) { | 2069 | if (machine__resolve(machine, &al, sample) < 0) { |
2061 | pr_err("problem processing %d event. skipping it\n", | 2070 | pr_err("problem processing %d event. skipping it\n", |
@@ -2070,7 +2079,7 @@ static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused, | |||
2070 | goto out; | 2079 | goto out; |
2071 | } | 2080 | } |
2072 | 2081 | ||
2073 | if (timehist_skip_sample(thread)) | 2082 | if (timehist_skip_sample(sched, thread)) |
2074 | goto out; | 2083 | goto out; |
2075 | 2084 | ||
2076 | tr = thread__get_runtime(thread); | 2085 | tr = thread__get_runtime(thread); |
@@ -2082,7 +2091,8 @@ static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused, | |||
2082 | tprev = perf_evsel__get_time(evsel, sample->cpu); | 2091 | tprev = perf_evsel__get_time(evsel, sample->cpu); |
2083 | 2092 | ||
2084 | timehist_update_runtime_stats(tr, sample->time, tprev); | 2093 | timehist_update_runtime_stats(tr, sample->time, tprev); |
2085 | timehist_print_sample(sample, thread); | 2094 | if (!sched->summary_only) |
2095 | timehist_print_sample(sample, thread); | ||
2086 | 2096 | ||
2087 | out: | 2097 | out: |
2088 | if (tr) { | 2098 | if (tr) { |
@@ -2122,6 +2132,131 @@ static int process_lost(struct perf_tool *tool __maybe_unused, | |||
2122 | } | 2132 | } |
2123 | 2133 | ||
2124 | 2134 | ||
2135 | static void print_thread_runtime(struct thread *t, | ||
2136 | struct thread_runtime *r) | ||
2137 | { | ||
2138 | double mean = avg_stats(&r->run_stats); | ||
2139 | float stddev; | ||
2140 | |||
2141 | printf("%*s %5d %9" PRIu64 " ", | ||
2142 | comm_width, timehist_get_commstr(t), t->ppid, | ||
2143 | (u64) r->run_stats.n); | ||
2144 | |||
2145 | print_sched_time(r->total_run_time, 8); | ||
2146 | stddev = rel_stddev_stats(stddev_stats(&r->run_stats), mean); | ||
2147 | print_sched_time(r->run_stats.min, 6); | ||
2148 | printf(" "); | ||
2149 | print_sched_time((u64) mean, 6); | ||
2150 | printf(" "); | ||
2151 | print_sched_time(r->run_stats.max, 6); | ||
2152 | printf(" "); | ||
2153 | printf("%5.2f", stddev); | ||
2154 | printf("\n"); | ||
2155 | } | ||
2156 | |||
2157 | struct total_run_stats { | ||
2158 | u64 sched_count; | ||
2159 | u64 task_count; | ||
2160 | u64 total_run_time; | ||
2161 | }; | ||
2162 | |||
2163 | static int __show_thread_runtime(struct thread *t, void *priv) | ||
2164 | { | ||
2165 | struct total_run_stats *stats = priv; | ||
2166 | struct thread_runtime *r; | ||
2167 | |||
2168 | if (thread__is_filtered(t)) | ||
2169 | return 0; | ||
2170 | |||
2171 | r = thread__priv(t); | ||
2172 | if (r && r->run_stats.n) { | ||
2173 | stats->task_count++; | ||
2174 | stats->sched_count += r->run_stats.n; | ||
2175 | stats->total_run_time += r->total_run_time; | ||
2176 | print_thread_runtime(t, r); | ||
2177 | } | ||
2178 | |||
2179 | return 0; | ||
2180 | } | ||
2181 | |||
2182 | static int show_thread_runtime(struct thread *t, void *priv) | ||
2183 | { | ||
2184 | if (t->dead) | ||
2185 | return 0; | ||
2186 | |||
2187 | return __show_thread_runtime(t, priv); | ||
2188 | } | ||
2189 | |||
2190 | static int show_deadthread_runtime(struct thread *t, void *priv) | ||
2191 | { | ||
2192 | if (!t->dead) | ||
2193 | return 0; | ||
2194 | |||
2195 | return __show_thread_runtime(t, priv); | ||
2196 | } | ||
2197 | |||
2198 | static void timehist_print_summary(struct perf_sched *sched, | ||
2199 | struct perf_session *session) | ||
2200 | { | ||
2201 | struct machine *m = &session->machines.host; | ||
2202 | struct total_run_stats totals; | ||
2203 | u64 task_count; | ||
2204 | struct thread *t; | ||
2205 | struct thread_runtime *r; | ||
2206 | int i; | ||
2207 | |||
2208 | memset(&totals, 0, sizeof(totals)); | ||
2209 | |||
2210 | if (comm_width < 30) | ||
2211 | comm_width = 30; | ||
2212 | |||
2213 | printf("\nRuntime summary\n"); | ||
2214 | printf("%*s parent sched-in ", comm_width, "comm"); | ||
2215 | printf(" run-time min-run avg-run max-run stddev\n"); | ||
2216 | printf("%*s (count) ", comm_width, ""); | ||
2217 | printf(" (msec) (msec) (msec) (msec) %%\n"); | ||
2218 | printf("%.105s\n", graph_dotted_line); | ||
2219 | |||
2220 | machine__for_each_thread(m, show_thread_runtime, &totals); | ||
2221 | task_count = totals.task_count; | ||
2222 | if (!task_count) | ||
2223 | printf("<no still running tasks>\n"); | ||
2224 | |||
2225 | printf("\nTerminated tasks:\n"); | ||
2226 | machine__for_each_thread(m, show_deadthread_runtime, &totals); | ||
2227 | if (task_count == totals.task_count) | ||
2228 | printf("<no terminated tasks>\n"); | ||
2229 | |||
2230 | /* CPU idle stats not tracked when samples were skipped */ | ||
2231 | if (sched->skipped_samples) | ||
2232 | return; | ||
2233 | |||
2234 | printf("\nIdle stats:\n"); | ||
2235 | for (i = 0; i <= idle_max_cpu; ++i) { | ||
2236 | t = idle_threads[i]; | ||
2237 | if (!t) | ||
2238 | continue; | ||
2239 | |||
2240 | r = thread__priv(t); | ||
2241 | if (r && r->run_stats.n) { | ||
2242 | totals.sched_count += r->run_stats.n; | ||
2243 | printf(" CPU %2d idle for ", i); | ||
2244 | print_sched_time(r->total_run_time, 6); | ||
2245 | printf(" msec\n"); | ||
2246 | } else | ||
2247 | printf(" CPU %2d idle entire time window\n", i); | ||
2248 | } | ||
2249 | |||
2250 | printf("\n" | ||
2251 | " Total number of unique tasks: %" PRIu64 "\n" | ||
2252 | "Total number of context switches: %" PRIu64 "\n" | ||
2253 | " Total run time (msec): ", | ||
2254 | totals.task_count, totals.sched_count); | ||
2255 | |||
2256 | print_sched_time(totals.total_run_time, 2); | ||
2257 | printf("\n"); | ||
2258 | } | ||
2259 | |||
2125 | typedef int (*sched_handler)(struct perf_tool *tool, | 2260 | typedef int (*sched_handler)(struct perf_tool *tool, |
2126 | union perf_event *event, | 2261 | union perf_event *event, |
2127 | struct perf_evsel *evsel, | 2262 | struct perf_evsel *evsel, |
@@ -2163,6 +2298,7 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2163 | }; | 2298 | }; |
2164 | 2299 | ||
2165 | struct perf_session *session; | 2300 | struct perf_session *session; |
2301 | struct perf_evlist *evlist; | ||
2166 | int err = -1; | 2302 | int err = -1; |
2167 | 2303 | ||
2168 | /* | 2304 | /* |
@@ -2185,6 +2321,8 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2185 | if (session == NULL) | 2321 | if (session == NULL) |
2186 | return -ENOMEM; | 2322 | return -ENOMEM; |
2187 | 2323 | ||
2324 | evlist = session->evlist; | ||
2325 | |||
2188 | symbol__init(&session->header.env); | 2326 | symbol__init(&session->header.env); |
2189 | 2327 | ||
2190 | setup_pager(); | 2328 | setup_pager(); |
@@ -2203,7 +2341,12 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2203 | if (init_idle_threads(sched->max_cpu)) | 2341 | if (init_idle_threads(sched->max_cpu)) |
2204 | goto out; | 2342 | goto out; |
2205 | 2343 | ||
2206 | timehist_header(); | 2344 | /* summary_only implies summary option, but don't overwrite summary if set */ |
2345 | if (sched->summary_only) | ||
2346 | sched->summary = sched->summary_only; | ||
2347 | |||
2348 | if (!sched->summary_only) | ||
2349 | timehist_header(); | ||
2207 | 2350 | ||
2208 | err = perf_session__process_events(session); | 2351 | err = perf_session__process_events(session); |
2209 | if (err) { | 2352 | if (err) { |
@@ -2211,6 +2354,13 @@ static int perf_sched__timehist(struct perf_sched *sched) | |||
2211 | goto out; | 2354 | goto out; |
2212 | } | 2355 | } |
2213 | 2356 | ||
2357 | sched->nr_events = evlist->stats.nr_events[0]; | ||
2358 | sched->nr_lost_events = evlist->stats.total_lost; | ||
2359 | sched->nr_lost_chunks = evlist->stats.nr_events[PERF_RECORD_LOST]; | ||
2360 | |||
2361 | if (sched->summary) | ||
2362 | timehist_print_summary(sched, session); | ||
2363 | |||
2214 | out: | 2364 | out: |
2215 | free_idle_threads(); | 2365 | free_idle_threads(); |
2216 | perf_session__delete(session); | 2366 | perf_session__delete(session); |
@@ -2569,6 +2719,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2569 | "file", "kallsyms pathname"), | 2719 | "file", "kallsyms pathname"), |
2570 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 2720 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
2571 | "Look for files with symbols relative to this directory"), | 2721 | "Look for files with symbols relative to this directory"), |
2722 | OPT_BOOLEAN('s', "summary", &sched.summary_only, | ||
2723 | "Show only syscall summary with statistics"), | ||
2724 | OPT_BOOLEAN('S', "with-summary", &sched.summary, | ||
2725 | "Show all syscalls and summary with statistics"), | ||
2572 | OPT_PARENT(sched_options) | 2726 | OPT_PARENT(sched_options) |
2573 | }; | 2727 | }; |
2574 | 2728 | ||