~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to tools/perf/util/hist.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "annotate.h"
 
2
#include "util.h"
 
3
#include "build-id.h"
 
4
#include "hist.h"
 
5
#include "session.h"
 
6
#include "sort.h"
 
7
#include <math.h>
 
8
 
 
9
static bool hists__filter_entry_by_dso(struct hists *hists,
 
10
                                       struct hist_entry *he);
 
11
static bool hists__filter_entry_by_thread(struct hists *hists,
 
12
                                          struct hist_entry *he);
 
13
 
 
14
enum hist_filter {
 
15
        HIST_FILTER__DSO,
 
16
        HIST_FILTER__THREAD,
 
17
        HIST_FILTER__PARENT,
 
18
};
 
19
 
 
20
struct callchain_param  callchain_param = {
 
21
        .mode   = CHAIN_GRAPH_REL,
 
22
        .min_percent = 0.5,
 
23
        .order  = ORDER_CALLEE
 
24
};
 
25
 
 
26
u16 hists__col_len(struct hists *hists, enum hist_column col)
 
27
{
 
28
        return hists->col_len[col];
 
29
}
 
30
 
 
31
void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
 
32
{
 
33
        hists->col_len[col] = len;
 
34
}
 
35
 
 
36
bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
 
37
{
 
38
        if (len > hists__col_len(hists, col)) {
 
39
                hists__set_col_len(hists, col, len);
 
40
                return true;
 
41
        }
 
42
        return false;
 
43
}
 
44
 
 
45
static void hists__reset_col_len(struct hists *hists)
 
46
{
 
47
        enum hist_column col;
 
48
 
 
49
        for (col = 0; col < HISTC_NR_COLS; ++col)
 
50
                hists__set_col_len(hists, col, 0);
 
51
}
 
52
 
 
53
static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
 
54
{
 
55
        u16 len;
 
56
 
 
57
        if (h->ms.sym)
 
58
                hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
 
59
        else {
 
60
                const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
 
61
 
 
62
                if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
 
63
                    !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
 
64
                    !symbol_conf.dso_list)
 
65
                        hists__set_col_len(hists, HISTC_DSO,
 
66
                                           unresolved_col_width);
 
67
        }
 
68
 
 
69
        len = thread__comm_len(h->thread);
 
70
        if (hists__new_col_len(hists, HISTC_COMM, len))
 
71
                hists__set_col_len(hists, HISTC_THREAD, len + 6);
 
72
 
 
73
        if (h->ms.map) {
 
74
                len = dso__name_len(h->ms.map->dso);
 
75
                hists__new_col_len(hists, HISTC_DSO, len);
 
76
        }
 
77
}
 
78
 
 
79
static void hist_entry__add_cpumode_period(struct hist_entry *self,
 
80
                                           unsigned int cpumode, u64 period)
 
81
{
 
82
        switch (cpumode) {
 
83
        case PERF_RECORD_MISC_KERNEL:
 
84
                self->period_sys += period;
 
85
                break;
 
86
        case PERF_RECORD_MISC_USER:
 
87
                self->period_us += period;
 
88
                break;
 
89
        case PERF_RECORD_MISC_GUEST_KERNEL:
 
90
                self->period_guest_sys += period;
 
91
                break;
 
92
        case PERF_RECORD_MISC_GUEST_USER:
 
93
                self->period_guest_us += period;
 
94
                break;
 
95
        default:
 
96
                break;
 
97
        }
 
98
}
 
99
 
 
100
static void hist_entry__decay(struct hist_entry *he)
 
101
{
 
102
        he->period = (he->period * 7) / 8;
 
103
        he->nr_events = (he->nr_events * 7) / 8;
 
104
}
 
105
 
 
106
static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
 
107
{
 
108
        u64 prev_period = he->period;
 
109
 
 
110
        if (prev_period == 0)
 
111
                return true;
 
112
 
 
113
        hist_entry__decay(he);
 
114
 
 
115
        if (!he->filtered)
 
116
                hists->stats.total_period -= prev_period - he->period;
 
117
 
 
118
        return he->period == 0;
 
119
}
 
120
 
 
121
static void __hists__decay_entries(struct hists *hists, bool zap_user,
 
122
                                   bool zap_kernel, bool threaded)
 
123
{
 
124
        struct rb_node *next = rb_first(&hists->entries);
 
125
        struct hist_entry *n;
 
126
 
 
127
        while (next) {
 
128
                n = rb_entry(next, struct hist_entry, rb_node);
 
129
                next = rb_next(&n->rb_node);
 
130
                /*
 
131
                 * We may be annotating this, for instance, so keep it here in
 
132
                 * case some it gets new samples, we'll eventually free it when
 
133
                 * the user stops browsing and it agains gets fully decayed.
 
134
                 */
 
135
                if (((zap_user && n->level == '.') ||
 
136
                     (zap_kernel && n->level != '.') ||
 
137
                     hists__decay_entry(hists, n)) &&
 
138
                    !n->used) {
 
139
                        rb_erase(&n->rb_node, &hists->entries);
 
140
 
 
141
                        if (sort__need_collapse || threaded)
 
142
                                rb_erase(&n->rb_node_in, &hists->entries_collapsed);
 
143
 
 
144
                        hist_entry__free(n);
 
145
                        --hists->nr_entries;
 
146
                }
 
147
        }
 
148
}
 
149
 
 
150
void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
 
151
{
 
152
        return __hists__decay_entries(hists, zap_user, zap_kernel, false);
 
153
}
 
