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