20 #include <linux/list.h>
23 #include <linux/rbtree.h>
37 #define SUPPORT_OLD_POWER_EVENTS 1
38 #define PWR_EVENT_EXIT -1
41 static unsigned int numcpus;
44 static u64 turbo_frequency;
48 static bool power_only;
112 #define TYPE_RUNNING 1
113 #define TYPE_WAITING 2
114 #define TYPE_BLOCKED 3
125 static struct per_pid *all_data;
159 static struct per_pid *find_create_pid(
int pid)
161 struct per_pid *cursor = all_data;
164 if (cursor->
pid == pid)
166 cursor = cursor->
next;
168 cursor =
zalloc(
sizeof(*cursor));
171 cursor->
next = all_data;
176 static void pid_set_comm(
int pid,
char *comm)
180 p = find_create_pid(pid);
188 c->
comm = strdup(comm);
196 c->
comm = strdup(comm);
205 p = find_create_pid(pid);
206 pp = find_create_pid(ppid);
209 pid_set_comm(pid, pp->
current->comm);
218 static void pid_exit(
int pid,
u64 timestamp)
221 p = find_create_pid(pid);
234 p = find_create_pid(pid);
244 sample =
zalloc(
sizeof(*sample));
264 #define MAX_CPUS 4096
267 static int cpus_cstate_state[
MAX_CPUS];
276 pid_set_comm(event->
comm.tid, event->
comm.comm);
285 pid_fork(event->
fork.pid, event->
fork.ppid, event->
fork.time);
294 pid_exit(event->
fork.pid, event->
fork.time);
306 #ifdef SUPPORT_OLD_POWER_EVENTS
307 static int use_old_power_events;
322 #define TASK_COMM_LEN 16
361 static void c_state_start(
int cpu,
u64 timestamp,
int state)
367 static void c_state_end(
int cpu,
u64 timestamp)
374 pwr->
state = cpus_cstate_state[
cpu];
379 pwr->
next = power_events;
384 static void p_state_change(
int cpu,
u64 timestamp,
u64 new_freq)
388 if (new_freq > 8000000)
391 pwr =
zalloc(
sizeof(*pwr));
395 pwr->
state = cpus_pstate_state[
cpu];
400 pwr->
next = power_events;
407 cpus_pstate_state[
cpu] = new_freq;
410 if ((
u64)new_freq > max_freq)
413 if (new_freq < min_freq || min_freq == 0)
416 if (new_freq == max_freq - 1000)
417 turbo_frequency = max_freq;
421 sched_wakeup(
int cpu,
u64 timestamp,
int pid,
struct trace_entry *te)
437 we->
next = wake_events;
439 p = find_create_pid(we->
wakee);
446 pid_put_sample(p->
pid, p->
current->state, cpu, p->
current->state_since, timestamp);
458 prev_p = find_create_pid(sw->
prev_pid);
462 if (prev_p->current && prev_p->current->state !=
TYPE_NONE)
472 if (prev_p->current) {
474 prev_p->current->state_since =
timestamp;
492 if (!first_time || first_time > sample->
time)
493 first_time = sample->
time;
494 if (last_time < sample->
time)
501 #ifdef SUPPORT_OLD_POWER_EVENTS
520 if (sample->
cpu > numcpus)
521 numcpus = sample->
cpu;
523 if (
strcmp(event_str,
"power:cpu_idle") == 0) {
531 else if (
strcmp(event_str,
"power:cpu_frequency") == 0) {
536 else if (
strcmp(event_str,
"sched:sched_wakeup") == 0)
537 sched_wakeup(sample->
cpu, sample->
time, sample->
pid, te);
539 else if (
strcmp(event_str,
"sched:sched_switch") == 0)
542 #ifdef SUPPORT_OLD_POWER_EVENTS
543 if (use_old_power_events) {
544 if (
strcmp(event_str,
"power:power_start") == 0)
548 else if (
strcmp(event_str,
"power:power_end") == 0)
549 c_state_end(sample->
cpu, sample->
time);
551 else if (
strcmp(event_str,
552 "power:power_frequency") == 0)
565 static void end_sample_processing(
void)
570 for (cpu = 0; cpu <= numcpus; cpu++) {
573 pwr =
zalloc(
sizeof(*pwr));
577 pwr->
state = cpus_cstate_state[
cpu];
582 pwr->
next = power_events;
588 pwr =
zalloc(
sizeof(*pwr));
592 pwr->
state = cpus_pstate_state[
cpu];
597 pwr->
next = power_events;
602 pwr->
state = min_freq;
610 static void sort_pids(
void)
622 if (new_list ==
NULL) {
647 cursor = cursor->
next;
656 static void draw_c_p_states(
void)
674 pwr->
state = min_freq;
681 static void draw_wakeups(
void)
689 int from = 0, to = 0;
690 char *task_from =
NULL, *task_to =
NULL;
701 task_from = strdup(c->
comm);
705 task_to = strdup(c->
comm);
714 task_from = strdup(c->
comm);
718 task_to = strdup(c->
comm);
737 else if (from && to &&
abs(from - to) == 1)
748 static void draw_cpu_usage(
void)
762 sample = sample->
next;
770 static void draw_process_bars(
void)
798 sample = sample->
next;
818 static void add_process_filter(
const char *
string)
821 struct process_filter *
filt =
malloc(
sizeof(*filt));
826 filt->
name = strdup(
string);
828 filt->
next = process_filter;
830 process_filter =
filt;
835 struct process_filter *
filt;
839 filt = process_filter;
850 static int determine_display_tasks_filtered(
void)
874 if (passes_filter(p, c)) {
897 return determine_display_tasks_filtered();
908 if (p->
total_time >= threshold && !power_only)
919 if (c->
total_time >= threshold && !power_only) {
936 #define TIME_THRESH 10000000
938 static void write_svg_file(
const char *
filename)
957 for (i = 0; i < numcpus; i++)
968 static int __cmd_timechart(
const char *input_name,
const char *output_name)
971 .
comm = process_comm_event,
972 .fork = process_fork_event,
973 .exit = process_exit_event,
974 .sample = process_sample_event,
975 .ordered_samples =
true,
978 0,
false, &perf_timechart);
991 end_sample_processing();
995 write_svg_file(output_name);
997 pr_info(
"Written %2.1f seconds of trace to %s.\n",
998 (
last_time - first_time) / 1000000000.0, output_name);
1004 static int __cmd_record(
int argc,
const char **argv)
1006 #ifdef SUPPORT_OLD_POWER_EVENTS
1007 const char *
const record_old_args[] = {
1008 "record",
"-a",
"-R",
"-f",
"-c",
"1",
1009 "-e",
"power:power_start",
1010 "-e",
"power:power_end",
1011 "-e",
"power:power_frequency",
1012 "-e",
"sched:sched_wakeup",
1013 "-e",
"sched:sched_switch",
1016 const char *
const record_new_args[] = {
1017 "record",
"-a",
"-R",
"-f",
"-c",
"1",
1018 "-e",
"power:cpu_frequency",
1019 "-e",
"power:cpu_idle",
1020 "-e",
"sched:sched_wakeup",
1021 "-e",
"sched:sched_switch",
1023 unsigned int rec_argc,
i,
j;
1024 const char **rec_argv;
1025 const char *
const *record_args = record_new_args;
1026 unsigned int record_elems =
ARRAY_SIZE(record_new_args);
1028 #ifdef SUPPORT_OLD_POWER_EVENTS
1031 use_old_power_events = 1;
1032 record_args = record_old_args;
1037 rec_argc = record_elems + argc - 1;
1038 rec_argv = calloc(rec_argc + 1,
sizeof(
char *));
1040 if (rec_argv ==
NULL)
1043 for (i = 0; i < record_elems; i++)
1044 rec_argv[i] = strdup(record_args[i]);
1046 for (j = 1; j < (
unsigned int)argc; j++, i++)
1047 rec_argv[i] = argv[j];
1053 parse_process(
const struct option *
opt __maybe_unused,
const char *
arg,
1054 int __maybe_unused unset)
1057 add_process_filter(arg);
1062 const char *
prefix __maybe_unused)
1064 const char *input_name;
1065 const char *output_name =
"output.svg";
1066 const struct option options[] = {
1067 OPT_STRING(
'i',
"input", &input_name,
"file",
"input file name"),
1068 OPT_STRING(
'o',
"output", &output_name,
"file",
"output file name"),
1070 OPT_BOOLEAN(
'P',
"power-only", &power_only,
"output power data only"),
1072 "process selector. Pass a pid or process name.",
1075 "Look for files with symbols relative to this directory"),
1078 const char *
const timechart_usage[] = {
1079 "perf timechart [<options>] {record}",
1088 if (argc && !
strncmp(argv[0],
"rec", 3))
1089 return __cmd_record(argc, argv);
1095 return __cmd_timechart(input_name, output_name);