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