1480accbbSJin Yao // SPDX-License-Identifier: GPL-2.0 2480accbbSJin Yao /* 3480accbbSJin Yao * Compare and figure out the top N hottest streams 4480accbbSJin Yao * Copyright (c) 2020, Intel Corporation. 5480accbbSJin Yao * Author: Jin Yao 6480accbbSJin Yao */ 7480accbbSJin Yao 8480accbbSJin Yao #include <inttypes.h> 9480accbbSJin Yao #include <stdlib.h> 10480accbbSJin Yao #include <linux/zalloc.h> 11480accbbSJin Yao #include "debug.h" 12480accbbSJin Yao #include "hist.h" 13480accbbSJin Yao #include "sort.h" 14480accbbSJin Yao #include "stream.h" 15480accbbSJin Yao #include "evlist.h" 16480accbbSJin Yao 17480accbbSJin Yao static void evsel_streams__delete(struct evsel_streams *es, int nr_evsel) 18480accbbSJin Yao { 19480accbbSJin Yao for (int i = 0; i < nr_evsel; i++) 20480accbbSJin Yao zfree(&es[i].streams); 21480accbbSJin Yao 22480accbbSJin Yao free(es); 23480accbbSJin Yao } 24480accbbSJin Yao 25480accbbSJin Yao void evlist_streams__delete(struct evlist_streams *els) 26480accbbSJin Yao { 27480accbbSJin Yao evsel_streams__delete(els->ev_streams, els->nr_evsel); 28480accbbSJin Yao free(els); 29480accbbSJin Yao } 30480accbbSJin Yao 31480accbbSJin Yao static struct evlist_streams *evlist_streams__new(int nr_evsel, 32480accbbSJin Yao int nr_streams_max) 33480accbbSJin Yao { 34480accbbSJin Yao struct evlist_streams *els; 35480accbbSJin Yao struct evsel_streams *es; 36480accbbSJin Yao 37480accbbSJin Yao els = zalloc(sizeof(*els)); 38480accbbSJin Yao if (!els) 39480accbbSJin Yao return NULL; 40480accbbSJin Yao 41480accbbSJin Yao es = calloc(nr_evsel, sizeof(struct evsel_streams)); 42480accbbSJin Yao if (!es) { 43480accbbSJin Yao free(els); 44480accbbSJin Yao return NULL; 45480accbbSJin Yao } 46480accbbSJin Yao 47480accbbSJin Yao for (int i = 0; i < nr_evsel; i++) { 48480accbbSJin Yao struct evsel_streams *s = &es[i]; 49480accbbSJin Yao 50480accbbSJin Yao s->streams = calloc(nr_streams_max, sizeof(struct stream)); 51480accbbSJin Yao if (!s->streams) 52480accbbSJin Yao goto err; 53480accbbSJin Yao 54480accbbSJin Yao s->nr_streams_max = nr_streams_max; 55480accbbSJin Yao s->evsel_idx = -1; 56480accbbSJin Yao } 57480accbbSJin Yao 58480accbbSJin Yao els->ev_streams = es; 59480accbbSJin Yao els->nr_evsel = nr_evsel; 60480accbbSJin Yao return els; 61480accbbSJin Yao 62480accbbSJin Yao err: 63480accbbSJin Yao evsel_streams__delete(es, nr_evsel); 64480accbbSJin Yao return NULL; 65480accbbSJin Yao } 66480accbbSJin Yao 67480accbbSJin Yao /* 68480accbbSJin Yao * The cnodes with high hit number are hot callchains. 69480accbbSJin Yao */ 70480accbbSJin Yao static void evsel_streams__set_hot_cnode(struct evsel_streams *es, 71480accbbSJin Yao struct callchain_node *cnode) 72480accbbSJin Yao { 73480accbbSJin Yao int i, idx = 0; 74480accbbSJin Yao u64 hit; 75480accbbSJin Yao 76480accbbSJin Yao if (es->nr_streams < es->nr_streams_max) { 77480accbbSJin Yao i = es->nr_streams; 78480accbbSJin Yao es->streams[i].cnode = cnode; 79480accbbSJin Yao es->nr_streams++; 80480accbbSJin Yao return; 81480accbbSJin Yao } 82480accbbSJin Yao 83480accbbSJin Yao /* 84480accbbSJin Yao * Considering a few number of hot streams, only use simple 85480accbbSJin Yao * way to find the cnode with smallest hit number and replace. 86480accbbSJin Yao */ 87480accbbSJin Yao hit = (es->streams[0].cnode)->hit; 88480accbbSJin Yao for (i = 1; i < es->nr_streams; i++) { 89480accbbSJin Yao if ((es->streams[i].cnode)->hit < hit) { 90480accbbSJin Yao hit = (es->streams[i].cnode)->hit; 91480accbbSJin Yao idx = i; 92480accbbSJin Yao } 93480accbbSJin Yao } 94480accbbSJin Yao 95480accbbSJin Yao if (cnode->hit > hit) 96480accbbSJin Yao es->streams[idx].cnode = cnode; 97480accbbSJin Yao } 98480accbbSJin Yao 99480accbbSJin Yao static void update_hot_callchain(struct hist_entry *he, 100480accbbSJin Yao struct evsel_streams *es) 101480accbbSJin Yao { 102480accbbSJin Yao struct rb_root *root = &he->sorted_chain; 103480accbbSJin Yao struct rb_node *rb_node = rb_first(root); 104480accbbSJin Yao struct callchain_node *cnode; 105480accbbSJin Yao 106480accbbSJin Yao while (rb_node) { 107480accbbSJin Yao cnode = rb_entry(rb_node, struct callchain_node, rb_node); 108480accbbSJin Yao evsel_streams__set_hot_cnode(es, cnode); 109480accbbSJin Yao rb_node = rb_next(rb_node); 110480accbbSJin Yao } 111480accbbSJin Yao } 112480accbbSJin Yao 113480accbbSJin Yao static void init_hot_callchain(struct hists *hists, struct evsel_streams *es) 114480accbbSJin Yao { 115480accbbSJin Yao struct rb_node *next = rb_first_cached(&hists->entries); 116480accbbSJin Yao 117480accbbSJin Yao while (next) { 118480accbbSJin Yao struct hist_entry *he; 119480accbbSJin Yao 120480accbbSJin Yao he = rb_entry(next, struct hist_entry, rb_node); 121480accbbSJin Yao update_hot_callchain(he, es); 122480accbbSJin Yao next = rb_next(&he->rb_node); 123480accbbSJin Yao } 12428904f4dSJin Yao 12528904f4dSJin Yao es->streams_hits = callchain_total_hits(hists); 126480accbbSJin Yao } 127480accbbSJin Yao 128480accbbSJin Yao static int evlist__init_callchain_streams(struct evlist *evlist, 129480accbbSJin Yao struct evlist_streams *els) 130480accbbSJin Yao { 131480accbbSJin Yao struct evsel_streams *es = els->ev_streams; 132480accbbSJin Yao struct evsel *pos; 133480accbbSJin Yao int i = 0; 134480accbbSJin Yao 135480accbbSJin Yao BUG_ON(els->nr_evsel < evlist->core.nr_entries); 136480accbbSJin Yao 137480accbbSJin Yao evlist__for_each_entry(evlist, pos) { 138480accbbSJin Yao struct hists *hists = evsel__hists(pos); 139480accbbSJin Yao 140480accbbSJin Yao hists__output_resort(hists, NULL); 141480accbbSJin Yao init_hot_callchain(hists, &es[i]); 142480accbbSJin Yao es[i].evsel_idx = pos->idx; 143480accbbSJin Yao i++; 144480accbbSJin Yao } 145480accbbSJin Yao 146480accbbSJin Yao return 0; 147480accbbSJin Yao } 148480accbbSJin Yao 149480accbbSJin Yao struct evlist_streams *evlist__create_streams(struct evlist *evlist, 150480accbbSJin Yao int nr_streams_max) 151480accbbSJin Yao { 152480accbbSJin Yao int nr_evsel = evlist->core.nr_entries, ret = -1; 153480accbbSJin Yao struct evlist_streams *els = evlist_streams__new(nr_evsel, 154480accbbSJin Yao nr_streams_max); 155480accbbSJin Yao 156480accbbSJin Yao if (!els) 157480accbbSJin Yao return NULL; 158480accbbSJin Yao 159480accbbSJin Yao ret = evlist__init_callchain_streams(evlist, els); 160480accbbSJin Yao if (ret) { 161480accbbSJin Yao evlist_streams__delete(els); 162480accbbSJin Yao return NULL; 163480accbbSJin Yao } 164480accbbSJin Yao 165480accbbSJin Yao return els; 166480accbbSJin Yao } 167dd1d8418SJin Yao 168dd1d8418SJin Yao struct evsel_streams *evsel_streams__entry(struct evlist_streams *els, 169dd1d8418SJin Yao int evsel_idx) 170dd1d8418SJin Yao { 171dd1d8418SJin Yao struct evsel_streams *es = els->ev_streams; 172dd1d8418SJin Yao 173dd1d8418SJin Yao for (int i = 0; i < els->nr_evsel; i++) { 174dd1d8418SJin Yao if (es[i].evsel_idx == evsel_idx) 175dd1d8418SJin Yao return &es[i]; 176dd1d8418SJin Yao } 177dd1d8418SJin Yao 178dd1d8418SJin Yao return NULL; 179dd1d8418SJin Yao } 180fa79aa64SJin Yao 181fa79aa64SJin Yao static struct stream *stream__callchain_match(struct stream *base_stream, 182fa79aa64SJin Yao struct evsel_streams *es_pair) 183fa79aa64SJin Yao { 184fa79aa64SJin Yao for (int i = 0; i < es_pair->nr_streams; i++) { 185fa79aa64SJin Yao struct stream *pair_stream = &es_pair->streams[i]; 186fa79aa64SJin Yao 187fa79aa64SJin Yao if (callchain_cnode_matched(base_stream->cnode, 188fa79aa64SJin Yao pair_stream->cnode)) { 189fa79aa64SJin Yao return pair_stream; 190fa79aa64SJin Yao } 191fa79aa64SJin Yao } 192fa79aa64SJin Yao 193fa79aa64SJin Yao return NULL; 194fa79aa64SJin Yao } 195fa79aa64SJin Yao 196fa79aa64SJin Yao static struct stream *stream__match(struct stream *base_stream, 197fa79aa64SJin Yao struct evsel_streams *es_pair) 198fa79aa64SJin Yao { 199fa79aa64SJin Yao return stream__callchain_match(base_stream, es_pair); 200fa79aa64SJin Yao } 201fa79aa64SJin Yao 202fa79aa64SJin Yao static void stream__link(struct stream *base_stream, struct stream *pair_stream) 203fa79aa64SJin Yao { 204fa79aa64SJin Yao base_stream->pair_cnode = pair_stream->cnode; 205fa79aa64SJin Yao pair_stream->pair_cnode = base_stream->cnode; 206fa79aa64SJin Yao } 207fa79aa64SJin Yao 208fa79aa64SJin Yao void evsel_streams__match(struct evsel_streams *es_base, 209fa79aa64SJin Yao struct evsel_streams *es_pair) 210fa79aa64SJin Yao { 211fa79aa64SJin Yao for (int i = 0; i < es_base->nr_streams; i++) { 212fa79aa64SJin Yao struct stream *base_stream = &es_base->streams[i]; 213fa79aa64SJin Yao struct stream *pair_stream; 214fa79aa64SJin Yao 215fa79aa64SJin Yao pair_stream = stream__match(base_stream, es_pair); 216fa79aa64SJin Yao if (pair_stream) 217fa79aa64SJin Yao stream__link(base_stream, pair_stream); 218fa79aa64SJin Yao } 219fa79aa64SJin Yao } 220*5bbd6badSJin Yao 221*5bbd6badSJin Yao static void print_callchain_pair(struct stream *base_stream, int idx, 222*5bbd6badSJin Yao struct evsel_streams *es_base, 223*5bbd6badSJin Yao struct evsel_streams *es_pair) 224*5bbd6badSJin Yao { 225*5bbd6badSJin Yao struct callchain_node *base_cnode = base_stream->cnode; 226*5bbd6badSJin Yao struct callchain_node *pair_cnode = base_stream->pair_cnode; 227*5bbd6badSJin Yao struct callchain_list *base_chain, *pair_chain; 228*5bbd6badSJin Yao char buf1[512], buf2[512], cbuf1[256], cbuf2[256]; 229*5bbd6badSJin Yao char *s1, *s2; 230*5bbd6badSJin Yao double pct; 231*5bbd6badSJin Yao 232*5bbd6badSJin Yao printf("\nhot chain pair %d:\n", idx); 233*5bbd6badSJin Yao 234*5bbd6badSJin Yao pct = (double)base_cnode->hit / (double)es_base->streams_hits; 235*5bbd6badSJin Yao scnprintf(buf1, sizeof(buf1), "cycles: %ld, hits: %.2f%%", 236*5bbd6badSJin Yao callchain_avg_cycles(base_cnode), pct * 100.0); 237*5bbd6badSJin Yao 238*5bbd6badSJin Yao pct = (double)pair_cnode->hit / (double)es_pair->streams_hits; 239*5bbd6badSJin Yao scnprintf(buf2, sizeof(buf2), "cycles: %ld, hits: %.2f%%", 240*5bbd6badSJin Yao callchain_avg_cycles(pair_cnode), pct * 100.0); 241*5bbd6badSJin Yao 242*5bbd6badSJin Yao printf("%35s\t%35s\n", buf1, buf2); 243*5bbd6badSJin Yao 244*5bbd6badSJin Yao printf("%35s\t%35s\n", 245*5bbd6badSJin Yao "---------------------------", 246*5bbd6badSJin Yao "--------------------------"); 247*5bbd6badSJin Yao 248*5bbd6badSJin Yao pair_chain = list_first_entry(&pair_cnode->val, 249*5bbd6badSJin Yao struct callchain_list, 250*5bbd6badSJin Yao list); 251*5bbd6badSJin Yao 252*5bbd6badSJin Yao list_for_each_entry(base_chain, &base_cnode->val, list) { 253*5bbd6badSJin Yao if (&pair_chain->list == &pair_cnode->val) 254*5bbd6badSJin Yao return; 255*5bbd6badSJin Yao 256*5bbd6badSJin Yao s1 = callchain_list__sym_name(base_chain, cbuf1, sizeof(cbuf1), 257*5bbd6badSJin Yao false); 258*5bbd6badSJin Yao s2 = callchain_list__sym_name(pair_chain, cbuf2, sizeof(cbuf2), 259*5bbd6badSJin Yao false); 260*5bbd6badSJin Yao 261*5bbd6badSJin Yao scnprintf(buf1, sizeof(buf1), "%35s\t%35s", s1, s2); 262*5bbd6badSJin Yao printf("%s\n", buf1); 263*5bbd6badSJin Yao pair_chain = list_next_entry(pair_chain, list); 264*5bbd6badSJin Yao } 265*5bbd6badSJin Yao } 266*5bbd6badSJin Yao 267*5bbd6badSJin Yao static void print_stream_callchain(struct stream *stream, int idx, 268*5bbd6badSJin Yao struct evsel_streams *es, bool pair) 269*5bbd6badSJin Yao { 270*5bbd6badSJin Yao struct callchain_node *cnode = stream->cnode; 271*5bbd6badSJin Yao struct callchain_list *chain; 272*5bbd6badSJin Yao char buf[512], cbuf[256], *s; 273*5bbd6badSJin Yao double pct; 274*5bbd6badSJin Yao 275*5bbd6badSJin Yao printf("\nhot chain %d:\n", idx); 276*5bbd6badSJin Yao 277*5bbd6badSJin Yao pct = (double)cnode->hit / (double)es->streams_hits; 278*5bbd6badSJin Yao scnprintf(buf, sizeof(buf), "cycles: %ld, hits: %.2f%%", 279*5bbd6badSJin Yao callchain_avg_cycles(cnode), pct * 100.0); 280*5bbd6badSJin Yao 281*5bbd6badSJin Yao if (pair) { 282*5bbd6badSJin Yao printf("%35s\t%35s\n", "", buf); 283*5bbd6badSJin Yao printf("%35s\t%35s\n", 284*5bbd6badSJin Yao "", "--------------------------"); 285*5bbd6badSJin Yao } else { 286*5bbd6badSJin Yao printf("%35s\n", buf); 287*5bbd6badSJin Yao printf("%35s\n", "--------------------------"); 288*5bbd6badSJin Yao } 289*5bbd6badSJin Yao 290*5bbd6badSJin Yao list_for_each_entry(chain, &cnode->val, list) { 291*5bbd6badSJin Yao s = callchain_list__sym_name(chain, cbuf, sizeof(cbuf), false); 292*5bbd6badSJin Yao 293*5bbd6badSJin Yao if (pair) 294*5bbd6badSJin Yao scnprintf(buf, sizeof(buf), "%35s\t%35s", "", s); 295*5bbd6badSJin Yao else 296*5bbd6badSJin Yao scnprintf(buf, sizeof(buf), "%35s", s); 297*5bbd6badSJin Yao 298*5bbd6badSJin Yao printf("%s\n", buf); 299*5bbd6badSJin Yao } 300*5bbd6badSJin Yao } 301*5bbd6badSJin Yao 302*5bbd6badSJin Yao static void callchain_streams_report(struct evsel_streams *es_base, 303*5bbd6badSJin Yao struct evsel_streams *es_pair) 304*5bbd6badSJin Yao { 305*5bbd6badSJin Yao struct stream *base_stream; 306*5bbd6badSJin Yao int i, idx = 0; 307*5bbd6badSJin Yao 308*5bbd6badSJin Yao printf("[ Matched hot streams ]\n"); 309*5bbd6badSJin Yao for (i = 0; i < es_base->nr_streams; i++) { 310*5bbd6badSJin Yao base_stream = &es_base->streams[i]; 311*5bbd6badSJin Yao if (base_stream->pair_cnode) { 312*5bbd6badSJin Yao print_callchain_pair(base_stream, ++idx, 313*5bbd6badSJin Yao es_base, es_pair); 314*5bbd6badSJin Yao } 315*5bbd6badSJin Yao } 316*5bbd6badSJin Yao 317*5bbd6badSJin Yao idx = 0; 318*5bbd6badSJin Yao printf("\n[ Hot streams in old perf data only ]\n"); 319*5bbd6badSJin Yao for (i = 0; i < es_base->nr_streams; i++) { 320*5bbd6badSJin Yao base_stream = &es_base->streams[i]; 321*5bbd6badSJin Yao if (!base_stream->pair_cnode) { 322*5bbd6badSJin Yao print_stream_callchain(base_stream, ++idx, 323*5bbd6badSJin Yao es_base, false); 324*5bbd6badSJin Yao } 325*5bbd6badSJin Yao } 326*5bbd6badSJin Yao 327*5bbd6badSJin Yao idx = 0; 328*5bbd6badSJin Yao printf("\n[ Hot streams in new perf data only ]\n"); 329*5bbd6badSJin Yao for (i = 0; i < es_pair->nr_streams; i++) { 330*5bbd6badSJin Yao base_stream = &es_pair->streams[i]; 331*5bbd6badSJin Yao if (!base_stream->pair_cnode) { 332*5bbd6badSJin Yao print_stream_callchain(base_stream, ++idx, 333*5bbd6badSJin Yao es_pair, true); 334*5bbd6badSJin Yao } 335*5bbd6badSJin Yao } 336*5bbd6badSJin Yao } 337*5bbd6badSJin Yao 338*5bbd6badSJin Yao void evsel_streams__report(struct evsel_streams *es_base, 339*5bbd6badSJin Yao struct evsel_streams *es_pair) 340*5bbd6badSJin Yao { 341*5bbd6badSJin Yao return callchain_streams_report(es_base, es_pair); 342*5bbd6badSJin Yao } 343