linux/tools/perf/ui/hist.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <inttypes.h>
   3#include <math.h>
   4#include <linux/compiler.h>
   5
   6#include "../util/callchain.h"
   7#include "../util/hist.h"
   8#include "../util/util.h"
   9#include "../util/sort.h"
  10#include "../util/evsel.h"
  11#include "../util/evlist.h"
  12
  13/* hist period print (hpp) functions */
  14
  15#define hpp__call_print_fn(hpp, fn, fmt, ...)                   \
  16({                                                              \
  17        int __ret = fn(hpp, fmt, ##__VA_ARGS__);                \
  18        advance_hpp(hpp, __ret);                                \
  19        __ret;                                                  \
  20})
  21
  22static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
  23                      hpp_field_fn get_field, const char *fmt, int len,
  24                      hpp_snprint_fn print_fn, bool fmt_percent)
  25{
  26        int ret;
  27        struct hists *hists = he->hists;
  28        struct perf_evsel *evsel = hists_to_evsel(hists);
  29        char *buf = hpp->buf;
  30        size_t size = hpp->size;
  31
  32        if (fmt_percent) {
  33                double percent = 0.0;
  34                u64 total = hists__total_period(hists);
  35
  36                if (total)
  37                        percent = 100.0 * get_field(he) / total;
  38
  39                ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
  40        } else
  41                ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
  42
  43        if (perf_evsel__is_group_event(evsel)) {
  44                int prev_idx, idx_delta;
  45                struct hist_entry *pair;
  46                int nr_members = evsel->nr_members;
  47
  48                prev_idx = perf_evsel__group_idx(evsel);
  49
  50                list_for_each_entry(pair, &he->pairs.head, pairs.node) {
  51                        u64 period = get_field(pair);
  52                        u64 total = hists__total_period(pair->hists);
  53
  54                        if (!total)
  55                                continue;
  56
  57                        evsel = hists_to_evsel(pair->hists);
  58                        idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
  59
  60                        while (idx_delta--) {
  61                                /*
  62                                 * zero-fill group members in the middle which
  63                                 * have no sample
  64                                 */
  65                                if (fmt_percent) {
  66                                        ret += hpp__call_print_fn(hpp, print_fn,
  67                                                                  fmt, len, 0.0);
  68                                } else {
  69                                        ret += hpp__call_print_fn(hpp, print_fn,
  70                                                                  fmt, len, 0ULL);
  71                                }
  72                        }
  73
  74                        if (fmt_percent) {
  75                                ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
  76                                                          100.0 * period / total);
  77                        } else {
  78                                ret += hpp__call_print_fn(hpp, print_fn, fmt,
  79                                                          len, period);
  80                        }
  81
  82                        prev_idx = perf_evsel__group_idx(evsel);
  83                }
  84
  85                idx_delta = nr_members - prev_idx - 1;
  86
  87                while (idx_delta--) {
  88                        /*
  89                         * zero-fill group members at last which have no sample
  90                         */
  91                        if (fmt_percent) {
  92                                ret += hpp__call_print_fn(hpp, print_fn,
  93                                                          fmt, len, 0.0);
  94                        } else {
  95                                ret += hpp__call_print_fn(hpp, print_fn,
  96                                                          fmt, len, 0ULL);
  97                        }
  98                }
  99        }
 100
 101        /*
 102         * Restore original buf and size as it's where caller expects
 103         * the result will be saved.
 104         */
 105        hpp->buf = buf;
 106        hpp->size = size;
 107
 108        return ret;
 109}
 110
 111int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
 112             struct hist_entry *he, hpp_field_fn get_field,
 113             const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
 114{
 115        int len = fmt->user_len ?: fmt->len;
 116
 117        if (symbol_conf.field_sep) {
 118                return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
 119                                  print_fn, fmt_percent);
 120        }
 121
 122        if (fmt_percent)
 123                len -= 2; /* 2 for a space and a % sign */
 124        else
 125                len -= 1;
 126
 127        return  __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
 128}
 129
 130int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
 131                 struct hist_entry *he, hpp_field_fn get_field,
 132                 const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
 133{
 134        if (!symbol_conf.cumulate_callchain) {
 135                int len = fmt->user_len ?: fmt->len;
 136                return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
 137        }
 138
 139        return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
 140}
 141
 142static int field_cmp(u64 field_a, u64 field_b)
 143{
 144        if (field_a > field_b)
 145                return 1;
 146        if (field_a < field_b)
 147                return -1;
 148        return 0;
 149}
 150
 151static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
 152                       hpp_field_fn get_field)
 153{
 154        s64 ret;
 155        int i, nr_members;
 156        struct perf_evsel *evsel;
 157        struct hist_entry *pair;
 158        u64 *fields_a, *fields_b;
 159
 160        ret = field_cmp(get_field(a), get_field(b));
 161        if (ret || !symbol_conf.event_group)
 162                return ret;
 163
 164        evsel = hists_to_evsel(a->hists);
 165        if (!perf_evsel__is_group_event(evsel))
 166                return ret;
 167
 168        nr_members = evsel->nr_members;
 169        fields_a = calloc(nr_members, sizeof(*fields_a));
 170        fields_b = calloc(nr_members, sizeof(*fields_b));
 171
 172        if (!fields_a || !fields_b)
 173                goto out;
 174
 175        list_for_each_entry(pair, &a->pairs.head, pairs.node) {
 176                evsel = hists_to_evsel(pair->hists);
 177                fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
 178        }
 179
 180        list_for_each_entry(pair, &b->pairs.head, pairs.node) {
 181                evsel = hists_to_evsel(pair->hists);
 182                fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
 183        }
 184
 185        for (i = 1; i < nr_members; i++) {
 186                ret = field_cmp(fields_a[i], fields_b[i]);
 187                if (ret)
 188                        break;
 189        }
 190
 191out:
 192        free(fields_a);
 193        free(fields_b);
 194
 195        return ret;
 196}
 197
 198static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
 199                           hpp_field_fn get_field)
 200{
 201        s64 ret = 0;
 202
 203        if (symbol_conf.cumulate_callchain) {
 204                /*
 205                 * Put caller above callee when they have equal period.
 206                 */
 207                ret = field_cmp(get_field(a), get_field(b));
 208                if (ret)
 209                        return ret;
 210
 211                if (a->thread != b->thread || !hist_entry__has_callchains(a) || !symbol_conf.use_callchain)
 212                        return 0;
 213
 214                ret = b->callchain->max_depth - a->callchain->max_depth;
 215                if (callchain_param.order == ORDER_CALLER)
 216                        ret = -ret;
 217        }
 218        return ret;
 219}
 220
 221static int hpp__width_fn(struct perf_hpp_fmt *fmt,
 222                         struct perf_hpp *hpp __maybe_unused,
 223                         struct hists *hists)
 224{
 225        int len = fmt->user_len ?: fmt->len;
 226        struct perf_evsel *evsel = hists_to_evsel(hists);
 227
 228        if (symbol_conf.event_group)
 229                len = max(len, evsel->nr_members * fmt->len);
 230
 231        if (len < (int)strlen(fmt->name))
 232                len = strlen(fmt->name);
 233
 234        return len;
 235}
 236
 237static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
 238                          struct hists *hists, int line __maybe_unused,
 239                          int *span __maybe_unused)
 240{
 241        int len = hpp__width_fn(fmt, hpp, hists);
 242        return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
 243}
 244
 245int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
 246{
 247        va_list args;
 248        ssize_t ssize = hpp->size;
 249        double percent;
 250        int ret, len;
 251
 252        va_start(args, fmt);
 253        len = va_arg(args, int);
 254        percent = va_arg(args, double);
 255        ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent);
 256        va_end(args);
 257
 258        return (ret >= ssize) ? (ssize - 1) : ret;
 259}
 260
 261static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
 262{
 263        va_list args;
 264        ssize_t ssize = hpp->size;
 265        int ret;
 266
 267        va_start(args, fmt);
 268        ret = vsnprintf(hpp->buf, hpp->size, fmt, args);
 269        va_end(args);
 270
 271        return (ret >= ssize) ? (ssize - 1) : ret;
 272}
 273
 274#define __HPP_COLOR_PERCENT_FN(_type, _field)                                   \
 275static u64 he_get_##_field(struct hist_entry *he)                               \
 276{                                                                               \
 277        return he->stat._field;                                                 \
 278}                                                                               \
 279                                                                                \
 280static int hpp__color_##_type(struct perf_hpp_fmt *fmt,                         \
 281                              struct perf_hpp *hpp, struct hist_entry *he)      \
 282{                                                                               \
 283        return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%",              \
 284                        hpp_color_scnprintf, true);                             \
 285}
 286
 287#define __HPP_ENTRY_PERCENT_FN(_type, _field)                                   \
 288static int hpp__entry_##_type(struct perf_hpp_fmt *fmt,                         \
 289                              struct perf_hpp *hpp, struct hist_entry *he)      \
 290{                                                                               \
 291        return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%",              \
 292                        hpp_entry_scnprintf, true);                             \
 293}
 294
 295#define __HPP_SORT_FN(_type, _field)                                            \
 296static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused,       \
 297                                 struct hist_entry *a, struct hist_entry *b)    \
 298{                                                                               \
 299        return __hpp__sort(a, b, he_get_##_field);                              \
 300}
 301
 302#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                               \
 303static u64 he_get_acc_##_field(struct hist_entry *he)                           \
 304{                                                                               \
 305        return he->stat_acc->_field;                                            \
 306}                                                                               \
 307                                                                                \
 308static int hpp__color_##_type(struct perf_hpp_fmt *fmt,                         \
 309                              struct perf_hpp *hpp, struct hist_entry *he)      \
 310{                                                                               \
 311        return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%",      \
 312                            hpp_color_scnprintf, true);                         \
 313}
 314
 315#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                               \
 316static int hpp__entry_##_type(struct perf_hpp_fmt *fmt,                         \
 317                              struct perf_hpp *hpp, struct hist_entry *he)      \
 318{                                                                               \
 319        return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%",      \
 320                            hpp_entry_scnprintf, true);                         \
 321}
 322
 323#define __HPP_SORT_ACC_FN(_type, _field)                                        \
 324static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused,       \
 325                                 struct hist_entry *a, struct hist_entry *b)    \
 326{                                                                               \
 327        return __hpp__sort_acc(a, b, he_get_acc_##_field);                      \
 328}
 329
 330#define __HPP_ENTRY_RAW_FN(_type, _field)                                       \
 331static u64 he_get_raw_##_field(struct hist_entry *he)                           \
 332{                                                                               \
 333        return he->stat._field;                                                 \
 334}                                                                               \
 335                                                                                \
 336static int hpp__entry_##_type(struct perf_hpp_fmt *fmt,                         \
 337                              struct perf_hpp *hpp, struct hist_entry *he)      \
 338{                                                                               \
 339        return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64,         \
 340                        hpp_entry_scnprintf, false);                            \
 341}
 342
 343#define __HPP_SORT_RAW_FN(_type, _field)                                        \
 344static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused,       \
 345                                 struct hist_entry *a, struct hist_entry *b)    \
 346{                                                                               \
 347        return __hpp__sort(a, b, he_get_raw_##_field);                          \
 348}
 349
 350
 351#define HPP_PERCENT_FNS(_type, _field)                                  \
 352__HPP_COLOR_PERCENT_FN(_type, _field)                                   \
 353__HPP_ENTRY_PERCENT_FN(_type, _field)                                   \
 354__HPP_SORT_FN(_type, _field)
 355
 356#define HPP_PERCENT_ACC_FNS(_type, _field)                              \
 357__HPP_COLOR_ACC_PERCENT_FN(_type, _field)                               \
 358__HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                               \
 359__HPP_SORT_ACC_FN(_type, _field)
 360
 361#define HPP_RAW_FNS(_type, _field)                                      \
 362__HPP_ENTRY_RAW_FN(_type, _field)                                       \
 363__HPP_SORT_RAW_FN(_type, _field)
 364
 365HPP_PERCENT_FNS(overhead, period)
 366HPP_PERCENT_FNS(overhead_sys, period_sys)
 367HPP_PERCENT_FNS(overhead_us, period_us)
 368HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys)
 369HPP_PERCENT_FNS(overhead_guest_us, period_guest_us)
 370HPP_PERCENT_ACC_FNS(overhead_acc, period)
 371
 372HPP_RAW_FNS(samples, nr_events)
 373HPP_RAW_FNS(period, period)
 374
 375static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
 376                            struct hist_entry *a __maybe_unused,
 377                            struct hist_entry *b __maybe_unused)
 378{
 379        return 0;
 380}
 381
 382static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a)
 383{
 384        return a->header == hpp__header_fn;
 385}
 386
 387static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
 388{
 389        if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b))
 390                return false;
 391
 392        return a->idx == b->idx;
 393}
 394
 395#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx)          \
 396        {                                               \
 397                .name   = _name,                        \
 398                .header = hpp__header_fn,               \
 399                .width  = hpp__width_fn,                \
 400                .color  = hpp__color_ ## _fn,           \
 401                .entry  = hpp__entry_ ## _fn,           \
 402                .cmp    = hpp__nop_cmp,                 \
 403                .collapse = hpp__nop_cmp,               \
 404                .sort   = hpp__sort_ ## _fn,            \
 405                .idx    = PERF_HPP__ ## _idx,           \
 406                .equal  = hpp__equal,                   \
 407        }
 408
 409#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx)      \
 410        {                                               \
 411                .name   = _name,                        \
 412                .header = hpp__header_fn,               \
 413                .width  = hpp__width_fn,                \
 414                .color  = hpp__color_ ## _fn,           \
 415                .entry  = hpp__entry_ ## _fn,           \
 416                .cmp    = hpp__nop_cmp,                 \
 417                .collapse = hpp__nop_cmp,               \
 418                .sort   = hpp__sort_ ## _fn,            \
 419                .idx    = PERF_HPP__ ## _idx,           \
 420                .equal  = hpp__equal,                   \
 421        }
 422
 423#define HPP__PRINT_FNS(_name, _fn, _idx)                \
 424        {                                               \
 425                .name   = _name,                        \
 426                .header = hpp__header_fn,               \
 427                .width  = hpp__width_fn,                \
 428                .entry  = hpp__entry_ ## _fn,           \
 429                .cmp    = hpp__nop_cmp,                 \
 430                .collapse = hpp__nop_cmp,               \
 431                .sort   = hpp__sort_ ## _fn,            \
 432                .idx    = PERF_HPP__ ## _idx,           \
 433                .equal  = hpp__equal,                   \
 434        }
 435
 436struct perf_hpp_fmt perf_hpp__format[] = {
 437        HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD),
 438        HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS),
 439        HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US),
 440        HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS),
 441        HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US),
 442        HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC),
 443        HPP__PRINT_FNS("Samples", samples, SAMPLES),
 444        HPP__PRINT_FNS("Period", period, PERIOD)
 445};
 446
 447struct perf_hpp_list perf_hpp_list = {
 448        .fields = LIST_HEAD_INIT(perf_hpp_list.fields),
 449        .sorts  = LIST_HEAD_INIT(perf_hpp_list.sorts),
 450        .nr_header_lines = 1,
 451};
 452
 453#undef HPP__COLOR_PRINT_FNS
 454#undef HPP__COLOR_ACC_PRINT_FNS
 455#undef HPP__PRINT_FNS
 456
 457#undef HPP_PERCENT_FNS
 458#undef HPP_PERCENT_ACC_FNS
 459#undef HPP_RAW_FNS
 460
 461#undef __HPP_HEADER_FN
 462#undef __HPP_WIDTH_FN
 463#undef __HPP_COLOR_PERCENT_FN
 464#undef __HPP_ENTRY_PERCENT_FN
 465#undef __HPP_COLOR_ACC_PERCENT_FN
 466#undef __HPP_ENTRY_ACC_PERCENT_FN
 467#undef __HPP_ENTRY_RAW_FN
 468#undef __HPP_SORT_FN
 469#undef __HPP_SORT_ACC_FN
 470#undef __HPP_SORT_RAW_FN
 471
 472
 473void perf_hpp__init(void)
 474{
 475        int i;
 476
 477        for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
 478                struct perf_hpp_fmt *fmt = &perf_hpp__format[i];
 479
 480                INIT_LIST_HEAD(&fmt->list);
 481
 482                /* sort_list may be linked by setup_sorting() */
 483                if (fmt->sort_list.next == NULL)
 484                        INIT_LIST_HEAD(&fmt->sort_list);
 485        }
 486
 487        /*
 488         * If user specified field order, no need to setup default fields.
 489         */
 490        if (is_strict_order(field_order))
 491                return;
 492
 493        if (symbol_conf.cumulate_callchain) {
 494                hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC);
 495                perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
 496        }
 497
 498        hpp_dimension__add_output(PERF_HPP__OVERHEAD);
 499
 500        if (symbol_conf.show_cpu_utilization) {
 501                hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS);
 502                hpp_dimension__add_output(PERF_HPP__OVERHEAD_US);
 503
 504                if (perf_guest) {
 505                        hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS);
 506                        hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US);
 507                }
 508        }
 509
 510        if (symbol_conf.show_nr_samples)
 511                hpp_dimension__add_output(PERF_HPP__SAMPLES);
 512
 513        if (symbol_conf.show_total_period)
 514                hpp_dimension__add_output(PERF_HPP__PERIOD);
 515}
 516
 517void perf_hpp_list__column_register(struct perf_hpp_list *list,
 518                                    struct perf_hpp_fmt *format)
 519{
 520        list_add_tail(&format->list, &list->fields);
 521}
 522
 523void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
 524                                        struct perf_hpp_fmt *format)
 525{
 526        list_add_tail(&format->sort_list, &list->sorts);
 527}
 528
 529void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list,
 530                                       struct perf_hpp_fmt *format)
 531{
 532        list_add(&format->sort_list, &list->sorts);
 533}
 534
 535void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
 536{
 537        list_del_init(&format->list);
 538}
 539
 540void perf_hpp__cancel_cumulate(void)
 541{
 542        struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp;
 543
 544        if (is_strict_order(field_order))
 545                return;
 546
 547        ovh = &perf_hpp__format[PERF_HPP__OVERHEAD];
 548        acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC];
 549
 550        perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) {
 551                if (acc->equal(acc, fmt)) {
 552                        perf_hpp__column_unregister(fmt);
 553                        continue;
 554                }
 555
 556                if (ovh->equal(ovh, fmt))
 557                        fmt->name = "Overhead";
 558        }
 559}
 560
 561static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
 562{
 563        return a->equal && a->equal(a, b);
 564}
 565
 566void perf_hpp__setup_output_field(struct perf_hpp_list *list)
 567{
 568        struct perf_hpp_fmt *fmt;
 569
 570        /* append sort keys to output field */
 571        perf_hpp_list__for_each_sort_list(list, fmt) {
 572                struct perf_hpp_fmt *pos;
 573
 574                /* skip sort-only fields ("sort_compute" in perf diff) */
 575                if (!fmt->entry && !fmt->color)
 576                        continue;
 577
 578                perf_hpp_list__for_each_format(list, pos) {
 579                        if (fmt_equal(fmt, pos))
 580                                goto next;
 581                }
 582
 583                perf_hpp__column_register(fmt);
 584next:
 585                continue;
 586        }
 587}
 588
 589void perf_hpp__append_sort_keys(struct perf_hpp_list *list)
 590{
 591        struct perf_hpp_fmt *fmt;
 592
 593        /* append output fields to sort keys */
 594        perf_hpp_list__for_each_format(list, fmt) {
 595                struct perf_hpp_fmt *pos;
 596
 597                perf_hpp_list__for_each_sort_list(list, pos) {
 598                        if (fmt_equal(fmt, pos))
 599                                goto next;
 600                }
 601
 602                perf_hpp__register_sort_field(fmt);
 603next:
 604                continue;
 605        }
 606}
 607
 608
 609static void fmt_free(struct perf_hpp_fmt *fmt)
 610{
 611        /*
 612         * At this point fmt should be completely
 613         * unhooked, if not it's a bug.
 614         */
 615        BUG_ON(!list_empty(&fmt->list));
 616        BUG_ON(!list_empty(&fmt->sort_list));
 617
 618        if (fmt->free)
 619                fmt->free(fmt);
 620}
 621
 622void perf_hpp__reset_output_field(struct perf_hpp_list *list)
 623{
 624        struct perf_hpp_fmt *fmt, *tmp;
 625
 626        /* reset output fields */
 627        perf_hpp_list__for_each_format_safe(list, fmt, tmp) {
 628                list_del_init(&fmt->list);
 629                list_del_init(&fmt->sort_list);
 630                fmt_free(fmt);
 631        }
 632
 633        /* reset sort keys */
 634        perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) {
 635                list_del_init(&fmt->list);
 636                list_del_init(&fmt->sort_list);
 637                fmt_free(fmt);
 638        }
 639}
 640
 641/*
 642 * See hists__fprintf to match the column widths
 643 */
 644unsigned int hists__sort_list_width(struct hists *hists)
 645{
 646        struct perf_hpp_fmt *fmt;
 647        int ret = 0;
 648        bool first = true;
 649        struct perf_hpp dummy_hpp;
 650
 651        hists__for_each_format(hists, fmt) {
 652                if (perf_hpp__should_skip(fmt, hists))
 653                        continue;
 654
 655                if (first)
 656                        first = false;
 657                else
 658                        ret += 2;
 659
 660                ret += fmt->width(fmt, &dummy_hpp, hists);
 661        }
 662
 663        if (verbose > 0 && hists__has(hists, sym)) /* Addr + origin */
 664                ret += 3 + BITS_PER_LONG / 4;
 665
 666        return ret;
 667}
 668
 669unsigned int hists__overhead_width(struct hists *hists)
 670{
 671        struct perf_hpp_fmt *fmt;
 672        int ret = 0;
 673        bool first = true;
 674        struct perf_hpp dummy_hpp;
 675
 676        hists__for_each_format(hists, fmt) {
 677                if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
 678                        break;
 679
 680                if (first)
 681                        first = false;
 682                else
 683                        ret += 2;
 684
 685                ret += fmt->width(fmt, &dummy_hpp, hists);
 686        }
 687
 688        return ret;
 689}
 690
 691void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
 692{
 693        if (perf_hpp__is_sort_entry(fmt))
 694                return perf_hpp__reset_sort_width(fmt, hists);
 695
 696        if (perf_hpp__is_dynamic_entry(fmt))
 697                return;
 698
 699        BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX);
 700
 701        switch (fmt->idx) {
 702        case PERF_HPP__OVERHEAD:
 703        case PERF_HPP__OVERHEAD_SYS:
 704        case PERF_HPP__OVERHEAD_US:
 705        case PERF_HPP__OVERHEAD_ACC:
 706                fmt->len = 8;
 707                break;
 708
 709        case PERF_HPP__OVERHEAD_GUEST_SYS:
 710        case PERF_HPP__OVERHEAD_GUEST_US:
 711                fmt->len = 9;
 712                break;
 713
 714        case PERF_HPP__SAMPLES:
 715        case PERF_HPP__PERIOD:
 716                fmt->len = 12;
 717                break;
 718
 719        default:
 720                break;
 721        }
 722}
 723
 724void hists__reset_column_width(struct hists *hists)
 725{
 726        struct perf_hpp_fmt *fmt;
 727        struct perf_hpp_list_node *node;
 728
 729        hists__for_each_format(hists, fmt)
 730                perf_hpp__reset_width(fmt, hists);
 731
 732        /* hierarchy entries have their own hpp list */
 733        list_for_each_entry(node, &hists->hpp_formats, list) {
 734                perf_hpp_list__for_each_format(&node->hpp, fmt)
 735                        perf_hpp__reset_width(fmt, hists);
 736        }
 737}
 738
 739void perf_hpp__set_user_width(const char *width_list_str)
 740{
 741        struct perf_hpp_fmt *fmt;
 742        const char *ptr = width_list_str;
 743
 744        perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
 745                char *p;
 746
 747                int len = strtol(ptr, &p, 10);
 748                fmt->user_len = len;
 749
 750                if (*p == ',')
 751                        ptr = p + 1;
 752                else
 753                        break;
 754        }
 755}
 756
 757static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt)
 758{
 759        struct perf_hpp_list_node *node = NULL;
 760        struct perf_hpp_fmt *fmt_copy;
 761        bool found = false;
 762        bool skip = perf_hpp__should_skip(fmt, hists);
 763
 764        list_for_each_entry(node, &hists->hpp_formats, list) {
 765                if (node->level == fmt->level) {
 766                        found = true;
 767                        break;
 768                }
 769        }
 770
 771        if (!found) {
 772                node = malloc(sizeof(*node));
 773                if (node == NULL)
 774                        return -1;
 775
 776                node->skip = skip;
 777                node->level = fmt->level;
 778                perf_hpp_list__init(&node->hpp);
 779
 780                hists->nr_hpp_node++;
 781                list_add_tail(&node->list, &hists->hpp_formats);
 782        }
 783
 784        fmt_copy = perf_hpp_fmt__dup(fmt);
 785        if (fmt_copy == NULL)
 786                return -1;
 787
 788        if (!skip)
 789                node->skip = false;
 790
 791        list_add_tail(&fmt_copy->list, &node->hpp.fields);
 792        list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts);
 793
 794        return 0;
 795}
 796
 797int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
 798                                  struct perf_evlist *evlist)
 799{
 800        struct perf_evsel *evsel;
 801        struct perf_hpp_fmt *fmt;
 802        struct hists *hists;
 803        int ret;
 804
 805        if (!symbol_conf.report_hierarchy)
 806                return 0;
 807
 808        evlist__for_each_entry(evlist, evsel) {
 809                hists = evsel__hists(evsel);
 810
 811                perf_hpp_list__for_each_sort_list(list, fmt) {
 812                        if (perf_hpp__is_dynamic_entry(fmt) &&
 813                            !perf_hpp__defined_dynamic_entry(fmt, hists))
 814                                continue;
 815
 816                        ret = add_hierarchy_fmt(hists, fmt);
 817                        if (ret < 0)
 818                                return ret;
 819                }
 820        }
 821
 822        return 0;
 823}
 824