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