By default 'perf sched timehist' dumps a run-time summary for each task. The
summary includes the number of times the process was scheduled and the min, max
and average run times when scheduled (ie., a measure of how CPU time the task
wanted). Summary of idle times per cpu is also shown.

Example output (abbreviated):

Runtime summary (times are in seconds)
                  comm  parent  number  run-time    min-run    max-run   
avg-run  stddev(%)
...
           make[12858]  12823       2   0.000826   0.000063   0.000763   
0.000413  84.74
           make[12859]  12823       4   0.002706   0.000026   0.001368   
0.000676  40.83
           make[12860]  12823       3   0.002703   0.000026   0.001941   
0.000901  62.01
           make[12861]  12823       2   0.000863   0.000061   0.000801   
0.000431  85.64
       qemu-kvm[14716]     -1     207   0.009341   0.000021   0.000187   
0.000045   3.19
 qemu-kvm[14720/14716]     -1     146   0.005790   0.000011   0.000105   
0.000039   3.84
 qemu-kvm[14721/14716]     -1     102   0.002717   0.000014   0.000068   
0.000026   2.44
 qemu-kvm[29548/29541]     -1      10   0.089883   0.000000   0.009992   
0.008988  11.11
...

Idle stats:
    CPU 0 idle for  0.096720
    CPU 2 idle for  0.096138
    CPU 4 idle for  0.084509
    CPU 6 idle for  0.092162
    CPU 8 idle for  0.069825
    CPU 10 idle for  0.057123
    CPU 12 idle for  0.087755
    CPU 14 idle for  0.075491

    Total number of unique tasks: 66
Total number of context switches: 1631
            Total run time (sec):  0.204370

Individual sched_switch events can be dumped using the --events option. Output
includes N elements of the stack trace on one line with options to control
stack depth as well as exclude elements of stack dump to get more relevant
information onto each line:

  447399.732590 [14] make[12858]       0.000000  0.000063  __cond_resched 
_cond_resched wait_for_completion stop_one_cpu sched_exec
  447399.732608 [08] <idle>            0.002126  0.001472  
schedule_preempt_disabled cpu_startup_entry start_secondary
  447399.732610 [14] migration/14[79]  0.003583  0.000019  smpboot_thread_fn 
kthread ret_from_fork
  447399.732611 [06] qemu-kvm[14716]   0.000592  0.000073  
poll_schedule_timeout do_select core_sys_select SyS_select system_call
  447399.732623 [02] <idle>            0.000059  0.000965  
schedule_preempt_disabled cpu_startup_entry start_secondary
  447399.732627 [00] <idle>            0.000048  0.000054  
schedule_preempt_disabled cpu_startup_entry rest_init start_kernel 
x86_64_start_reservations
  447399.732658 [00] qemu-kvm[14721/14716] 0.000975  0.000030  kvm_vcpu_block 
kvm_arch_vcpu_ioctl_run kvm_vcpu_ioctl do_vfs_ioctl sys_ioctl
  447399.732671 [02] qemu-kvm[14720/14716] 0.000965  0.000047  kvm_vcpu_block 
kvm_arch_vcpu_ioctl_run kvm_vcpu_ioctl do_vfs_ioctl sys_ioctl
  447399.732686 [04] <idle>                0.000133  0.000172  
schedule_preempt_disabled cpu_startup_entry start_secondary
  447399.732700 [04] perf[12823]           0.000172  0.000013  do_wait 
SyS_wait4 system_call __libc_wait

Options include the usual report controls for kernel symbols, symfs, limiting
output to command names of interest.

Signed-off-by: David Ahern <dsah...@gmail.com>
Cc: Frederic Weisbecker <fweis...@gmail.com>
Cc: Ingo Molnar <mi...@kernel.org>
Cc: Jiri Olsa <jo...@redhat.com>
Cc: Mike Galbraith <efa...@gmx.de>
Cc: Namhyung Kim <namhy...@kernel.org>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Stephane Eranian <eran...@google.com>
---
 tools/perf/builtin-sched.c |  722 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 721 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 1ec1ff4..a45a40f 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -16,6 +16,8 @@
 
 #include "util/debug.h"
 
