aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsahern@gmail.com>2016-11-16 01:06:29 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-11-23 08:44:07 -0500
commit49394a2a24c78ce034fe0e732b1b54922c23fd92 (patch)
tree02707b0cb678b1e91b8f177d9b4b2f46ebdacc24
parent69b7e48070ca2ecee7498166259b5826b22e8b2e (diff)
perf sched timehist: Introduce timehist command
'perf sched timehist' provides an analysis of scheduling events. Example usage: perf sched record -- sleep 1 perf sched timehist By default it shows the individual schedule events, including the wait time (time between sched-out and next sched-in events for the task), the task scheduling delay (time between wakeup and actually running) and run time for the task: time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) -------------- ------ -------------------- --------- --------- --------- 79371.874569 [0011] gcc[31949] 0.014 0.000 1.148 79371.874591 [0010] gcc[31951] 0.000 0.000 0.024 79371.874603 [0010] migration/10[59] 3.350 0.004 0.011 79371.874604 [0011] <idle> 1.148 0.000 0.035 79371.874723 [0005] <idle> 0.016 0.000 1.383 79371.874746 [0005] gcc[31949] 0.153 0.078 0.022 ... Times are in msec.usec. Committer note: Add above explanation as the 'perf sched timehist' entry for 'man perf-sched'. 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-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/Documentation/perf-sched.txt50
-rw-r--r--tools/perf/builtin-sched.c594
2 files changed, 637 insertions, 7 deletions
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt
index 1cc08cc47ac5..72730da307b9 100644
--- a/tools/perf/Documentation/perf-sched.txt
+++ b/tools/perf/Documentation/perf-sched.txt
@@ -8,11 +8,11 @@ perf-sched - Tool to trace/measure scheduler properties (latencies)
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
10[verse] 10[verse]
11'perf sched' {record|latency|map|replay|script} 11'perf sched' {record|latency|map|replay|script|timehist}
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15There are five variants of perf sched: 15There are several variants of 'perf sched':
16 16
17 'perf sched record <command>' to record the scheduling events 17 'perf sched record <command>' to record the scheduling events
18 of an arbitrary workload. 18 of an arbitrary workload.
@@ -36,6 +36,30 @@ There are five variants of perf sched:
36 are running on a CPU. A '*' denotes the CPU that had the event, and 36 are running on a CPU. A '*' denotes the CPU that had the event, and
37 a dot signals an idle CPU. 37 a dot signals an idle CPU.
38 38
39 'perf sched timehist' provides an analysis of scheduling events.
40
41 Example usage:
42 perf sched record -- sleep 1
43 perf sched timehist
44
45 By default it shows the individual schedule events, including the wait
46 time (time between sched-out and next sched-in events for the task), the
47 task scheduling delay (time between wakeup and actually running) and run
48 time for the task:
49
50 time cpu task name wait time sch delay run time
51 [tid/pid] (msec) (msec) (msec)
52 -------------- ------ -------------------- --------- --------- ---------
53 79371.874569 [0011] gcc[31949] 0.014 0.000 1.148
54 79371.874591 [0010] gcc[31951] 0.000 0.000 0.024
55 79371.874603 [0010] migration/10[59] 3.350 0.004 0.011
56 79371.874604 [0011] <idle> 1.148 0.000 0.035
57 79371.874723 [0005] <idle> 0.016 0.000 1.383
58 79371.874746 [0005] gcc[31949] 0.153 0.078 0.022
59 ...
60
61 Times are in msec.usec.
62
39OPTIONS 63OPTIONS
40------- 64-------
41-i:: 65-i::
@@ -66,6 +90,28 @@ OPTIONS for 'perf sched map'
66--color-pids:: 90--color-pids::
67 Highlight the given pids. 91 Highlight the given pids.
68 92
93OPTIONS for 'perf sched timehist'
94---------------------------------
95-k::
96--vmlinux=<file>::
97 vmlinux pathname
98
99--kallsyms=<file>::
100 kallsyms pathname
101
102-s::
103--summary::
104 Show only a summary of scheduling by thread with min, max, and average
105 run times (in sec) and relative stddev.
106
107-S::
108--with-summary::
109 Show all scheduling events followed by a summary by thread with min,
110 max, and average run times (in sec) and relative stddev.
111
112--symfs=<directory>::
113 Look for files with symbols relative to this directory.
114
69SEE ALSO 115SEE ALSO
70-------- 116--------
71linkperf:perf-record[1] 117linkperf:perf-record[1]
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index fb3441211e4b..c0ac0c9557e8 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -13,12 +13,14 @@
13#include "util/cloexec.h" 13#include "util/cloexec.h"
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 17
17#include <subcmd/parse-options.h> 18#include <subcmd/parse-options.h>
18#include "util/trace-event.h" 19#include "util/trace-event.h"
19 20
20#include "util/debug.h" 21#include "util/debug.h"
21 22
23#include <linux/log2.h>
22#include <sys/prctl.h> 24#include <sys/prctl.h>
23#include <sys/resource.h> 25#include <sys/resource.h>
24 26
@@ -194,6 +196,29 @@ struct perf_sched {
194 struct perf_sched_map map; 196 struct perf_sched_map map;
195}; 197};
196 198
199/* per thread run time data */
200struct thread_runtime {
201 u64 last_time; /* time of previous sched in/out event */
202 u64 dt_run; /* run time */
203 u64 dt_wait; /* time between CPU access (off cpu) */
204 u64 dt_delay; /* time between wakeup and sched-in */
205 u64 ready_to_run; /* time of wakeup */
206
207 struct stats run_stats;
208 u64 total_run_time;
209};
210
211/* per event run time data */
212struct evsel_runtime {
213 u64 *last_time; /* time this event was last seen per cpu */
214 u32 ncpu; /* highest cpu slot allocated */
215};
216
217/* track idle times per cpu */
218static struct thread **idle_threads;
219static int idle_max_cpu;
220static char idle_comm[] = "<idle>";
221
197static u64 get_nsecs(void) 222static u64 get_nsecs(void)
198{ 223{
199 struct timespec ts; 224 struct timespec ts;
@@ -1654,6 +1679,546 @@ out_delete:
1654 return rc; 1679 return rc;
1655} 1680}
1656 1681
1682/*
1683 * scheduling times are printed as msec.usec
1684 */
1685static inline void print_sched_time(unsigned long long nsecs, int width)
1686{
1687 unsigned long msecs;
1688 unsigned long usecs;
1689
1690 msecs = nsecs / NSEC_PER_MSEC;
1691 nsecs -= msecs * NSEC_PER_MSEC;
1692 usecs = nsecs / NSEC_PER_USEC;
1693 printf("%*lu.%03lu ", width, msecs, usecs);
1694}
1695
1696/*
1697 * returns runtime data for event, allocating memory for it the
1698 * first time it is used.
1699 */
1700static struct evsel_runtime *perf_evsel__get_runtime(struct perf_evsel *evsel)
1701{
1702 struct evsel_runtime *r = evsel->priv;
1703
1704 if (r == NULL) {
1705 r = zalloc(sizeof(struct evsel_runtime));
1706 evsel->priv = r;
1707 }
1708
1709 return r;
1710}
1711
1712/*
1713 * save last time event was seen per cpu
1714 */
1715static void perf_evsel__save_time(struct perf_evsel *evsel,
1716 u64 timestamp, u32 cpu)
1717{
1718 struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
1719
1720 if (r == NULL)
1721 return;
1722
1723 if ((cpu >= r->ncpu) || (r->last_time == NULL)) {
1724 int i, n = __roundup_pow_of_two(cpu+1);
1725 void *p = r->last_time;
1726
1727 p = realloc(r->last_time, n * sizeof(u64));
1728 if (!p)
1729 return;
1730
1731 r->last_time = p;
1732 for (i = r->ncpu; i < n; ++i)
1733 r->last_time[i] = (u64) 0;
1734
1735 r->ncpu = n;
1736 }
1737
1738 r->last_time[cpu] = timestamp;
1739}
1740
1741/* returns last time this event was seen on the given cpu */
1742static u64 perf_evsel__get_time(struct perf_evsel *evsel, u32 cpu)
1743{
1744 struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
1745
1746 if ((r == NULL) || (r->last_time == NULL) || (cpu >= r->ncpu))
1747 return 0;
1748
1749 return r->last_time[cpu];
1750}
1751
1752static int comm_width = 20;
1753
1754static char *timehist_get_commstr(struct thread *thread)
1755{
1756 static char str[32];
1757 const char *comm = thread__comm_str(thread);
1758 pid_t tid = thread->tid;
1759 pid_t pid = thread->pid_;
1760 int n;
1761
1762 if (pid == 0)
1763 n = scnprintf(str, sizeof(str), "%s", comm);
1764
1765 else if (tid != pid)
1766 n = scnprintf(str, sizeof(str), "%s[%d/%d]", comm, tid, pid);
1767
1768 else
1769 n = scnprintf(str, sizeof(str), "%s[%d]", comm, tid);
1770
1771 if (n > comm_width)
1772 comm_width = n;
1773
1774 return str;
1775}
1776
1777static void timehist_header(void)
1778{
1779 printf("%15s %6s ", "time", "cpu");
1780
1781 printf(" %-20s %9s %9s %9s",
1782 "task name", "wait time", "sch delay", "run time");
1783
1784 printf("\n");
1785
1786 /*
1787 * units row
1788 */
1789 printf("%15s %-6s ", "", "");
1790
1791 printf(" %-20s %9s %9s %9s\n", "[tid/pid]", "(msec)", "(msec)", "(msec)");
1792
1793 /*
1794 * separator
1795 */
1796 printf("%.15s %.6s ", graph_dotted_line, graph_dotted_line);
1797
1798 printf(" %.20s %.9s %.9s %.9s",
1799 graph_dotted_line, graph_dotted_line, graph_dotted_line,
1800 graph_dotted_line);
1801
1802 printf("\n");
1803}
1804
1805static void timehist_print_sample(struct perf_sample *sample,
1806 struct thread *thread)
1807{
1808 struct thread_runtime *tr = thread__priv(thread);
1809 char tstr[64];
1810
1811 timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
1812 printf("%15s [%04d] ", tstr, sample->cpu);
1813
1814 printf(" %-*s ", comm_width, timehist_get_commstr(thread));
1815
1816 print_sched_time(tr->dt_wait, 6);
1817 print_sched_time(tr->dt_delay, 6);
1818 print_sched_time(tr->dt_run, 6);
1819 printf("\n");
1820}
1821
1822/*
1823 * Explanation of delta-time stats:
1824 *
1825 * t = time of current schedule out event
1826 * tprev = time of previous sched out event
1827 * also time of schedule-in event for current task
1828 * last_time = time of last sched change event for current task
1829 * (i.e, time process was last scheduled out)
1830 * ready_to_run = time of wakeup for current task
1831 *
1832 * -----|------------|------------|------------|------
1833 * last ready tprev t
1834 * time to run
1835 *
1836 * |-------- dt_wait --------|
1837 * |- dt_delay -|-- dt_run --|
1838 *
1839 * dt_run = run time of current task
1840 * dt_wait = time between last schedule out event for task and tprev
1841 * represents time spent off the cpu
1842 * dt_delay = time between wakeup and schedule-in of task
1843 */
1844
1845static void timehist_update_runtime_stats(struct thread_runtime *r,
1846 u64 t, u64 tprev)
1847{
1848 r->dt_delay = 0;
1849 r->dt_wait = 0;
1850 r->dt_run = 0;
1851 if (tprev) {
1852 r->dt_run = t - tprev;
1853 if (r->ready_to_run) {
1854 if (r->ready_to_run > tprev)
1855 pr_debug("time travel: wakeup time for task > previous sched_switch event\n");
1856 else
1857 r->dt_delay = tprev - r->ready_to_run;
1858 }
1859
1860 if (r->last_time > tprev)
1861 pr_debug("time travel: last sched out time for task > previous sched_switch event\n");
1862 else if (r->last_time)
1863 r->dt_wait = tprev - r->last_time;
1864 }
1865
1866 update_stats(&r->run_stats, r->dt_run);
1867 r->total_run_time += r->dt_run;
1868}
1869
1870static bool is_idle_sample(struct perf_sample *sample,
1871 struct perf_evsel *evsel)
1872{
1873 /* pid 0 == swapper == idle task */
1874 if (sample->pid == 0)
1875 return true;
1876
1877 if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0) {
1878 if (perf_evsel__intval(evsel, sample, "prev_pid") == 0)
1879 return true;
1880 }
1881 return false;
1882}
1883
1884/*
1885 * Track idle stats per cpu by maintaining a local thread
1886 * struct for the idle task on each cpu.
1887 */
1888static int init_idle_threads(int ncpu)
1889{
1890 int i;
1891
1892 idle_threads = zalloc(ncpu * sizeof(struct thread *));
1893 if (!idle_threads)
1894 return -ENOMEM;
1895
1896 idle_max_cpu = ncpu - 1;
1897
1898 /* allocate the actual thread struct if needed */
1899 for (i = 0; i < ncpu; ++i) {
1900 idle_threads[i] = thread__new(0, 0);
1901 if (idle_threads[i] == NULL)
1902 return -ENOMEM;
1903
1904 thread__set_comm(idle_threads[i], idle_comm, 0);
1905 }
1906
1907 return 0;
1908}
1909
1910static void free_idle_threads(void)
1911{
1912 int i;
1913
1914 if (idle_threads == NULL)
1915 return;
1916
1917 for (i = 0; i <= idle_max_cpu; ++i) {
1918 if ((idle_threads[i]))
1919 thread__delete(idle_threads[i]);
1920 }
1921
1922 free(idle_threads);
1923}
1924
1925static struct thread *get_idle_thread(int cpu)
1926{
1927 /*
1928 * expand/allocate array of pointers to local thread
1929 * structs if needed
1930 */
1931 if ((cpu >= idle_max_cpu) || (idle_threads == NULL)) {
1932 int i, j = __roundup_pow_of_two(cpu+1);
1933 void *p;
1934
1935 p = realloc(idle_threads, j * sizeof(struct thread *));
1936 if (!p)
1937 return NULL;
1938
1939 idle_threads = (struct thread **) p;
1940 i = idle_max_cpu ? idle_max_cpu + 1 : 0;
1941 for (; i < j; ++i)
1942 idle_threads[i] = NULL;
1943
1944 idle_max_cpu = j;
1945 }
1946
1947 /* allocate a new thread struct if needed */
1948 if (idle_threads[cpu] == NULL) {
1949 idle_threads[cpu] = thread__new(0, 0);
1950
1951 if (idle_threads[cpu]) {
1952 idle_threads[cpu]->tid = 0;
1953 thread__set_comm(idle_threads[cpu], idle_comm, 0);
1954 }
1955 }
1956
1957 return idle_threads[cpu];
1958}
1959
1960/*
1961 * handle runtime stats saved per thread
1962 */
1963static struct thread_runtime *thread__init_runtime(struct thread *thread)
1964{
1965 struct thread_runtime *r;
1966
1967 r = zalloc(sizeof(struct thread_runtime));
1968 if (!r)
1969 return NULL;
1970
1971 init_stats(&r->run_stats);
1972 thread__set_priv(thread, r);
1973
1974 return r;
1975}
1976
1977static struct thread_runtime *thread__get_runtime(struct thread *thread)
1978{
1979 struct thread_runtime *tr;
1980
1981 tr = thread__priv(thread);
1982 if (tr == NULL) {
1983 tr = thread__init_runtime(thread);
1984 if (tr == NULL)
1985 pr_debug("Failed to malloc memory for runtime data.\n");
1986 }
1987
1988 return tr;
1989}
1990
1991static struct thread *timehist_get_thread(struct perf_sample *sample,
1992 struct machine *machine,
1993 struct perf_evsel *evsel)
1994{
1995 struct thread *thread;
1996
1997 if (is_idle_sample(sample, evsel)) {
1998 thread = get_idle_thread(sample->cpu);
1999 if (thread == NULL)
2000 pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
2001
2002 } else {
2003 thread = machine__findnew_thread(machine, sample->pid, sample->tid);
2004 if (thread == NULL) {
2005 pr_debug("Failed to get thread for tid %d. skipping sample.\n",
2006 sample->tid);
2007 }
2008 }
2009
2010 return thread;
2011}
2012
2013static bool timehist_skip_sample(struct thread *thread)
2014{
2015 bool rc = false;
2016
2017 if (thread__is_filtered(thread))
2018 rc = true;
2019
2020 return rc;
2021}
2022
2023static int timehist_sched_wakeup_event(struct perf_tool *tool __maybe_unused,
2024 union perf_event *event __maybe_unused,
2025 struct perf_evsel *evsel,
2026 struct perf_sample *sample,
2027 struct machine *machine)
2028{
2029 struct thread *thread;
2030 struct thread_runtime *tr = NULL;
2031 /* want pid of awakened task not pid in sample */
2032 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
2033
2034 thread = machine__findnew_thread(machine, 0, pid);
2035 if (thread == NULL)
2036 return -1;
2037
2038 tr = thread__get_runtime(thread);
2039 if (tr == NULL)
2040 return -1;
2041
2042 if (tr->ready_to_run == 0)
2043 tr->ready_to_run = sample->time;
2044
2045 return 0;
2046}
2047
2048static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused,
2049 union perf_event *event,
2050 struct perf_evsel *evsel,
2051 struct perf_sample *sample,
2052 struct machine *machine)
2053{
2054 struct addr_location al;
2055 struct thread *thread;
2056 struct thread_runtime *tr = NULL;
2057 u64 tprev;
2058 int rc = 0;
2059
2060 if (machine__resolve(machine, &al, sample) < 0) {
2061 pr_err("problem processing %d event. skipping it\n",
2062 event->header.type);
2063 rc = -1;
2064 goto out;
2065 }
2066
2067 thread = timehist_get_thread(sample, machine, evsel);
2068 if (thread == NULL) {
2069 rc = -1;
2070 goto out;
2071 }
2072
2073 if (timehist_skip_sample(thread))
2074 goto out;
2075
2076 tr = thread__get_runtime(thread);
2077 if (tr == NULL) {
2078 rc = -1;
2079 goto out;
2080 }
2081
2082 tprev = perf_evsel__get_time(evsel, sample->cpu);
2083
2084 timehist_update_runtime_stats(tr, sample->time, tprev);
2085 timehist_print_sample(sample, thread);
2086
2087out:
2088 if (tr) {
2089 /* time of this sched_switch event becomes last time task seen */
2090 tr->last_time = sample->time;
2091
2092 /* sched out event for task so reset ready to run time */
2093 tr->ready_to_run = 0;
2094 }
2095
2096 perf_evsel__save_time(evsel, sample->time, sample->cpu);
2097
2098 return rc;
2099}
2100
2101static int timehist_sched_switch_event(struct perf_tool *tool,
2102 union perf_event *event,
2103 struct perf_evsel *evsel,
2104 struct perf_sample *sample,
2105 struct machine *machine __maybe_unused)
2106{
2107 return timehist_sched_change_event(tool, event, evsel, sample, machine);
2108}
2109
2110static int process_lost(struct perf_tool *tool __maybe_unused,
2111 union perf_event *event,
2112 struct perf_sample *sample,
2113 struct machine *machine __maybe_unused)
2114{
2115 char tstr[64];
2116
2117 timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
2118 printf("%15s ", tstr);
2119 printf("lost %" PRIu64 " events on cpu %d\n", event->lost.lost, sample->cpu);
2120
2121 return 0;
2122}
2123
2124
2125typedef int (*sched_handler)(struct perf_tool *tool,
2126 union perf_event *event,
2127 struct perf_evsel *evsel,
2128 struct perf_sample *sample,
2129 struct machine *machine);
2130
2131static int perf_timehist__process_sample(struct perf_tool *tool,
2132 union perf_event *event,
2133 struct perf_sample *sample,
2134 struct perf_evsel *evsel,
2135 struct machine *machine)
2136{
2137 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
2138 int err = 0;
2139 int this_cpu = sample->cpu;
2140
2141 if (this_cpu > sched->max_cpu)
2142 sched->max_cpu = this_cpu;
2143
2144 if (evsel->handler != NULL) {
2145 sched_handler f = evsel->handler;
2146
2147 err = f(tool, event, evsel, sample, machine);
2148 }
2149
2150 return err;
2151}
2152
2153static int perf_sched__timehist(struct perf_sched *sched)
2154{
2155 const struct perf_evsel_str_handler handlers[] = {
2156 { "sched:sched_switch", timehist_sched_switch_event, },
2157 { "sched:sched_wakeup", timehist_sched_wakeup_event, },
2158 { "sched:sched_wakeup_new", timehist_sched_wakeup_event, },
2159 };
2160 struct perf_data_file file = {
2161 .path = input_name,
2162 .mode = PERF_DATA_MODE_READ,
2163 };
2164
2165 struct perf_session *session;
2166 int err = -1;
2167
2168 /*
2169 * event handlers for timehist option
2170 */
2171 sched->tool.sample = perf_timehist__process_sample;
2172 sched->tool.mmap = perf_event__process_mmap;
2173 sched->tool.comm = perf_event__process_comm;
2174 sched->tool.exit = perf_event__process_exit;
2175 sched->tool.fork = perf_event__process_fork;
2176 sched->tool.lost = process_lost;
2177 sched->tool.attr = perf_event__process_attr;
2178 sched->tool.tracing_data = perf_event__process_tracing_data;
2179 sched->tool.build_id = perf_event__process_build_id;
2180
2181 sched->tool.ordered_events = true;
2182 sched->tool.ordering_requires_timestamps = true;
2183
2184 session = perf_session__new(&file, false, &sched->tool);
2185 if (session == NULL)
2186 return -ENOMEM;
2187
2188 symbol__init(&session->header.env);
2189
2190 setup_pager();
2191
2192 /* setup per-evsel handlers */
2193 if (perf_session__set_tracepoints_handlers(session, handlers))
2194 goto out;
2195
2196 if (!perf_session__has_traces(session, "record -R"))
2197 goto out;
2198
2199 /* pre-allocate struct for per-CPU idle stats */
2200 sched->max_cpu = session->header.env.nr_cpus_online;
2201 if (sched->max_cpu == 0)
2202 sched->max_cpu = 4;
2203 if (init_idle_threads(sched->max_cpu))
2204 goto out;
2205
2206 timehist_header();
2207
2208 err = perf_session__process_events(session);
2209 if (err) {
2210 pr_err("Failed to process events, error %d", err);
2211 goto out;
2212 }
2213
2214out:
2215 free_idle_threads();
2216 perf_session__delete(session);
2217
2218 return err;
2219}
2220
2221
1657static void print_bad_events(struct perf_sched *sched) 2222static void print_bad_events(struct perf_sched *sched)
1658{ 2223{
1659 if (sched->nr_unordered_timestamps && sched->nr_timestamps) { 2224 if (sched->nr_unordered_timestamps && sched->nr_timestamps) {
@@ -1970,8 +2535,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1970 const struct option latency_options[] = { 2535 const struct option latency_options[] = {
1971 OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", 2536 OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
1972 "sort by key(s): runtime, switch, avg, max"), 2537 "sort by key(s): runtime, switch, avg, max"),
1973 OPT_INCR('v', "verbose", &verbose,
1974 "be more verbose (show symbol address, etc)"),
1975 OPT_INTEGER('C', "CPU", &sched.profile_cpu, 2538 OPT_INTEGER('C', "CPU", &sched.profile_cpu,
1976 "CPU to profile on"), 2539 "CPU to profile on"),
1977 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 2540 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
@@ -1983,8 +2546,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1983 const struct option replay_options[] = { 2546 const struct option replay_options[] = {
1984 OPT_UINTEGER('r', "repeat", &sched.replay_repeat, 2547 OPT_UINTEGER('r', "repeat", &sched.replay_repeat,
1985 "repeat the workload replay N times (-1: infinite)"), 2548 "repeat the workload replay N times (-1: infinite)"),
1986 OPT_INCR('v', "verbose", &verbose,
1987 "be more verbose (show symbol address, etc)"),
1988 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 2549 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1989 "dump raw trace in ASCII"), 2550 "dump raw trace in ASCII"),
1990 OPT_BOOLEAN('f', "force", &sched.force, "don't complain, do it"), 2551 OPT_BOOLEAN('f', "force", &sched.force, "don't complain, do it"),
@@ -2001,6 +2562,16 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
2001 "display given CPUs in map"), 2562 "display given CPUs in map"),
2002 OPT_PARENT(sched_options) 2563 OPT_PARENT(sched_options)
2003 }; 2564 };
2565 const struct option timehist_options[] = {
2566 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
2567 "file", "vmlinux pathname"),
2568 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
2569 "file", "kallsyms pathname"),
2570 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
2571 "Look for files with symbols relative to this directory"),
2572 OPT_PARENT(sched_options)
2573 };
2574
2004 const char * const latency_usage[] = { 2575 const char * const latency_usage[] = {
2005 "perf sched latency [<options>]", 2576 "perf sched latency [<options>]",
2006 NULL 2577 NULL
@@ -2013,8 +2584,13 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
2013 "perf sched map [<options>]", 2584 "perf sched map [<options>]",
2014 NULL 2585 NULL
2015 }; 2586 };
2587 const char * const timehist_usage[] = {
2588 "perf sched timehist [<options>]",
2589 NULL
2590 };
2016 const char *const sched_subcommands[] = { "record", "latency", "map", 2591 const char *const sched_subcommands[] = { "record", "latency", "map",
2017 "replay", "script", NULL }; 2592 "replay", "script",
2593 "timehist", NULL };
2018 const char *sched_usage[] = { 2594 const char *sched_usage[] = {
2019 NULL, 2595 NULL,
2020 NULL 2596 NULL
@@ -2077,6 +2653,14 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
2077 usage_with_options(replay_usage, replay_options); 2653 usage_with_options(replay_usage, replay_options);
2078 } 2654 }
2079 return perf_sched__replay(&sched); 2655 return perf_sched__replay(&sched);
2656 } else if (!strcmp(argv[0], "timehist")) {
2657 if (argc) {
2658 argc = parse_options(argc, argv, timehist_options,
2659 timehist_usage, 0);
2660 if (argc)
2661 usage_with_options(timehist_usage, timehist_options);
2662 }
2663 return perf_sched__timehist(&sched);
2080 } else { 2664 } else {
2081 usage_with_options(sched_usage, sched_options); 2665 usage_with_options(sched_usage, sched_options);
2082 } 2666 }