154
 
 
155
void hists__decay_entries_threaded(struct hists *hists,
 
156
                                   bool zap_user, bool zap_kernel)
 
157
{
 
158
        return __hists__decay_entries(hists, zap_user, zap_kernel, true);
 
159
}
 
160
 
 
161
/*
 
162
 * histogram, sorted on item, collects periods
 
163
 */
 
164
 
 
165
static struct hist_entry *hist_entry__new(struct hist_entry *template)
 
166
{
 
167
        size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
 
168
        struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
 
169
 
 
170
        if (self != NULL) {
 
171
                *self = *template;
 
172
                self->nr_events = 1;
 
173
                if (self->ms.map)
 
174
                        self->ms.map->referenced = true;
 
175
                if (symbol_conf.use_callchain)
 
176
                        callchain_init(self->callchain);
 
177
        }
 
178
 
 
179
        return self;
 
180
}
 
181
 
 
182
static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
 
183
{
 
184
        if (!h->filtered) {
 
185
                hists__calc_col_len(hists, h);
 
186
                ++hists->nr_entries;
 
187
                hists->stats.total_period += h->period;
 
188
        }
 
189
}
 
190
 
 
191
static u8 symbol__parent_filter(const struct symbol *parent)
 
192
{
 
193
        if (symbol_conf.exclude_other && parent == NULL)
 
194
                return 1 << HIST_FILTER__PARENT;
 
195
        return 0;
 
196
}
 
197
 
 
198
struct hist_entry *__hists__add_entry(struct hists *hists,
 
199
                                      struct addr_location *al,
 
200
                                      struct symbol *sym_parent, u64 period)
 
201
{
 
202
        struct rb_node **p;
 
203
        struct rb_node *parent = NULL;
 
204
        struct hist_entry *he;
 
205
        struct hist_entry entry = {
 
206
                .thread = al->thread,
 
207
                .ms = {
 
208
                        .map    = al->map,
 
209
                        .sym    = al->sym,
 
210
                },
 
211
                .cpu    = al->cpu,
 
212
                .ip     = al->addr,
 
213
                .level  = al->level,
 
214
                .period = period,
 
215
                .parent = sym_parent,
 
216
                .filtered = symbol__parent_filter(sym_parent),
 
217
        };
 
218
        int cmp;
 
219
 
 
220
        pthread_mutex_lock(&hists->lock);
 
221
 
 
222
        p = &hists->entries_in->rb_node;
 
223
 
 
224
        while (*p != NULL) {
 
225
                parent = *p;
 
226
                he = rb_entry(parent, struct hist_entry, rb_node_in);
 
227
 
 
228
                cmp = hist_entry__cmp(&entry, he);
 
229
 
 
230
                if (!cmp) {
 
231
                        he->period += period;
 
232
                        ++he->nr_events;
 
233
                        goto out;
 
234
                }
 
235
 
 
236
                if (cmp < 0)
 
237
                        p = &(*p)->rb_left;
 
238
                else
 
239
                        p = &(*p)->rb_right;
 
240
        }
 
241
 
 
242
        he = hist_entry__new(&entry);
 
243
        if (!he)
 
244
                goto out_unlock;
 
245
 
 
246
        rb_link_node(&he->rb_node_in, parent, p);
 
247
        rb_insert_color(&he->rb_node_in, hists->entries_in);
 
248
out:
 
249
        hist_entry__add_cpumode_period(he, al->cpumode, period);
 
250
out_unlock:
 
251
        pthread_mutex_unlock(&hists->lock);
 
252
        return he;
 
253
}
 
254
 
 
255
int64_t
 
256
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
 
257
{
 
258
        struct sort_entry *se;
 
259
        int64_t cmp = 0;
 
260
 
 
261
        list_for_each_entry(se, &hist_entry__sort_list, list) {
 
262
                cmp = se->se_cmp(left, right);
 
263
                if (cmp)
 
264
                        break;
 
265
        }
 
266
 
 
267
        return cmp;
 
268
}
 
269
 
 
270
int64_t
 
271
hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
 
272
{
 
273
        struct sort_entry *se;
 
274
        int64_t cmp = 0;
 
275
 
 
276
        list_for_each_entry(se, &hist_entry__sort_list, list) {
 
277
                int64_t (*f)(struct hist_entry *, struct hist_entry *);
 
278
 
 
279
                f = se->se_collapse ?: se->se_cmp;
 
280
 
 
281
                cmp = f(left, right);
 
282
                if (cmp)
 
283
                        break;
 
284
        }
 
285
 
 
286
        return cmp;
 
287
}
 
288
 
 
289
void hist_entry__free(struct hist_entry *he)
 
290
{
 
291
        free(he);
 
292
}
 
293
 
 
294
/*
 
295
 * collapse the histogram
 
296
 */
 
297
 
 
298
static bool hists__collapse_insert_entry(struct hists *hists,
 
299
                                         struct rb_root *root,
 
300
                                         struct hist_entry *he)
 
