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