perf hists: Catch and handle out-of-date hist entry maps.
[linux-flexiantxendom0.git] / tools / perf / util / hist.c
index 9b9d12b..e0a0970 100644 (file)
@@ -6,6 +6,11 @@
 #include "sort.h"
 #include <math.h>
 
+static bool hists__filter_entry_by_dso(struct hists *hists,
+                                      struct hist_entry *he);
+static bool hists__filter_entry_by_thread(struct hists *hists,
+                                         struct hist_entry *he);
+
 enum hist_filter {
        HIST_FILTER__DSO,
        HIST_FILTER__THREAD,
@@ -100,15 +105,21 @@ static void hist_entry__decay(struct hist_entry *he)
 
 static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
 {
-       if (he->period == 0)
+       u64 prev_period = he->period;
+
+       if (prev_period == 0)
                return true;
-       hists->stats.total_period -= he->period;
+
        hist_entry__decay(he);
-       hists->stats.total_period += he->period;
+
+       if (!he->filtered)
+               hists->stats.total_period -= prev_period - he->period;
+
        return he->period == 0;
 }
 
-void hists__decay_entries(struct hists *hists)
+static void __hists__decay_entries(struct hists *hists, bool zap_user,
+                                  bool zap_kernel, bool threaded)
 {
        struct rb_node *next = rb_first(&hists->entries);
        struct hist_entry *n;
@@ -121,10 +132,13 @@ void hists__decay_entries(struct hists *hists)
                 * case some it gets new samples, we'll eventually free it when
                 * the user stops browsing and it agains gets fully decayed.
                 */
-               if (hists__decay_entry(hists, n) && !n->used) {
+               if (((zap_user && n->level == '.') ||
+                    (zap_kernel && n->level != '.') ||
+                    hists__decay_entry(hists, n)) &&
+                   !n->used) {
                        rb_erase(&n->rb_node, &hists->entries);
 
-                       if (sort__need_collapse)
+                       if (sort__need_collapse || threaded)
                                rb_erase(&n->rb_node_in, &hists->entries_collapsed);
 
                        hist_entry__free(n);
@@ -133,6 +147,17 @@ void hists__decay_entries(struct hists *hists)
        }
 }
 
+void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
+{
+       return __hists__decay_entries(hists, zap_user, zap_kernel, false);
+}
+
+void hists__decay_entries_threaded(struct hists *hists,
+                                  bool zap_user, bool zap_kernel)
+{
+       return __hists__decay_entries(hists, zap_user, zap_kernel, true);
+}
+
 /*
  * histogram, sorted on item, collects periods
  */
@@ -205,6 +230,18 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                if (!cmp) {
                        he->period += period;
                        ++he->nr_events;
+
+                       /* If the map of an existing hist_entry has
+                        * become out-of-date due to an exec() or
+                        * similar, update it.  Otherwise we will
+                        * mis-adjust symbol addresses when computing
+                        * the history counter to increment.
+                        */
+                       if (he->ms.map != entry.ms.map) {
+                               he->ms.map = entry.ms.map;
+                               if (he->ms.map)
+                                       he->ms.map->referenced = true;
+                       }
                        goto out;
                }
 
@@ -323,6 +360,12 @@ static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
        return root;
 }
 
+static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
+{
+       hists__filter_entry_by_dso(hists, he);
+       hists__filter_entry_by_thread(hists, he);
+}
+
 static void __hists__collapse_resort(struct hists *hists, bool threaded)
 {
        struct rb_root *root;
@@ -334,15 +377,20 @@ static void __hists__collapse_resort(struct hists *hists, bool threaded)
 
        root = hists__get_rotate_entries_in(hists);
        next = rb_first(root);
-       hists->stats.total_period = 0;
 
        while (next) {
                n = rb_entry(next, struct hist_entry, rb_node_in);
                next = rb_next(&n->rb_node_in);
 
                rb_erase(&n->rb_node_in, root);
-               if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n))
-                       hists__inc_nr_entries(hists, n);
+               if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
+                       /*
+                        * If it wasn't combined with one of the entries already
+                        * collapsed, we need to apply the filters that may have
+                        * been set by, say, the hist_browser.
+                        */
+                       hists__apply_filters(hists, n);
+               }
        }
 }
 
@@ -404,6 +452,7 @@ static void __hists__output_resort(struct hists *hists, bool threaded)
        hists->entries = RB_ROOT;
 
        hists->nr_entries = 0;
+       hists->stats.total_period = 0;
        hists__reset_col_len(hists);
 
        while (next) {
@@ -687,17 +736,17 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows)
 
        while (next && row++ < max_rows) {
                n = rb_entry(next, struct hist_entry, rb_node);
-               hists__calc_col_len(hists, n);
+               if (!n->filtered)
+                       hists__calc_col_len(hists, n);
                next = rb_next(&n->rb_node);
        }
 }
 
-int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
-                        struct hists *hists, struct hists *pair_hists,
-                        bool show_displacement, long displacement,
-                        bool color, u64 session_total)
+static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s,
+                                    size_t size, struct hists *pair_hists,
+                                    bool show_displacement, long displacement,
+                                    bool color, u64 session_total)
 {
-       struct sort_entry *se;
        u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
        u64 nr_events;
        const char *sep = symbol_conf.field_sep;
@@ -730,7 +779,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
                                                     sep ? "%.2f" : "   %6.2f%%",
                                                     (period * 100.0) / total);
                else