301
{
 
302
        struct rb_node **p = &root->rb_node;
 
303
        struct rb_node *parent = NULL;
 
304
        struct hist_entry *iter;
 
305
        int64_t cmp;
 
306
 
 
307
        while (*p != NULL) {
 
308
                parent = *p;
 
309
                iter = rb_entry(parent, struct hist_entry, rb_node_in);
 
310
 
 
311
                cmp = hist_entry__collapse(iter, he);
 
312
 
 
313
                if (!cmp) {
 
314
                        iter->period += he->period;
 
315
                        iter->nr_events += he->nr_events;
 
316
                        if (symbol_conf.use_callchain) {
 
317
                                callchain_cursor_reset(&hists->callchain_cursor);
 
318
                                callchain_merge(&hists->callchain_cursor, iter->callchain,
 
319
                                                he->callchain);
 
320
                        }
 
321
                        hist_entry__free(he);
 
322
                        return false;
 
323
                }
 
324
 
 
325
                if (cmp < 0)
 
326
                        p = &(*p)->rb_left;
 
327
                else
 
328
                        p = &(*p)->rb_right;
 
329
        }
 
330
 
 
331
        rb_link_node(&he->rb_node_in, parent, p);
 
332
        rb_insert_color(&he->rb_node_in, root);
 
333
        return true;
 
334
}
 
335
 
 
336
static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
 
337
{
 
338
        struct rb_root *root;
 
339
 
 
340
        pthread_mutex_lock(&hists->lock);
 
341
 
 
342
        root = hists->entries_in;
 
343
        if (++hists->entries_in > &hists->entries_in_array[1])
 
344
                hists->entries_in = &hists->entries_in_array[0];
 
345
 
 
346
        pthread_mutex_unlock(&hists->lock);
 
347
 
 
348
        return root;
 
349
}
 
350
 
 
351
static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
 
352
{
 
353
        hists__filter_entry_by_dso(hists, he);
 
354
        hists__filter_entry_by_thread(hists, he);
 
355
}
 
356
 
 
357
static void __hists__collapse_resort(struct hists *hists, bool threaded)
 
358
{
 
359
        struct rb_root *root;
 
360
        struct rb_node *next;
 
361
        struct hist_entry *n;
 
362
 
 
363
        if (!sort__need_collapse && !threaded)
 
364
                return;
 
365
 
 
366
        root = hists__get_rotate_entries_in(hists);
 
367
        next = rb_first(root);
 
368
 
 
369
        while (next) {
 
370
                n = rb_entry(next, struct hist_entry, rb_node_in);
 
371
                next = rb_next(&n->rb_node_in);
 
372
 
 
373
                rb_erase(&n->rb_node_in, root);
 
374
                if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
 
375
                        /*
 
376
                         * If it wasn't combined with one of the entries already
 
377
                         * collapsed, we need to apply the filters that may have
 
378
                         * been set by, say, the hist_browser.
 
379
                         */
 
380
                        hists__apply_filters(hists, n);
 
381
                }
 
382
        }
 
383
}
 
384
 
 
385
void hists__collapse_resort(struct hists *hists)
 
386
{
 
387
        return __hists__collapse_resort(hists, false);
 
388
}
 
389
 
 
390
void hists__collapse_resort_threaded(struct hists *hists)
 
391
{
 
392
        return __hists__collapse_resort(hists, true);
 
393
}
 
394
 
 
395
/*
 
396
 * reverse the map, sort on period.
 
397
 */
 
398
 
 
399
static void __hists__insert_output_entry(struct rb_root *entries,
 
400
                                         struct hist_entry *he,
 
401
                                         u64 min_callchain_hits)
 
402
{
 
403
        struct rb_node **p = &entries->rb_node;
 
404
        struct rb_node *parent = NULL;
 
405
        struct hist_entry *iter;
 
406
 
 
407
        if (symbol_conf.use_callchain)
 
408
                callchain_param.sort(&he->sorted_chain, he->callchain,
 
409
                                      min_callchain_hits, &callchain_param);
 
410
 
 
411
        while (*p != NULL) {
 
412
                parent = *p;
 
413
                iter = rb_entry(parent, struct hist_entry, rb_node);
 
414
 
 
415
                if (he->period > iter->period)
 
416
                        p = &(*p)->rb_left;
 
417
                else
 
418
                        p = &(*p)->rb_right;
 
419
        }
 
420
 
 
421
        rb_link_node(&he->rb_node, parent, p);
 
422
        rb_insert_color(&he->rb_node, entries);
 
423
}
 
424
 
 
425
static void __hists__output_resort(struct hists *hists, bool threaded)
 
426
{
 
427
        struct rb_root *root;
 
428
        struct rb_node *next;
 
429
        struct hist_entry *n;
 
430
        u64 min_callchain_hits;
 
431
 
 
432
        min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);
 
433
 
 
434
        if (sort__need_collapse || threaded)
 
435
                root = &hists->entries_collapsed;
 
436
        else
 
437
                root = hists->entries_in;
 
438
 
 
439
        next = rb_first(root);
 
440
        hists->entries = RB_ROOT;
 
441
 
 
442
        hists->nr_entries = 0;
 
443
        hists->stats.total_period = 0;
 
444
        hists__reset_col_len(hists);
 
445
 
 
446
        while (next) {
 
447
                n = rb_entry(next, struct hist_entry, rb_node_in);
 
448
                next = rb_next(&n->rb_node_in);
 
449
 
 
450
                __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
 
451
                hists__inc_nr_entries(hists, n);
 
452
        }
 
453
}
 
454
 
 
455
void hists__output_resort(struct hists *hists)
 
456
{
 
457
        return __hists__output_resort(hists, false);
 
458
}
 
459
 
 
460
void hists__output_resort_threaded(struct hists *hists)
 
461
{
 
462
        return __hists__output_resort(hists, true);
 
463
}
 
464
 
 
465
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
 
466
{
 
467
        int i;
 
468
        int ret = fprintf(fp, "            ");
 
469
 
 
470
        for (i = 0; i < left_margin; i++)
 
471
                ret += fprintf(fp, " ");
 
472
 
 
473
        return ret;
 
474
}
 
