162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Compare and figure out the top N hottest streams 462306a36Sopenharmony_ci * Copyright (c) 2020, Intel Corporation. 562306a36Sopenharmony_ci * Author: Jin Yao 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <inttypes.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <linux/zalloc.h> 1162306a36Sopenharmony_ci#include "debug.h" 1262306a36Sopenharmony_ci#include "hist.h" 1362306a36Sopenharmony_ci#include "sort.h" 1462306a36Sopenharmony_ci#include "stream.h" 1562306a36Sopenharmony_ci#include "evlist.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic void evsel_streams__delete(struct evsel_streams *es, int nr_evsel) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci for (int i = 0; i < nr_evsel; i++) 2062306a36Sopenharmony_ci zfree(&es[i].streams); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci free(es); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid evlist_streams__delete(struct evlist_streams *els) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci evsel_streams__delete(els->ev_streams, els->nr_evsel); 2862306a36Sopenharmony_ci free(els); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct evlist_streams *evlist_streams__new(int nr_evsel, 3262306a36Sopenharmony_ci int nr_streams_max) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct evlist_streams *els; 3562306a36Sopenharmony_ci struct evsel_streams *es; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci els = zalloc(sizeof(*els)); 3862306a36Sopenharmony_ci if (!els) 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci es = calloc(nr_evsel, sizeof(struct evsel_streams)); 4262306a36Sopenharmony_ci if (!es) { 4362306a36Sopenharmony_ci free(els); 4462306a36Sopenharmony_ci return NULL; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (int i = 0; i < nr_evsel; i++) { 4862306a36Sopenharmony_ci struct evsel_streams *s = &es[i]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci s->streams = calloc(nr_streams_max, sizeof(struct stream)); 5162306a36Sopenharmony_ci if (!s->streams) 5262306a36Sopenharmony_ci goto err; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci s->nr_streams_max = nr_streams_max; 5562306a36Sopenharmony_ci s->evsel_idx = -1; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci els->ev_streams = es; 5962306a36Sopenharmony_ci els->nr_evsel = nr_evsel; 6062306a36Sopenharmony_ci return els; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cierr: 6362306a36Sopenharmony_ci evsel_streams__delete(es, nr_evsel); 6462306a36Sopenharmony_ci return NULL; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * The cnodes with high hit number are hot callchains. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic void evsel_streams__set_hot_cnode(struct evsel_streams *es, 7162306a36Sopenharmony_ci struct callchain_node *cnode) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int i, idx = 0; 7462306a36Sopenharmony_ci u64 hit; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (es->nr_streams < es->nr_streams_max) { 7762306a36Sopenharmony_ci i = es->nr_streams; 7862306a36Sopenharmony_ci es->streams[i].cnode = cnode; 7962306a36Sopenharmony_ci es->nr_streams++; 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * Considering a few number of hot streams, only use simple 8562306a36Sopenharmony_ci * way to find the cnode with smallest hit number and replace. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci hit = (es->streams[0].cnode)->hit; 8862306a36Sopenharmony_ci for (i = 1; i < es->nr_streams; i++) { 8962306a36Sopenharmony_ci if ((es->streams[i].cnode)->hit < hit) { 9062306a36Sopenharmony_ci hit = (es->streams[i].cnode)->hit; 9162306a36Sopenharmony_ci idx = i; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (cnode->hit > hit) 9662306a36Sopenharmony_ci es->streams[idx].cnode = cnode; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void update_hot_callchain(struct hist_entry *he, 10062306a36Sopenharmony_ci struct evsel_streams *es) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct rb_root *root = &he->sorted_chain; 10362306a36Sopenharmony_ci struct rb_node *rb_node = rb_first(root); 10462306a36Sopenharmony_ci struct callchain_node *cnode; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci while (rb_node) { 10762306a36Sopenharmony_ci cnode = rb_entry(rb_node, struct callchain_node, rb_node); 10862306a36Sopenharmony_ci evsel_streams__set_hot_cnode(es, cnode); 10962306a36Sopenharmony_ci rb_node = rb_next(rb_node); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void init_hot_callchain(struct hists *hists, struct evsel_streams *es) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct rb_node *next = rb_first_cached(&hists->entries); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci while (next) { 11862306a36Sopenharmony_ci struct hist_entry *he; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci he = rb_entry(next, struct hist_entry, rb_node); 12162306a36Sopenharmony_ci update_hot_callchain(he, es); 12262306a36Sopenharmony_ci next = rb_next(&he->rb_node); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci es->streams_hits = callchain_total_hits(hists); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int evlist__init_callchain_streams(struct evlist *evlist, 12962306a36Sopenharmony_ci struct evlist_streams *els) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct evsel_streams *es = els->ev_streams; 13262306a36Sopenharmony_ci struct evsel *pos; 13362306a36Sopenharmony_ci int i = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci BUG_ON(els->nr_evsel < evlist->core.nr_entries); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci evlist__for_each_entry(evlist, pos) { 13862306a36Sopenharmony_ci struct hists *hists = evsel__hists(pos); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci hists__output_resort(hists, NULL); 14162306a36Sopenharmony_ci init_hot_callchain(hists, &es[i]); 14262306a36Sopenharmony_ci es[i].evsel_idx = pos->core.idx; 14362306a36Sopenharmony_ci i++; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistruct evlist_streams *evlist__create_streams(struct evlist *evlist, 15062306a36Sopenharmony_ci int nr_streams_max) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int nr_evsel = evlist->core.nr_entries, ret = -1; 15362306a36Sopenharmony_ci struct evlist_streams *els = evlist_streams__new(nr_evsel, 15462306a36Sopenharmony_ci nr_streams_max); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!els) 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = evlist__init_callchain_streams(evlist, els); 16062306a36Sopenharmony_ci if (ret) { 16162306a36Sopenharmony_ci evlist_streams__delete(els); 16262306a36Sopenharmony_ci return NULL; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return els; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistruct evsel_streams *evsel_streams__entry(struct evlist_streams *els, 16962306a36Sopenharmony_ci int evsel_idx) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct evsel_streams *es = els->ev_streams; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (int i = 0; i < els->nr_evsel; i++) { 17462306a36Sopenharmony_ci if (es[i].evsel_idx == evsel_idx) 17562306a36Sopenharmony_ci return &es[i]; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return NULL; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic struct stream *stream__callchain_match(struct stream *base_stream, 18262306a36Sopenharmony_ci struct evsel_streams *es_pair) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci for (int i = 0; i < es_pair->nr_streams; i++) { 18562306a36Sopenharmony_ci struct stream *pair_stream = &es_pair->streams[i]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (callchain_cnode_matched(base_stream->cnode, 18862306a36Sopenharmony_ci pair_stream->cnode)) { 18962306a36Sopenharmony_ci return pair_stream; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return NULL; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic struct stream *stream__match(struct stream *base_stream, 19762306a36Sopenharmony_ci struct evsel_streams *es_pair) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci return stream__callchain_match(base_stream, es_pair); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void stream__link(struct stream *base_stream, struct stream *pair_stream) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci base_stream->pair_cnode = pair_stream->cnode; 20562306a36Sopenharmony_ci pair_stream->pair_cnode = base_stream->cnode; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid evsel_streams__match(struct evsel_streams *es_base, 20962306a36Sopenharmony_ci struct evsel_streams *es_pair) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci for (int i = 0; i < es_base->nr_streams; i++) { 21262306a36Sopenharmony_ci struct stream *base_stream = &es_base->streams[i]; 21362306a36Sopenharmony_ci struct stream *pair_stream; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci pair_stream = stream__match(base_stream, es_pair); 21662306a36Sopenharmony_ci if (pair_stream) 21762306a36Sopenharmony_ci stream__link(base_stream, pair_stream); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void print_callchain_pair(struct stream *base_stream, int idx, 22262306a36Sopenharmony_ci struct evsel_streams *es_base, 22362306a36Sopenharmony_ci struct evsel_streams *es_pair) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct callchain_node *base_cnode = base_stream->cnode; 22662306a36Sopenharmony_ci struct callchain_node *pair_cnode = base_stream->pair_cnode; 22762306a36Sopenharmony_ci struct callchain_list *base_chain, *pair_chain; 22862306a36Sopenharmony_ci char buf1[512], buf2[512], cbuf1[256], cbuf2[256]; 22962306a36Sopenharmony_ci char *s1, *s2; 23062306a36Sopenharmony_ci double pct; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci printf("\nhot chain pair %d:\n", idx); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pct = (double)base_cnode->hit / (double)es_base->streams_hits; 23562306a36Sopenharmony_ci scnprintf(buf1, sizeof(buf1), "cycles: %ld, hits: %.2f%%", 23662306a36Sopenharmony_ci callchain_avg_cycles(base_cnode), pct * 100.0); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci pct = (double)pair_cnode->hit / (double)es_pair->streams_hits; 23962306a36Sopenharmony_ci scnprintf(buf2, sizeof(buf2), "cycles: %ld, hits: %.2f%%", 24062306a36Sopenharmony_ci callchain_avg_cycles(pair_cnode), pct * 100.0); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci printf("%35s\t%35s\n", buf1, buf2); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci printf("%35s\t%35s\n", 24562306a36Sopenharmony_ci "---------------------------", 24662306a36Sopenharmony_ci "--------------------------"); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci pair_chain = list_first_entry(&pair_cnode->val, 24962306a36Sopenharmony_ci struct callchain_list, 25062306a36Sopenharmony_ci list); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci list_for_each_entry(base_chain, &base_cnode->val, list) { 25362306a36Sopenharmony_ci if (&pair_chain->list == &pair_cnode->val) 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci s1 = callchain_list__sym_name(base_chain, cbuf1, sizeof(cbuf1), 25762306a36Sopenharmony_ci false); 25862306a36Sopenharmony_ci s2 = callchain_list__sym_name(pair_chain, cbuf2, sizeof(cbuf2), 25962306a36Sopenharmony_ci false); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci scnprintf(buf1, sizeof(buf1), "%35s\t%35s", s1, s2); 26262306a36Sopenharmony_ci printf("%s\n", buf1); 26362306a36Sopenharmony_ci pair_chain = list_next_entry(pair_chain, list); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void print_stream_callchain(struct stream *stream, int idx, 26862306a36Sopenharmony_ci struct evsel_streams *es, bool pair) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct callchain_node *cnode = stream->cnode; 27162306a36Sopenharmony_ci struct callchain_list *chain; 27262306a36Sopenharmony_ci char buf[512], cbuf[256], *s; 27362306a36Sopenharmony_ci double pct; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci printf("\nhot chain %d:\n", idx); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci pct = (double)cnode->hit / (double)es->streams_hits; 27862306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "cycles: %ld, hits: %.2f%%", 27962306a36Sopenharmony_ci callchain_avg_cycles(cnode), pct * 100.0); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (pair) { 28262306a36Sopenharmony_ci printf("%35s\t%35s\n", "", buf); 28362306a36Sopenharmony_ci printf("%35s\t%35s\n", 28462306a36Sopenharmony_ci "", "--------------------------"); 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci printf("%35s\n", buf); 28762306a36Sopenharmony_ci printf("%35s\n", "--------------------------"); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci list_for_each_entry(chain, &cnode->val, list) { 29162306a36Sopenharmony_ci s = callchain_list__sym_name(chain, cbuf, sizeof(cbuf), false); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (pair) 29462306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "%35s\t%35s", "", s); 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "%35s", s); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci printf("%s\n", buf); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void callchain_streams_report(struct evsel_streams *es_base, 30362306a36Sopenharmony_ci struct evsel_streams *es_pair) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct stream *base_stream; 30662306a36Sopenharmony_ci int i, idx = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci printf("[ Matched hot streams ]\n"); 30962306a36Sopenharmony_ci for (i = 0; i < es_base->nr_streams; i++) { 31062306a36Sopenharmony_ci base_stream = &es_base->streams[i]; 31162306a36Sopenharmony_ci if (base_stream->pair_cnode) { 31262306a36Sopenharmony_ci print_callchain_pair(base_stream, ++idx, 31362306a36Sopenharmony_ci es_base, es_pair); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci idx = 0; 31862306a36Sopenharmony_ci printf("\n[ Hot streams in old perf data only ]\n"); 31962306a36Sopenharmony_ci for (i = 0; i < es_base->nr_streams; i++) { 32062306a36Sopenharmony_ci base_stream = &es_base->streams[i]; 32162306a36Sopenharmony_ci if (!base_stream->pair_cnode) { 32262306a36Sopenharmony_ci print_stream_callchain(base_stream, ++idx, 32362306a36Sopenharmony_ci es_base, false); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci idx = 0; 32862306a36Sopenharmony_ci printf("\n[ Hot streams in new perf data only ]\n"); 32962306a36Sopenharmony_ci for (i = 0; i < es_pair->nr_streams; i++) { 33062306a36Sopenharmony_ci base_stream = &es_pair->streams[i]; 33162306a36Sopenharmony_ci if (!base_stream->pair_cnode) { 33262306a36Sopenharmony_ci print_stream_callchain(base_stream, ++idx, 33362306a36Sopenharmony_ci es_pair, true); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid evsel_streams__report(struct evsel_streams *es_base, 33962306a36Sopenharmony_ci struct evsel_streams *es_pair) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return callchain_streams_report(es_base, es_pair); 34262306a36Sopenharmony_ci} 343