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,
 217                                        struct callchain_node *self,
 218                                        u64 total_samples)
 219{
 220        struct callchain_list *chain;
 221        size_t ret = 0;
 222
 223        if (!self)
 224                return 0;
 225
 226        ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
 227
 228
 229        list_for_each_entry(chain, &self->val, list) {
 230                if (chain->ip >= PERF_CONTEXT_MAX)
 231                        continue;
 232                if (chain->ms.sym)
 233                        ret += fprintf(fp, "                %s\n", chain->ms.sym->name);
 234                else
 235                        ret += fprintf(fp, "                %p\n",
 236                                        (void *)(long)chain->ip);
 237        }
 238
 239        return ret;
 240}
 241
 242static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
 243                                      u64 total_samples)
 244{
 245        size_t ret = 0;
 246        u32 entries_printed = 0;
 247        struct rb_node *rb_node;
 248        struct callchain_node *chain;
 249
 250        rb_node = rb_first(self);
 251        while (rb_node) {
 252                double percent;
 253
 254                chain = rb_entry(rb_node, struct callchain_node, rb_node);
 255                percent = chain->hit * 100.0 / total_samples;
 256
 257                ret = percent_color_fprintf(fp, "           %6.2f%%\n", percent);
 258                ret += __callchain__fprintf_flat(fp, chain, total_samples);
 259                ret += fprintf(fp, "\n");
 260                if (++entries_printed == callchain_param.print_limit)
 261                        break;
 262
 263                rb_node = rb_next(rb_node);
 264        }
 265
 266        return ret;
 267}
 268
 269static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
 270                                            u64 total_samples, int left_margin,
 271                                            FILE *fp)
 272{
 273        switch (callchain_param.mode) {
 274        case CHAIN_GRAPH_REL:
 275                return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
 276                                                left_margin);
 277                break;
 278        case CHAIN_GRAPH_ABS:
 279                return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
 280                                                left_margin);
 281                break;
 282        case CHAIN_FLAT:
 283                return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
 284                break;
 285        case CHAIN_NONE:
 286                break;
 287        default:
 288                pr_err("Bad callchain mode\n");
 289        }
 290
 291        return 0;
 292}
 293
 294static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
 295                                            struct hists *hists,
 296                                            FILE *fp)
 297{
 298        int left_margin = 0;
 299        u64 total_period = hists->stats.total_period;
 300
 301        if (sort__first_dimension == SORT_COMM) {
 302                struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
 303                                                         typeof(*se), list);
 304                left_margin = hists__col_len(hists, se->se_width_idx);
 305                left_margin -= thread__comm_len(he->thread);
 306        }
 307
 308        return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
 309}
 310
 311static int hist_entry__fprintf(struct hist_entry *he, size_t size,
 312                               struct hists *hists, FILE *fp)
 313{
 314        char bf[512];
 315        int ret;
 316        struct perf_hpp hpp = {
 317                .buf            = bf,
 318                .size           = size,
 319        };
 320        bool color = !symbol_conf.field_sep;
 321
 322        if (size == 0 || size > sizeof(bf))
 323                size = hpp.size = sizeof(bf);
 324
 325        ret = hist_entry__period_snprintf(&hpp, he, color);
 326        hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
 327
 328        ret = fprintf(fp, "%s\n", bf);
 329
 330        if (symbol_conf.use_callchain)
 331                ret += hist_entry__callchain_fprintf(he, hists, fp);
 332
 333        return ret;
 334}
 335
 336size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
 337                      int max_cols, FILE *fp)
 338{
 339        struct perf_hpp_fmt *fmt;
 340        struct sort_entry *se;
 341        struct rb_node *nd;
 342        size_t ret = 0;
 343        unsigned int width;
 344        const char *sep = symbol_conf.field_sep;
 345        const char *col_width = symbol_conf.col_width_list_str;
 346        int nr_rows = 0;
 347        char bf[96];
 348        struct perf_hpp dummy_hpp = {
 349                .buf    = bf,
 350                .size   = sizeof(bf),
 351                .ptr    = hists_to_evsel(hists),
 352        };
 353        bool first = true;
 354
 355        init_rem_hits();
 356
 357        if (!show_header)
 358                goto print_entries;
 359
 360        fprintf(fp, "# ");
 361
 362        perf_hpp__for_each_format(fmt) {
 363                if (!first)
 364                        fprintf(fp, "%s", sep ?: "  ");
 365                else
 366                        first = false;
 367
 368                fmt->header(&dummy_hpp);
 369                fprintf(fp, "%s", bf);
 370        }
 371
 372        list_for_each_entry(se, &hist_entry__sort_list, list) {
 373                if (se->elide)
 374                        continue;
 375                if (sep) {
 376                        fprintf(fp, "%c%s", *sep, se->se_header);
 377                        continue;
 378                }
 379                width = strlen(se->se_header);
 380                if (symbol_conf.col_width_list_str) {
 381                        if (col_width) {
 382                                hists__set_col_len(hists, se->se_width_idx,
 383                                                   atoi(col_width));
 384                                col_width = strchr(col_width, ',');
 385                                if (col_width)
 386                                        ++col_width;
 387                        }
 388                }
 389                if (!hists__new_col_len(hists, se->se_width_idx, width))
 390                        width = hists__col_len(hists, se->se_width_idx);
 391                fprintf(fp, "  %*s", width, se->se_header);
 392        }
 393
 394        fprintf(fp, "\n");
 395        if (max_rows && ++nr_rows >= max_rows)
 396                goto out;
 397
 398        if (sep)
 399                goto print_entries;
 400
 401        first = true;
 402
 403        fprintf(fp, "# ");
 404
 405        perf_hpp__for_each_format(fmt) {
 406                unsigned int i;
 407
 408                if (!first)
 409                        fprintf(fp, "%s", sep ?: "  ");
 410                else
 411                        first = false;
 412
 413                width = fmt->width(&dummy_hpp);
 414                for (i = 0; i < width; i++)
 415                        fprintf(fp, ".");
 416        }
 417
 418        list_for_each_entry(se, &hist_entry__sort_list, list) {
 419                unsigned int i;
 420
 421                if (se->elide)
 422                        continue;
 423
 424                fprintf(fp, "  ");
 425                width = hists__col_len(hists, se->se_width_idx);
 426                if (width == 0)
 427                        width = strlen(se->se_header);
 428                for (i = 0; i < width; i++)
 429                        fprintf(fp, ".");
 430        }
 431
 432        fprintf(fp, "\n");
 433        if (max_rows && ++nr_rows >= max_rows)
 434                goto out;
 435
 436        fprintf(fp, "#\n");
 437        if (max_rows && ++nr_rows >= max_rows)
 438                goto out;
 439
 440print_entries:
 441        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 442                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 443
 444                if (h->filtered)
 445                        continue;
 446
 447                ret += hist_entry__fprintf(h, max_cols, hists, fp);
 448
 449                if (max_rows && ++nr_rows >= max_rows)
 450                        goto out;
 451
 452                if (h->ms.map == NULL && verbose > 1) {
 453                        __map_groups__fprintf_maps(&h->thread->mg,
 454                                                   MAP__FUNCTION, verbose, fp);
 455                        fprintf(fp, "%.10s end\n", graph_dotted_line);
 456                }
 457        }
 458out:
 459        free(rem_sq_bracket);
 460
 461        return ret;
 462}
 463
 464size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
 465{
 466        int i;
 467        size_t ret = 0;
 468
 469        for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
 470                const char *name;
 471
 472                if (stats->nr_events[i] == 0)
 473                        continue;
 474
 475                name = perf_event__name(i);
 476                if (!strcmp(name, "UNKNOWN"))
 477                        continue;
 478
 479                ret += fprintf(fp, "%16s events: %10d\n", name,
 480                               stats->nr_events[i]);
 481        }
 482
 483        return ret;
 484}
 485