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