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