475
 
 
476
static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
 
477
                                          int left_margin)
 
478
{
 
479
        int i;
 
480
        size_t ret = callchain__fprintf_left_margin(fp, left_margin);
 
481
 
 
482
        for (i = 0; i < depth; i++)
 
483
                if (depth_mask & (1 << i))
 
484
                        ret += fprintf(fp, "|          ");
 
485
                else
 
486
                        ret += fprintf(fp, "           ");
 
487
 
 
488
        ret += fprintf(fp, "\n");
 
489
 
 
490
        return ret;
 
491
}
 
492
 
 
493
static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
 
494
                                     int depth, int depth_mask, int period,
 
495
                                     u64 total_samples, u64 hits,
 
496
                                     int left_margin)
 
497
{
 
498
        int i;
 
499
        size_t ret = 0;
 
500
 
 
501
        ret += callchain__fprintf_left_margin(fp, left_margin);
 
502
        for (i = 0; i < depth; i++) {
 
503
                if (depth_mask & (1 << i))
 
504
                        ret += fprintf(fp, "|");
 
505
                else
 
506
                        ret += fprintf(fp, " ");
 
507
                if (!period && i == depth - 1) {
 
508
                        double percent;
 
509
 
 
510
                        percent = hits * 100.0 / total_samples;
 
511
                        ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
 
512
                } else
 
513
                        ret += fprintf(fp, "%s", "          ");
 
514
        }
 
515
        if (chain->ms.sym)
 
516
                ret += fprintf(fp, "%s\n", chain->ms.sym->name);
 
517
        else
 
518
                ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
 
519
 
 
520
        return ret;
 
521
}
 
522
 
 
523
static struct symbol *rem_sq_bracket;
 
524
static struct callchain_list rem_hits;
 
525
 
 
526
static void init_rem_hits(void)
 
527
{
 
528
        rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
 
529
        if (!rem_sq_bracket) {
 
530
                fprintf(stderr, "Not enough memory to display remaining hits\n");
 
531
                return;
 
532
        }
 
533
 
 
534
        strcpy(rem_sq_bracket->name, "[...]");
 
535
        rem_hits.ms.sym = rem_sq_bracket;
 
536
}
 
537
 
 
538
static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
 
539
                                         u64 total_samples, int depth,
 
540
                                         int depth_mask, int left_margin)
 
541
{
 
542
        struct rb_node *node, *next;
 
543
        struct callchain_node *child;
 
544
        struct callchain_list *chain;
 
545
        int new_depth_mask = depth_mask;
 
546
        u64 new_total;
 
547
        u64 remaining;
 
548
        size_t ret = 0;
 
549
        int i;
 
550
        uint entries_printed = 0;
 
551
 
 
552
        if (callchain_param.mode == CHAIN_GRAPH_REL)
 
553
                new_total = self->children_hit;
 
554
        else
 
555
                new_total = total_samples;
 
556
 
 
557
        remaining = new_total;
 
558
 
 
559
        node = rb_first(&self->rb_root);
 
560
        while (node) {
 
561
                u64 cumul;
 
562
 
 
563
                child = rb_entry(node, struct callchain_node, rb_node);
 
564
                cumul = callchain_cumul_hits(child);
 
565
                remaining -= cumul;
 
566
 
 
567
                /*
 
568
                 * The depth mask manages the output of pipes that show
 
569
                 * the depth. We don't want to keep the pipes of the current
 
570
                 * level for the last child of this depth.
 
571
                 * Except if we have remaining filtered hits. They will
 
572
                 * supersede the last child
 
573
                 */
 
574
                next = rb_next(node);
 
575
                if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
 
576
                        new_depth_mask &= ~(1 << (depth - 1));
 
577
 
 
578
                /*
 
579
                 * But we keep the older depth mask for the line separator
 
580
                 * to keep the level link until we reach the last child
 
581
                 */
 
582
                ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
 
583
                                                   left_margin);
 
584
                i = 0;
 
585
                list_for_each_entry(chain, &child->val, list) {
 
586
                        ret += ipchain__fprintf_graph(fp, chain, depth,
 
587
                                                      new_depth_mask, i++,
 
588
                                                      new_total,
 
589
                                                      cumul,
 
590
                                                      left_margin);
 
591
                }
 
592
                ret += __callchain__fprintf_graph(fp, child, new_total,
 
593
                                                  depth + 1,
 
594
                                                  new_depth_mask | (1 << depth),
 
595
                                                  left_margin);
 
596
                node = next;
 
597
                if (++entries_printed == callchain_param.print_limit)
 
598
                        break;
 
599
        }
 
600
 
 
601
        if (callchain_param.mode == CHAIN_GRAPH_REL &&
 
602
                remaining && remaining != new_total) {
 
603
 
 
604
                if (!rem_sq_bracket)
 
605
                        return ret;
 
606
 
 
607
                new_depth_mask &= ~(1 << (depth - 1));
 
608
 
 
609
                ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
 
610
                                              new_depth_mask, 0, new_total,
 
611
                                              remaining, left_margin);
 
612
        }
 
613
 
 
614
        return ret;
 
615
}
 
616
 
 
617
static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
 
618
                                       u64 total_samples, int left_margin)
 
