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 "builtin.h"
  16
  17#include "util/util.h"
  18
  19#include "util/color.h"
  20#include <linux/list.h>
  21#include "util/cache.h"
  22#include <linux/rbtree.h>
  23#include "util/symbol.h"
  24#include "util/callchain.h"
  25#include "util/strlist.h"
  26
  27#include "perf.h"
  28#include "util/header.h"
  29#include "util/parse-options.h"
  30#include "util/parse-events.h"
  31#include "util/event.h"
  32#include "util/session.h"
  33#include "util/svghelper.h"
  34
  35static char             const *input_name = "perf.data";
  36static char             const *output_name = "output.svg";
  37
  38static unsigned int     numcpus;
  39static u64              min_freq;       /* Lowest CPU frequency seen */
  40static u64              max_freq;       /* Highest CPU frequency seen */
  41static u64              turbo_frequency;
  42
  43static u64              first_time, last_time;
  44
  45static bool             power_only;
  46
  47
  48struct per_pid;
  49struct per_pidcomm;
  50
  51struct cpu_sample;
  52struct power_event;
  53struct wake_event;
  54
  55struct sample_wrapper;
  56
  57/*
  58 * Datastructure layout:
  59 * We keep an list of "pid"s, matching the kernels notion of a task struct.
  60 * Each "pid" entry, has a list of "comm"s.
  61 *      this is because we want to track different programs different, while
  62 *      exec will reuse the original pid (by design).
  63 * Each comm has a list of samples that will be used to draw
  64 * final graph.
  65 */
  66
  67struct per_pid {
  68        struct per_pid *next;
  69
  70        int             pid;
  71        int             ppid;
  72
  73        u64             start_time;
  74        u64             end_time;
  75        u64             total_time;
  76        int             display;
  77
  78        struct per_pidcomm *all;
  79        struct per_pidcomm *current;
  80};
  81
  82
  83struct per_pidcomm {
  84        struct per_pidcomm *next;
  85
  86        u64             start_time;
  87        u64             end_time;
  88        u64             total_time;
  89
  90        int             Y;
  91        int             display;
  92
  93        long            state;
  94        u64             state_since;
  95
  96        char            *comm;
  97
  98        struct cpu_sample *samples;
  99};
 100
 101struct sample_wrapper {
 102        struct sample_wrapper *next;
 103
 104        u64             timestamp;
 105        unsigned char   data[0];
 106};
 107
 108#define TYPE_NONE       0
 109#define TYPE_RUNNING    1
 110#define TYPE_WAITING    2
 111#define TYPE_BLOCKED    3
 112
 113struct cpu_sample {
 114        struct cpu_sample *next;
 115
 116        u64 start_time;
 117        u64 end_time;
 118        int type;
 119        int cpu;
 120};
 121
 122static struct per_pid *all_data;
 123
 124#define CSTATE 1
 125#define PSTATE 2
 126
 127struct power_event {
 128        struct power_event *next;
 129        int type;
 130        int state;
 131        u64 start_time;
 132        u64 end_time;
 133        int cpu;
 134};
 135
 136struct wake_event {
 137        struct wake_event *next;
 138        int waker;
 139        int wakee;
 140        u64 time;
 141};
 142
 143static struct power_event    *power_events;
 144static struct wake_event     *wake_events;
 145
 146struct process_filter;
 147struct process_filter {
 148        char                    *name;
 149        int                     pid;
 150        struct process_filter   *next;
 151};
 152
 153static struct process_filter *process_filter;
 154
 155
 156static struct per_pid *find_create_pid(int pid)
 157{
 158        struct per_pid *cursor = all_data;
 159
 160        while (cursor) {
 161                if (cursor->pid == pid)
 162                        return cursor;
 163                cursor = cursor->next;
 164        }
 165        cursor = malloc(sizeof(struct per_pid));
 166        assert(cursor != NULL);
 167        memset(cursor, 0, sizeof(struct per_pid));
 168        cursor->pid = pid;
 169        cursor->next = all_data;
 170        all_data = cursor;
 171        return cursor;
 172}
 173
 174static void pid_set_comm(int pid, char *comm)
 175{
 176        struct per_pid *p;
 177        struct per_pidcomm *c;
 178        p = find_create_pid(pid);
 179        c = p->all;
 180        while (c) {
 181                if (c->comm && strcmp(c->comm, comm) == 0) {
 182                        p->current = c;
 183                        return;
 184                }
 185                if (!c->comm) {
 186                        c->comm = strdup(comm);
 187                        p->current = c;
 188                        return;
 189                }
 190                c = c->next;
 191        }
 192        c = malloc(sizeof(struct per_pidcomm));
 193        assert(c != NULL);
 194        memset(c, 0, sizeof(struct per_pidcomm));
 195        c->comm = strdup(comm);
 196        p->current = c;
 197        c->next = p->all;
 198        p->all = c;
 199}
 200
 201static void pid_fork(int pid, int ppid, u64 timestamp)
 202{
 203        struct per_pid *p, *pp;
 204        p = find_create_pid(pid);
 205        pp = find_create_pid(ppid);
 206        p->ppid = ppid;
 207        if (pp->current && pp->current->comm && !p->current)
 208                pid_set_comm(pid, pp->current->comm);
 209
 210        p->start_time = timestamp;
 211        if (p->current) {
 212                p->current->start_time = timestamp;
 213                p->current->state_since = timestamp;
 214        }
 215}
 216
 217static void pid_exit(int pid, u64 timestamp)
 218{
 219        struct per_pid *p;
 220        p = find_create_pid(pid);
 221        p->end_time = timestamp;
 222        if (p->current)
 223                p->current->end_time = timestamp;
 224}
 225
 226static void
 227pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
 228{
 229        struct per_pid *p;
 230        struct per_pidcomm *c;
 231        struct cpu_sample *sample;
 232
 233        p = find_create_pid(pid);
 234        c = p->current;
 235        if (!c) {
 236                c = malloc(sizeof(struct per_pidcomm));
 237                assert(c != NULL);
 238                memset(c, 0, sizeof(struct per_pidcomm));
 239                p->current = c;
 240                c->next = p->all;
 241                p->all = c;
 242        }
 243
 244        sample = malloc(sizeof(struct cpu_sample));
 245        assert(sample != NULL);
 246        memset(sample, 0, sizeof(struct cpu_sample));
 247        sample->start_time = start;
 248        sample->end_time = end;
 249        sample->type = type;
 250        sample->next = c->samples;
 251        sample->cpu = cpu;
 252        c->samples = sample;
 253
 254        if (sample->type == TYPE_RUNNING && end > start && start > 0) {
 255                c->total_time += (end-start);
 256                p->total_time += (end-start);
 257        }
 258
 259        if (c->start_time == 0 || c->start_time > start)
 260                c->start_time = start;
 261        if (p->start_time == 0 || p->start_time > start)
 262                p->start_time = start;
 263
 264        if (cpu > numcpus)
 265                numcpus = cpu;
 266}
 267
 268#define MAX_CPUS 4096
 269
 270static u64 cpus_cstate_start_times[MAX_CPUS];
 271static int cpus_cstate_state[MAX_CPUS];
 272static u64 cpus_pstate_start_times[MAX_CPUS];
 273static u64 cpus_pstate_state[MAX_CPUS];
 274
 275static int process_comm_event(event_t *event, struct perf_session *session __used)
 276{
 277        pid_set_comm(event->comm.tid, event->comm.comm);
 278        return 0;
 279}
 280
 281static int process_fork_event(event_t *event, struct perf_session *session __used)
 282{
 283        pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
 284        return 0;
 285}
 286
 287static int process_exit_event(event_t *event, struct perf_session *session __used)
 288{
 289        pid_exit(event->fork.pid, event->fork.time);
 290        return 0;
 291}
 292
 293struct trace_entry {
 294        unsigned short          type;
 295        unsigned char           flags;
 296        unsigned char           preempt_count;
 297        int                     pid;
 298        int                     lock_depth;
 299};
 300
 301struct power_entry {
 302        struct trace_entry te;
 303        s64     type;
 304        s64     value;
 305};
 306
 307#define TASK_COMM_LEN 16
 308struct wakeup_entry {
 309        struct trace_entry te;
 310        char comm[TASK_COMM_LEN];
 311        int   pid;
 312        int   prio;
 313        int   success;
 314};
 315
 316/*
 317 * trace_flag_type is an enumeration that holds different
 318 * states when a trace occurs. These are:
 319 *  IRQS_OFF            - interrupts were disabled
 320 *  IRQS_NOSUPPORT      - arch does not support irqs_disabled_flags
 321 *  NEED_RESCED         - reschedule is requested
 322 *  HARDIRQ             - inside an interrupt handler
 323 *  SOFTIRQ             - inside a softirq handler
 324 */
 325enum trace_flag_type {
 326        TRACE_FLAG_IRQS_OFF             = 0x01,
 327        TRACE_FLAG_IRQS_NOSUPPORT       = 0x02,
 328        TRACE_FLAG_NEED_RESCHED         = 0x04,
 329        TRACE_FLAG_HARDIRQ              = 0x08,
 330        TRACE_FLAG_SOFTIRQ              = 0x10,
 331};
 332
 333
 334
 335struct sched_switch {
 336        struct trace_entry te;
 337        char prev_comm[TASK_COMM_LEN];
 338        int  prev_pid;
 339        int  prev_prio;
 340        long prev_state; /* Arjan weeps. */
 341        char next_comm[TASK_COMM_LEN];
 342        int  next_pid;
 343        int  next_prio;
 344};
 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(int cpu, u64 timestamp)
 353{
 354        struct power_event *pwr;
 355        pwr = malloc(sizeof(struct power_event));
 356        if (!pwr)
 357                return;
 358        memset(pwr, 0, sizeof(struct power_event));
 359
 360        pwr->state = cpus_cstate_state[cpu];
 361        pwr->start_time = cpus_cstate_start_times[cpu];
 362        pwr->end_time = timestamp;
 363        pwr->cpu = cpu;
 364        pwr->type = CSTATE;
 365        pwr->next = power_events;
 366
 367        power_events = pwr;
 368}
 369
 370static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
 371{
 372        struct power_event *pwr;
 373        pwr = malloc(sizeof(struct power_event));
 374
 375        if (new_freq > 8000000) /* detect invalid data */
 376                return;
 377
 378        if (!pwr)
 379                return;
 380        memset(pwr, 0, sizeof(struct power_event));
 381
 382        pwr->state = cpus_pstate_state[cpu];
 383        pwr->start_time = cpus_pstate_start_times[cpu];
 384        pwr->end_time = timestamp;
 385        pwr->cpu = cpu;
 386        pwr->type = PSTATE;
 387        pwr->next = power_events;
 388
 389        if (!pwr->start_time)
 390                pwr->start_time = first_time;
 391
 392        power_events = pwr;
 393
 394        cpus_pstate_state[cpu] = new_freq;
 395        cpus_pstate_start_times[cpu] = timestamp;
 396
 397        if ((u64)new_freq > max_freq)
 398                max_freq = new_freq;
 399
 400        if (new_freq < min_freq || min_freq == 0)
 401                min_freq = new_freq;
 402
 403        if (new_freq == max_freq - 1000)
 404                        turbo_frequency = max_freq;
 405}
 406
 407static void
 408sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
 409{
 410        struct wake_event *we;
 411        struct per_pid *p;
 412        struct wakeup_entry *wake = (void *)te;
 413
 414        we = malloc(sizeof(struct wake_event));
 415        if (!we)
 416                return;
 417
 418        memset(we, 0, sizeof(struct wake_event));
 419        we->time = timestamp;
 420        we->waker = pid;
 421
 422        if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
 423                we->waker = -1;
 424
 425        we->wakee = wake->pid;
 426        we->next = wake_events;
 427        wake_events = we;
 428        p = find_create_pid(we->wakee);
 429
 430        if (p && p->current && p->current->state == TYPE_NONE) {
 431                p->current->state_since = timestamp;
 432                p->current->state = TYPE_WAITING;
 433        }
 434        if (p && p->current && p->current->state == TYPE_BLOCKED) {
 435                pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
 436                p->current->state_since = timestamp;
 437                p->current->state = TYPE_WAITING;
 438        }
 439}
 440
 441static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
 442{
 443        struct per_pid *p = NULL, *prev_p;
 444        struct sched_switch *sw = (void *)te;
 445
 446
 447        prev_p = find_create_pid(sw->prev_pid);
 448
 449        p = find_create_pid(sw->next_pid);
 450
 451        if (prev_p->current && prev_p->current->state != TYPE_NONE)
 452                pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
 453        if (p && p->current) {
 454                if (p->current->state != TYPE_NONE)
 455                        pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
 456
 457                        p->current->state_since = timestamp;
 458                        p->current->state = TYPE_RUNNING;
 459        }
 460
 461        if (prev_p->current) {
 462                prev_p->current->state = TYPE_NONE;
 463                prev_p->current->state_since = timestamp;
 464                if (sw->prev_state & 2)
 465                        prev_p->current->state = TYPE_BLOCKED;
 466                if (sw->prev_state == 0)
 467                        prev_p->current->state = TYPE_WAITING;
 468        }
 469}
 470
 471
 472static int process_sample_event(event_t *event, struct perf_session *session)
 473{
 474        struct sample_data data;
 475        struct trace_entry *te;
 476
 477        memset(&data, 0, sizeof(data));
 478
 479        event__parse_sample(event, session->sample_type, &data);
 480
 481        if (session->sample_type & PERF_SAMPLE_TIME) {
 482                if (!first_time || first_time > data.time)
 483                        first_time = data.time;
 484                if (last_time < data.time)
 485                        last_time = data.time;
 486        }
 487
 488        te = (void *)data.raw_data;
 489        if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) {
 490                char *event_str;
 491                struct power_entry *pe;
 492
 493                pe = (void *)te;
 494
 495                event_str = perf_header__find_event(te->type);
 496
 497                if (!event_str)
 498                        return 0;
 499
 500                if (strcmp(event_str, "power:power_start") == 0)
 501                        c_state_start(data.cpu, data.time, pe->value);
 502
 503                if (strcmp(event_str, "power:power_end") == 0)
 504                        c_state_end(data.cpu, data.time);
 505
 506                if (strcmp(event_str, "power:power_frequency") == 0)
 507                        p_state_change(data.cpu, data.time, pe->value);
 508
 509                if (strcmp(event_str, "sched:sched_wakeup") == 0)
 510                        sched_wakeup(data.cpu, data.time, data.pid, te);
 511
 512                if (strcmp(event_str, "sched:sched_switch") == 0)
 513                        sched_switch(data.cpu, data.time, te);
 514        }
 515        return 0;
 516}
 517
 518/*
 519 * After the last sample we need to wrap up the current C/P state
 520 * and close out each CPU for these.
 521 */
 522static void end_sample_processing(void)
 523{
 524        u64 cpu;
 525        struct power_event *pwr;
 526
 527        for (cpu = 0; cpu <= numcpus; cpu++) {
 528                pwr = malloc(sizeof(struct power_event));
 529                if (!pwr)
 530                        return;
 531                memset(pwr, 0, sizeof(struct power_event));
 532
 533                /* C state */
 534#if 0
 535                pwr->state = cpus_cstate_state[cpu];
 536                pwr->start_time = cpus_cstate_start_times[cpu];
 537                pwr->end_time = last_time;
 538                pwr->cpu = cpu;
 539                pwr->type = CSTATE;
 540                pwr->next = power_events;
 541
 542                power_events = pwr;
 543#endif
 544                /* P state */
 545
 546                pwr = malloc(sizeof(struct power_event));
 547                if (!pwr)
 548                        return;
 549                memset(pwr, 0, sizeof(struct power_event));
 550
 551                pwr->state = cpus_pstate_state[cpu];
 552                pwr->start_time = cpus_pstate_start_times[cpu];
 553                pwr->end_time = last_time;
 554                pwr->cpu = cpu;
 555                pwr->type = PSTATE;
 556                pwr->next = power_events;
 557
 558                if (!pwr->start_time)
 559                        pwr->start_time = first_time;
 560                if (!pwr->state)
 561                        pwr->state = min_freq;
 562                power_events = pwr;
 563        }
 564}
 565
 566/*
 567 * Sort the pid datastructure
 568 */
 569static void sort_pids(void)
 570{
 571        struct per_pid *new_list, *p, *cursor, *prev;
 572        /* sort by ppid first, then by pid, lowest to highest */
 573
 574        new_list = NULL;
 575
 576        while (all_data) {
 577                p = all_data;
 578                all_data = p->next;
 579                p->next = NULL;
 580
 581                if (new_list == NULL) {
 582                        new_list = p;
 583                        p->next = NULL;
 584                        continue;
 585                }
 586                prev = NULL;
 587                cursor = new_list;
 588                while (cursor) {
 589                        if (cursor->ppid > p->ppid ||
 590                                (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
 591                                /* must insert before */
 592                                if (prev) {
 593                                        p->next = prev->next;
 594                                        prev->next = p;
 595                                        cursor = NULL;
 596                                        continue;
 597                                } else {
 598                                        p->next = new_list;
 599                                        new_list = p;
 600                                        cursor = NULL;
 601                                        continue;
 602                                }
 603                        }
 604
 605                        prev = cursor;
 606                        cursor = cursor->next;
 607                        if (!cursor)
 608                                prev->next = p;
 609                }
 610        }
 611        all_data = new_list;
 612}
 613
 614
 615static void draw_c_p_states(void)
 616{
 617        struct power_event *pwr;
 618        pwr = power_events;
 619
 620        /*
 621         * two pass drawing so that the P state bars are on top of the C state blocks
 622         */
 623        while (pwr) {
 624                if (pwr->type == CSTATE)
 625                        svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
 626                pwr = pwr->next;
 627        }
 628
 629        pwr = power_events;
 630        while (pwr) {
 631                if (pwr->type == PSTATE) {
 632                        if (!pwr->state)
 633                                pwr->state = min_freq;
 634                        svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
 635                }
 636                pwr = pwr->next;
 637        }
 638}
 639
 640static void draw_wakeups(void)
 641{
 642        struct wake_event *we;
 643        struct per_pid *p;
 644        struct per_pidcomm *c;
 645
 646        we = wake_events;
 647        while (we) {
 648                int from = 0, to = 0;
 649                char *task_from = NULL, *task_to = NULL;
 650
 651                /* locate the column of the waker and wakee */
 652                p = all_data;
 653                while (p) {
 654                        if (p->pid == we->waker || p->pid == we->wakee) {
 655                                c = p->all;
 656                                while (c) {
 657                                        if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
 658                                                if (p->pid == we->waker && !from) {
 659                                                        from = c->Y;
 660                                                        task_from = strdup(c->comm);
 661                                                }
 662                                                if (p->pid == we->wakee && !to) {
 663                                                        to = c->Y;
 664                                                        task_to = strdup(c->comm);
 665                                                }
 666                                        }
 667                                        c = c->next;
 668                                }
 669                                c = p->all;
 670                                while (c) {
 671                                        if (p->pid == we->waker && !from) {
 672                                                from = c->Y;
 673                                                task_from = strdup(c->comm);
 674                                        }
 675                                        if (p->pid == we->wakee && !to) {
 676                                                to = c->Y;
 677                                                task_to = strdup(c->comm);
 678                                        }
 679                                        c = c->next;
 680                                }
 681                        }
 682                        p = p->next;
 683                }
 684
 685                if (!task_from) {
 686                        task_from = malloc(40);
 687                        sprintf(task_from, "[%i]", we->waker);
 688                }
 689                if (!task_to) {
 690                        task_to = malloc(40);
 691                        sprintf(task_to, "[%i]", we->wakee);
 692                }
 693
 694                if (we->waker == -1)
 695                        svg_interrupt(we->time, to);
 696                else if (from && to && abs(from - to) == 1)
 697                        svg_wakeline(we->time, from, to);
 698                else
 699                        svg_partial_wakeline(we->time, from, task_from, to, task_to);
 700                we = we->next;
 701
 702                free(task_from);
 703                free(task_to);
 704        }
 705}
 706
 707static void draw_cpu_usage(void)
 708{
 709        struct per_pid *p;
 710        struct per_pidcomm *c;
 711        struct cpu_sample *sample;
 712        p = all_data;
 713        while (p) {
 714                c = p->all;
 715                while (c) {
 716                        sample = c->samples;
 717                        while (sample) {
 718                                if (sample->type == TYPE_RUNNING)
 719                                        svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
 720
 721                                sample = sample->next;
 722                        }
 723                        c = c->next;
 724                }
 725                p = p->next;
 726        }
 727}
 728
 729static void draw_process_bars(void)
 730{
 731        struct per_pid *p;
 732        struct per_pidcomm *c;
 733        struct cpu_sample *sample;
 734        int Y = 0;
 735
 736        Y = 2 * numcpus + 2;
 737
 738        p = all_data;
 739        while (p) {
 740                c = p->all;
 741                while (c) {
 742                        if (!c->display) {
 743                                c->Y = 0;
 744                                c = c->next;
 745                                continue;
 746                        }
 747
 748                        svg_box(Y, c->start_time, c->end_time, "process");
 749                        sample = c->samples;
 750                        while (sample) {
 751                                if (sample->type == TYPE_RUNNING)
 752                                        svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
 753                                if (sample->type == TYPE_BLOCKED)
 754                                        svg_box(Y, sample->start_time, sample->end_time, "blocked");
 755                                if (sample->type == TYPE_WAITING)
 756                                        svg_waiting(Y, sample->start_time, sample->end_time);
 757                                sample = sample->next;
 758                        }
 759
 760                        if (c->comm) {
 761                                char comm[256];
 762                                if (c->total_time > 5000000000) /* 5 seconds */
 763                                        sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
 764                                else
 765                                        sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
 766
 767                                svg_text(Y, c->start_time, comm);
 768                        }
 769                        c->Y = Y;
 770                        Y++;
 771                        c = c->next;
 772                }
 773                p = p->next;
 774        }
 775}
 776
 777static void add_process_filter(const char *string)
 778{
 779        struct process_filter *filt;
 780        int pid;
 781
 782        pid = strtoull(string, NULL, 10);
 783        filt = malloc(sizeof(struct process_filter));
 784        if (!filt)
 785                return;
 786
 787        filt->name = strdup(string);
 788        filt->pid  = pid;
 789        filt->next = process_filter;
 790
 791        process_filter = filt;
 792}
 793
 794static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
 795{
 796        struct process_filter *filt;
 797        if (!process_filter)
 798                return 1;
 799
 800        filt = process_filter;
 801        while (filt) {
 802                if (filt->pid && p->pid == filt->pid)
 803                        return 1;
 804                if (strcmp(filt->name, c->comm) == 0)
 805                        return 1;
 806                filt = filt->next;
 807        }
 808        return 0;
 809}
 810
 811static int determine_display_tasks_filtered(void)
 812{
 813        struct per_pid *p;
 814        struct per_pidcomm *c;
 815        int count = 0;
 816
 817        p = all_data;
 818        while (p) {
 819                p->display = 0;
 820                if (p->start_time == 1)
 821                        p->start_time = first_time;
 822
 823                /* no exit marker, task kept running to the end */
 824                if (p->end_time == 0)
 825                        p->end_time = last_time;
 826
 827                c = p->all;
 828
 829                while (c) {
 830                        c->display = 0;
 831
 832                        if (c->start_time == 1)
 833                                c->start_time = first_time;
 834
 835                        if (passes_filter(p, c)) {
 836                                c->display = 1;
 837                                p->display = 1;
 838                                count++;
 839                        }
 840
 841                        if (c->end_time == 0)
 842                                c->end_time = last_time;
 843
 844                        c = c->next;
 845                }
 846                p = p->next;
 847        }
 848        return count;
 849}
 850
 851static int determine_display_tasks(u64 threshold)
 852{
 853        struct per_pid *p;
 854        struct per_pidcomm *c;
 855        int count = 0;
 856
 857        if (process_filter)
 858                return determine_display_tasks_filtered();
 859
 860        p = all_data;
 861        while (p) {
 862                p->display = 0;
 863                if (p->start_time == 1)
 864                        p->start_time = first_time;
 865
 866                /* no exit marker, task kept running to the end */
 867                if (p->end_time == 0)
 868                        p->end_time = last_time;
 869                if (p->total_time >= threshold && !power_only)
 870                        p->display = 1;
 871
 872                c = p->all;
 873
 874                while (c) {
 875                        c->display = 0;
 876
 877                        if (c->start_time == 1)
 878                                c->start_time = first_time;
 879
 880                        if (c->total_time >= threshold && !power_only) {
 881                                c->display = 1;
 882                                count++;
 883                        }
 884
 885                        if (c->end_time == 0)
 886                                c->end_time = last_time;
 887
 888                        c = c->next;
 889                }
 890                p = p->next;
 891        }
 892        return count;
 893}
 894
 895
 896
 897#define TIME_THRESH 10000000
 898
 899static void write_svg_file(const char *filename)
 900{
 901        u64 i;
 902        int count;
 903
 904        numcpus++;
 905
 906
 907        count = determine_display_tasks(TIME_THRESH);
 908
 909        /* We'd like to show at least 15 tasks; be less picky if we have fewer */
 910        if (count < 15)
 911                count = determine_display_tasks(TIME_THRESH / 10);
 912
 913        open_svg(filename, numcpus, count, first_time, last_time);
 914
 915        svg_time_grid();
 916        svg_legenda();
 917
 918        for (i = 0; i < numcpus; i++)
 919                svg_cpu_box(i, max_freq, turbo_frequency);
 920
 921        draw_cpu_usage();
 922        draw_process_bars();
 923        draw_c_p_states();
 924        draw_wakeups();
 925
 926        svg_close();
 927}
 928
 929static struct perf_event_ops event_ops = {
 930        .comm                   = process_comm_event,
 931        .fork                   = process_fork_event,
 932        .exit                   = process_exit_event,
 933        .sample                 = process_sample_event,
 934        .ordered_samples        = true,
 935};
 936
 937static int __cmd_timechart(void)
 938{
 939        struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
 940        int ret = -EINVAL;
 941
 942        if (session == NULL)
 943                return -ENOMEM;
 944
 945        if (!perf_session__has_traces(session, "timechart record"))
 946                goto out_delete;
 947
 948        ret = perf_session__process_events(session, &event_ops);
 949        if (ret)
 950                goto out_delete;
 951
 952        end_sample_processing();
 953
 954        sort_pids();
 955
 956        write_svg_file(output_name);
 957
 958        pr_info("Written %2.1f seconds of trace to %s.\n",
 959                (last_time - first_time) / 1000000000.0, output_name);
 960out_delete:
 961        perf_session__delete(session);
 962        return ret;
 963}
 964
 965static const char * const timechart_usage[] = {
 966        "perf timechart [<options>] {record}",
 967        NULL
 968};
 969
 970static const char *record_args[] = {
 971        "record",
 972        "-a",
 973        "-R",
 974        "-f",
 975        "-c", "1",
 976        "-e", "power:power_start",
 977        "-e", "power:power_end",
 978        "-e", "power:power_frequency",
 979        "-e", "sched:sched_wakeup",
 980        "-e", "sched:sched_switch",
 981};
 982
 983static int __cmd_record(int argc, const char **argv)
 984{
 985        unsigned int rec_argc, i, j;
 986        const char **rec_argv;
 987
 988        rec_argc = ARRAY_SIZE(record_args) + argc - 1;
 989        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 990
 991        for (i = 0; i < ARRAY_SIZE(record_args); i++)
 992                rec_argv[i] = strdup(record_args[i]);
 993
 994        for (j = 1; j < (unsigned int)argc; j++, i++)
 995                rec_argv[i] = argv[j];
 996
 997        return cmd_record(i, rec_argv, NULL);
 998}
 999
