File Index
Files and directories
Sign in
FILE

stream.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/util/stream.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Compare and figure out the top N hottest streams
  4 * Copyright (c) 2020, Intel Corporation.
  5 * Author: Jin Yao
  6 */
  7
  8#include <inttypes.h>
  9#include <stdlib.h>
 10#include <linux/zalloc.h>
 11#include "debug.h"
 12#include "hist.h"
 13#include "sort.h"
 14#include "stream.h"
 15#include "evlist.h"
 16
 17static void evsel_streams__delete(struct evsel_streams *es, int nr_evsel)
 18{
 19	for (int i = 0; i < nr_evsel; i++)
 20		zfree(&es[i].streams);
 21
 22	free(es);
 23}
 24
 25void evlist_streams__delete(struct evlist_streams *els)
 26{
 27	evsel_streams__delete(els->ev_streams, els->nr_evsel);
 28	free(els);
 29}
 30
 31static struct evlist_streams *evlist_streams__new(int nr_evsel,
 32						  int nr_streams_max)
 33{
 34	struct evlist_streams *els;
 35	struct evsel_streams *es;
 36
 37	els = zalloc(sizeof(*els));
 38	if (!els)
 39		return NULL;
 40
 41	es = calloc(nr_evsel, sizeof(struct evsel_streams));
 42	if (!es) {
 43		free(els);
 44		return NULL;
 45	}
 46
 47	for (int i = 0; i < nr_evsel; i++) {
 48		struct evsel_streams *s = &es[i];
 49
 50		s->streams = calloc(nr_streams_max, sizeof(struct stream));
 51		if (!s->streams)
 52			goto err;
 53
 54		s->nr_streams_max = nr_streams_max;
 55	}
 56
 57	els->ev_streams = es;
 58	els->nr_evsel = nr_evsel;
 59	return els;
 60
 61err:
 62	evsel_streams__delete(es, nr_evsel);
 63	return NULL;
 64}
 65
 66/*
 67 * The cnodes with high hit number are hot callchains.
 68 */
 69static void evsel_streams__set_hot_cnode(struct evsel_streams *es,
 70					 struct callchain_node *cnode)
 71{
 72	int i, idx = 0;
 73	u64 hit;
 74
 75	if (es->nr_streams < es->nr_streams_max) {
 76		i = es->nr_streams;
 77		es->streams[i].cnode = cnode;
 78		es->nr_streams++;
 79		return;
 80	}
 81
 82	/*
 83	 * Considering a few number of hot streams, only use simple
 84	 * way to find the cnode with smallest hit number and replace.
 85	 */
 86	hit = (es->streams[0].cnode)->hit;
 87	for (i = 1; i < es->nr_streams; i++) {
 88		if ((es->streams[i].cnode)->hit < hit) {
 89			hit = (es->streams[i].cnode)->hit;
 90			idx = i;
 91		}
 92	}
 93
 94	if (cnode->hit > hit)
 95		es->streams[idx].cnode = cnode;
 96}
 97
 98static void update_hot_callchain(struct hist_entry *he,
 99				 struct evsel_streams *es)