619
{
 
620
        struct callchain_list *chain;
 
621
        bool printed = false;
 
622
        int i = 0;
 
623
        int ret = 0;
 
624
        u32 entries_printed = 0;
 
625
 
 
626
        list_for_each_entry(chain, &self->val, list) {
 
627
                if (!i++ && sort__first_dimension == SORT_SYM)
 
628
                        continue;
 
629
 
 
630
                if (!printed) {
 
631
                        ret += callchain__fprintf_left_margin(fp, left_margin);
 
632
                        ret += fprintf(fp, "|\n");
 
633
                        ret += callchain__fprintf_left_margin(fp, left_margin);
 
634
                        ret += fprintf(fp, "---");
 
635
 
 
636
                        left_margin += 3;
 
637
                        printed = true;
 
638
                } else
 
639
                        ret += callchain__fprintf_left_margin(fp, left_margin);
 
640
 
 
641
                if (chain->ms.sym)
 
642
                        ret += fprintf(fp, " %s\n", chain->ms.sym->name);
 
643
                else
 
644
                        ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
 
645
 
 
646
                if (++entries_printed == callchain_param.print_limit)
 
647
                        break;
 
648
        }
 
649
 
 
650
        ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
 
651
 
 
652
        return ret;
 
653
}
 
654
 
 
655
static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
 
656
                                      u64 total_samples)
 
657
{
 
658
        struct callchain_list *chain;
 
659
        size_t ret = 0;
 
660
 
 
661
        if (!self)
 
662
                return 0;
 
663
 
 
664
        ret += callchain__fprintf_flat(fp, self->parent, total_samples);
 
665
 
 
666
 
 
667
        list_for_each_entry(chain, &self->val, list) {
 
668
                if (chain->ip >= PERF_CONTEXT_MAX)
 
669
                        continue;
 
670
                if (chain->ms.sym)
 
671
                        ret += fprintf(fp, "                %s\n", chain->ms.sym->name);
 
672
                else
 
673
                        ret += fprintf(fp, "                %p\n",
 
674
                                        (void *)(long)chain->ip);
 
675
        }
 
676
 
 
677
        return ret;
 
678
}
 
679
 
 
680
static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
 
681
                                            u64 total_samples, int left_margin)
 
682
{
 
683
        struct rb_node *rb_node;
 
684
        struct callchain_node *chain;
 
685
        size_t ret = 0;
 
686
        u32 entries_printed = 0;
 
687
 
 
688
        rb_node = rb_first(&self->sorted_chain);
 
689
        while (rb_node) {
 
690
                double percent;
 
691
 
 
692
                chain = rb_entry(rb_node, struct callchain_node, rb_node);
 
693
                percent = chain->hit * 100.0 / total_samples;
 
694
                switch (callchain_param.mode) {
 
695
                case CHAIN_FLAT:
 
696
                        ret += percent_color_fprintf(fp, "           %6.2f%%\n",
 
697
                                                     percent);
 
698
                        ret += callchain__fprintf_flat(fp, chain, total_samples);
 
699
                        break;
 
700
                case CHAIN_GRAPH_ABS: /* Falldown */
 
701
                case CHAIN_GRAPH_REL:
 
702
                        ret += callchain__fprintf_graph(fp, chain, total_samples,
 
703
                                                        left_margin);
 
704
                case CHAIN_NONE:
 
705
                default:
 
706
                        break;
 
707
                }
 
708
                ret += fprintf(fp, "\n");
 
709
                if (++entries_printed == callchain_param.print_limit)
 
710
                        break;
 
711
                rb_node = rb_next(rb_node);
 
712
        }
 
713
 
 
714
        return ret;
 
715
}
 
716
 
 
717
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
 
718
{
 
719
        struct rb_node *next = rb_first(&hists->entries);
 
720
        struct hist_entry *n;
 
721
        int row = 0;
 
722
 
 
723
        hists__reset_col_len(hists);
 
724
 
 
725
        while (next && row++ < max_rows) {
 
726
                n = rb_entry(next, struct hist_entry, rb_node);
 
727
                if (!n->filtered)
 
728
                        hists__calc_col_len(hists, n);
 
729
                next = rb_next(&n->rb_node);
 
730
        }
 
731
}
 
732
 
 
733
static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s,
 
734
                                     size_t size, struct hists *pair_hists,
 
735
                                     bool show_displacement, long displacement,
 
736
                                     bool color, u64 session_total)
 
737
{
 
738
        u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
 
739
        u64 nr_events;
 
740
        const char *sep = symbol_conf.field_sep;
 
741
        int ret;
 
742
 
 
743
        if (symbol_conf.exclude_other && !self->parent)
 
744
                return 0;
 
745
 
 
746
        if (pair_hists) {
 
747
                period = self->pair ? self->pair->period : 0;
 
748
                nr_events = self->pair ? self->pair->nr_events : 0;
 
749
                total = pair_hists->stats.total_period;
 
750
                period_sys = self->pair ? self->pair->period_sys : 0;
 
751
                period_us = self->pair ? self->pair->period_us : 0;
 
752
                period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
 
753
                period_guest_us = self->pair ? self->pair->period_guest_us : 0;
 
754
        } else {
 
755
                period = self->period;
 
756
                nr_events = self->nr_events;
 
757
                total = session_total;
 
758
                period_sys = self->period_sys;
 
759
                period_us = self->period_us;
 
760
                period_guest_sys = self->period_guest_sys;
 
761
                period_guest_us = self->period_guest_us;
 
762
        }
 
763
 
 
764
        if (total) {
 
765
                if (color)
 
766
                        ret = percent_color_snprintf(s, size,
 
767
                                                     sep ? "%.2f" : "   %6.2f%%",
 
768
                                                     (period * 100.0) / total);
 
769
                else
 
770
                        ret = snprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
 
771
                                       (period * 100.0) / total);
 
772
                if (symbol_conf.show_cpu_utilization) {
 
773
                        ret += percent_color_snprintf(s + ret, size - ret,
 
774
                                        sep ? "%.2f" : "   %6.2f%%",
 
775
                                        (period_sys * 100.0) / total);
 
776
                        ret += percent_color_snprintf(s + ret, size - ret,
 
777
                                        sep ? "%.2f" : "   %6.2f%%",
 
778
                                        (period_us * 100.0) / total);
 
779
                        if (perf_guest) {
 
780
                                ret += percent_color_snprintf(s + ret,
 
781
                                                size - ret,
 
782
                                                sep ? "%.2f" : "   %6.2f%%",
 
783
                                                (period_guest_sys * 100.0) /
 
784
                                                                total);
 
785
                                ret += percent_color_snprintf(s + ret,
 
786
                                                size - ret,
 
787
                                                sep ? "%.2f" : "   %6.2f%%",
 
788
                                                (period_guest_us * 100.0) /
 
789
                                                                total);
 
790
                        }
 
791
                }
 
792
        } else
 
