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