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