793
                ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
 
794
 
 
795
        if (symbol_conf.show_nr_samples) {
 
796
                if (sep)
 
797
                        ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
 
798
                else
 
799
                        ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
 
800
        }
 
801
 
 
802
        if (symbol_conf.show_total_period) {
 
803
                if (sep)
 
804
                        ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
 
805
                else
 
806
                        ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
 
807
        }
 
808
 
 
809
        if (pair_hists) {
 
810
                char bf[32];
 
811
                double old_percent = 0, new_percent = 0, diff;
 
812
 
 
813
                if (total > 0)
 
814
                        old_percent = (period * 100.0) / total;
 
815
                if (session_total > 0)
 
816
                        new_percent = (self->period * 100.0) / session_total;
 
817
 
 
818
                diff = new_percent - old_percent;
 
819
 
 
820
                if (fabs(diff) >= 0.01)
 
821
                        snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
 
822
                else
 
823
                        snprintf(bf, sizeof(bf), " ");
 
824
 
 
825
                if (sep)
 
826
                        ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
 
827
                else
 
828
                        ret += snprintf(s + ret, size - ret, "%11.11s", bf);
 
829
 
 
830
                if (show_displacement) {
 
831
                        if (displacement)
 
832
                                snprintf(bf, sizeof(bf), "%+4ld", displacement);
 
833
                        else
 
834
                                snprintf(bf, sizeof(bf), " ");
 
835
 
 
836
                        if (sep)
 
837
                                ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
 
838
                        else
 
839
                                ret += snprintf(s + ret, size - ret, "%6.6s", bf);
 
840
                }
 
841
        }
 
842
 
 
843
        return ret;
 
844
}
 
845
 
 
846
int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
 
847
                         struct hists *hists)
 
848
{
 
849
        const char *sep = symbol_conf.field_sep;
 
850
        struct sort_entry *se;
 
851
        int ret = 0;
 
852
 
 
853
        list_for_each_entry(se, &hist_entry__sort_list, list) {
 
854
                if (se->elide)
 
855
                        continue;
 
856
 
 
857
                ret += snprintf(s + ret, size - ret, "%s", sep ?: "  ");
 
858
                ret += se->se_snprintf(he, s + ret, size - ret,
 
859
                                       hists__col_len(hists, se->se_width_idx));
 
860
        }
 
861
 
 
862
        return ret;
 
863
}
 
864
 
 
865
int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
 
866
                        struct hists *pair_hists, bool show_displacement,
 
867
                        long displacement, FILE *fp, u64 session_total)
 
868
{
 
869
        char bf[512];
 
870
        int ret;
 
871
 
 
872
        if (size == 0 || size > sizeof(bf))
 
873
                size = sizeof(bf);
 
874
 
 
875
        ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
 
876
                                        show_displacement, displacement,
 
877
                                        true, session_total);
 
878
        hist_entry__snprintf(he, bf + ret, size - ret, hists);
 
879
        return fprintf(fp, "%s\n", bf);
 
880
}
 
881
 
 
882
static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
 
883
                                            struct hists *hists, FILE *fp,
 
884
                                            u64 session_total)
 
885
{
 
886
        int left_margin = 0;
 
887
 
 
888
        if (sort__first_dimension == SORT_COMM) {
 
889
                struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
 
890
                                                         typeof(*se), list);
 
891
                left_margin = hists__col_len(hists, se->se_width_idx);
 
892
                left_margin -= thread__comm_len(self->thread);
 
893
        }
 
894
 
 
895
        return hist_entry_callchain__fprintf(fp, self, session_total,
 
896
                                             left_margin);
 
897
}
 
898
 
 
899
size_t hists__fprintf(struct hists *hists, struct hists *pair,
 
900
                      bool show_displacement, bool show_header, int max_rows,
 
901
                      int max_cols, FILE *fp)
 
