|
@@ -2448,6 +2448,9 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
|
|
last_tr->dt_wait = 0;
|
|
|
last_tr->dt_delay = 0;
|
|
|
|
|
|
+ if (itr->cursor.nr)
|
|
|
+ callchain_append(&itr->callchain, &itr->cursor, t - tprev);
|
|
|
+
|
|
|
itr->last_thread = NULL;
|
|
|
}
|
|
|
}
|
|
@@ -2557,6 +2560,60 @@ static int show_deadthread_runtime(struct thread *t, void *priv)
|
|
|
return __show_thread_runtime(t, priv);
|
|
|
}
|
|
|
|
|
|
+static size_t callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
|
|
|
+{
|
|
|
+ const char *sep = " <- ";
|
|
|
+ struct callchain_list *chain;
|
|
|
+ size_t ret = 0;
|
|
|
+ char bf[1024];
|
|
|
+ bool first;
|
|
|
+
|
|
|
+ if (node == NULL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = callchain__fprintf_folded(fp, node->parent);
|
|
|
+ first = (ret == 0);
|
|
|
+
|
|
|
+ list_for_each_entry(chain, &node->val, list) {
|
|
|
+ if (chain->ip >= PERF_CONTEXT_MAX)
|
|
|
+ continue;
|
|
|
+ if (chain->ms.sym && chain->ms.sym->ignore)
|
|
|
+ continue;
|
|
|
+ ret += fprintf(fp, "%s%s", first ? "" : sep,
|
|
|
+ callchain_list__sym_name(chain, bf, sizeof(bf),
|
|
|
+ false));
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t timehist_print_idlehist_callchain(struct rb_root *root)
|
|
|
+{
|
|
|
+ size_t ret = 0;
|
|
|
+ FILE *fp = stdout;
|
|
|
+ struct callchain_node *chain;
|
|
|
+ struct rb_node *rb_node = rb_first(root);
|
|
|
+
|
|
|
+ printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains");
|
|
|
+ printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line,
|
|
|
+ graph_dotted_line);
|
|
|
+
|
|
|
+ while (rb_node) {
|
|
|
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
|
|
|
+ rb_node = rb_next(rb_node);
|
|
|
+
|
|
|
+ ret += fprintf(fp, " ");
|
|
|
+ print_sched_time(chain->hit, 12);
|
|
|
+ ret += 16; /* print_sched_time returns 2nd arg + 4 */
|
|
|
+ ret += fprintf(fp, " %8d ", chain->count);
|
|
|
+ ret += callchain__fprintf_folded(fp, chain);
|
|
|
+ ret += fprintf(fp, "\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void timehist_print_summary(struct perf_sched *sched,
|
|
|
struct perf_session *session)
|
|
|
{
|
|
@@ -2615,6 +2672,35 @@ static void timehist_print_summary(struct perf_sched *sched,
|
|
|
printf(" CPU %2d idle entire time window\n", i);
|
|
|
}
|
|
|
|
|
|
+ if (sched->idle_hist && symbol_conf.use_callchain) {
|
|
|
+ callchain_param.mode = CHAIN_FOLDED;
|
|
|
+ callchain_param.value = CCVAL_PERIOD;
|
|
|
+
|
|
|
+ callchain_register_param(&callchain_param);
|
|
|
+
|
|
|
+ printf("\nIdle stats by callchain:\n");
|
|
|
+ for (i = 0; i < idle_max_cpu; ++i) {
|
|
|
+ struct idle_thread_runtime *itr;
|
|
|
+
|
|
|
+ t = idle_threads[i];
|
|
|
+ if (!t)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ itr = thread__priv(t);
|
|
|
+ if (itr == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ callchain_param.sort(&itr->sorted_root, &itr->callchain,
|
|
|
+ 0, &callchain_param);
|
|
|
+
|
|
|
+ printf(" CPU %2d:", i);
|
|
|
+ print_sched_time(itr->tr.total_run_time, 6);
|
|
|
+ printf(" msec\n");
|
|
|
+ timehist_print_idlehist_callchain(&itr->sorted_root);
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
printf("\n"
|
|
|
" Total number of unique tasks: %" PRIu64 "\n"
|
|
|
"Total number of context switches: %" PRIu64 "\n"
|