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