+#include "util/strlist.h"
+#include "asm/bug.h"
 #include <sys/prctl.h>
 #include <sys/resource.h>
 
@@ -169,8 +171,39 @@ struct perf_sched {
        u64              cpu_last_switched[MAX_CPUS];
        struct rb_root   atom_root, sorted_atom_root;
        struct list_head sort_list, cmp_pid;
+
+       /* options for timehist command */
+       bool            show_events;
+       bool            no_callchain;
+       unsigned int    max_stack_depth;
+       bool            show_cpu_visual;
+};
+
+/* per thread run time data */
+struct thread_runtime {
+       u64 dt_run;
+       u64 dt_between;
+       u64 last_time;
+
+       u64 count;
+       u64 total_run_time;
+       u64 min_run_time;
+       u64 max_run_time;
+       double mean, M2;
+       char commstr[32];
+};
+
+/* per event run time data */
+struct evsel_runtime {
+       u64 *last_time; /* time this event was last seen per cpu */
+       u32 ncpu;       /* highest cpu slot allocated */
 };
 
+/* track idle times per cpu */
+static struct thread **idle_threads;
+static int idle_max_cpu;
+static char idle_comm[] = "<idle>";
+
 static u64 get_nsecs(void)
 {
        struct timespec ts;
@@ -1479,6 +1512,652 @@ out_delete:
        return -1;
 }
 
