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