902
{
 
903
        struct sort_entry *se;
 
904
        struct rb_node *nd;
 
905
        size_t ret = 0;
 
906
        unsigned long position = 1;
 
907
        long displacement = 0;
 
908
        unsigned int width;
 
909
        const char *sep = symbol_conf.field_sep;
 
910
        const char *col_width = symbol_conf.col_width_list_str;
 
911
        int nr_rows = 0;
 
912
 
 
913
        init_rem_hits();
 
914
 
 
915
        if (!show_header)
 
916
                goto print_entries;
 
917
 
 
918
        fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
 
919
 
 
920
        if (symbol_conf.show_nr_samples) {
 
921
                if (sep)
 
922
                        fprintf(fp, "%cSamples", *sep);
 
923
                else
 
924
                        fputs("  Samples  ", fp);
 
925
        }
 
926
 
 
927
        if (symbol_conf.show_total_period) {
 
928
                if (sep)
 
929
                        ret += fprintf(fp, "%cPeriod", *sep);
 
930
                else
 
931
                        ret += fprintf(fp, "   Period    ");
 
932
        }
 
933
 
 
934
        if (symbol_conf.show_cpu_utilization) {
 
935
                if (sep) {
 
936
                        ret += fprintf(fp, "%csys", *sep);
 
937
                        ret += fprintf(fp, "%cus", *sep);
 
938
                        if (perf_guest) {
 
939
                                ret += fprintf(fp, "%cguest sys", *sep);
 
940
                                ret += fprintf(fp, "%cguest us", *sep);
 
941
                        }
 
942
                } else {
 
943
                        ret += fprintf(fp, "  sys  ");
 
944
                        ret += fprintf(fp, "  us  ");
 
945
                        if (perf_guest) {
 
946
                                ret += fprintf(fp, "  guest sys  ");
 
947
                                ret += fprintf(fp, "  guest us  ");
 
948
                        }
 
949
                }
 
950
        }
 
951
 
 
952
        if (pair) {
 
953
                if (sep)
 
954
                        ret += fprintf(fp, "%cDelta", *sep);
 
955
                else
 
956
                        ret += fprintf(fp, "  Delta    ");
 
957
 
 
958
                if (show_displacement) {
 
959
                        if (sep)
 
960
                                ret += fprintf(fp, "%cDisplacement", *sep);
 
961
                        else
 
962
                                ret += fprintf(fp, " Displ");
 
963
                }
 
964
        }
 
965
 
 
966
        list_for_each_entry(se, &hist_entry__sort_list, list) {
 
967
                if (se->elide)
 
968
                        continue;
 
969
                if (sep) {
 
970
                        fprintf(fp, "%c%s", *sep, se->se_header);
 
971
                        continue;
 
972
                }
 
973
                width = strlen(se->se_header);
 
974
                if (symbol_conf.col_width_list_str) {
 
975
                        if (col_width) {
 
976
                                hists__set_col_len(hists, se->se_width_idx,
 
977
                                                   atoi(col_width));
 
978
                                col_width = strchr(col_width, ',');
 
979
                                if (col_width)
 
980
                                        ++col_width;
 
981
                        }
 
982
                }
 
983
                if (!hists__new_col_len(hists, se->se_width_idx, width))
 
984
                        width = hists__col_len(hists, se->se_width_idx);
 
985
                fprintf(fp, "  %*s", width, se->se_header);
 
986
        }
 
987
 
 
988
        fprintf(fp, "\n");
 
989
        if (max_rows && ++nr_rows >= max_rows)
 
990
                goto out;
 
991
 
 
992
        if (sep)
 
993
                goto print_entries;
 
994
 
 
995
        fprintf(fp, "# ........");
 
996
        if (symbol_conf.show_nr_samples)
 
997
                fprintf(fp, " ..........");
 
998
        if (symbol_conf.show_total_period)
 
999
                fprintf(fp, " ............");
 
1000
        if (pair) {
 
1001
                fprintf(fp, " ..........");
 
1002
                if (show_displacement)
 
1003
                        fprintf(fp, " .....");
 
1004
        }
 
1005
        list_for_each_entry(se, &hist_entry__sort_list, list) {
 
1006
                unsigned int i;
 
1007
 
 
1008
                if (se->elide)
 
1009
                        continue;
 
1010
 
 
1011
                fprintf(fp, "  ");
 
1012
                width = hists__col_len(hists, se->se_width_idx);
 
1013
                if (width == 0)
 
1014
                        width = strlen(se->se_header);
 
1015
                for (i = 0; i < width; i++)
 
1016
                        fprintf(fp, ".");
 
1017
        }
 
1018
 
 
1019
        fprintf(fp, "\n");
 
1020
        if (max_rows && ++nr_rows >= max_rows)
 
1021
                goto out;
 
1022
 
 
1023
        fprintf(fp, "#\n");
 
1024
        if (max_rows && ++nr_rows >= max_rows)
 
1025
                goto out;
 
1026
 
 
1027
print_entries:
 
1028
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 
1029
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
1030
 
 
1031
                if (h->filtered)
 
1032
                        continue;
 
1033
 
 
1034
                if (show_displacement) {
 
1035
                        if (h->pair != NULL)
 
1036
                                displacement = ((long)h->pair->position -
 
1037
                                                (long)position);
 
1038
                        else
 
1039
                                displacement = 0;
 
1040
                        ++position;
 
1041
                }
 
1042
                ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
 
1043
                                           displacement, fp, hists->stats.total_period);
 
1044
 
 
1045
                if (symbol_conf.use_callchain)
 
1046
                        ret += hist_entry__fprintf_callchain(h, hists, fp,
 
1047
                                                             hists->stats.total_period);
 
1048
                if (max_rows && ++nr_rows >= max_rows)
 
1049
                        goto out;
 
1050
 
 
1051
                if (h->ms.map == NULL && verbose > 1) {
 
1052
                        __map_groups__fprintf_maps(&h->thread->mg,
 
1053
                                                   MAP__FUNCTION, verbose, fp);
 
1054
                        fprintf(fp, "%.10s end\n", graph_dotted_line);
 
1055
                }
 
