1 /* 2 * builtin-timechart.c - make an svg timechart of system activity 3 * 4 * (C) Copyright 2009 Intel Corporation 5 * 6 * Authors: 7 * Arjan van de Ven <arjan@linux.intel.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; version 2 12 * of the License. 13 */ 14 15 #include "builtin.h" 16 17 #include "util/util.h" 18 19 #include "util/color.h" 20 #include <linux/list.h> 21 #include "util/cache.h" 22 #include "util/evsel.h" 23 #include <linux/rbtree.h> 24 #include "util/symbol.h" 25 #include "util/callchain.h" 26 #include "util/strlist.h" 27 28 #include "perf.h" 29 #include "util/header.h" 30 #include "util/parse-options.h" 31 #include "util/parse-events.h" 32 #include "util/event.h" 33 #include "util/session.h" 34 #include "util/svghelper.h" 35 #include "util/tool.h" 36 37 #define SUPPORT_OLD_POWER_EVENTS 1 38 #define PWR_EVENT_EXIT -1 39 40 41 static const char *input_name; 42 static const char *output_name = "output.svg"; 43 44 static unsigned int numcpus; 45 static u64 min_freq; /* Lowest CPU frequency seen */ 46 static u64 max_freq; /* Highest CPU frequency seen */ 47 static u64 turbo_frequency; 48 49 static u64 first_time, last_time; 50 51 static bool power_only; 52 53 54 struct per_pid; 55 struct per_pidcomm; 56 57 struct cpu_sample; 58 struct power_event; 59 struct wake_event; 60 61 struct sample_wrapper; 62 63 /* 64 * Datastructure layout: 65 * We keep an list of "pid"s, matching the kernels notion of a task struct. 66 * Each "pid" entry, has a list of "comm"s. 67 * this is because we want to track different programs different, while 68 * exec will reuse the original pid (by design). 69 * Each comm has a list of samples that will be used to draw 70 * final graph. 71 */ 72 73 struct per_pid { 74 struct per_pid *next; 75 76 int pid; 77 int ppid; 78 79 u64 start_time; 80 u64 end_time; 81 u64 total_time; 82 int display; 83 84 struct per_pidcomm *all; 85 struct per_pidcomm *current; 86 }; 87 88 89 struct per_pidcomm { 90 struct per_pidcomm *next; 91 92 u64 start_time; 93 u64 end_time; 94 u64 total_time; 95 96 int Y; 97 int display; 98 99 long state; 100 u64 state_since; 101 102 char *comm; 103 104 struct cpu_sample *samples; 105 }; 106 107 struct sample_wrapper { 108 struct sample_wrapper *next; 109 110 u64 timestamp; 111 unsigned char data[0]; 112 }; 113 114 #define TYPE_NONE 0 115 #define TYPE_RUNNING 1 116 #define TYPE_WAITING 2 117 #define TYPE_BLOCKED 3 118 119 struct cpu_sample { 120 struct cpu_sample *next; 121 122 u64 start_time; 123 u64 end_time; 124 int type; 125 int cpu; 126 }; 127 128 static struct per_pid *all_data; 129 130 #define CSTATE 1 131 #define PSTATE 2 132 133 struct power_event { 134 struct power_event *next; 135 int type; 136 int state; 137 u64 start_time; 138 u64 end_time; 139 int cpu; 140 }; 141 142 struct wake_event { 143 struct wake_event *next; 144 int waker; 145 int wakee; 146 u64 time; 147 }; 148 149 static struct power_event *power_events; 150 static struct wake_event *wake_events; 151 152 struct process_filter; 153 struct process_filter { 154 char *name; 155 int pid; 156 struct process_filter *next; 157 }; 158 159 static struct process_filter *process_filter; 160 161 162 static struct per_pid *find_create_pid(int pid) 163 { 164 struct per_pid *cursor = all_data; 165 166 while (cursor) { 167 if (cursor->pid == pid) 168 return cursor; 169 cursor = cursor->next; 170 } 171 cursor = zalloc(sizeof(*cursor)); 172 assert(cursor != NULL); 173 cursor->pid = pid; 174 cursor->next = all_data; 175 all_data = cursor; 176 return cursor; 177 } 178 179 static void pid_set_comm(int pid, char *comm) 180 { 181 struct per_pid *p; 182 struct per_pidcomm *c; 183 p = find_create_pid(pid); 184 c = p->all; 185 while (c) { 186 if (c->comm && strcmp(c->comm, comm) == 0) { 187 p->current = c; 188 return; 189 } 190 if (!c->comm) { 191 c->comm = strdup(comm); 192 p->current = c; 193 return; 194 } 195 c = c->next; 196 } 197 c = zalloc(sizeof(*c)); 198 assert(c != NULL); 199 c->comm = strdup(comm); 200 p->current = c; 201 c->next = p->all; 202 p->all = c; 203 } 204 205 static void pid_fork(int pid, int ppid, u64 timestamp) 206 { 207 struct per_pid *p, *pp; 208 p = find_create_pid(pid); 209 pp = find_create_pid(ppid); 210 p->ppid = ppid; 211 if (pp->current && pp->current->comm && !p->current) 212 pid_set_comm(pid, pp->current->comm); 213 214 p->start_time = timestamp; 215 if (p->current) { 216 p->current->start_time = timestamp; 217 p->current->state_since = timestamp; 218 } 219 } 220 221 static void pid_exit(int pid, u64 timestamp) 222 { 223 struct per_pid *p; 224 p = find_create_pid(pid); 225 p->end_time = timestamp; 226 if (p->current) 227 p->current->end_time = timestamp; 228 } 229 230 static void 231 pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) 232 { 233 struct per_pid *p; 234 struct per_pidcomm *c; 235 struct cpu_sample *sample; 236 237 p = find_create_pid(pid); 238 c = p->current; 239 if (!c) { 240 c = zalloc(sizeof(*c)); 241 assert(c != NULL); 242 p->current = c; 243 c->next = p->all; 244 p->all = c; 245 } 246 247 sample = zalloc(sizeof(*sample)); 248 assert(sample != NULL); 249 sample->start_time = start; 250 sample->end_time = end; 251 sample->type = type; 252 sample->next = c->samples; 253 sample->cpu = cpu; 254 c->samples = sample; 255 256 if (sample->type == TYPE_RUNNING && end > start && start > 0) { 257 c->total_time += (end-start); 258 p->total_time += (end-start); 259 } 260 261 if (c->start_time == 0 || c->start_time > start) 262 c->start_time = start; 263 if (p->start_time == 0 || p->start_time > start) 264 p->start_time = start; 265 } 266 267 #define MAX_CPUS 4096 268 269 static u64 cpus_cstate_start_times[MAX_CPUS]; 270 static int cpus_cstate_state[MAX_CPUS]; 271 static u64 cpus_pstate_start_times[MAX_CPUS]; 272 static u64 cpus_pstate_state[MAX_CPUS]; 273 274 static int process_comm_event(struct perf_tool *tool __maybe_unused, 275 union perf_event *event, 276 struct perf_sample *sample __maybe_unused, 277 struct machine *machine __maybe_unused) 278 { 279 pid_set_comm(event->comm.tid, event->comm.comm); 280 return 0; 281 } 282 283 static int process_fork_event(struct perf_tool *tool __maybe_unused, 284 union perf_event *event, 285 struct perf_sample *sample __maybe_unused, 286 struct machine *machine __maybe_unused) 287 { 288 pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); 289 return 0; 290 } 291 292 static int process_exit_event(struct perf_tool *tool __maybe_unused, 293 union perf_event *event, 294 struct perf_sample *sample __maybe_unused, 295 struct machine *machine __maybe_unused) 296 { 297 pid_exit(event->fork.pid, event->fork.time); 298 return 0; 299 } 300 301 struct trace_entry { 302 unsigned short type; 303 unsigned char flags; 304 unsigned char preempt_count; 305 int pid; 306 int lock_depth; 307 }; 308 309 #ifdef SUPPORT_OLD_POWER_EVENTS 310 static int use_old_power_events; 311 struct power_entry_old { 312 struct trace_entry te; 313 u64 type; 314 u64 value; 315 u64 cpu_id; 316 }; 317 #endif 318 319 struct power_processor_entry { 320 struct trace_entry te; 321 u32 state; 322 u32 cpu_id; 323 }; 324 325 #define TASK_COMM_LEN 16 326 struct wakeup_entry { 327 struct trace_entry te; 328 char comm[TASK_COMM_LEN]; 329 int pid; 330 int prio; 331 int success; 332 }; 333 334 /* 335 * trace_flag_type is an enumeration that holds different 336 * states when a trace occurs. These are: 337 * IRQS_OFF - interrupts were disabled 338 * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags 339 * NEED_RESCED - reschedule is requested 340 * HARDIRQ - inside an interrupt handler 341 * SOFTIRQ - inside a softirq handler 342 */ 343 enum trace_flag_type { 344 TRACE_FLAG_IRQS_OFF = 0x01, 345 TRACE_FLAG_IRQS_NOSUPPORT = 0x02, 346 TRACE_FLAG_NEED_RESCHED = 0x04, 347 TRACE_FLAG_HARDIRQ = 0x08, 348 TRACE_FLAG_SOFTIRQ = 0x10, 349 }; 350 351 352 353 struct sched_switch { 354 struct trace_entry te; 355 char prev_comm[TASK_COMM_LEN]; 356 int prev_pid; 357 int prev_prio; 358 long prev_state; /* Arjan weeps. */ 359 char next_comm[TASK_COMM_LEN]; 360 int next_pid; 361 int next_prio; 362 }; 363 364 static void c_state_start(int cpu, u64 timestamp, int state) 365 { 366 cpus_cstate_start_times[cpu] = timestamp; 367 cpus_cstate_state[cpu] = state; 368 } 369 370 static void c_state_end(int cpu, u64 timestamp) 371 { 372 struct power_event *pwr = zalloc(sizeof(*pwr)); 373 374 if (!pwr) 375 return; 376 377 pwr->state = cpus_cstate_state[cpu]; 378 pwr->start_time = cpus_cstate_start_times[cpu]; 379 pwr->end_time = timestamp; 380 pwr->cpu = cpu; 381 pwr->type = CSTATE; 382 pwr->next = power_events; 383 384 power_events = pwr; 385 } 386 387 static void p_state_change(int cpu, u64 timestamp, u64 new_freq) 388 { 389 struct power_event *pwr; 390 391 if (new_freq > 8000000) /* detect invalid data */ 392 return; 393 394 pwr = zalloc(sizeof(*pwr)); 395 if (!pwr) 396 return; 397 398 pwr->state = cpus_pstate_state[cpu]; 399 pwr->start_time = cpus_pstate_start_times[cpu]; 400 pwr->end_time = timestamp; 401 pwr->cpu = cpu; 402 pwr->type = PSTATE; 403 pwr->next = power_events; 404 405 if (!pwr->start_time) 406 pwr->start_time = first_time; 407 408 power_events = pwr; 409 410 cpus_pstate_state[cpu] = new_freq; 411 cpus_pstate_start_times[cpu] = timestamp; 412 413 if ((u64)new_freq > max_freq) 414 max_freq = new_freq; 415 416 if (new_freq < min_freq || min_freq == 0) 417 min_freq = new_freq; 418 419 if (new_freq == max_freq - 1000) 420 turbo_frequency = max_freq; 421 } 422 423 static void 424 sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te) 425 { 426 struct per_pid *p; 427 struct wakeup_entry *wake = (void *)te; 428 struct wake_event *we = zalloc(sizeof(*we)); 429 430 if (!we) 431 return; 432 433 we->time = timestamp; 434 we->waker = pid; 435 436 if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ)) 437 we->waker = -1; 438 439 we->wakee = wake->pid; 440 we->next = wake_events; 441 wake_events = we; 442 p = find_create_pid(we->wakee); 443 444 if (p && p->current && p->current->state == TYPE_NONE) { 445 p->current->state_since = timestamp; 446 p->current->state = TYPE_WAITING; 447 } 448 if (p && p->current && p->current->state == TYPE_BLOCKED) { 449 pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp); 450 p->current->state_since = timestamp; 451 p->current->state = TYPE_WAITING; 452 } 453 } 454 455 static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) 456 { 457 struct per_pid *p = NULL, *prev_p; 458 struct sched_switch *sw = (void *)te; 459 460 461 prev_p = find_create_pid(sw->prev_pid); 462 463 p = find_create_pid(sw->next_pid); 464 465 if (prev_p->current && prev_p->current->state != TYPE_NONE) 466 pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp); 467 if (p && p->current) { 468 if (p->current->state != TYPE_NONE) 469 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); 470 471 p->current->state_since = timestamp; 472 p->current->state = TYPE_RUNNING; 473 } 474 475 if (prev_p->current) { 476 prev_p->current->state = TYPE_NONE; 477 prev_p->current->state_since = timestamp; 478 if (sw->prev_state & 2) 479 prev_p->current->state = TYPE_BLOCKED; 480 if (sw->prev_state == 0) 481 prev_p->current->state = TYPE_WAITING; 482 } 483 } 484 485 486 static int process_sample_event(struct perf_tool *tool __maybe_unused, 487 union perf_event *event __maybe_unused, 488 struct perf_sample *sample, 489 struct perf_evsel *evsel, 490 struct machine *machine __maybe_unused) 491 { 492 struct trace_entry *te; 493 494 if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { 495 if (!first_time || first_time > sample->time) 496 first_time = sample->time; 497 if (last_time < sample->time) 498 last_time = sample->time; 499 } 500 501 te = (void *)sample->raw_data; 502 if ((evsel->attr.sample_type & PERF_SAMPLE_RAW) && sample->raw_size > 0) { 503 char *event_str; 504 #ifdef SUPPORT_OLD_POWER_EVENTS 505 struct power_entry_old *peo; 506 peo = (void *)te; 507 #endif 508 /* 509 * FIXME: use evsel, its already mapped from id to perf_evsel, 510 * remove perf_header__find_event infrastructure bits. 511 * Mapping all these "power:cpu_idle" strings to the tracepoint 512 * ID and then just comparing against evsel->attr.config. 513 * 514 * e.g.: 515 * 516 * if (evsel->attr.config == power_cpu_idle_id) 517 */ 518 event_str = perf_header__find_event(te->type); 519 520 if (!event_str) 521 return 0; 522 523 if (sample->cpu > numcpus) 524 numcpus = sample->cpu; 525 526 if (strcmp(event_str, "power:cpu_idle") == 0) { 527 struct power_processor_entry *ppe = (void *)te; 528 if (ppe->state == (u32)PWR_EVENT_EXIT) 529 c_state_end(ppe->cpu_id, sample->time); 530 else 531 c_state_start(ppe->cpu_id, sample->time, 532 ppe->state); 533 } 534 else if (strcmp(event_str, "power:cpu_frequency") == 0) { 535 struct power_processor_entry *ppe = (void *)te; 536 p_state_change(ppe->cpu_id, sample->time, ppe->state); 537 } 538 539 else if (strcmp(event_str, "sched:sched_wakeup") == 0) 540 sched_wakeup(sample->cpu, sample->time, sample->pid, te); 541 542 else if (strcmp(event_str, "sched:sched_switch") == 0) 543 sched_switch(sample->cpu, sample->time, te); 544 545 #ifdef SUPPORT_OLD_POWER_EVENTS 546 if (use_old_power_events) { 547 if (strcmp(event_str, "power:power_start") == 0) 548 c_state_start(peo->cpu_id, sample->time, 549 peo->value); 550 551 else if (strcmp(event_str, "power:power_end") == 0) 552 c_state_end(sample->cpu, sample->time); 553 554 else if (strcmp(event_str, 555 "power:power_frequency") == 0) 556 p_state_change(peo->cpu_id, sample->time, 557 peo->value); 558 } 559 #endif 560 } 561 return 0; 562 } 563 564 /* 565 * After the last sample we need to wrap up the current C/P state 566 * and close out each CPU for these. 567 */ 568 static void end_sample_processing(void) 569 { 570 u64 cpu; 571 struct power_event *pwr; 572 573 for (cpu = 0; cpu <= numcpus; cpu++) { 574 /* C state */ 575 #if 0 576 pwr = zalloc(sizeof(*pwr)); 577 if (!pwr) 578 return; 579 580 pwr->state = cpus_cstate_state[cpu]; 581 pwr->start_time = cpus_cstate_start_times[cpu]; 582 pwr->end_time = last_time; 583 pwr->cpu = cpu; 584 pwr->type = CSTATE; 585 pwr->next = power_events; 586 587 power_events = pwr; 588 #endif 589 /* P state */ 590 591 pwr = zalloc(sizeof(*pwr)); 592 if (!pwr) 593 return; 594 595 pwr->state = cpus_pstate_state[cpu]; 596 pwr->start_time = cpus_pstate_start_times[cpu]; 597 pwr->end_time = last_time; 598 pwr->cpu = cpu; 599 pwr->type = PSTATE; 600 pwr->next = power_events; 601 602 if (!pwr->start_time) 603 pwr->start_time = first_time; 604 if (!pwr->state) 605 pwr->state = min_freq; 606 power_events = pwr; 607 } 608 } 609 610 /* 611 * Sort the pid datastructure 612 */ 613 static void sort_pids(void) 614 { 615 struct per_pid *new_list, *p, *cursor, *prev; 616 /* sort by ppid first, then by pid, lowest to highest */ 617 618 new_list = NULL; 619 620 while (all_data) { 621 p = all_data; 622 all_data = p->next; 623 p->next = NULL; 624 625 if (new_list == NULL) { 626 new_list = p; 627 p->next = NULL; 628 continue; 629 } 630 prev = NULL; 631 cursor = new_list; 632 while (cursor) { 633 if (cursor->ppid > p->ppid || 634 (cursor->ppid == p->ppid && cursor->pid > p->pid)) { 635 /* must insert before */ 636 if (prev) { 637 p->next = prev->next; 638 prev->next = p; 639 cursor = NULL; 640 continue; 641 } else { 642 p->next = new_list; 643 new_list = p; 644 cursor = NULL; 645 continue; 646 } 647 } 648 649 prev = cursor; 650 cursor = cursor->next; 651 if (!cursor) 652 prev->next = p; 653 } 654 } 655 all_data = new_list; 656 } 657 658 659 static void draw_c_p_states(void) 660 { 661 struct power_event *pwr; 662 pwr = power_events; 663 664 /* 665 * two pass drawing so that the P state bars are on top of the C state blocks 666 */ 667 while (pwr) { 668 if (pwr->type == CSTATE) 669 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state); 670 pwr = pwr->next; 671 } 672 673 pwr = power_events; 674 while (pwr) { 675 if (pwr->type == PSTATE) { 676 if (!pwr->state) 677 pwr->state = min_freq; 678 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state); 679 } 680 pwr = pwr->next; 681 } 682 } 683 684 static void draw_wakeups(void) 685 { 686 struct wake_event *we; 687 struct per_pid *p; 688 struct per_pidcomm *c; 689 690 we = wake_events; 691 while (we) { 692 int from = 0, to = 0; 693 char *task_from = NULL, *task_to = NULL; 694 695 /* locate the column of the waker and wakee */ 696 p = all_data; 697 while (p) { 698 if (p->pid == we->waker || p->pid == we->wakee) { 699 c = p->all; 700 while (c) { 701 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { 702 if (p->pid == we->waker && !from) { 703 from = c->Y; 704 task_from = strdup(c->comm); 705 } 706 if (p->pid == we->wakee && !to) { 707 to = c->Y; 708 task_to = strdup(c->comm); 709 } 710 } 711 c = c->next; 712 } 713 c = p->all; 714 while (c) { 715 if (p->pid == we->waker && !from) { 716 from = c->Y; 717 task_from = strdup(c->comm); 718 } 719 if (p->pid == we->wakee && !to) { 720 to = c->Y; 721 task_to = strdup(c->comm); 722 } 723 c = c->next; 724 } 725 } 726 p = p->next; 727 } 728 729 if (!task_from) { 730 task_from = malloc(40); 731 sprintf(task_from, "[%i]", we->waker); 732 } 733 if (!task_to) { 734 task_to = malloc(40); 735 sprintf(task_to, "[%i]", we->wakee); 736 } 737 738 if (we->waker == -1) 739 svg_interrupt(we->time, to); 740 else if (from && to && abs(from - to) == 1) 741 svg_wakeline(we->time, from, to); 742 else 743 svg_partial_wakeline(we->time, from, task_from, to, task_to); 744 we = we->next; 745 746 free(task_from); 747 free(task_to); 748 } 749 } 750 751 static void draw_cpu_usage(void) 752 { 753 struct per_pid *p; 754 struct per_pidcomm *c; 755 struct cpu_sample *sample; 756 p = all_data; 757 while (p) { 758 c = p->all; 759 while (c) { 760 sample = c->samples; 761 while (sample) { 762 if (sample->type == TYPE_RUNNING) 763 svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm); 764 765 sample = sample->next; 766 } 767 c = c->next; 768 } 769 p = p->next; 770 } 771 } 772 773 static void draw_process_bars(void) 774 { 775 struct per_pid *p; 776 struct per_pidcomm *c; 777 struct cpu_sample *sample; 778 int Y = 0; 779 780 Y = 2 * numcpus + 2; 781 782 p = all_data; 783 while (p) { 784 c = p->all; 785 while (c) { 786 if (!c->display) { 787 c->Y = 0; 788 c = c->next; 789 continue; 790 } 791 792 svg_box(Y, c->start_time, c->end_time, "process"); 793 sample = c->samples; 794 while (sample) { 795 if (sample->type == TYPE_RUNNING) 796 svg_sample(Y, sample->cpu, sample->start_time, sample->end_time); 797 if (sample->type == TYPE_BLOCKED) 798 svg_box(Y, sample->start_time, sample->end_time, "blocked"); 799 if (sample->type == TYPE_WAITING) 800 svg_waiting(Y, sample->start_time, sample->end_time); 801 sample = sample->next; 802 } 803 804 if (c->comm) { 805 char comm[256]; 806 if (c->total_time > 5000000000) /* 5 seconds */ 807 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0); 808 else 809 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0); 810 811 svg_text(Y, c->start_time, comm); 812 } 813 c->Y = Y; 814 Y++; 815 c = c->next; 816 } 817 p = p->next; 818 } 819 } 820 821 static void add_process_filter(const char *string) 822 { 823 int pid = strtoull(string, NULL, 10); 824 struct process_filter *filt = malloc(sizeof(*filt)); 825 826 if (!filt) 827 return; 828 829 filt->name = strdup(string); 830 filt->pid = pid; 831 filt->next = process_filter; 832 833 process_filter = filt; 834 } 835 836 static int passes_filter(struct per_pid *p, struct per_pidcomm *c) 837 { 838 struct process_filter *filt; 839 if (!process_filter) 840 return 1; 841 842 filt = process_filter; 843 while (filt) { 844 if (filt->pid && p->pid == filt->pid) 845 return 1; 846 if (strcmp(filt->name, c->comm) == 0) 847 return 1; 848 filt = filt->next; 849 } 850 return 0; 851 } 852 853 static int determine_display_tasks_filtered(void) 854 { 855 struct per_pid *p; 856 struct per_pidcomm *c; 857 int count = 0; 858 859 p = all_data; 860 while (p) { 861 p->display = 0; 862 if (p->start_time == 1) 863 p->start_time = first_time; 864 865 /* no exit marker, task kept running to the end */ 866 if (p->end_time == 0) 867 p->end_time = last_time; 868 869 c = p->all; 870 871 while (c) { 872 c->display = 0; 873 874 if (c->start_time == 1) 875 c->start_time = first_time; 876 877 if (passes_filter(p, c)) { 878 c->display = 1; 879 p->display = 1; 880 count++; 881 } 882 883 if (c->end_time == 0) 884 c->end_time = last_time; 885 886 c = c->next; 887 } 888 p = p->next; 889 } 890 return count; 891 } 892 893 static int determine_display_tasks(u64 threshold) 894 { 895 struct per_pid *p; 896 struct per_pidcomm *c; 897 int count = 0; 898 899 if (process_filter) 900 return determine_display_tasks_filtered(); 901 902 p = all_data; 903 while (p) { 904 p->display = 0; 905 if (p->start_time == 1) 906 p->start_time = first_time; 907 908 /* no exit marker, task kept running to the end */ 909 if (p->end_time == 0) 910 p->end_time = last_time; 911 if (p->total_time >= threshold && !power_only) 912 p->display = 1; 913 914 c = p->all; 915 916 while (c) { 917 c->display = 0; 918 919 if (c->start_time == 1) 920 c->start_time = first_time; 921 922 if (c->total_time >= threshold && !power_only) { 923 c->display = 1; 924 count++; 925 } 926 927 if (c->end_time == 0) 928 c->end_time = last_time; 929 930 c = c->next; 931 } 932 p = p->next; 933 } 934 return count; 935 } 936 937 938 939 #define TIME_THRESH 10000000 940 941 static void write_svg_file(const char *filename) 942 { 943 u64 i; 944 int count; 945 946 numcpus++; 947 948 949 count = determine_display_tasks(TIME_THRESH); 950 951 /* We'd like to show at least 15 tasks; be less picky if we have fewer */ 952 if (count < 15) 953 count = determine_display_tasks(TIME_THRESH / 10); 954 955 open_svg(filename, numcpus, count, first_time, last_time); 956 957 svg_time_grid(); 958 svg_legenda(); 959 960 for (i = 0; i < numcpus; i++) 961 svg_cpu_box(i, max_freq, turbo_frequency); 962 963 draw_cpu_usage(); 964 draw_process_bars(); 965 draw_c_p_states(); 966 draw_wakeups(); 967 968 svg_close(); 969 } 970 971 static struct perf_tool perf_timechart = { 972 .comm = process_comm_event, 973 .fork = process_fork_event, 974 .exit = process_exit_event, 975 .sample = process_sample_event, 976 .ordered_samples = true, 977 }; 978 979 static int __cmd_timechart(void) 980 { 981 struct perf_session *session = perf_session__new(input_name, O_RDONLY, 982 0, false, &perf_timechart); 983 int ret = -EINVAL; 984 985 if (session == NULL) 986 return -ENOMEM; 987 988 if (!perf_session__has_traces(session, "timechart record")) 989 goto out_delete; 990 991 ret = perf_session__process_events(session, &perf_timechart); 992 if (ret) 993 goto out_delete; 994 995 end_sample_processing(); 996 997 sort_pids(); 998 999 write_svg_file(output_name); 1000 1001 pr_info("Written %2.1f seconds of trace to %s.\n", 1002 (last_time - first_time) / 1000000000.0, output_name); 1003 out_delete: 1004 perf_session__delete(session); 1005 return ret; 1006 } 1007 1008 static const char * const timechart_usage[] = { 1009 "perf timechart [<options>] {record}", 1010 NULL 1011 }; 1012 1013 #ifdef SUPPORT_OLD_POWER_EVENTS 1014 static const char * const record_old_args[] = { 1015 "record", 1016 "-a", 1017 "-R", 1018 "-f", 1019 "-c", "1", 1020 "-e", "power:power_start", 1021 "-e", "power:power_end", 1022 "-e", "power:power_frequency", 1023 "-e", "sched:sched_wakeup", 1024 "-e", "sched:sched_switch", 1025 }; 1026 #endif 1027 1028 static const char * const record_new_args[] = { 1029 "record", 1030 "-a", 1031 "-R", 1032 "-f", 1033 "-c", "1", 1034 "-e", "power:cpu_frequency", 1035 "-e", "power:cpu_idle", 1036 "-e", "sched:sched_wakeup", 1037 "-e", "sched:sched_switch", 1038 }; 1039 1040 static int __cmd_record(int argc, const char **argv) 1041 { 1042 unsigned int rec_argc, i, j; 1043 const char **rec_argv; 1044 const char * const *record_args = record_new_args; 1045 unsigned int record_elems = ARRAY_SIZE(record_new_args); 1046 1047 #ifdef SUPPORT_OLD_POWER_EVENTS 1048 if (!is_valid_tracepoint("power:cpu_idle") && 1049 is_valid_tracepoint("power:power_start")) { 1050 use_old_power_events = 1; 1051 record_args = record_old_args; 1052 record_elems = ARRAY_SIZE(record_old_args); 1053 } 1054 #endif 1055 1056 rec_argc = record_elems + argc - 1; 1057 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 1058 1059 if (rec_argv == NULL) 1060 return -ENOMEM; 1061 1062 for (i = 0; i < record_elems; i++) 1063 rec_argv[i] = strdup(record_args[i]); 1064 1065 for (j = 1; j < (unsigned int)argc; j++, i++) 1066 rec_argv[i] = argv[j]; 1067 1068 return cmd_record(i, rec_argv, NULL); 1069 } 1070 1071 static int 1072 parse_process(const struct option *opt __maybe_unused, const char *arg, 1073 int __maybe_unused unset) 1074 { 1075 if (arg) 1076 add_process_filter(arg); 1077 return 0; 1078 } 1079 1080 static const struct option options[] = { 1081 OPT_STRING('i', "input", &input_name, "file", 1082 "input file name"), 1083 OPT_STRING('o', "output", &output_name, "file", 1084 "output file name"), 1085 OPT_INTEGER('w', "width", &svg_page_width, 1086 "page width"), 1087 OPT_BOOLEAN('P', "power-only", &power_only, 1088 "output power data only"), 1089 OPT_CALLBACK('p', "process", NULL, "process", 1090 "process selector. Pass a pid or process name.", 1091 parse_process), 1092 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", 1093 "Look for files with symbols relative to this directory"), 1094 OPT_END() 1095 }; 1096 1097 1098 int cmd_timechart(int argc, const char **argv, 1099 const char *prefix __maybe_unused) 1100 { 1101 argc = parse_options(argc, argv, options, timechart_usage, 1102 PARSE_OPT_STOP_AT_NON_OPTION); 1103 1104 symbol__init(); 1105 1106 if (argc && !strncmp(argv[0], "rec", 3)) 1107 return __cmd_record(argc, argv); 1108 else if (argc) 1109 usage_with_options(timechart_usage, options); 1110 1111 setup_pager(); 1112 1113 return __cmd_timechart(); 1114 } 1115