100{
101	struct rb_root *root = &he->sorted_chain;
102	struct rb_node *rb_node = rb_first(root);
103	struct callchain_node *cnode;
104
105	while (rb_node) {
106		cnode = rb_entry(rb_node, struct callchain_node, rb_node);
107		evsel_streams__set_hot_cnode(es, cnode);
108		rb_node = rb_next(rb_node);
109	}
110}
111
112static void init_hot_callchain(struct hists *hists, struct evsel_streams *es)
113{
114	struct rb_node *next = rb_first_cached(&hists->entries);
115
116	while (next) {
117		struct hist_entry *he;
118
119		he = rb_entry(next, struct hist_entry, rb_node);
120		update_hot_callchain(he, es);
121		next = rb_next(&he->rb_node);
122	}
123
124	es->streams_hits = callchain_total_hits(hists);
125}
126
127static int evlist__init_callchain_streams(struct evlist *evlist,
128					  struct evlist_streams *els)
129{
130	struct evsel_streams *es = els->ev_streams;
131	struct evsel *pos;
132	int i = 0;
133
134	BUG_ON(els->nr_evsel < evlist->core.nr_entries);
135
136	evlist__for_each_entry(evlist, pos) {
137		struct hists *hists = evsel__hists(pos);
138
139		hists__output_resort(hists, NULL);
140		init_hot_callchain(hists, &es[i]);
141		es[i].evsel = pos;
142		i++;
143	}
144
145	return 0;
146}
147
148struct evlist_streams *evlist__create_streams(struct evlist *evlist,
149					      int nr_streams_max)
150{
151	int nr_evsel = evlist->core.nr_entries, ret = -1;
152	struct evlist_streams *els = evlist_streams__new(nr_evsel,
153							 nr_streams_max);
154
155	if (!els)
156		return NULL;
157
158	ret = evlist__init_callchain_streams(evlist, els);
159	if (ret) {
160		evlist_streams__delete(els);
161		return NULL;
162	}
163
164	return els;
165}
166
167struct evsel_streams *evsel_streams__entry(struct evlist_streams *els,
168					   const struct evsel *evsel)
169{
170	struct evsel_streams *es = els->ev_streams;
171
172	for (int i = 0; i < els->nr_evsel; i++) {
173		if (es[i].evsel == evsel)
174			return &es[i];
175	}
176
177	return NULL;
178}
179
180static struct stream *stream__callchain_match(struct stream *base_stream,
181					      struct evsel_streams *es_pair)
182{
183	for (int i = 0; i < es_pair->nr_streams; i++) {
184		struct stream *pair_stream = &es_pair->streams[i];
185
186		if (callchain_cnode_matched(base_stream->cnode,
187					    pair_stream->cnode)) {
188			return pair_stream;
189		}
190	}
191
192	return NULL;
193}
194
195static struct stream *stream__match(struct stream *base_stream,
196				    struct evsel_streams *es_pair)
197{
198	return stream__callchain_match(base_stream, es_pair);
199}
200
201static void stream__link(struct stream *base_stream, struct stream *pair_stream)
202{
203	base_stream->pair_cnode = pair_stream->cnode;
204	pair_stream->pair_cnode = base_stream->cnode;
205}
206
207void evsel_streams__match(struct evsel_streams *es_base,
208			  struct evsel_streams *es_pair)
209{
210	for (int i = 0; i < es_base->nr_streams; i++) {
211		struct stream *base_stream = &es_base->streams[i];
212		struct stream *pair_stream;
213
214		pair_stream = stream__match(base_stream, es_pair);
215		if (pair_stream)
216			stream__link(base_stream, pair_stream);
217	}
218}
219
220static void print_callchain_pair(struct stream *base_stream, int idx,
221				 struct evsel_streams *es_base,
222				 struct evsel_streams *es_pair)
223{
224	struct callchain_node *base_cnode = base_stream->cnode;
225	struct callchain_node *pair_cnode = base_stream->pair_cnode;
226	struct callchain_list *base_chain, *pair_chain;
227	char buf1[512], buf2[512], cbuf1[256], cbuf2[256];
228	char *s1, *s2;
229	double pct;
230
231	printf("\nhot chain pair %d:\n", idx);
232
233	pct = (double)base_cnode->hit / (double)es_base->streams_hits;
234	scnprintf(buf1, sizeof(buf1), "cycles: %ld, hits: %.2f%%",
235		  callchain_avg_cycles(base_cnode), pct * 100.0);
236
237	pct = (double)pair_cnode->hit / (double)es_pair->streams_hits;
238	scnprintf(buf2, sizeof(buf2), "cycles: %ld, hits: %.2f%%",
239		  callchain_avg_cycles(pair_cnode), pct * 100.0);
240
241	printf("%35s\t%35s\n", buf1, buf2);
242
243	printf("%35s\t%35s\n",
244	       "---------------------------",
245	       "--------------------------");
246
247	pair_chain = list_first_entry(&pair_cnode->val,
248				      struct callchain_list,
249				      list);
250
251	list_for_each_entry(base_chain, &base_cnode->val, list) {
252		if (&pair_chain->list == &pair_cnode->val)
253			return;
254
255		s1 = callchain_list__sym_name(base_chain, cbuf1, sizeof(cbuf1),
256					      false);
257		s2 = callchain_list__sym_name(pair_chain, cbuf2, sizeof(cbuf2),
258					      false);
259
260		scnprintf(buf1, sizeof(buf1), "%35s\t%35s", s1, s2);
261		printf("%s\n", buf1);
262		pair_chain = list_next_entry(pair_chain, list);
263	}
264}
265
266static void print_stream_callchain(struct stream *stream, int idx,
267				   struct evsel_streams *es, bool pair)
268{
269	struct callchain_node *cnode = stream->cnode;
270	struct callchain_list *chain;
271	char buf[512], cbuf[256], *s;
272	double pct;
273
274	printf("\nhot chain %d:\n", idx);
275
276	pct = (double)cnode->hit / (double)es->streams_hits;
277	scnprintf(buf, sizeof(buf), "cycles: %ld, hits: %.2f%%",
278		  callchain_avg_cycles(cnode), pct * 100.0);
279
280	if (pair) {
281		printf("%35s\t%35s\n", "", buf);
282		printf("%35s\t%35s\n",
283		       "", "--------------------------");
284	} else {
285		printf("%35s\n", buf);
286		printf("%35s\n", "--------------------------");
287	}
288
289	list_for_each_entry(chain, &cnode->val, list) {
290		s = callchain_list__sym_name(chain, cbuf, sizeof(cbuf), false);
291
292		if (pair)
293			scnprintf(buf, sizeof(buf), "%35s\t%35s", "", s);
294		else
295			scnprintf(buf, sizeof(buf), "%35s", s);
296
297		printf("%s\n", buf);
298	}
299}
300
301static void callchain_streams_report(struct evsel_streams *es_base,
302				     struct evsel_streams *es_pair)
303{
304	struct stream *base_stream;
305	int i, idx = 0;
306
307	printf("[ Matched hot streams ]\n");
308	for (i = 0; i < es_base->nr_streams; i++) {
309		base_stream = &es_base->streams[i];
310		if (base_stream->pair_cnode) {
311			print_callchain_pair(base_stream, ++idx,
312					     es_base, es_pair);
313		}
314	}
315
316	idx = 0;
317	printf("\n[ Hot streams in old perf data only ]\n");
318	for (i = 0; i < es_base->nr_streams; i++) {
319		base_stream = &es_base->streams[i];
320		if (!base_stream->pair_cnode) {
321			print_stream_callchain(base_stream, ++idx,
322					       es_base, false);
323		}
324	}
325
326	idx = 0;
327	printf("\n[ Hot streams in new perf data only ]\n");
328	for (i = 0; i < es_pair->nr_streams; i++) {
329		base_stream = &es_pair->streams[i];
330		if (!base_stream->pair_cnode) {
331			print_stream_callchain(base_stream, ++idx,
332					       es_pair, true);
333		}
334	}
335}
336
337void evsel_streams__report(struct evsel_streams *es_base,
338			   struct evsel_streams *es_pair)
339{
340	return callchain_streams_report(es_base, es_pair);
341}
~/ Read-only file preview.