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