mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
perf c2c: add function view stats merge and memory management
Add the per-entry stats/cstats aggregation helpers and hierarchy teardown. Child common fields are released through hist_entry__delete(), while the function-view free callback handles the private child tree and containing allocation. Also add a helper for pruning writer entries with no stores or cacheline children. These are used by the entry-creation and builder patches that follow and are __maybe_unused until then. Signed-off-by: Jiebin Sun <jiebin.sun@intel.com> Reviewed-by: Tianyou Li <tianyou.li@intel.com> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> Reviewed-by: Ian Rogers <irogers@google.com> Cc: Dapeng Mi <dapeng1.mi@linux.intel.com> Cc: James Clark <james.clark@linaro.org> Cc: Thomas Falcon <thomas.falcon@intel.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
parent
e888662432
commit
929c4ebb54
|
|
@ -680,3 +680,166 @@ c2c_function_hists__reinit(struct c2c_hists *c2c_hists,
|
|||
|
||||
return function_hpp_list__parse(&c2c_hists->list, output, sort, env);
|
||||
}
|
||||
|
||||
/* Welford online merge of two "stats" (from util/stat.h) accumulators. */
|
||||
static void c2c_stats_merge(struct stats *dest, const struct stats *src)
|
||||
{
|
||||
double delta;
|
||||
|
||||
if (src->n == 0)
|
||||
return;
|
||||
|
||||
if (dest->n == 0) {
|
||||
*dest = *src;
|
||||
return;
|
||||
}
|
||||
|
||||
delta = src->mean - dest->mean;
|
||||
dest->M2 += src->M2 + delta * delta * dest->n * src->n / (dest->n + src->n);
|
||||
dest->mean = (dest->mean * dest->n + src->mean * src->n) / (dest->n + src->n);
|
||||
dest->n += src->n;
|
||||
|
||||
/* Update min/max */
|
||||
if (src->max > dest->max)
|
||||
dest->max = src->max;
|
||||
if (src->min < dest->min)
|
||||
dest->min = src->min;
|
||||
}
|
||||
|
||||
/* Merge compute_stats during function aggregation. */
|
||||
static void __maybe_unused c2c_add_cstats(struct compute_stats *dest,
|
||||
const struct compute_stats *src)
|
||||
{
|
||||
c2c_stats_merge(&dest->rmt_hitm, &src->rmt_hitm);
|
||||
c2c_stats_merge(&dest->lcl_hitm, &src->lcl_hitm);
|
||||
c2c_stats_merge(&dest->rmt_peer, &src->rmt_peer);
|
||||
c2c_stats_merge(&dest->lcl_peer, &src->lcl_peer);
|
||||
c2c_stats_merge(&dest->load, &src->load);
|
||||
}
|
||||
|
||||
static bool __maybe_unused hist_entry__add_c2c_stats(struct hist_entry *he,
|
||||
const struct c2c_stats *stats)
|
||||
{
|
||||
u64 nr_events = c2c_hitm_count(stats) + stats->rmt_peer + stats->lcl_peer;
|
||||
u64 weight1 = c2c_hitm_count(stats);
|
||||
|
||||
/*
|
||||
* Allocate before touching he->stat, so a failure here leaves the
|
||||
* entry unmodified and the caller can bail out without having
|
||||
* half-updated the statistics.
|
||||
*/
|
||||
if (symbol_conf.cumulate_callchain && !he->stat_acc) {
|
||||
he->stat_acc = calloc(1, sizeof(struct he_stat));
|
||||
if (!he->stat_acc)
|
||||
return false;
|
||||
}
|
||||
|
||||
he->stat.nr_events += nr_events;
|
||||
he->stat.period += nr_events;
|
||||
he->stat.weight1 += weight1;
|
||||
|
||||
if (!symbol_conf.cumulate_callchain)
|
||||
return true;
|
||||
|
||||
he->stat_acc->nr_events += nr_events;
|
||||
he->stat_acc->period += nr_events;
|
||||
he->stat_acc->weight1 += weight1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void c2c_he__free_hierarchy(struct hist_entry *he);
|
||||
|
||||
/*
|
||||
* Free a function-view histogram entry (hist_entry_ops::free).
|
||||
*/
|
||||
static void __maybe_unused c2c_function_he_free(void *ptr)
|
||||
{
|
||||
struct hist_entry *he = ptr;
|
||||
struct c2c_hist_entry *c2c_he;
|
||||
|
||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||
|
||||
if (c2c_he->hists) {
|
||||
perf_hpp__reset_output_field(&c2c_he->hists->list);
|
||||
hists__delete_all_entries(&c2c_he->hists->hists);
|
||||
zfree(&c2c_he->hists);
|
||||
}
|
||||
|
||||
c2c_he__free_hierarchy(he);
|
||||
|
||||
zfree(&c2c_he->nodeset);
|
||||
zfree(&c2c_he->cpuset);
|
||||
zfree(&c2c_he->nodestr);
|
||||
zfree(&c2c_he->node_stats);
|
||||
|
||||
free(c2c_he);
|
||||
}
|
||||
|
||||
static void c2c_he__free_hierarchy(struct hist_entry *he)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct hist_entry *child_he;
|
||||
|
||||
/*
|
||||
* A leaf entry stores its callchains in the sorted_chain member, which
|
||||
* shares a union with the hroot_in/hroot_out child trees, so its
|
||||
* hroot_out is not a valid subtree to walk. Leaf entries never have a
|
||||
* child hierarchy here, so stop before touching hroot_out.
|
||||
*/
|
||||
if (he->leaf)
|
||||
return;
|
||||
|
||||
if (RB_EMPTY_ROOT(&he->hroot_out.rb_root))
|
||||
return;
|
||||
|
||||
nd = rb_first_cached(&he->hroot_out);
|
||||
while (nd) {
|
||||
struct rb_node *next = rb_next(nd);
|
||||
|
||||
child_he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
rb_erase_cached(&child_he->rb_node, &he->hroot_out);
|
||||
hist_entry__delete(child_he);
|
||||
|
||||
nd = next;
|
||||
}
|
||||
|
||||
/* All children erased; clear the tree (and its cached leftmost). */
|
||||
he->hroot_out = RB_ROOT_CACHED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop level-2 writing functions that carry no stores or
|
||||
* no cacheline children. Writers are only added when they store into a shared
|
||||
* line, so this is mainly a safety net. Returns the number of surviving
|
||||
* writers.
|
||||
*/
|
||||
static int __maybe_unused c2c_he__prune_empty_writers(struct hist_entry *l1_he)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int surviving = 0;
|
||||
|
||||
if (!l1_he->has_children)
|
||||
return 0;
|
||||
|
||||
nd = rb_first_cached(&l1_he->hroot_out);
|
||||
while (nd) {
|
||||
struct rb_node *next = rb_next(nd);
|
||||
struct hist_entry *l2_he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
|
||||
if (l2_he->has_children && hist_entry__displayed_stores(l2_he) > 0) {
|
||||
surviving++;
|
||||
} else {
|
||||
rb_erase_cached(&l2_he->rb_node, &l1_he->hroot_out);
|
||||
hist_entry__delete(l2_he);
|
||||
}
|
||||
nd = next;
|
||||
}
|
||||
|
||||
if (!surviving) {
|
||||
l1_he->hroot_out = RB_ROOT_CACHED;
|
||||
l1_he->has_children = false;
|
||||
l1_he->unfolded = false;
|
||||
}
|
||||
return surviving;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user