linux/tools/perf/ui/stdio/hist.c
<<
>>
Prefs
   1#include <stdio.h>
   2
   3#include "../../util/util.h"
   4#include "../../util/hist.h"
   5#include "../../util/sort.h"
   6#include "../../util/evsel.h"
   7
   8
   9static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
  10{
  11        int i;
  12        int ret = fprintf(fp, "            ");
  13
  14        for (i = 0; i < left_margin; i++)
  15                ret += fprintf(fp, " ");
  16
  17        return ret;
  18}
  19
  20static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
  21                                          int left_margin)
  22{
  23        int i;
  24        size_t ret = callchain__fprintf_left_margin(fp, left_margin);
  25
  26        for (i = 0; i < depth; i++)
  27                if (depth_mask & (1 << i))
  28                        ret += fprintf(fp, "|          ");
  29                else
  30                        ret += fprintf(fp, "           ");
  31
  32        ret += fprintf(fp, "\n");
  33
  34        return ret;
  35}
  36
  37static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
  38                                     int depth, int depth_mask, int period,
  39                                     u64 total_samples, u64 hits,
  40                                     int left_margin)
  41{
  42        int i;
  43        size_t ret = 0;
  44
  45        ret += callchain__fprintf_left_margin(fp, left_margin);
  46        for (i = 0; i < depth; i++) {
  47                if (depth_mask & (1 << i))
  48                        ret += fprintf(fp, "|");
  49                else
  50                        ret += fprintf(fp, " ");
  51                if (!period && i == depth - 1) {
  52                        double percent;
  53
  54                        percent = hits * 100.0 / total_samples;
  55                        ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
  56                } else
  57                        ret += fprintf(fp, "%s", "          ");
  58        }
  59        if (chain->ms.sym)
  60                ret += fprintf(fp, "%s\n", chain->ms.sym->name);
  61        else
  62                ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
  63
  64        return ret;
  65}
  66
  67static struct symbol *rem_sq_bracket;
  68static struct callchain_list rem_hits;
  69
  70static void init_rem_hits(void)
  71{
  72        rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
  73        if (!rem_sq_bracket) {
  74                fprintf(stderr, "Not enough memory to display remaining hits\n");
  75                return;
  76        }
  77
  78        strcpy(rem_sq_bracket->name, "[...]");
  79        rem_hits.ms.sym = rem_sq_bracket;
  80}
  81
  82static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
  83                                         u64 total_samples, int depth,
  84                                         int depth_mask, int left_margin)
  85{
  86        struct rb_node *node, *next;
  87        struct callchain_node *child;
  88        struct callchain_list *chain;
  89        int new_depth_mask = depth_mask;
  90        u64 remaining;
  91        size_t ret = 0;
  92        int i;
  93        uint entries_printed = 0;
  94
  95        remaining = total_samples;
  96
  97        node = rb_first(root);
  98        while (node) {
  99                u64 new_total;
 100                u64 cumul;
 101
 102                child = rb_entry(node, struct callchain_node, rb_node);
 103                cumul = callchain_cumul_hits(child);
 104                remaining -= cumul;
 105
 106                /*
 107                 * The depth mask manages the output of pipes that show
 108                 * the depth. We don't want to keep the pipes of the current
 109                 * level for the last child of this depth.
 110                 * Except if we have remaining filtered hits. They will
 111                 * supersede the last child
 112                 */
 113                next = rb_next(node);
 114                if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
 115                        new_depth_mask &= ~(1 << (depth - 1));
 116
 117                /*
 118                 * But we keep the older depth mask for the line separator
 119                 * to keep the level link until we reach the last child
 120                 */
 121                ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
 122                                                   left_margin);
 123                i = 0;
 124                list_for_each_entry(chain, &child->val, list) {
 125                        ret += ipchain__fprintf_graph(fp, chain, depth,
 126                                                      new_depth_mask, i++,
 127                                                      total_samples,
 128                                                      cumul,
 129                                                      left_margin);
 130                }
 131
 132                if (callchain_param.mode == CHAIN_GRAPH_REL)
 133                        new_total = child->children_hit;
 134                else
 135                        new_total = total_samples;
 136
 137                ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
 138                                                  depth + 1,
 139                                                  new_depth_mask | (1 << depth),
 140                                                  left_margin);
 141                node = next;
 142                if (++entries_printed == callchain_param.print_limit)
 143                        break;
 144        }
 145
 146        if (callchain_param.mode == CHAIN_GRAPH_REL &&
 147                remaining && remaining != total_samples) {
 148
 149                if (!rem_sq_bracket)
 150                        return ret;
 151
 152                new_depth_mask &= ~(1 << (depth - 1));
 153                ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
 154                                              new_depth_mask, 0, total_samples,
 155                                              remaining, left_margin);
 156        }
 157
 158        return ret;
 159}
 160
 161static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
 162                                       u64 total_samples, int left_margin)
 163{
 164        struct callchain_node *cnode;
 165        struct callchain_list *chain;
 166        u32 entries_printed = 0;
 167        bool printed = false;
 168        struct rb_node *node;
 169        int i = 0;
 170        int ret = 0;
 171
 172        /*
 173         * If have one single callchain root, don't bother printing
 174         * its percentage (100 % in fractal mode and the same percentage
 175         * than the hist in graph mode). This also avoid one level of column.
 176         */
 177        node = rb_first(root);
 178        if (node && !rb_next(node)) {
 179                cnode = rb_entry(node, struct callchain_node, rb_node);
 180                list_for_each_entry(chain, &cnode->val, list) {
 181                        /*
 182                         * If we sort by symbol, the first entry is the same than
 183                         * the symbol. No need to print it otherwise it appears as
 184                         * displayed twice.
 185                         */
 186                        if (!i++ && sort__first_dimension == SORT_SYM)
 187                                continue;
 188                        if (!printed) {
 189                                ret += callchain__fprintf_left_margin(fp, left_margin);
 190                                ret += fprintf(fp, "|\n");
 191                                ret += callchain__fprintf_left_margin(fp, left_margin);
 192                                ret += fprintf(fp, "---");
 193                                left_margin += 3;
 194                                printed = true;
 195                        } else
 196                                ret += callchain__fprintf_left_margin(fp, left_margin);
 197
 198                        if (chain->ms.sym)
 199                                ret += fprintf(fp, " %s\n", chain->ms.sym->name);
 200                        else
 201                                ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
 202
 203                        if (++entries_printed == callchain_param.print_limit)
 204                                break;
 205                }
 206                root = &cnode->rb_root;
 207        }
 208
 209        ret += __callchain__fprintf_graph(fp, root, total_samples,
 210                                          1, 1, left_margin);
 211        ret += fprintf(fp, "\n");
 212
 213        return ret;
 214}
 215
 216static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
 217                                        u64 total_samples)
 218{
 219        struct callchain_list *chain;
 220        size_t ret = 0;
 221
 222        if (!node)
 223                return 0;
 224
 225        ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
 226
 227
 228        list_for_each_entry(chain, &node->val, list) {
 229                if (chain->ip >= PERF_CONTEXT_MAX)
 230                        continue;
 231                if (chain->ms.sym)
 232                        ret += fprintf(fp, "                %s\n", chain->ms.sym->name);
 233                else
 234                        ret += fprintf(fp, "                %p\n",
 235                                        (void *)(long)chain->ip);
 236        }
 237
 238        return ret;
 239}
 240
 241static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
 242                                      u64 total_samples)
 243{
 244        size_t ret = 0;
 245        u32 entries_printed = 0;
 246        struct callchain_node *chain;
 247        struct rb_node *rb_node = rb_first(tree);
 248
 249        while (rb_node) {
 250                double percent;
 251
 252                chain = rb_entry(rb_node, struct callchain_node, rb_node);
 253                percent = chain->hit * 100.0 / total_samples;
 254
 255                ret = percent_color_fprintf(fp, "           %6.2f%%\n", percent);
 256                ret += __callchain__fprintf_flat(fp, chain, total_samples);
 257                ret += fprintf(fp, "\n");
 258                if (++entries_printed == callchain_param.print_limit)
 259                        break;
 260
 261                rb_node = rb_next(rb_node);
 262        }
 263
 264        return ret;
 265}
 266
 267static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
 268                                            u64 total_samples, int left_margin,
 269                                            FILE *fp)
 270{
 271        switch (callchain_param.mode) {
 272        case CHAIN_GRAPH_REL:
 273                return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
 274                                                left_margin);
 275                break;
 276        case CHAIN_GRAPH_ABS:
 277                return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
 278                                                left_margin);
 279                break;
 280        case CHAIN_FLAT:
 281                return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
 282                break;
 283        case CHAIN_NONE:
 284                break;
 285        default:
 286                pr_err("Bad callchain mode\n");
 287        }
 288
 289        return 0;
 290}
 291
 292static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
 293                                            struct hists *hists,
 294                                            FILE *fp)
 295{
 296        int left_margin = 0;
 297        u64 total_period = hists->stats.total_period;
 298
 299        if (sort__first_dimension == SORT_COMM) {
 300                struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
 301                                                         typeof(*se), list);
 302                left_margin = hists__col_len(hists, se->se_width_idx);
 303                left_margin -= thread__comm_len(he->thread);
 304        }
 305
 306        return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
 307}
 308
 309static int hist_entry__period_snprintf(struct perf_hpp *hpp,
 310                                       struct hist_entry *he)
 311{
 312        const char *sep = symbol_conf.field_sep;
 313        struct perf_hpp_fmt *fmt;
 314        char *start = hpp->buf;
 315        int ret;
 316        bool first = true;
 317
 318        if (symbol_conf.exclude_other && !he->parent)
 319                return 0;
 320
 321        perf_hpp__for_each_format(fmt) {
 322                /*
 323                 * If there's no field_sep, we still need
 324                 * to display initial '  '.
 325                 */
 326                if (!sep || !first) {
 327                        ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
 328                        advance_hpp(hpp, ret);
 329                } else
 330                        first = false;
 331
 332                if (perf_hpp__use_color() && fmt->color)
 333                        ret = fmt->color(fmt, hpp, he);
 334                else
 335                        ret = fmt->entry(fmt, hpp, he);
 336
 337                advance_hpp(hpp, ret);
 338        }
 339
 340        return hpp->buf - start;
 341}
 342
 343static int hist_entry__fprintf(struct hist_entry *he, size_t size,
 344                               struct hists *hists,
 345                               char *bf, size_t bfsz, FILE *fp)
 346{
 347        int ret;
 348        struct perf_hpp hpp = {
 349                .buf            = bf,
 350                .size           = size,
 351        };
 352
 353        if (size == 0 || size > bfsz)
 354                size = hpp.size = bfsz;
 355
 356        ret = hist_entry__period_snprintf(&hpp, he);
 357        hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
 358
 359        ret = fprintf(fp, "%s\n", bf);
 360
 361        if (symbol_conf.use_callchain)
 362                ret += hist_entry__callchain_fprintf(he, hists, fp);
 363
 364        return ret;
 365}
 366
 367size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
 368                      int max_cols, float min_pcnt, FILE *fp)
 369{
 370        struct perf_hpp_fmt *fmt;
 371        struct sort_entry *se;
 372        struct rb_node *nd;
 373        size_t ret = 0;
 374        unsigned int width;
 375        const char *sep = symbol_conf.field_sep;
 376        const char *col_width = symbol_conf.col_width_list_str;
 377        int nr_rows = 0;
 378        char bf[96];
 379        struct perf_hpp dummy_hpp = {
 380                .buf    = bf,
 381                .size   = sizeof(bf),
 382        };
 383        bool first = true;
 384        size_t linesz;
 385        char *line = NULL;
 386
 387        init_rem_hits();
 388
 389        if (!show_header)
 390                goto print_entries;
 391
 392        fprintf(fp, "# ");
 393
 394        perf_hpp__for_each_format(fmt) {
 395                if (!first)
 396                        fprintf(fp, "%s", sep ?: "  ");
 397                else
 398                        first = false;
 399
 400                fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
 401                fprintf(fp, "%s", bf);
 402        }
 403
 404        list_for_each_entry(se, &hist_entry__sort_list, list) {
 405                if (se->elide)
 406                        continue;
 407                if (sep) {
 408                        fprintf(fp, "%c%s", *sep, se->se_header);
 409                        continue;
 410                }
 411                width = strlen(se->se_header);
 412                if (symbol_conf.col_width_list_str) {
 413                        if (col_width) {
 414                                hists__set_col_len(hists, se->se_width_idx,
 415                                                   atoi(col_width));
 416                                col_width = strchr(col_width, ',');
 417                                if (col_width)
 418                                        ++col_width;
 419                        }
 420                }
 421                if (!hists__new_col_len(hists, se->se_width_idx, width))
 422                        width = hists__col_len(hists, se->se_width_idx);
 423                fprintf(fp, "  %*s", width, se->se_header);
 424        }
 425
 426        fprintf(fp, "\n");
 427        if (max_rows && ++nr_rows >= max_rows)
 428                goto out;
 429
 430        if (sep)
 431                goto print_entries;
 432
 433        first = true;
 434
 435        fprintf(fp, "# ");
 436
 437        perf_hpp__for_each_format(fmt) {
 438                unsigned int i;
 439
 440                if (!first)
 441                        fprintf(fp, "%s", sep ?: "  ");
 442                else
 443                        first = false;
 444
 445                width = fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
 446                for (i = 0; i < width; i++)
 447                        fprintf(fp, ".");
 448        }
 449
 450        list_for_each_entry(se, &hist_entry__sort_list, list) {
 451                unsigned int i;
 452
 453                if (se->elide)
 454                        continue;
 455
 456                fprintf(fp, "  ");
 457                width = hists__col_len(hists, se->se_width_idx);
 458                if (width == 0)
 459                        width = strlen(se->se_header);
 460                for (i = 0; i < width; i++)
 461                        fprintf(fp, ".");
 462        }
 463
 464        fprintf(fp, "\n");
 465        if (max_rows && ++nr_rows >= max_rows)
 466                goto out;
 467
 468        fprintf(fp, "#\n");
 469        if (max_rows && ++nr_rows >= max_rows)
 470                goto out;
 471
 472print_entries:
 473        linesz = hists__sort_list_width(hists) + 3 + 1;
 474        linesz += perf_hpp__color_overhead();
 475        line = malloc(linesz);
 476        if (line == NULL) {
 477                ret = -1;
 478                goto out;
 479        }
 480
 481        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 482                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 483                float percent = h->stat.period * 100.0 /
 484                                        hists->stats.total_period;
 485
 486                if (h->filtered)
 487                        continue;
 488
 489                if (percent < min_pcnt)
 490                        continue;
 491
 492                ret += hist_entry__fprintf(h, max_cols, hists, line, linesz, fp);
 493
 494                if (max_rows && ++nr_rows >= max_rows)
 495                        break;
 496
 497                if (h->ms.map == NULL && verbose > 1) {
 498                        __map_groups__fprintf_maps(&h->thread->mg,
 499                                                   MAP__FUNCTION, verbose, fp);
 500                        fprintf(fp, "%.10s end\n", graph_dotted_line);
 501                }
 502        }
 503
 504        free(line);
 505out:
 506        zfree(&rem_sq_bracket);
 507
 508        return ret;
 509}
 510
 511size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
 512{
 513        int i;
 514        size_t ret = 0;
 515
 516        for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
 517                const char *name;
 518
 519                if (stats->nr_events[i] == 0)
 520                        continue;
 521
 522                name = perf_event__name(i);
 523                if (!strcmp(name, "UNKNOWN"))
 524                        continue;
 525
 526                ret += fprintf(fp, "%16s events: %10d\n", name,
 527                               stats->nr_events[i]);
 528        }
 529
 530        return ret;
 531}
 532