-                       ret = snprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
+                       ret = scnprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
                                       (period * 100.0) / total);
                if (symbol_conf.show_cpu_utilization) {
                        ret += percent_color_snprintf(s + ret, size - ret,
@@ -753,20 +802,20 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
                        }
                }
        } else
-               ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
+               ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
 
        if (symbol_conf.show_nr_samples) {
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
+                       ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
                else
-                       ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
+                       ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
        }
 
        if (symbol_conf.show_total_period) {
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
+                       ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
                else
-                       ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
+                       ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period);
        }
 
        if (pair_hists) {
@@ -781,34 +830,44 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
                diff = new_percent - old_percent;
 
                if (fabs(diff) >= 0.01)
-                       snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+                       ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
                else
-                       snprintf(bf, sizeof(bf), " ");
+                       ret += scnprintf(bf, sizeof(bf), " ");
 
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+                       ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
                else
-                       ret += snprintf(s + ret, size - ret, "%11.11s", bf);
+                       ret += scnprintf(s + ret, size - ret, "%11.11s", bf);
 
                if (show_displacement) {
                        if (displacement)
-                               snprintf(bf, sizeof(bf), "%+4ld", displacement);
+                               ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement);
                        else
-                               snprintf(bf, sizeof(bf), " ");
+                               ret += scnprintf(bf, sizeof(bf), " ");
 
                        if (sep)
-                               ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+                               ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
                        else
-                               ret += snprintf(s + ret, size - ret, "%6.6s", bf);
+                               ret += scnprintf(s + ret, size - ret, "%6.6s", bf);
                }
        }
 
+       return ret;
+}
+
+int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
+                        struct hists *hists)
+{
+       const char *sep = symbol_conf.field_sep;
+       struct sort_entry *se;
+       int ret = 0;
+
        list_for_each_entry(se, &hist_entry__sort_list, list) {
                if (se->elide)
                        continue;
 
-               ret += snprintf(s + ret, size - ret, "%s", sep ?: "  ");
-               ret += se->se_snprintf(self, s + ret, size - ret,
+               ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  ");
+               ret += se->se_snprintf(he, s + ret, size - ret,
                                       hists__col_len(hists, se->se_width_idx));
        }
 
@@ -820,13 +879,15 @@ int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
                        long displacement, FILE *fp, u64 session_total)
 {
        char bf[512];
+       int ret;
 
        if (size == 0 || size > sizeof(bf))
                size = sizeof(bf);
 
-       hist_entry__snprintf(he, bf, size, hists, pair_hists,
-                            show_displacement, displacement,
-                            true, session_total);
+       ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
+                                       show_displacement, displacement,
+                                       true, session_total);
+       hist_entry__snprintf(he, bf + ret, size - ret, hists);
        return fprintf(fp, "%s\n", bf);
 }
 
@@ -1061,7 +1122,20 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
        hists__calc_col_len(hists, h);
 }
 
-void hists__filter_by_dso(struct hists *hists, const struct dso *dso)
+
+static bool hists__filter_entry_by_dso(struct hists *hists,
+                                      struct hist_entry *he)
+{
+       if (hists->dso_filter != NULL &&
+           (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
+               he->filtered |= (1 << HIST_FILTER__DSO);
+               return true;
+       }
+
+       return false;
+}
+
+void hists__filter_by_dso(struct hists *hists)
 {
        struct rb_node *nd;
 
@@ -1075,16 +1149,26 @@ void hists__filter_by_dso(struct hists *hists, const struct dso *dso)
                if (symbol_conf.exclude_other && !h->parent)
                        continue;
 
-               if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
-                       h->filtered |= (1 << HIST_FILTER__DSO);
+               if (hists__filter_entry_by_dso(hists, h))
                        continue;
-               }
 
                hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
        }
 }
 
-void hists__filter_by_thread(struct hists *hists, const struct thread *thread)
+static bool hists__filter_entry_by_thread(struct hists *hists,
+                                         struct hist_entry *he)
+{
+       if (hists->thread_filter != NULL &&
+           he->thread != hists->thread_filter) {
+               he->filtered |= (1 << HIST_FILTER__THREAD);
+               return true;
+       }
+
+       return false;
+}
+
+void hists__filter_by_thread(struct hists *hists)
 {
        struct rb_node *nd;
 
@@ -1095,10 +1179,8 @@ void hists__filter_by_thread(struct hists *hists, const struct thread *thread)
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
-               if (thread != NULL && h->thread != thread) {
-                       h->filtered |= (1 << HIST_FILTER__THREAD);
+               if (hists__filter_entry_by_thread(hists, h))
                        continue;
-               }
 
                hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
        }
@@ -1141,13 +1223,3 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
 
        return ret;
 }
-
-void hists__init(struct hists *hists)
-{
-       memset(hists, 0, sizeof(*hists));
-       hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
-       hists->entries_in = &hists->entries_in_array[0];
-       hists->entries_collapsed = RB_ROOT;
-       hists->entries = RB_ROOT;
-       pthread_mutex_init(&hists->lock, NULL);
-}