1000static int
1001parse_process(const struct option *opt __used, const char *arg, int __used unset)
1002{
1003        if (arg)
1004                add_process_filter(arg);
1005        return 0;
1006}
1007
1008static const struct option options[] = {
1009        OPT_STRING('i', "input", &input_name, "file",
1010                    "input file name"),
1011        OPT_STRING('o', "output", &output_name, "file",
1012                    "output file name"),
1013        OPT_INTEGER('w', "width", &svg_page_width,
1014                    "page width"),
1015        OPT_BOOLEAN('P', "power-only", &power_only,
1016                    "output power data only"),
1017        OPT_CALLBACK('p', "process", NULL, "process",
1018                      "process selector. Pass a pid or process name.",
1019                       parse_process),
1020        OPT_END()
1021};
1022
1023
1024int cmd_timechart(int argc, const char **argv, const char *prefix __used)
1025{
1026        argc = parse_options(argc, argv, options, timechart_usage,
1027                        PARSE_OPT_STOP_AT_NON_OPTION);
1028
1029        symbol__init();
1030
1031        if (argc && !strncmp(argv[0], "rec", 3))
1032                return __cmd_record(argc, argv);
1033        else if (argc)
1034                usage_with_options(timechart_usage, options);
1035
1036        setup_pager();
1037
1038        return __cmd_timechart();
1039}
1040
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.