1056
        }
 
1057
out:
 
1058
        free(rem_sq_bracket);
 
1059
 
 
1060
        return ret;
 
1061
}
 
1062
 
 
1063
/*
 
1064
 * See hists__fprintf to match the column widths
 
1065
 */
 
1066
unsigned int hists__sort_list_width(struct hists *hists)
 
1067
{
 
1068
        struct sort_entry *se;
 
1069
        int ret = 9; /* total % */
 
1070
 
 
1071
        if (symbol_conf.show_cpu_utilization) {
 
1072
                ret += 7; /* count_sys % */
 
1073
                ret += 6; /* count_us % */
 
1074
                if (perf_guest) {
 
1075
                        ret += 13; /* count_guest_sys % */
 
1076
                        ret += 12; /* count_guest_us % */
 
1077
                }
 
1078
        }
 
1079
 
 
1080
        if (symbol_conf.show_nr_samples)
 
1081
                ret += 11;
 
1082
 
 
1083
        if (symbol_conf.show_total_period)
 
1084
                ret += 13;
 
1085
 
 
1086
        list_for_each_entry(se, &hist_entry__sort_list, list)
 
1087
                if (!se->elide)
 
1088
                        ret += 2 + hists__col_len(hists, se->se_width_idx);
 
1089
 
 
1090
        if (verbose) /* Addr + origin */
 
1091
                ret += 3 + BITS_PER_LONG / 4;
 
1092
 
 
1093
        return ret;
 
1094
}
 
1095
 
 
1096
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
 
1097
                                       enum hist_filter filter)
 
1098
{
 
1099
        h->filtered &= ~(1 << filter);
 
1100
        if (h->filtered)
 
1101
                return;
 
1102
 
 
1103
        ++hists->nr_entries;
 
1104
        if (h->ms.unfolded)
 
1105
                hists->nr_entries += h->nr_rows;
 
1106
        h->row_offset = 0;
 
1107
        hists->stats.total_period += h->period;
 
1108
        hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
 
1109
 
 
1110
        hists__calc_col_len(hists, h);
 
1111
}
 
1112
 
 
1113
 
 
1114
static bool hists__filter_entry_by_dso(struct hists *hists,
 
1115
                                       struct hist_entry *he)
 
1116
{
 
1117
        if (hists->dso_filter != NULL &&
 
1118
            (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
 
1119
                he->filtered |= (1 << HIST_FILTER__DSO);
 
1120
                return true;
 
1121
        }
 
1122
 
 
1123
        return false;
 
1124
}
 
1125
 
 
1126
void hists__filter_by_dso(struct hists *hists)
 
1127
{
 
1128
        struct rb_node *nd;
 
1129
 
 
1130
        hists->nr_entries = hists->stats.total_period = 0;
 
1131
        hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
 
1132
        hists__reset_col_len(hists);
 
1133
 
 
1134
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 
1135
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
1136
 
 
1137
                if (symbol_conf.exclude_other && !h->parent)
 
1138
                        continue;
 
1139
 
 
1140
                if (hists__filter_entry_by_dso(hists, h))
 
1141
                        continue;
 
1142
 
 
1143
                hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
 
1144
        }
 
1145
}
 
1146
 
 
1147
static bool hists__filter_entry_by_thread(struct hists *hists,
 
1148
                                          struct hist_entry *he)
 
1149
{
 
1150
        if (hists->thread_filter != NULL &&
 
1151
            he->thread != hists->thread_filter) {
 
1152
                he->filtered |= (1 << HIST_FILTER__THREAD);
 
1153
                return true;
 
1154
        }
 
1155
 
 
1156
        return false;
 
1157
}
 
1158
 
 
1159
void hists__filter_by_thread(struct hists *hists)
 
1160
{
 
1161
        struct rb_node *nd;
 
1162
 
 
1163
        hists->nr_entries = hists->stats.total_period = 0;
 
1164
        hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
 
1165
        hists__reset_col_len(hists);
 
1166
 
 
1167
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 
1168
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
1169
 
 
1170
                if (hists__filter_entry_by_thread(hists, h))
 
1171
                        continue;
 
1172
 
 
1173
                hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
 
1174
        }
 
1175
}
 
1176
 
 
1177
int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
 
1178
{
 
1179
        return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
 
1180
}
 
1181
 
 
1182
int hist_entry__annotate(struct hist_entry *he, size_t privsize)
 
1183
{
 
1184
        return symbol__annotate(he->ms.sym, he->ms.map, privsize);
 
1185
}
 
1186
 
 
1187
void hists__inc_nr_events(struct hists *hists, u32 type)
 
1188
{
 
1189
        ++hists->stats.nr_events[0];
 
1190
        ++hists->stats.nr_events[type];
 
1191
}
 
1192
 
 
1193
size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
 
1194
{
 
1195
        int i;
 
1196
        size_t ret = 0;
 
1197
 
 
1198
        for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
 
1199
                const char *name;
 
1200
 
 
1201
                if (hists->stats.nr_events[i] == 0)
 
1202
                        continue;
 
1203
 
 
1204
                name = perf_event__name(i);
 
1205
                if (!strcmp(name, "UNKNOWN"))
 
1206
                        continue;
 
1207
 
 
1208
                ret += fprintf(fp, "%16s events: %10d\n", name,
 
1209
                               hists->stats.nr_events[i]);
 
1210
        }
 
1211
 
 
1212
        return ret;
 
1213
}