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 <traceevent/event-parse.h> 16 17 #include "builtin.h" 18 19 #include "util/util.h" 20 21 #include "util/color.h" 22 #include <linux/list.h> 23 #include "util/cache.h" 24 #include "util/evlist.h" 25 #include "util/evsel.h" 26 #include <linux/kernel.h> 27 #include <linux/rbtree.h> 28 #include <linux/time64.h> 29 #include "util/symbol.h" 30 #include "util/callchain.h" 31 #include "util/strlist.h" 32 33 #include "perf.h" 34 #include "util/header.h" 35 #include <subcmd/parse-options.h> 36 #include "util/parse-events.h" 37 #include "util/event.h" 38 #include "util/session.h" 39 #include "util/svghelper.h" 40 #include "util/tool.h" 41 #include "util/data.h" 42 #include "util/debug.h" 43 44 #define SUPPORT_OLD_POWER_EVENTS 1 45 #define PWR_EVENT_EXIT -1 46 47 struct per_pid; 48 struct power_event; 49 struct wake_event; 50 51 struct timechart { 52 struct perf_tool tool; 53 struct per_pid *all_data; 54 struct power_event *power_events; 55 struct wake_event *wake_events; 56 int proc_num; 57 unsigned int numcpus; 58 u64 min_freq, /* Lowest CPU frequency seen */ 59 max_freq, /* Highest CPU frequency seen */ 60 turbo_frequency, 61 first_time, last_time; 62 bool power_only, 63 tasks_only, 64 with_backtrace, 65 topology; 66 bool force; 67 /* IO related settings */ 68 bool io_only, 69 skip_eagain; 70 u64 io_events; 71 u64 min_time, 72 merge_dist; 73 }; 74 75 struct per_pidcomm; 76 struct cpu_sample; 77 struct io_sample; 78 79 /* 80 * Datastructure layout: 81 * We keep an list of "pid"s, matching the kernels notion of a task struct. 82 * Each "pid" entry, has a list of "comm"s. 83 * this is because we want to track different programs different, while 84 * exec will reuse the original pid (by design). 85 * Each comm has a list of samples that will be used to draw 86 * final graph. 87 */ 88 89 struct per_pid { 90 struct per_pid *next; 91 92 int pid; 93 int ppid; 94 95 u64 start_time; 96 u64 end_time; 97 u64 total_time; 98 u64 total_bytes; 99 int display; 100 101 struct per_pidcomm *all; 102 struct per_pidcomm *current; 103 }; 104 105 106 struct per_pidcomm { 107 struct per_pidcomm *next; 108 109 u64 start_time; 110 u64 end_time; 111 u64 total_time; 112 u64 max_bytes; 113 u64 total_bytes; 114 115 int Y; 116 int display; 117 118 long state; 119 u64 state_since; 120 121 char *comm; 122 123 struct cpu_sample *samples; 124 struct io_sample *io_samples; 125 }; 126 127 struct sample_wrapper { 128 struct sample_wrapper *next; 129 130 u64 timestamp; 131 unsigned char data[0]; 132 }; 133 134 #define TYPE_NONE 0 135 #define TYPE_RUNNING 1 136 #define TYPE_WAITING 2 137 #define TYPE_BLOCKED 3 138 139 struct cpu_sample { 140 struct cpu_sample *next; 141 142 u64 start_time; 143 u64 end_time; 144 int type; 145 int cpu; 146 const char *backtrace; 147 }; 148 149 enum { 150 IOTYPE_READ, 151 IOTYPE_WRITE, 152 IOTYPE_SYNC, 153 IOTYPE_TX, 154 IOTYPE_RX, 155 IOTYPE_POLL, 156 }; 157 158 struct io_sample { 159 struct io_sample *next; 160 161 u64 start_time; 162 u64 end_time; 163 u64 bytes; 164 int type; 165 int fd; 166 int err; 167 int merges; 168 }; 169 170 #define CSTATE 1 171 #define PSTATE 2 172 173 struct power_event { 174 struct power_event *next; 175 int type; 176 int state; 177 u64 start_time; 178 u64 end_time; 179 int cpu; 180 }; 181 182 struct wake_event { 183 struct wake_event *next; 184 int waker; 185 int wakee; 186 u64 time; 187 const char *backtrace; 188 }; 189 190 struct process_filter { 191 char *name; 192 int pid; 193 struct process_filter *next; 194 }; 195 196 static struct process_filter *process_filter; 197 198 199 static struct per_pid *find_create_pid(struct timechart *tchart, int pid) 200 { 201 struct per_pid *cursor = tchart->all_data; 202 203 while (cursor) { 204 if (cursor->pid == pid) 205 return cursor; 206 cursor = cursor->next; 207 } 208 cursor = zalloc(sizeof(*cursor)); 209 assert(cursor != NULL); 210 cursor->pid = pid; 211 cursor->next = tchart->all_data; 212 tchart->all_data = cursor; 213 return cursor; 214 } 215 216 static void pid_set_comm(struct timechart *tchart, int pid, char *comm) 217 { 218 struct per_pid *p; 219 struct per_pidcomm *c; 220 p = find_create_pid(tchart, pid); 221 c = p->all; 222 while (c) { 223 if (c->comm && strcmp(c->comm, comm) == 0) { 224 p->current = c; 225 return; 226 } 227 if (!c->comm) { 228 c->comm = strdup(comm); 229 p->current = c; 230 return; 231 } 232 c = c->next; 233 } 234 c = zalloc(sizeof(*c)); 235 assert(c != NULL); 236 c->comm = strdup(comm); 237 p->current = c; 238 c->next = p->all; 239 p->all = c; 240 } 241 242 static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp) 243 { 244 struct per_pid *p, *pp; 245 p = find_create_pid(tchart, pid); 246 pp = find_create_pid(tchart, ppid); 247 p->ppid = ppid; 248 if (pp->current && pp->current->comm && !p->current) 249 pid_set_comm(tchart, pid, pp->current->comm); 250 251 p->start_time = timestamp; 252 if (p->current && !p->current->start_time) { 253 p->current->start_time = timestamp; 254 p->current->state_since = timestamp; 255 } 256 } 257 258 static void pid_exit(struct timechart *tchart, int pid, u64 timestamp) 259 { 260 struct per_pid *p; 261 p = find_create_pid(tchart, pid); 262 p->end_time = timestamp; 263 if (p->current) 264 p->current->end_time = timestamp; 265 } 266 267 static void pid_put_sample(struct timechart *tchart, int pid, int type, 268 unsigned int cpu, u64 start, u64 end, 269 const char *backtrace) 270 { 271 struct per_pid *p; 272 struct per_pidcomm *c; 273 struct cpu_sample *sample; 274 275 p = find_create_pid(tchart, pid); 276 c = p->current; 277 if (!c) { 278 c = zalloc(sizeof(*c)); 279 assert(c != NULL); 280 p->current = c; 281 c->next = p->all; 282 p->all = c; 283 } 284 285 sample = zalloc(sizeof(*sample)); 286 assert(sample != NULL); 287 sample->start_time = start; 288 sample->end_time = end; 289 sample->type = type; 290 sample->next = c->samples; 291 sample->cpu = cpu; 292 sample->backtrace = backtrace; 293 c->samples = sample; 294 295 if (sample->type == TYPE_RUNNING && end > start && start > 0) { 296 c->total_time += (end-start); 297 p->total_time += (end-start); 298 } 299 300 if (c->start_time == 0 || c->start_time > start) 301 c->start_time = start; 302 if (p->start_time == 0 || p->start_time > start) 303 p->start_time = start; 304 } 305 306 #define MAX_CPUS 4096 307 308 static u64 cpus_cstate_start_times[MAX_CPUS]; 309 static int cpus_cstate_state[MAX_CPUS]; 310 static u64 cpus_pstate_start_times[MAX_CPUS]; 311 static u64 cpus_pstate_state[MAX_CPUS]; 312 313 static int process_comm_event(struct perf_tool *tool, 314 union perf_event *event, 315 struct perf_sample *sample __maybe_unused, 316 struct machine *machine __maybe_unused) 317 { 318 struct timechart *tchart = container_of(tool, struct timechart, tool); 319 pid_set_comm(tchart, event->comm.tid, event->comm.comm); 320 return 0; 321 } 322 323 static int process_fork_event(struct perf_tool *tool, 324 union perf_event *event, 325 struct perf_sample *sample __maybe_unused, 326 struct machine *machine __maybe_unused) 327 { 328 struct timechart *tchart = container_of(tool, struct timechart, tool); 329 pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time); 330 return 0; 331 } 332 333 static int process_exit_event(struct perf_tool *tool, 334 union perf_event *event, 335 struct perf_sample *sample __maybe_unused, 336 struct machine *machine __maybe_unused) 337 { 338 struct timechart *tchart = container_of(tool, struct timechart, tool); 339 pid_exit(tchart, event->fork.pid, event->fork.time); 340 return 0; 341 } 342 343 #ifdef SUPPORT_OLD_POWER_EVENTS 344 static int use_old_power_events; 345 #endif 346 347 static void c_state_start(int cpu, u64 timestamp, int state) 348 { 349 cpus_cstate_start_times[cpu] = timestamp; 350 cpus_cstate_state[cpu] = state; 351 } 352 353 static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp) 354 { 355 struct power_event *pwr = zalloc(sizeof(*pwr)); 356 357 if (!pwr) 358 return; 359 360 pwr->state = cpus_cstate_state[cpu]; 361 pwr->start_time = cpus_cstate_start_times[cpu]; 362 pwr->end_time = timestamp; 363 pwr->cpu = cpu; 364 pwr->type = CSTATE; 365 pwr->next = tchart->power_events; 366 367 tchart->power_events = pwr; 368 } 369 370 static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq) 371 { 372 struct power_event *pwr; 373 374 if (new_freq > 8000000) /* detect invalid data */ 375 return; 376 377 pwr = zalloc(sizeof(*pwr)); 378 if (!pwr) 379 return; 380 381 pwr->state = cpus_pstate_state[cpu]; 382 pwr->start_time = cpus_pstate_start_times[cpu]; 383 pwr->end_time = timestamp; 384 pwr->cpu = cpu; 385 pwr->type = PSTATE; 386 pwr->next = tchart->power_events; 387 388 if (!pwr->start_time) 389 pwr->start_time = tchart->first_time; 390 391 tchart->power_events = pwr; 392 393 cpus_pstate_state[cpu] = new_freq; 394 cpus_pstate_start_times[cpu] = timestamp; 395 396 if ((u64)new_freq > tchart->max_freq) 397 tchart->max_freq = new_freq; 398 399 if (new_freq < tchart->min_freq || tchart->min_freq == 0) 400 tchart->min_freq = new_freq; 401 402 if (new_freq == tchart->max_freq - 1000) 403 tchart->turbo_frequency = tchart->max_freq; 404 } 405 406 static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp, 407 int waker, int wakee, u8 flags, const char *backtrace) 408 { 409 struct per_pid *p; 410 struct wake_event *we = zalloc(sizeof(*we)); 411 412 if (!we) 413 return; 414 415 we->time = timestamp; 416 we->waker = waker; 417 we->backtrace = backtrace; 418 419 if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) 420 we->waker = -1; 421 422 we->wakee = wakee; 423 we->next = tchart->wake_events; 424 tchart->wake_events = we; 425 p = find_create_pid(tchart, we->wakee); 426 427 if (p && p->current && p->current->state == TYPE_NONE) { 428 p->current->state_since = timestamp; 429 p->current->state = TYPE_WAITING; 430 } 431 if (p && p->current && p->current->state == TYPE_BLOCKED) { 432 pid_put_sample(tchart, p->pid, p->current->state, cpu, 433 p->current->state_since, timestamp, NULL); 434 p->current->state_since = timestamp; 435 p->current->state = TYPE_WAITING; 436 } 437 } 438 439 static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, 440 int prev_pid, int next_pid, u64 prev_state, 441 const char *backtrace) 442 { 443 struct per_pid *p = NULL, *prev_p; 444 445 prev_p = find_create_pid(tchart, prev_pid); 446 447 p = find_create_pid(tchart, next_pid); 448 449 if (prev_p->current && prev_p->current->state != TYPE_NONE) 450 pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu, 451 prev_p->current->state_since, timestamp, 452 backtrace); 453 if (p && p->current) { 454 if (p->current->state != TYPE_NONE) 455 pid_put_sample(tchart, next_pid, p->current->state, cpu, 456 p->current->state_since, timestamp, 457 backtrace); 458 459 p->current->state_since = timestamp; 460 p->current->state = TYPE_RUNNING; 461 } 462 463 if (prev_p->current) { 464 prev_p->current->state = TYPE_NONE; 465 prev_p->current->state_since = timestamp; 466 if (prev_state & 2) 467 prev_p->current->state = TYPE_BLOCKED; 468 if (prev_state == 0) 469 prev_p->current->state = TYPE_WAITING; 470 } 471 } 472 473 static const char *cat_backtrace(union perf_event *event, 474 struct perf_sample *sample, 475 struct machine *machine) 476 { 477 struct addr_location al; 478 unsigned int i; 479 char *p = NULL; 480 size_t p_len; 481 u8 cpumode = PERF_RECORD_MISC_USER; 482 struct addr_location tal; 483 struct ip_callchain *chain = sample->callchain; 484 FILE *f = open_memstream(&p, &p_len); 485 486 if (!f) { 487 perror("open_memstream error"); 488 return NULL; 489 } 490 491 if (!chain) 492 goto exit; 493 494 if (machine__resolve(machine, &al, sample) < 0) { 495 fprintf(stderr, "problem processing %d event, skipping it.\n", 496 event->header.type); 497 goto exit; 498 } 499 500 for (i = 0; i < chain->nr; i++) { 501 u64 ip; 502 503 if (callchain_param.order == ORDER_CALLEE) 504 ip = chain->ips[i]; 505 else 506 ip = chain->ips[chain->nr - i - 1]; 507 508 if (ip >= PERF_CONTEXT_MAX) { 509 switch (ip) { 510 case PERF_CONTEXT_HV: 511 cpumode = PERF_RECORD_MISC_HYPERVISOR; 512 break; 513 case PERF_CONTEXT_KERNEL: 514 cpumode = PERF_RECORD_MISC_KERNEL; 515 break; 516 case PERF_CONTEXT_USER: 517 cpumode = PERF_RECORD_MISC_USER; 518 break; 519 default: 520 pr_debug("invalid callchain context: " 521 "%"PRId64"\n", (s64) ip); 522 523 /* 524 * It seems the callchain is corrupted. 525 * Discard all. 526 */ 527 zfree(&p); 528 goto exit_put; 529 } 530 continue; 531 } 532 533 tal.filtered = 0; 534 thread__find_addr_location(al.thread, cpumode, 535 MAP__FUNCTION, ip, &tal); 536 537 if (tal.sym) 538 fprintf(f, "..... %016" PRIx64 " %s\n", ip, 539 tal.sym->name); 540 else 541 fprintf(f, "..... %016" PRIx64 "\n", ip); 542 } 543 exit_put: 544 addr_location__put(&al); 545 exit: 546 fclose(f); 547 548 return p; 549 } 550 551 typedef int (*tracepoint_handler)(struct timechart *tchart, 552 struct perf_evsel *evsel, 553 struct perf_sample *sample, 554 const char *backtrace); 555 556 static int process_sample_event(struct perf_tool *tool, 557 union perf_event *event, 558 struct perf_sample *sample, 559 struct perf_evsel *evsel, 560 struct machine *machine) 561 { 562 struct timechart *tchart = container_of(tool, struct timechart, tool); 563 564 if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { 565 if (!tchart->first_time || tchart->first_time > sample->time) 566 tchart->first_time = sample->time; 567 if (tchart->last_time < sample->time) 568 tchart->last_time = sample->time; 569 } 570 571 if (evsel->handler != NULL) { 572 tracepoint_handler f = evsel->handler; 573 return f(tchart, evsel, sample, 574 cat_backtrace(event, sample, machine)); 575 } 576 577 return 0; 578 } 579 580 static int 581 process_sample_cpu_idle(struct timechart *tchart __maybe_unused, 582 struct perf_evsel *evsel, 583 struct perf_sample *sample, 584 const char *backtrace __maybe_unused) 585 { 586 u32 state = perf_evsel__intval(evsel, sample, "state"); 587 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); 588 589 if (state == (u32)PWR_EVENT_EXIT) 590 c_state_end(tchart, cpu_id, sample->time); 591 else 592 c_state_start(cpu_id, sample->time, state); 593 return 0; 594 } 595 596 static int 597 process_sample_cpu_frequency(struct timechart *tchart, 598 struct perf_evsel *evsel, 599 struct perf_sample *sample, 600 const char *backtrace __maybe_unused) 601 { 602 u32 state = perf_evsel__intval(evsel, sample, "state"); 603 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); 604 605 p_state_change(tchart, cpu_id, sample->time, state); 606 return 0; 607 } 608 609 static int 610 process_sample_sched_wakeup(struct timechart *tchart, 611 struct perf_evsel *evsel, 612 struct perf_sample *sample, 613 const char *backtrace) 614 { 615 u8 flags = perf_evsel__intval(evsel, sample, "common_flags"); 616 int waker = perf_evsel__intval(evsel, sample, "common_pid"); 617 int wakee = perf_evsel__intval(evsel, sample, "pid"); 618 619 sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace); 620 return 0; 621 } 622 623 static int 624 process_sample_sched_switch(struct timechart *tchart, 625 struct perf_evsel *evsel, 626 struct perf_sample *sample, 627 const char *backtrace) 628 { 629 int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"); 630 int next_pid = perf_evsel__intval(evsel, sample, "next_pid"); 631 u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); 632 633 sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid, 634 prev_state, backtrace); 635 return 0; 636 } 637 638 #ifdef SUPPORT_OLD_POWER_EVENTS 639 static int 640 process_sample_power_start(struct timechart *tchart __maybe_unused, 641 struct perf_evsel *evsel, 642 struct perf_sample *sample, 643 const char *backtrace __maybe_unused) 644 { 645 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); 646 u64 value = perf_evsel__intval(evsel, sample, "value"); 647 648 c_state_start(cpu_id, sample->time, value); 649 return 0; 650 } 651 652 static int 653 process_sample_power_end(struct timechart *tchart, 654 struct perf_evsel *evsel __maybe_unused, 655 struct perf_sample *sample, 656 const char *backtrace __maybe_unused) 657 { 658 c_state_end(tchart, sample->cpu, sample->time); 659 return 0; 660 } 661 662 static int 663 process_sample_power_frequency(struct timechart *tchart, 664 struct perf_evsel *evsel, 665 struct perf_sample *sample, 666 const char *backtrace __maybe_unused) 667 { 668 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id"); 669 u64 value = perf_evsel__intval(evsel, sample, "value"); 670 671 p_state_change(tchart, cpu_id, sample->time, value); 672 return 0; 673 } 674 #endif /* SUPPORT_OLD_POWER_EVENTS */ 675 676 /* 677 * After the last sample we need to wrap up the current C/P state 678 * and close out each CPU for these. 679 */ 680 static void end_sample_processing(struct timechart *tchart) 681 { 682 u64 cpu; 683 struct power_event *pwr; 684 685 for (cpu = 0; cpu <= tchart->numcpus; cpu++) { 686 /* C state */ 687 #if 0 688 pwr = zalloc(sizeof(*pwr)); 689 if (!pwr) 690 return; 691 692 pwr->state = cpus_cstate_state[cpu]; 693 pwr->start_time = cpus_cstate_start_times[cpu]; 694 pwr->end_time = tchart->last_time; 695 pwr->cpu = cpu; 696 pwr->type = CSTATE; 697 pwr->next = tchart->power_events; 698 699 tchart->power_events = pwr; 700 #endif 701 /* P state */ 702 703 pwr = zalloc(sizeof(*pwr)); 704 if (!pwr) 705 return; 706 707 pwr->state = cpus_pstate_state[cpu]; 708 pwr->start_time = cpus_pstate_start_times[cpu]; 709 pwr->end_time = tchart->last_time; 710 pwr->cpu = cpu; 711 pwr->type = PSTATE; 712 pwr->next = tchart->power_events; 713 714 if (!pwr->start_time) 715 pwr->start_time = tchart->first_time; 716 if (!pwr->state) 717 pwr->state = tchart->min_freq; 718 tchart->power_events = pwr; 719 } 720 } 721 722 static int pid_begin_io_sample(struct timechart *tchart, int pid, int type, 723 u64 start, int fd) 724 { 725 struct per_pid *p = find_create_pid(tchart, pid); 726 struct per_pidcomm *c = p->current; 727 struct io_sample *sample; 728 struct io_sample *prev; 729 730 if (!c) { 731 c = zalloc(sizeof(*c)); 732 if (!c) 733 return -ENOMEM; 734 p->current = c; 735 c->next = p->all; 736 p->all = c; 737 } 738 739 prev = c->io_samples; 740 741 if (prev && prev->start_time && !prev->end_time) { 742 pr_warning("Skip invalid start event: " 743 "previous event already started!\n"); 744 745 /* remove previous event that has been started, 746 * we are not sure we will ever get an end for it */ 747 c->io_samples = prev->next; 748 free(prev); 749 return 0; 750 } 751 752 sample = zalloc(sizeof(*sample)); 753 if (!sample) 754 return -ENOMEM; 755 sample->start_time = start; 756 sample->type = type; 757 sample->fd = fd; 758 sample->next = c->io_samples; 759 c->io_samples = sample; 760 761 if (c->start_time == 0 || c->start_time > start) 762 c->start_time = start; 763 764 return 0; 765 } 766 767 static int pid_end_io_sample(struct timechart *tchart, int pid, int type, 768 u64 end, long ret) 769 { 770 struct per_pid *p = find_create_pid(tchart, pid); 771 struct per_pidcomm *c = p->current; 772 struct io_sample *sample, *prev; 773 774 if (!c) { 775 pr_warning("Invalid pidcomm!\n"); 776 return -1; 777 } 778 779 sample = c->io_samples; 780 781 if (!sample) /* skip partially captured events */ 782 return 0; 783 784 if (sample->end_time) { 785 pr_warning("Skip invalid end event: " 786 "previous event already ended!\n"); 787 return 0; 788 } 789 790 if (sample->type != type) { 791 pr_warning("Skip invalid end event: invalid event type!\n"); 792 return 0; 793 } 794 795 sample->end_time = end; 796 prev = sample->next; 797 798 /* we want to be able to see small and fast transfers, so make them 799 * at least min_time long, but don't overlap them */ 800 if (sample->end_time - sample->start_time < tchart->min_time) 801 sample->end_time = sample->start_time + tchart->min_time; 802 if (prev && sample->start_time < prev->end_time) { 803 if (prev->err) /* try to make errors more visible */ 804 sample->start_time = prev->end_time; 805 else 806 prev->end_time = sample->start_time; 807 } 808 809 if (ret < 0) { 810 sample->err = ret; 811 } else if (type == IOTYPE_READ || type == IOTYPE_WRITE || 812 type == IOTYPE_TX || type == IOTYPE_RX) { 813 814 if ((u64)ret > c->max_bytes) 815 c->max_bytes = ret; 816 817 c->total_bytes += ret; 818 p->total_bytes += ret; 819 sample->bytes = ret; 820 } 821 822 /* merge two requests to make svg smaller and render-friendly */ 823 if (prev && 824 prev->type == sample->type && 825 prev->err == sample->err && 826 prev->fd == sample->fd && 827 prev->end_time + tchart->merge_dist >= sample->start_time) { 828 829 sample->bytes += prev->bytes; 830 sample->merges += prev->merges + 1; 831 832 sample->start_time = prev->start_time; 833 sample->next = prev->next; 834 free(prev); 835 836 if (!sample->err && sample->bytes > c->max_bytes) 837 c->max_bytes = sample->bytes; 838 } 839 840 tchart->io_events++; 841 842 return 0; 843 } 844 845 static int 846 process_enter_read(struct timechart *tchart, 847 struct perf_evsel *evsel, 848 struct perf_sample *sample) 849 { 850 long fd = perf_evsel__intval(evsel, sample, "fd"); 851 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, 852 sample->time, fd); 853 } 854 855 static int 856 process_exit_read(struct timechart *tchart, 857 struct perf_evsel *evsel, 858 struct perf_sample *sample) 859 { 860 long ret = perf_evsel__intval(evsel, sample, "ret"); 861 return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, 862 sample->time, ret); 863 } 864 865 static int 866 process_enter_write(struct timechart *tchart, 867 struct perf_evsel *evsel, 868 struct perf_sample *sample) 869 { 870 long fd = perf_evsel__intval(evsel, sample, "fd"); 871 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, 872 sample->time, fd); 873 } 874 875 static int 876 process_exit_write(struct timechart *tchart, 877 struct perf_evsel *evsel, 878 struct perf_sample *sample) 879 { 880 long ret = perf_evsel__intval(evsel, sample, "ret"); 881 return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, 882 sample->time, ret); 883 } 884 885 static int 886 process_enter_sync(struct timechart *tchart, 887 struct perf_evsel *evsel, 888 struct perf_sample *sample) 889 { 890 long fd = perf_evsel__intval(evsel, sample, "fd"); 891 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, 892 sample->time, fd); 893 } 894 895 static int 896 process_exit_sync(struct timechart *tchart, 897 struct perf_evsel *evsel, 898 struct perf_sample *sample) 899 { 900 long ret = perf_evsel__intval(evsel, sample, "ret"); 901 return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, 902 sample->time, ret); 903 } 904 905 static int 906 process_enter_tx(struct timechart *tchart, 907 struct perf_evsel *evsel, 908 struct perf_sample *sample) 909 { 910 long fd = perf_evsel__intval(evsel, sample, "fd"); 911 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, 912 sample->time, fd); 913 } 914 915 static int 916 process_exit_tx(struct timechart *tchart, 917 struct perf_evsel *evsel, 918 struct perf_sample *sample) 919 { 920 long ret = perf_evsel__intval(evsel, sample, "ret"); 921 return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, 922 sample->time, ret); 923 } 924 925 static int 926 process_enter_rx(struct timechart *tchart, 927 struct perf_evsel *evsel, 928 struct perf_sample *sample) 929 { 930 long fd = perf_evsel__intval(evsel, sample, "fd"); 931 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, 932 sample->time, fd); 933 } 934 935 static int 936 process_exit_rx(struct timechart *tchart, 937 struct perf_evsel *evsel, 938 struct perf_sample *sample) 939 { 940 long ret = perf_evsel__intval(evsel, sample, "ret"); 941 return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, 942 sample->time, ret); 943 } 944 945 static int 946 process_enter_poll(struct timechart *tchart, 947 struct perf_evsel *evsel, 948 struct perf_sample *sample) 949 { 950 long fd = perf_evsel__intval(evsel, sample, "fd"); 951 return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, 952 sample->time, fd); 953 } 954 955 static int 956 process_exit_poll(struct timechart *tchart, 957 struct perf_evsel *evsel, 958 struct perf_sample *sample) 959 { 960 long ret = perf_evsel__intval(evsel, sample, "ret"); 961 return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, 962 sample->time, ret); 963 } 964 965 /* 966 * Sort the pid datastructure 967 */ 968 static void sort_pids(struct timechart *tchart) 969 { 970 struct per_pid *new_list, *p, *cursor, *prev; 971 /* sort by ppid first, then by pid, lowest to highest */ 972 973 new_list = NULL; 974 975 while (tchart->all_data) { 976 p = tchart->all_data; 977 tchart->all_data = p->next; 978 p->next = NULL; 979 980 if (new_list == NULL) { 981 new_list = p; 982 p->next = NULL; 983 continue; 984 } 985 prev = NULL; 986 cursor = new_list; 987 while (cursor) { 988 if (cursor->ppid > p->ppid || 989 (cursor->ppid == p->ppid && cursor->pid > p->pid)) { 990 /* must insert before */ 991 if (prev) { 992 p->next = prev->next; 993 prev->next = p; 994 cursor = NULL; 995 continue; 996 } else { 997 p->next = new_list; 998 new_list = p; 999 cursor = NULL; 1000 continue; 1001 } 1002 } 1003 1004 prev = cursor; 1005 cursor = cursor->next; 1006 if (!cursor) 1007 prev->next = p; 1008 } 1009 } 1010 tchart->all_data = new_list; 1011 } 1012 1013 1014 static void draw_c_p_states(struct timechart *tchart) 1015 { 1016 struct power_event *pwr; 1017 pwr = tchart->power_events; 1018 1019 /* 1020 * two pass drawing so that the P state bars are on top of the C state blocks 1021 */ 1022 while (pwr) { 1023 if (pwr->type == CSTATE) 1024 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state); 1025 pwr = pwr->next; 1026 } 1027 1028 pwr = tchart->power_events; 1029 while (pwr) { 1030 if (pwr->type == PSTATE) { 1031 if (!pwr->state) 1032 pwr->state = tchart->min_freq; 1033 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state); 1034 } 1035 pwr = pwr->next; 1036 } 1037 } 1038 1039 static void draw_wakeups(struct timechart *tchart) 1040 { 1041 struct wake_event *we; 1042 struct per_pid *p; 1043 struct per_pidcomm *c; 1044 1045 we = tchart->wake_events; 1046 while (we) { 1047 int from = 0, to = 0; 1048 char *task_from = NULL, *task_to = NULL; 1049 1050 /* locate the column of the waker and wakee */ 1051 p = tchart->all_data; 1052 while (p) { 1053 if (p->pid == we->waker || p->pid == we->wakee) { 1054 c = p->all; 1055 while (c) { 1056 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { 1057 if (p->pid == we->waker && !from) { 1058 from = c->Y; 1059 task_from = strdup(c->comm); 1060 } 1061 if (p->pid == we->wakee && !to) { 1062 to = c->Y; 1063 task_to = strdup(c->comm); 1064 } 1065 } 1066 c = c->next; 1067 } 1068 c = p->all; 1069 while (c) { 1070 if (p->pid == we->waker && !from) { 1071 from = c->Y; 1072 task_from = strdup(c->comm); 1073 } 1074 if (p->pid == we->wakee && !to) { 1075 to = c->Y; 1076 task_to = strdup(c->comm); 1077 } 1078 c = c->next; 1079 } 1080 } 1081 p = p->next; 1082 } 1083 1084 if (!task_from) { 1085 task_from = malloc(40); 1086 sprintf(task_from, "[%i]", we->waker); 1087 } 1088 if (!task_to) { 1089 task_to = malloc(40); 1090 sprintf(task_to, "[%i]", we->wakee); 1091 } 1092 1093 if (we->waker == -1) 1094 svg_interrupt(we->time, to, we->backtrace); 1095 else if (from && to && abs(from - to) == 1) 1096 svg_wakeline(we->time, from, to, we->backtrace); 1097 else 1098 svg_partial_wakeline(we->time, from, task_from, to, 1099 task_to, we->backtrace); 1100 we = we->next; 1101 1102 free(task_from); 1103 free(task_to); 1104 } 1105 } 1106 1107 static void draw_cpu_usage(struct timechart *tchart) 1108 { 1109 struct per_pid *p; 1110 struct per_pidcomm *c; 1111 struct cpu_sample *sample; 1112 p = tchart->all_data; 1113 while (p) { 1114 c = p->all; 1115 while (c) { 1116 sample = c->samples; 1117 while (sample) { 1118 if (sample->type == TYPE_RUNNING) { 1119 svg_process(sample->cpu, 1120 sample->start_time, 1121 sample->end_time, 1122 p->pid, 1123 c->comm, 1124 sample->backtrace); 1125 } 1126 1127 sample = sample->next; 1128 } 1129 c = c->next; 1130 } 1131 p = p->next; 1132 } 1133 } 1134 1135 static void draw_io_bars(struct timechart *tchart) 1136 { 1137 const char *suf; 1138 double bytes; 1139 char comm[256]; 1140 struct per_pid *p; 1141 struct per_pidcomm *c; 1142 struct io_sample *sample; 1143 int Y = 1; 1144 1145 p = tchart->all_data; 1146 while (p) { 1147 c = p->all; 1148 while (c) { 1149 if (!c->display) { 1150 c->Y = 0; 1151 c = c->next; 1152 continue; 1153 } 1154 1155 svg_box(Y, c->start_time, c->end_time, "process3"); 1156 sample = c->io_samples; 1157 for (sample = c->io_samples; sample; sample = sample->next) { 1158 double h = (double)sample->bytes / c->max_bytes; 1159 1160 if (tchart->skip_eagain && 1161 sample->err == -EAGAIN) 1162 continue; 1163 1164 if (sample->err) 1165 h = 1; 1166 1167 if (sample->type == IOTYPE_SYNC) 1168 svg_fbox(Y, 1169 sample->start_time, 1170 sample->end_time, 1171 1, 1172 sample->err ? "error" : "sync", 1173 sample->fd, 1174 sample->err, 1175 sample->merges); 1176 else if (sample->type == IOTYPE_POLL) 1177 svg_fbox(Y, 1178 sample->start_time, 1179 sample->end_time, 1180 1, 1181 sample->err ? "error" : "poll", 1182 sample->fd, 1183 sample->err, 1184 sample->merges); 1185 else if (sample->type == IOTYPE_READ) 1186 svg_ubox(Y, 1187 sample->start_time, 1188 sample->end_time, 1189 h, 1190 sample->err ? "error" : "disk", 1191 sample->fd, 1192 sample->err, 1193 sample->merges); 1194 else if (sample->type == IOTYPE_WRITE) 1195 svg_lbox(Y, 1196 sample->start_time, 1197 sample->end_time, 1198 h, 1199 sample->err ? "error" : "disk", 1200 sample->fd, 1201 sample->err, 1202 sample->merges); 1203 else if (sample->type == IOTYPE_RX) 1204 svg_ubox(Y, 1205 sample->start_time, 1206 sample->end_time, 1207 h, 1208 sample->err ? "error" : "net", 1209 sample->fd, 1210 sample->err, 1211 sample->merges); 1212 else if (sample->type == IOTYPE_TX) 1213 svg_lbox(Y, 1214 sample->start_time, 1215 sample->end_time, 1216 h, 1217 sample->err ? "error" : "net", 1218 sample->fd, 1219 sample->err, 1220 sample->merges); 1221 } 1222 1223 suf = ""; 1224 bytes = c->total_bytes; 1225 if (bytes > 1024) { 1226 bytes = bytes / 1024; 1227 suf = "K"; 1228 } 1229 if (bytes > 1024) { 1230 bytes = bytes / 1024; 1231 suf = "M"; 1232 } 1233 if (bytes > 1024) { 1234 bytes = bytes / 1024; 1235 suf = "G"; 1236 } 1237 1238 1239 sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf); 1240 svg_text(Y, c->start_time, comm); 1241 1242 c->Y = Y; 1243 Y++; 1244 c = c->next; 1245 } 1246 p = p->next; 1247 } 1248 } 1249 1250 static void draw_process_bars(struct timechart *tchart) 1251 { 1252 struct per_pid *p; 1253 struct per_pidcomm *c; 1254 struct cpu_sample *sample; 1255 int Y = 0; 1256 1257 Y = 2 * tchart->numcpus + 2; 1258 1259 p = tchart->all_data; 1260 while (p) { 1261 c = p->all; 1262 while (c) { 1263 if (!c->display) { 1264 c->Y = 0; 1265 c = c->next; 1266 continue; 1267 } 1268 1269 svg_box(Y, c->start_time, c->end_time, "process"); 1270 sample = c->samples; 1271 while (sample) { 1272 if (sample->type == TYPE_RUNNING) 1273 svg_running(Y, sample->cpu, 1274 sample->start_time, 1275 sample->end_time, 1276 sample->backtrace); 1277 if (sample->type == TYPE_BLOCKED) 1278 svg_blocked(Y, sample->cpu, 1279 sample->start_time, 1280 sample->end_time, 1281 sample->backtrace); 1282 if (sample->type == TYPE_WAITING) 1283 svg_waiting(Y, sample->cpu, 1284 sample->start_time, 1285 sample->end_time, 1286 sample->backtrace); 1287 sample = sample->next; 1288 } 1289 1290 if (c->comm) { 1291 char comm[256]; 1292 if (c->total_time > 5000000000) /* 5 seconds */ 1293 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / (double)NSEC_PER_SEC); 1294 else 1295 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / (double)NSEC_PER_MSEC); 1296 1297 svg_text(Y, c->start_time, comm); 1298 } 1299 c->Y = Y; 1300 Y++; 1301 c = c->next; 1302 } 1303 p = p->next; 1304 } 1305 } 1306 1307 static void add_process_filter(const char *string) 1308 { 1309 int pid = strtoull(string, NULL, 10); 1310 struct process_filter *filt = malloc(sizeof(*filt)); 1311 1312 if (!filt) 1313 return; 1314 1315 filt->name = strdup(string); 1316 filt->pid = pid; 1317 filt->next = process_filter; 1318 1319 process_filter = filt; 1320 } 1321 1322 static int passes_filter(struct per_pid *p, struct per_pidcomm *c) 1323 { 1324 struct process_filter *filt; 1325 if (!process_filter) 1326 return 1; 1327 1328 filt = process_filter; 1329 while (filt) { 1330 if (filt->pid && p->pid == filt->pid) 1331 return 1; 1332 if (strcmp(filt->name, c->comm) == 0) 1333 return 1; 1334 filt = filt->next; 1335 } 1336 return 0; 1337 } 1338 1339 static int determine_display_tasks_filtered(struct timechart *tchart) 1340 { 1341 struct per_pid *p; 1342 struct per_pidcomm *c; 1343 int count = 0; 1344 1345 p = tchart->all_data; 1346 while (p) { 1347 p->display = 0; 1348 if (p->start_time == 1) 1349 p->start_time = tchart->first_time; 1350 1351 /* no exit marker, task kept running to the end */ 1352 if (p->end_time == 0) 1353 p->end_time = tchart->last_time; 1354 1355 c = p->all; 1356 1357 while (c) { 1358 c->display = 0; 1359 1360 if (c->start_time == 1) 1361 c->start_time = tchart->first_time; 1362 1363 if (passes_filter(p, c)) { 1364 c->display = 1; 1365 p->display = 1; 1366 count++; 1367 } 1368 1369 if (c->end_time == 0) 1370 c->end_time = tchart->last_time; 1371 1372 c = c->next; 1373 } 1374 p = p->next; 1375 } 1376 return count; 1377 } 1378 1379 static int determine_display_tasks(struct timechart *tchart, u64 threshold) 1380 { 1381 struct per_pid *p; 1382 struct per_pidcomm *c; 1383 int count = 0; 1384 1385 p = tchart->all_data; 1386 while (p) { 1387 p->display = 0; 1388 if (p->start_time == 1) 1389 p->start_time = tchart->first_time; 1390 1391 /* no exit marker, task kept running to the end */ 1392 if (p->end_time == 0) 1393 p->end_time = tchart->last_time; 1394 if (p->total_time >= threshold) 1395 p->display = 1; 1396 1397 c = p->all; 1398 1399 while (c) { 1400 c->display = 0; 1401 1402 if (c->start_time == 1) 1403 c->start_time = tchart->first_time; 1404 1405 if (c->total_time >= threshold) { 1406 c->display = 1; 1407 count++; 1408 } 1409 1410 if (c->end_time == 0) 1411 c->end_time = tchart->last_time; 1412 1413 c = c->next; 1414 } 1415 p = p->next; 1416 } 1417 return count; 1418 } 1419 1420 static int determine_display_io_tasks(struct timechart *timechart, u64 threshold) 1421 { 1422 struct per_pid *p; 1423 struct per_pidcomm *c; 1424 int count = 0; 1425 1426 p = timechart->all_data; 1427 while (p) { 1428 /* no exit marker, task kept running to the end */ 1429 if (p->end_time == 0) 1430 p->end_time = timechart->last_time; 1431 1432 c = p->all; 1433 1434 while (c) { 1435 c->display = 0; 1436 1437 if (c->total_bytes >= threshold) { 1438 c->display = 1; 1439 count++; 1440 } 1441 1442 if (c->end_time == 0) 1443 c->end_time = timechart->last_time; 1444 1445 c = c->next; 1446 } 1447 p = p->next; 1448 } 1449 return count; 1450 } 1451 1452 #define BYTES_THRESH (1 * 1024 * 1024) 1453 #define TIME_THRESH 10000000 1454 1455 static void write_svg_file(struct timechart *tchart, const char *filename) 1456 { 1457 u64 i; 1458 int count; 1459 int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH; 1460 1461 if (tchart->power_only) 1462 tchart->proc_num = 0; 1463 1464 /* We'd like to show at least proc_num tasks; 1465 * be less picky if we have fewer */ 1466 do { 1467 if (process_filter) 1468 count = determine_display_tasks_filtered(tchart); 1469 else if (tchart->io_events) 1470 count = determine_display_io_tasks(tchart, thresh); 1471 else 1472 count = determine_display_tasks(tchart, thresh); 1473 thresh /= 10; 1474 } while (!process_filter && thresh && count < tchart->proc_num); 1475 1476 if (!tchart->proc_num) 1477 count = 0; 1478 1479 if (tchart->io_events) { 1480 open_svg(filename, 0, count, tchart->first_time, tchart->last_time); 1481 1482 svg_time_grid(0.5); 1483 svg_io_legenda(); 1484 1485 draw_io_bars(tchart); 1486 } else { 1487 open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); 1488 1489 svg_time_grid(0); 1490 1491 svg_legenda(); 1492 1493 for (i = 0; i < tchart->numcpus; i++) 1494 svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); 1495 1496 draw_cpu_usage(tchart); 1497 if (tchart->proc_num) 1498 draw_process_bars(tchart); 1499 if (!tchart->tasks_only) 1500 draw_c_p_states(tchart); 1501 if (tchart->proc_num) 1502 draw_wakeups(tchart); 1503 } 1504 1505 svg_close(); 1506 } 1507 1508 static int process_header(struct perf_file_section *section __maybe_unused, 1509 struct perf_header *ph, 1510 int feat, 1511 int fd __maybe_unused, 1512 void *data) 1513 { 1514 struct timechart *tchart = data; 1515 1516 switch (feat) { 1517 case HEADER_NRCPUS: 1518 tchart->numcpus = ph->env.nr_cpus_avail; 1519 break; 1520 1521 case HEADER_CPU_TOPOLOGY: 1522 if (!tchart->topology) 1523 break; 1524 1525 if (svg_build_topology_map(ph->env.sibling_cores, 1526 ph->env.nr_sibling_cores, 1527 ph->env.sibling_threads, 1528 ph->env.nr_sibling_threads)) 1529 fprintf(stderr, "problem building topology\n"); 1530 break; 1531 1532 default: 1533 break; 1534 } 1535 1536 return 0; 1537 } 1538 1539 static int __cmd_timechart(struct timechart *tchart, const char *output_name) 1540 { 1541 const struct perf_evsel_str_handler power_tracepoints[] = { 1542 { "power:cpu_idle", process_sample_cpu_idle }, 1543 { "power:cpu_frequency", process_sample_cpu_frequency }, 1544 { "sched:sched_wakeup", process_sample_sched_wakeup }, 1545 { "sched:sched_switch", process_sample_sched_switch }, 1546 #ifdef SUPPORT_OLD_POWER_EVENTS 1547 { "power:power_start", process_sample_power_start }, 1548 { "power:power_end", process_sample_power_end }, 1549 { "power:power_frequency", process_sample_power_frequency }, 1550 #endif 1551 1552 { "syscalls:sys_enter_read", process_enter_read }, 1553 { "syscalls:sys_enter_pread64", process_enter_read }, 1554 { "syscalls:sys_enter_readv", process_enter_read }, 1555 { "syscalls:sys_enter_preadv", process_enter_read }, 1556 { "syscalls:sys_enter_write", process_enter_write }, 1557 { "syscalls:sys_enter_pwrite64", process_enter_write }, 1558 { "syscalls:sys_enter_writev", process_enter_write }, 1559 { "syscalls:sys_enter_pwritev", process_enter_write }, 1560 { "syscalls:sys_enter_sync", process_enter_sync }, 1561 { "syscalls:sys_enter_sync_file_range", process_enter_sync }, 1562 { "syscalls:sys_enter_fsync", process_enter_sync }, 1563 { "syscalls:sys_enter_msync", process_enter_sync }, 1564 { "syscalls:sys_enter_recvfrom", process_enter_rx }, 1565 { "syscalls:sys_enter_recvmmsg", process_enter_rx }, 1566 { "syscalls:sys_enter_recvmsg", process_enter_rx }, 1567 { "syscalls:sys_enter_sendto", process_enter_tx }, 1568 { "syscalls:sys_enter_sendmsg", process_enter_tx }, 1569 { "syscalls:sys_enter_sendmmsg", process_enter_tx }, 1570 { "syscalls:sys_enter_epoll_pwait", process_enter_poll }, 1571 { "syscalls:sys_enter_epoll_wait", process_enter_poll }, 1572 { "syscalls:sys_enter_poll", process_enter_poll }, 1573 { "syscalls:sys_enter_ppoll", process_enter_poll }, 1574 { "syscalls:sys_enter_pselect6", process_enter_poll }, 1575 { "syscalls:sys_enter_select", process_enter_poll }, 1576 1577 { "syscalls:sys_exit_read", process_exit_read }, 1578 { "syscalls:sys_exit_pread64", process_exit_read }, 1579 { "syscalls:sys_exit_readv", process_exit_read }, 1580 { "syscalls:sys_exit_preadv", process_exit_read }, 1581 { "syscalls:sys_exit_write", process_exit_write }, 1582 { "syscalls:sys_exit_pwrite64", process_exit_write }, 1583 { "syscalls:sys_exit_writev", process_exit_write }, 1584 { "syscalls:sys_exit_pwritev", process_exit_write }, 1585 { "syscalls:sys_exit_sync", process_exit_sync }, 1586 { "syscalls:sys_exit_sync_file_range", process_exit_sync }, 1587 { "syscalls:sys_exit_fsync", process_exit_sync }, 1588 { "syscalls:sys_exit_msync", process_exit_sync }, 1589 { "syscalls:sys_exit_recvfrom", process_exit_rx }, 1590 { "syscalls:sys_exit_recvmmsg", process_exit_rx }, 1591 { "syscalls:sys_exit_recvmsg", process_exit_rx }, 1592 { "syscalls:sys_exit_sendto", process_exit_tx }, 1593 { "syscalls:sys_exit_sendmsg", process_exit_tx }, 1594 { "syscalls:sys_exit_sendmmsg", process_exit_tx }, 1595 { "syscalls:sys_exit_epoll_pwait", process_exit_poll }, 1596 { "syscalls:sys_exit_epoll_wait", process_exit_poll }, 1597 { "syscalls:sys_exit_poll", process_exit_poll }, 1598 { "syscalls:sys_exit_ppoll", process_exit_poll }, 1599 { "syscalls:sys_exit_pselect6", process_exit_poll }, 1600 { "syscalls:sys_exit_select", process_exit_poll }, 1601 }; 1602 struct perf_data_file file = { 1603 .path = input_name, 1604 .mode = PERF_DATA_MODE_READ, 1605 .force = tchart->force, 1606 }; 1607 1608 struct perf_session *session = perf_session__new(&file, false, 1609 &tchart->tool); 1610 int ret = -EINVAL; 1611 1612 if (session == NULL) 1613 return -1; 1614 1615 symbol__init(&session->header.env); 1616 1617 (void)perf_header__process_sections(&session->header, 1618 perf_data_file__fd(session->file), 1619 tchart, 1620 process_header); 1621 1622 if (!perf_session__has_traces(session, "timechart record")) 1623 goto out_delete; 1624 1625 if (perf_session__set_tracepoints_handlers(session, 1626 power_tracepoints)) { 1627 pr_err("Initializing session tracepoint handlers failed\n"); 1628 goto out_delete; 1629 } 1630 1631 ret = perf_session__process_events(session); 1632 if (ret) 1633 goto out_delete; 1634 1635 end_sample_processing(tchart); 1636 1637 sort_pids(tchart); 1638 1639 write_svg_file(tchart, output_name); 1640 1641 pr_info("Written %2.1f seconds of trace to %s.\n", 1642 (tchart->last_time - tchart->first_time) / (double)NSEC_PER_SEC, output_name); 1643 out_delete: 1644 perf_session__delete(session); 1645 return ret; 1646 } 1647 1648 static int timechart__io_record(int argc, const char **argv) 1649 { 1650 unsigned int rec_argc, i; 1651 const char **rec_argv; 1652 const char **p; 1653 char *filter = NULL; 1654 1655 const char * const common_args[] = { 1656 "record", "-a", "-R", "-c", "1", 1657 }; 1658 unsigned int common_args_nr = ARRAY_SIZE(common_args); 1659 1660 const char * const disk_events[] = { 1661 "syscalls:sys_enter_read", 1662 "syscalls:sys_enter_pread64", 1663 "syscalls:sys_enter_readv", 1664 "syscalls:sys_enter_preadv", 1665 "syscalls:sys_enter_write", 1666 "syscalls:sys_enter_pwrite64", 1667 "syscalls:sys_enter_writev", 1668 "syscalls:sys_enter_pwritev", 1669 "syscalls:sys_enter_sync", 1670 "syscalls:sys_enter_sync_file_range", 1671 "syscalls:sys_enter_fsync", 1672 "syscalls:sys_enter_msync", 1673 1674 "syscalls:sys_exit_read", 1675 "syscalls:sys_exit_pread64", 1676 "syscalls:sys_exit_readv", 1677 "syscalls:sys_exit_preadv", 1678 "syscalls:sys_exit_write", 1679 "syscalls:sys_exit_pwrite64", 1680 "syscalls:sys_exit_writev", 1681 "syscalls:sys_exit_pwritev", 1682 "syscalls:sys_exit_sync", 1683 "syscalls:sys_exit_sync_file_range", 1684 "syscalls:sys_exit_fsync", 1685 "syscalls:sys_exit_msync", 1686 }; 1687 unsigned int disk_events_nr = ARRAY_SIZE(disk_events); 1688 1689 const char * const net_events[] = { 1690 "syscalls:sys_enter_recvfrom", 1691 "syscalls:sys_enter_recvmmsg", 1692 "syscalls:sys_enter_recvmsg", 1693 "syscalls:sys_enter_sendto", 1694 "syscalls:sys_enter_sendmsg", 1695 "syscalls:sys_enter_sendmmsg", 1696 1697 "syscalls:sys_exit_recvfrom", 1698 "syscalls:sys_exit_recvmmsg", 1699 "syscalls:sys_exit_recvmsg", 1700 "syscalls:sys_exit_sendto", 1701 "syscalls:sys_exit_sendmsg", 1702 "syscalls:sys_exit_sendmmsg", 1703 }; 1704 unsigned int net_events_nr = ARRAY_SIZE(net_events); 1705 1706 const char * const poll_events[] = { 1707 "syscalls:sys_enter_epoll_pwait", 1708 "syscalls:sys_enter_epoll_wait", 1709 "syscalls:sys_enter_poll", 1710 "syscalls:sys_enter_ppoll", 1711 "syscalls:sys_enter_pselect6", 1712 "syscalls:sys_enter_select", 1713 1714 "syscalls:sys_exit_epoll_pwait", 1715 "syscalls:sys_exit_epoll_wait", 1716 "syscalls:sys_exit_poll", 1717 "syscalls:sys_exit_ppoll", 1718 "syscalls:sys_exit_pselect6", 1719 "syscalls:sys_exit_select", 1720 }; 1721 unsigned int poll_events_nr = ARRAY_SIZE(poll_events); 1722 1723 rec_argc = common_args_nr + 1724 disk_events_nr * 4 + 1725 net_events_nr * 4 + 1726 poll_events_nr * 4 + 1727 argc; 1728 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 1729 1730 if (rec_argv == NULL) 1731 return -ENOMEM; 1732 1733 if (asprintf(&filter, "common_pid != %d", getpid()) < 0) 1734 return -ENOMEM; 1735 1736 p = rec_argv; 1737 for (i = 0; i < common_args_nr; i++) 1738 *p++ = strdup(common_args[i]); 1739 1740 for (i = 0; i < disk_events_nr; i++) { 1741 if (!is_valid_tracepoint(disk_events[i])) { 1742 rec_argc -= 4; 1743 continue; 1744 } 1745 1746 *p++ = "-e"; 1747 *p++ = strdup(disk_events[i]); 1748 *p++ = "--filter"; 1749 *p++ = filter; 1750 } 1751 for (i = 0; i < net_events_nr; i++) { 1752 if (!is_valid_tracepoint(net_events[i])) { 1753 rec_argc -= 4; 1754 continue; 1755 } 1756 1757 *p++ = "-e"; 1758 *p++ = strdup(net_events[i]); 1759 *p++ = "--filter"; 1760 *p++ = filter; 1761 } 1762 for (i = 0; i < poll_events_nr; i++) { 1763 if (!is_valid_tracepoint(poll_events[i])) { 1764 rec_argc -= 4; 1765 continue; 1766 } 1767 1768 *p++ = "-e"; 1769 *p++ = strdup(poll_events[i]); 1770 *p++ = "--filter"; 1771 *p++ = filter; 1772 } 1773 1774 for (i = 0; i < (unsigned int)argc; i++) 1775 *p++ = argv[i]; 1776 1777 return cmd_record(rec_argc, rec_argv); 1778 } 1779 1780 1781 static int timechart__record(struct timechart *tchart, int argc, const char **argv) 1782 { 1783 unsigned int rec_argc, i, j; 1784 const char **rec_argv; 1785 const char **p; 1786 unsigned int record_elems; 1787 1788 const char * const common_args[] = { 1789 "record", "-a", "-R", "-c", "1", 1790 }; 1791 unsigned int common_args_nr = ARRAY_SIZE(common_args); 1792 1793 const char * const backtrace_args[] = { 1794 "-g", 1795 }; 1796 unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args); 1797 1798 const char * const power_args[] = { 1799 "-e", "power:cpu_frequency", 1800 "-e", "power:cpu_idle", 1801 }; 1802 unsigned int power_args_nr = ARRAY_SIZE(power_args); 1803 1804 const char * const old_power_args[] = { 1805 #ifdef SUPPORT_OLD_POWER_EVENTS 1806 "-e", "power:power_start", 1807 "-e", "power:power_end", 1808 "-e", "power:power_frequency", 1809 #endif 1810 }; 1811 unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args); 1812 1813 const char * const tasks_args[] = { 1814 "-e", "sched:sched_wakeup", 1815 "-e", "sched:sched_switch", 1816 }; 1817 unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args); 1818 1819 #ifdef SUPPORT_OLD_POWER_EVENTS 1820 if (!is_valid_tracepoint("power:cpu_idle") && 1821 is_valid_tracepoint("power:power_start")) { 1822 use_old_power_events = 1; 1823 power_args_nr = 0; 1824 } else { 1825 old_power_args_nr = 0; 1826 } 1827 #endif 1828 1829 if (tchart->power_only) 1830 tasks_args_nr = 0; 1831 1832 if (tchart->tasks_only) { 1833 power_args_nr = 0; 1834 old_power_args_nr = 0; 1835 } 1836 1837 if (!tchart->with_backtrace) 1838 backtrace_args_no = 0; 1839 1840 record_elems = common_args_nr + tasks_args_nr + 1841 power_args_nr + old_power_args_nr + backtrace_args_no; 1842 1843 rec_argc = record_elems + argc; 1844 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 1845 1846 if (rec_argv == NULL) 1847 return -ENOMEM; 1848 1849 p = rec_argv; 1850 for (i = 0; i < common_args_nr; i++) 1851 *p++ = strdup(common_args[i]); 1852 1853 for (i = 0; i < backtrace_args_no; i++) 1854 *p++ = strdup(backtrace_args[i]); 1855 1856 for (i = 0; i < tasks_args_nr; i++) 1857 *p++ = strdup(tasks_args[i]); 1858 1859 for (i = 0; i < power_args_nr; i++) 1860 *p++ = strdup(power_args[i]); 1861 1862 for (i = 0; i < old_power_args_nr; i++) 1863 *p++ = strdup(old_power_args[i]); 1864 1865 for (j = 0; j < (unsigned int)argc; j++) 1866 *p++ = argv[j]; 1867 1868 return cmd_record(rec_argc, rec_argv); 1869 } 1870 1871 static int 1872 parse_process(const struct option *opt __maybe_unused, const char *arg, 1873 int __maybe_unused unset) 1874 { 1875 if (arg) 1876 add_process_filter(arg); 1877 return 0; 1878 } 1879 1880 static int 1881 parse_highlight(const struct option *opt __maybe_unused, const char *arg, 1882 int __maybe_unused unset) 1883 { 1884 unsigned long duration = strtoul(arg, NULL, 0); 1885 1886 if (svg_highlight || svg_highlight_name) 1887 return -1; 1888 1889 if (duration) 1890 svg_highlight = duration; 1891 else 1892 svg_highlight_name = strdup(arg); 1893 1894 return 0; 1895 } 1896 1897 static int 1898 parse_time(const struct option *opt, const char *arg, int __maybe_unused unset) 1899 { 1900 char unit = 'n'; 1901 u64 *value = opt->value; 1902 1903 if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) { 1904 switch (unit) { 1905 case 'm': 1906 *value *= NSEC_PER_MSEC; 1907 break; 1908 case 'u': 1909 *value *= NSEC_PER_USEC; 1910 break; 1911 case 'n': 1912 break; 1913 default: 1914 return -1; 1915 } 1916 } 1917 1918 return 0; 1919 } 1920 1921 int cmd_timechart(int argc, const char **argv) 1922 { 1923 struct timechart tchart = { 1924 .tool = { 1925 .comm = process_comm_event, 1926 .fork = process_fork_event, 1927 .exit = process_exit_event, 1928 .sample = process_sample_event, 1929 .ordered_events = true, 1930 }, 1931 .proc_num = 15, 1932 .min_time = NSEC_PER_MSEC, 1933 .merge_dist = 1000, 1934 }; 1935 const char *output_name = "output.svg"; 1936 const struct option timechart_common_options[] = { 1937 OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), 1938 OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, "output processes data only"), 1939 OPT_END() 1940 }; 1941 const struct option timechart_options[] = { 1942 OPT_STRING('i', "input", &input_name, "file", "input file name"), 1943 OPT_STRING('o', "output", &output_name, "file", "output file name"), 1944 OPT_INTEGER('w', "width", &svg_page_width, "page width"), 1945 OPT_CALLBACK(0, "highlight", NULL, "duration or task name", 1946 "highlight tasks. Pass duration in ns or process name.", 1947 parse_highlight), 1948 OPT_CALLBACK('p', "process", NULL, "process", 1949 "process selector. Pass a pid or process name.", 1950 parse_process), 1951 OPT_CALLBACK(0, "symfs", NULL, "directory", 1952 "Look for files with symbols relative to this directory", 1953 symbol__config_symfs), 1954 OPT_INTEGER('n', "proc-num", &tchart.proc_num, 1955 "min. number of tasks to print"), 1956 OPT_BOOLEAN('t', "topology", &tchart.topology, 1957 "sort CPUs according to topology"), 1958 OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain, 1959 "skip EAGAIN errors"), 1960 OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time", 1961 "all IO faster than min-time will visually appear longer", 1962 parse_time), 1963 OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time", 1964 "merge events that are merge-dist us apart", 1965 parse_time), 1966 OPT_BOOLEAN('f', "force", &tchart.force, "don't complain, do it"), 1967 OPT_PARENT(timechart_common_options), 1968 }; 1969 const char * const timechart_subcommands[] = { "record", NULL }; 1970 const char *timechart_usage[] = { 1971 "perf timechart [<options>] {record}", 1972 NULL 1973 }; 1974 const struct option timechart_record_options[] = { 1975 OPT_BOOLEAN('I', "io-only", &tchart.io_only, 1976 "record only IO data"), 1977 OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), 1978 OPT_PARENT(timechart_common_options), 1979 }; 1980 const char * const timechart_record_usage[] = { 1981 "perf timechart record [<options>]", 1982 NULL 1983 }; 1984 argc = parse_options_subcommand(argc, argv, timechart_options, timechart_subcommands, 1985 timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); 1986 1987 if (tchart.power_only && tchart.tasks_only) { 1988 pr_err("-P and -T options cannot be used at the same time.\n"); 1989 return -1; 1990 } 1991 1992 if (argc && !strncmp(argv[0], "rec", 3)) { 1993 argc = parse_options(argc, argv, timechart_record_options, 1994 timechart_record_usage, 1995 PARSE_OPT_STOP_AT_NON_OPTION); 1996 1997 if (tchart.power_only && tchart.tasks_only) { 1998 pr_err("-P and -T options cannot be used at the same time.\n"); 1999 return -1; 2000 } 2001 2002 if (tchart.io_only) 2003 return timechart__io_record(argc, argv); 2004 else 2005 return timechart__record(&tchart, argc, argv); 2006 } else if (argc) 2007 usage_with_options(timechart_usage, timechart_options); 2008 2009 setup_pager(); 2010 2011 return __cmd_timechart(&tchart, output_name); 2012 } 2013