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