+static inline void printf_nsecs(unsigned long long nsecs, int width_sec)
+{
+       unsigned long secs;
+       unsigned long usecs;
+
+       secs = nsecs / NSECS_PER_SEC;
+       nsecs -= secs * NSECS_PER_SEC;
+       usecs = nsecs / NSECS_PER_USEC;
+       printf("%*lu.%06lu ", width_sec, secs, usecs);
+}
+
+static struct evsel_runtime *perf_evsel__get_runtime(struct perf_evsel *evsel)
+{
+       struct evsel_runtime *r = evsel->priv;
+
+       if (r == NULL) {
+               r = zalloc(sizeof(struct evsel_runtime));
+               evsel->priv = r;
+       }
+
+       return r;
+}
+
+static void perf_evsel__save_time(struct perf_evsel *evsel,
+                                 u64 timestamp, u32 cpu)
+{
+       struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
+
+       if (r == NULL)
+               return;
+
+       if ((cpu > r->ncpu) || (r->last_time == NULL)) {
+               unsigned int i;
+               void *p = r->last_time;
+
+               r->last_time = realloc(r->last_time, (cpu+1) * sizeof(u64));
+               if (!r->last_time) {
+                       free(p);
+                       return;
+               }
+
+               i = r->ncpu ? r->ncpu + 1 : 0;
+               for (; i <= cpu; ++i)
+                       r->last_time[i] = (u64) 0;
+
+               r->ncpu = cpu;
+       }
+
+       r->last_time[cpu] = timestamp;
+}
+
+static u64 perf_evsel__get_time(struct perf_evsel *evsel, u32 cpu)
+{
+       struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
+
+       if (r == NULL)
+               return 0;
+
+       if (!r->last_time)
+               return 0;
+
+       if ((cpu > r->ncpu) || (r->last_time == NULL))
+               return 0;
+
+       return r->last_time[cpu];
+}
+
+static void timehist_header(struct perf_sched *sched)
+{
+       u32 max_cpus = sched->max_cpu;
+       u32 i, j;
+
+       printf("%-15s %-4s", "time", "cpu");
+
+       if (sched->show_cpu_visual && max_cpus) {
+               printf(" ");
+               for (i = 0, j = 0; i < max_cpus; ++i) {
+                       printf("%x", j++);
+                       if (j > 15)
+                               j = 0;
+               }
+               printf(" ");
+       }
+
+       printf(" %-20s %9s %9s",
+               "task name[tid/pid]", "b/n time", "run time");
+
+       printf("\n");
+
+       printf("%15s %4s", "---------------", "----");
+
+       if (sched->show_cpu_visual && max_cpus) {
+               printf(" ");
+               for (i = 0; i < max_cpus; ++i)
+                       printf("-");
+               printf(" ");
+       }
+
+       printf(" %20s %9s %9s",
+               "--------------------", "---------", "---------");
+
+       printf("\n");
+}
+
+static char *timehist_time_str(char *tstr, int len, u64 t)
+{
+       unsigned long secs, usecs;
+       unsigned long long nsecs;
+
+       nsecs = t;
+       secs = nsecs / NSECS_PER_SEC;
+       nsecs -= secs * NSECS_PER_SEC;
+       usecs = nsecs / NSECS_PER_USEC;
+       snprintf(tstr, len, "%5lu.%06lu", secs, usecs);
+
+       return tstr;
+}
+
+static unsigned int comm_width = 20;
+
+static void timehist_print_sample(struct perf_sched *sched,
+                                 struct perf_evsel *evsel,
+                                 struct perf_sample *sample,
+                                 struct thread *thread,
+                                 struct machine *machine)
+{
+       struct thread_runtime *tr = thread__priv(thread);
+       union perf_event *event = evsel->handler.data;
+       char tstr[64];
+       u32 max_cpus = sched->max_cpu;
+
+       printf("%15s ", timehist_time_str(tstr, sizeof(tstr), sample->time));
+
+       printf("[%02d] ", sample->cpu);
+
+       if (sched->show_cpu_visual && max_cpus) {
+               u32 i;
+               char c;
+               for (i = 0; i < max_cpus; ++i) {
+                       c = i == sample->cpu ? 's' : ' ';
+                       printf("%c", c);
+               }
+               printf("  ");
+       }
+
+       printf("%-*s ", comm_width, tr->commstr);
+
+       printf_nsecs(tr->dt_between, 2);
+       printf_nsecs(tr->dt_run, 2);
+
+       perf_evsel__print_ip(evsel, event, sample, machine,
+                            PRINT_IP_OPT_SYM | PRINT_IP_OPT_ONELINE,
+                            sched->max_stack_depth);
+
+       printf("\n");
+}
+
+static int timehist_check_attr(struct perf_sched *sched,
+                              struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel;
+       struct evsel_runtime *er;
+
+       list_for_each_entry(evsel, &evlist->entries, node) {
+               er = perf_evsel__get_runtime(evsel);
+               if (er == NULL) {
+                       pr_err("Failed to allocate memory for evsel runtime 
data\n");
+                       return -1;
+               }
+
+               if (!sched->no_callchain &&
+                   !(evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN)) {
+                       sched->no_callchain = 1;
+                       symbol_conf.use_callchain = 0;
+               }
+       }
+
+       return 0;
+}
+
+static bool is_idle_sample(struct perf_sample *sample,
+                          struct perf_evsel *evsel,
+                          struct machine *machine)
+{
+       struct thread *thread;
+       struct callchain_cursor *cursor = &callchain_cursor;
+       struct callchain_cursor_node *node;
+       struct addr_location al;
+       int iter = 5;
+
+       /* pid 0 == swapper == idle task */
+       if (sample->pid == 0)
+               return true;
+
+       /* want main thread for process - has maps */
+       thread = machine__findnew_thread(machine, sample->pid);
+       if (thread == NULL) {
+               pr_debug("Failed to get thread for pid %d.\n", sample->pid);
+               return false;
+       }
+
+       if (!symbol_conf.use_callchain || sample->callchain == NULL)
+               return false;
+
+       if (machine__resolve_callchain(machine, evsel, thread,
+                                       sample, NULL, &al) != 0) {
+               if (verbose)
+                       error("Failed to resolve callchain. Skipping\n");
+
+               return false;
+       }
+       callchain_cursor_commit(cursor);
+
+       /* idle symbol should be early in the stack */
+       while (iter) {
+               node = callchain_cursor_current(cursor);
+               if (!node)
+                       break;
+
+               if (symbol__is_idle(node->sym))
+                       return true;
+
+               callchain_cursor_advance(cursor);
+
+               iter--;
+       }
+
+       return false;
+}
+
+static void timehist_update_thread_stats(struct thread_runtime *r,
+                                        u64 t, u64 dt_run)
+{
+       u64 dt_task = 0;
+       double delta;
+
+       r->count++;
+
+       /* current run time and total run time */
+       r->dt_run = dt_run;
+       r->total_run_time += dt_run;
+
+       /* time b/n sched events for task */
+       if (r->last_time)
+               dt_task = t - r->last_time;
+
+       /* subtract run time for this event to get time between
+        * sched in events - i.e., how long between accesses to CPU
+        */
+       if (dt_task > dt_run)
+               r->dt_between = dt_task - dt_run;
+
+       /*
+        * update average and stddev stats
+        */
+       delta    = dt_run - r->mean;
+       r->mean += delta / r->count;
+       r->M2   += delta * (dt_run - r->mean);
+
+       if (dt_run > r->max_run_time)
+               r->max_run_time = dt_run;
+
+       if (dt_run < r->min_run_time)
+               r->min_run_time = dt_run;
+}
+
+static struct thread *get_idle_thread(int cpu)
+{
+       /* expand/allocate array of pointers to local thread
+        * structs if needed
+        */
+       if ((cpu > idle_max_cpu) || (idle_threads == NULL)) {
+               int i, j = 15;
+               void *p;
+
+               if (cpu > j)
+                       j = cpu;
+               p = realloc(idle_threads, (j+1) * sizeof(struct thread *));
+               if (!p)
+                       return NULL;
+
+               idle_threads = (struct thread **) p;
+               i = idle_max_cpu ? idle_max_cpu + 1 : 0;
+               for (; i <= cpu; ++i)
+                       idle_threads[i] = NULL;
+
+               idle_max_cpu = cpu;
+       }
+
+       /* allocate the actual thread struct if needed */
+       if (idle_threads[cpu] == NULL) {
+               idle_threads[cpu] = zalloc(sizeof(struct thread));
+               if (idle_threads[cpu]) {
+                       idle_threads[cpu]->tid = 0;
+                       idle_threads[cpu]->comm = idle_comm;
+               }
+       }
+
+       return idle_threads[cpu];
+}
+
+static void timehist_set_commstr(struct thread_runtime *r,
+                                const char *comm,
+                                pid_t pid, pid_t tid)
+{
+       unsigned int i, n;
+
+       if ((r == NULL) || (*r->commstr != '\0'))
+               return;
+
+       if (pid == 0) {
+               snprintf(r->commstr, sizeof(r->commstr)-1, "%s", comm);
+
+       } else if (tid != pid) {
+               snprintf(r->commstr, sizeof(r->commstr)-1, "%s[%d/%d]",
+                        comm, tid, pid);
+       } else {
+               snprintf(r->commstr, sizeof(r->commstr)-1, "%s[%d]",
+                        comm, tid);
+       }
+
+       for (i = 0; i < strlen(r->commstr); ++i) {
+               if (r->commstr[i] == ' ')
+                       r->commstr[i] = '-';
+       }
+
+       n = strlen(r->commstr);
+       if (n > comm_width)
+               comm_width = n;
+}
+
+static struct thread_runtime *thread__init_runtime(struct thread *thread, 
pid_t pid)
+{
+       struct thread_runtime *r;
+
+       r = zalloc(sizeof(struct thread_runtime));
+       if (!r)
+               return NULL;
+
+       r->min_run_time = (u64) -1;
+       thread__set_priv(thread, r);
+
+       /* optimization - saving comm string used in stack dump */
+       timehist_set_commstr(r, thread->comm, pid, thread->tid);
+
+       return r;
+}
+
+static struct thread *timehist_get_thread(struct perf_evsel *evsel,
+                                         struct perf_sample *sample,
+                                         struct machine *machine)
+{
+       struct thread *thread;
+
+       if (is_idle_sample(sample, evsel, machine)) {
+               thread = get_idle_thread(sample->cpu);
+               if (thread == NULL)
+                       pr_err("failed to get idle thread for cpu %d.\n", 
sample->cpu);
+
+       } else {
+               thread = machine__findnew_thread(machine, sample->tid);
+               if (thread == NULL) {
+                       pr_debug("Failed to get thread for tid %d. skipping 
sample.\n",
+                                sample->tid);
+               }
+       }
+
+       return thread;
+}
+
+static int timehist_sched_change_event(struct perf_tool *tool,
+                                      struct perf_evsel *evsel,
+                                      struct perf_sample *sample,
+                                      struct machine *machine)
+{
+       struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
+       struct thread *thread;
+       struct thread_runtime *tr = NULL;
+       u64 dt_run = 0, tprev;
+       int rc = 0;
+
+       thread = timehist_get_thread(evsel, sample, machine);
+       if (thread == NULL) {
+               rc = -1;
+               goto out;
+       }
+
+       tr = thread__priv(thread);
+       if (tr == NULL) {
+               /* pid argument is not needed once Adrian's patch is picked up 
*/
+               tr = thread__init_runtime(thread, sample->pid);
+               if (tr == NULL) {
+                       pr_debug("failed to malloc memory for runtime data.\n");
+                       rc = -1;
+                       goto out;
+               }
+       }
+
+       tprev = perf_evsel__get_time(evsel, sample->cpu);
+       if (tprev)
+               dt_run = sample->time - tprev;
+
+       timehist_update_thread_stats(tr, sample->time, dt_run);
+       if (sched->show_events)
+               timehist_print_sample(sched, evsel, sample, thread, machine);
+
+out:
+       if (tr)
+               tr->last_time = sample->time;
+       perf_evsel__save_time(evsel, sample->time, sample->cpu);
+
+       return rc;
+}
+
+static int timehist_sched_switch_event(struct perf_tool *tool,
+                              struct perf_evsel *evsel,
+                              struct perf_sample *sample,
+                              struct machine *machine)
+{
+       return timehist_sched_change_event(tool, evsel, sample, machine);
+}
+
+static int process_lost(struct perf_tool *tool __maybe_unused,
+                       union perf_event *event,
+                       struct perf_sample *sample,
+                       struct machine *machine __maybe_unused)
+{
+       char tstr[64];
+
+       printf("%15s ", timehist_time_str(tstr, sizeof(tstr), sample->time));
+       printf("lost %" PRIu64 " events on cpu %d\n", event->lost.lost, 
sample->cpu);
+
+       return 0;
+}
+
+
+static void print_thread_runtime(struct thread *t, struct thread_runtime *r)
+{
+       double variance;
+       float stddev;
+
+       printf("    %*s  %5d  %6" PRIu64 "  ",
+              comm_width, r->commstr, t->ppid, r->count);
+
+       printf_nsecs(r->total_run_time, 2);
+       if (r->count > 1) {
+               variance = r->M2 / (r->count - 1);
+               stddev = 100.0 * sqrt(variance / r->count) / r->mean;
+
+               printf(" ");
+               printf_nsecs(r->min_run_time, 2);
+               printf(" ");
+               printf_nsecs(r->max_run_time, 2);
+               printf(" ");
+               printf_nsecs((u64) r->mean, 2);
+               printf(" ");
+               printf("%5.2f", stddev);
+       }
+       printf("\n");
+}
+
+static void timehist_print_summary(struct perf_session *session)
+{
+       struct machine *m = &session->machines.host;
+       struct rb_node *nd;
+       struct thread *t;
+       struct thread_runtime *r;
+       u64 total_run_time = 0;
+       u64 total_count = 0;
+       int i, total_task = 0;
+
+       printf("\nRuntime summary (times are in seconds)\n");
+       printf("    %*s  parent  number  ", comm_width, "comm");
+       printf("run-time    min-run    max-run   avg-run  stddev(%%)\n");
+
+       nd = rb_first(&m->threads);
+       while (nd) {
+               t = rb_entry(nd, struct thread, rb_node);
+
+               r = thread__priv(t);
+               if (r && r->count) {
+                       total_count += r->count;
+                       total_run_time += r->total_run_time;
+                       total_task++;
+                       print_thread_runtime(t, r);
+               }
+
+               nd = rb_next(nd);
+       }
+
+       printf("\nTerminated tasks:\n");
+       list_for_each_entry(t, &m->dead_threads, node) {
+               r = thread__priv(t);
+               if (r && r->count) {
+                       total_count += r->count;
+                       total_run_time += r->total_run_time;
+                       total_task++;
+
+                       print_thread_runtime(t, r);
+               }
+       }
+
+       printf("\nIdle stats:\n");
+       for (i = 0; i <= idle_max_cpu; ++i) {
+               t = idle_threads[i];
+               if (!t)
+                       continue;
+
+               r = thread__priv(t);
+               if (r && r->count) {
+                       total_count += r->count;
+                       printf("    CPU %d idle for ", i);
+                       printf_nsecs(r->total_run_time, 2);
+                       printf("\n");
+               }
+       }
+
+       WARN_ONCE(!symbol_conf.vmlinux_name && !symbol_conf.kallsyms_name,
+                 "kernel symbols not given. If this is an offline analysis 
idle time computations will be wrong\n");
+
+       printf("\n"
+              "    Total number of unique tasks: %d\n"
+              "Total number of context switches: %" PRIu64 "\n"
+              "            Total run time (sec): ",
+              total_task, total_count);
+       printf_nsecs(total_run_time, 2);
+       printf("\n");
+}
+static int perf_timehist__process_sample(struct perf_tool *tool,
+                                        union perf_event *event,
+                                        struct perf_sample *sample,
+                                        struct perf_evsel *evsel,
+                                        struct machine *machine)
+{
+       int err = 0;
+
+       evsel->hists.stats.total_period += sample->period;
+       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+
+       if (evsel->handler.func != NULL || verbose) {
+               tracepoint_handler f = evsel->handler.func;
+               evsel->handler.data = event;
+
+               if (f)
+                       err = f(tool, evsel, sample, machine);
+       }
+
+       return err;
+}
+
+/* list of symbols to exclude from stack dump */
+static int setup_excl_sym(void)
+{
+       char buf[4096];
+
+       snprintf(buf, sizeof(buf),
+                       "schedule,__schedule,"
+                       "schedule_timeout,schedule_timeout_interruptible,"
+                       
"schedule_hrtimeout_range_clock,schedule_hrtimeout_range,"
+                       "syscall,system_call_done,ia32_syscall_done,%s",
+                       symbol_conf.excl_sym_list_str ? 
symbol_conf.excl_sym_list_str : "");
+
+       symbol_conf.excl_sym_list_str = strdup(buf);
+
+       return 0;
+}
+
+static int perf_sched__timehist(struct perf_sched *sched)
+{
+       const struct perf_evsel_str_handler handlers[] = {
+               { "sched:sched_switch",       timehist_sched_switch_event, },
+       };
+
+       struct perf_session *session;
+       int err = -1;
+
+       /*
+        * event handlers for timehist option
+        */
+       sched->tool.sample       = perf_timehist__process_sample;
+       sched->tool.mmap         = perf_event__process_mmap;
+       sched->tool.comm         = perf_event__process_comm;
+       sched->tool.exit         = perf_event__process_exit;
+       sched->tool.fork         = perf_event__process_fork;
+       sched->tool.lost         = process_lost;
+       sched->tool.attr         = perf_event__process_attr;
+       sched->tool.tracing_data = perf_event__process_tracing_data;
+       sched->tool.build_id     = perf_event__process_build_id;
+
+       sched->tool.ordered_samples = true;
+       sched->tool.ordering_requires_timestamps = true;
+
+       /* if not given default to stack depth of 5 */
+       if (sched->max_stack_depth == 0)
+               sched->max_stack_depth = 5;
+
+       symbol_conf.use_callchain = !sched->no_callchain;
+       setup_excl_sym();
+       if (symbol__init() < 0)
+               return -1;
+
+       session = perf_session__new(input_name, O_RDONLY, 0, false, 
&sched->tool);
+       if (session == NULL)
+               return -ENOMEM;
+
+       setup_pager();
+
+       if (timehist_check_attr(sched, session->evlist) != 0)
+               return -ENOMEM;
+
+       perf_session__fprintf_info(session, stdout, true);
+
+       /* pre-allocate struct for per-CPU idle stats */
+       sched->max_cpu = session->header.env.nr_cpus_online;
+       if (sched->max_cpu > 0) {
+               idle_max_cpu = sched->max_cpu - 1;
+               idle_threads = zalloc(sched->max_cpu * sizeof(struct thread *));
+       }
+
+       /* setup per-evsel handlers */
+       if (perf_session__set_tracepoints_handlers(session, handlers))
+               goto out;
+
+       if (perf_session__has_traces(session, "record -R")) {
+               if (sched->show_events)
+                       timehist_header(sched);
+
+               err = perf_session__process_events(session, &sched->tool);
+               if (err) {
+                       pr_err("Failed to process events, error %d", err);
+                       goto out;
+               }
+
+               sched->nr_events      = session->stats.nr_events[0];
+               sched->nr_lost_events = session->stats.total_lost;
+               sched->nr_lost_chunks = 
session->stats.nr_events[PERF_RECORD_LOST];
+       }
+
+       timehist_print_summary(session);
+
+out:
+       perf_session__delete(session);
+
+       return err;
+}
+
 static void print_bad_events(struct perf_sched *sched)
 {
        if (sched->nr_unordered_timestamps && sched->nr_timestamps) {
@@ -1702,6 +2381,36 @@ int cmd_sched(int argc, const char **argv, const char 
*prefix __maybe_unused)
                    "dump raw trace in ASCII"),
        OPT_END()
        };
+       const struct option timehist_options[] = {
+       OPT_STRING('i', "input", &input_name, "file",
+                   "input file name"),
+       OPT_INCR('v', "verbose", &verbose,
+                   "be more verbose (show symbol address, etc)"),
+       OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+                   "dump raw trace in ASCII"),
+       OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+                  "file", "vmlinux pathname"),
+       OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
+                  "file", "kallsyms pathname"),
+       OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
+                  "only display events for these comms"),
+       OPT_UINTEGER('s', "stack-depth", &sched.max_stack_depth,
+                  "Maximum number of functions to display backtrace."),
+       OPT_STRING('x', "excl", &symbol_conf.excl_sym_list_str, "sym[,sym...]",
+                  "symbols to skip in backtrace"),
+       OPT_BOOLEAN('G', "hide-call-graph", &sched.no_callchain,
+                   "When printing symbols do not display call chain"),
+       OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+                   "Look for files with symbols relative to this directory"),
+       OPT_BOOLEAN(0, "cpu-visual", &sched.show_cpu_visual, "Show CPU visual"),
+       OPT_BOOLEAN(0, "events", &sched.show_events, "show all events"),
+       OPT_END()
+       };
+       const char * const timehist_usage[] = {
+               "perf sched timehist [<options>]",
+               NULL
+       };
+
        const char * const latency_usage[] = {
                "perf sched latency [<options>]",
                NULL
@@ -1740,7 +2449,6 @@ int cmd_sched(int argc, const char **argv, const char 
*prefix __maybe_unused)
        if (!strcmp(argv[0], "script"))
                return cmd_script(argc, argv, prefix);
 
-       symbol__init();
        if (!strncmp(argv[0], "rec", 3)) {
                return __cmd_record(argc, argv);
        } else if (!strncmp(argv[0], "lat", 3)) {
@@ -1751,10 +2459,12 @@ int cmd_sched(int argc, const char **argv, const char 
*prefix __maybe_unused)
                                usage_with_options(latency_usage, 
latency_options);
                }
                setup_sorting(&sched, latency_options, latency_usage);
+               symbol__init();
                return perf_sched__lat(&sched);
        } else if (!strcmp(argv[0], "map")) {
                sched.tp_handler = &map_ops;
                setup_sorting(&sched, latency_options, latency_usage);
+               symbol__init();
                return perf_sched__map(&sched);
        } else if (!strncmp(argv[0], "rep", 3)) {
                sched.tp_handler = &replay_ops;
@@ -1763,7 +2473,17 @@ int cmd_sched(int argc, const char **argv, const char 
*prefix __maybe_unused)
                        if (argc)
                                usage_with_options(replay_usage, 
replay_options);
                }
+               symbol__init();
                return perf_sched__replay(&sched);
+       } else if (!strcmp(argv[0], "timehist")) {
+               if (argc) {
+                       argc = parse_options(argc, argv, timehist_options,
+                                            timehist_usage, 0);
+                       if (argc)
+                               usage_with_options(timehist_usage, 
timehist_options);
+               }
+
+               return perf_sched__timehist(&sched);
        } else {
                usage_with_options(sched_usage, sched_options);
        }
-- 
1.7.10.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to