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