1 // SPDX-License-Identifier: GPL-2.0
3 * Compare and figure out the top N hottest streams
4 * Copyright (c) 2020, Intel Corporation.
10 #include <linux/zalloc.h>
17 static void evsel_streams__delete(struct evsel_streams
*es
, int nr_evsel
)
19 for (int i
= 0; i
< nr_evsel
; i
++)
20 zfree(&es
[i
].streams
);
25 void evlist_streams__delete(struct evlist_streams
*els
)
27 evsel_streams__delete(els
->ev_streams
, els
->nr_evsel
);
31 static struct evlist_streams
*evlist_streams__new(int nr_evsel
,
34 struct evlist_streams
*els
;
35 struct evsel_streams
*es
;
37 els
= zalloc(sizeof(*els
));
41 es
= calloc(nr_evsel
, sizeof(struct evsel_streams
));
47 for (int i
= 0; i
< nr_evsel
; i
++) {
48 struct evsel_streams
*s
= &es
[i
];
50 s
->streams
= calloc(nr_streams_max
, sizeof(struct stream
));
54 s
->nr_streams_max
= nr_streams_max
;
59 els
->nr_evsel
= nr_evsel
;
63 evsel_streams__delete(es
, nr_evsel
);
68 * The cnodes with high hit number are hot callchains.
70 static void evsel_streams__set_hot_cnode(struct evsel_streams
*es
,
71 struct callchain_node
*cnode
)
76 if (es
->nr_streams
< es
->nr_streams_max
) {
78 es
->streams
[i
].cnode
= cnode
;
84 * Considering a few number of hot streams, only use simple
85 * way to find the cnode with smallest hit number and replace.
87 hit
= (es
->streams
[0].cnode
)->hit
;
88 for (i
= 1; i
< es
->nr_streams
; i
++) {
89 if ((es
->streams
[i
].cnode
)->hit
< hit
) {
90 hit
= (es
->streams
[i
].cnode
)->hit
;
96 es
->streams
[idx
].cnode
= cnode
;
99 static void update_hot_callchain(struct hist_entry
*he
,
100 struct evsel_streams
*es
)
102 struct rb_root
*root
= &he
->sorted_chain
;
103 struct rb_node
*rb_node
= rb_first(root
);
104 struct callchain_node
*cnode
;
107 cnode
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
108 evsel_streams__set_hot_cnode(es
, cnode
);
109 rb_node
= rb_next(rb_node
);
113 static void init_hot_callchain(struct hists
*hists
, struct evsel_streams
*es
)
115 struct rb_node
*next
= rb_first_cached(&hists
->entries
);
118 struct hist_entry
*he
;
120 he
= rb_entry(next
, struct hist_entry
, rb_node
);
121 update_hot_callchain(he
, es
);
122 next
= rb_next(&he
->rb_node
);
125 es
->streams_hits
= callchain_total_hits(hists
);
128 static int evlist__init_callchain_streams(struct evlist
*evlist
,
129 struct evlist_streams
*els
)
131 struct evsel_streams
*es
= els
->ev_streams
;
135 BUG_ON(els
->nr_evsel
< evlist
->core
.nr_entries
);
137 evlist__for_each_entry(evlist
, pos
) {
138 struct hists
*hists
= evsel__hists(pos
);
140 hists__output_resort(hists
, NULL
);
141 init_hot_callchain(hists
, &es
[i
]);
142 es
[i
].evsel_idx
= pos
->idx
;
149 struct evlist_streams
*evlist__create_streams(struct evlist
*evlist
,
152 int nr_evsel
= evlist
->core
.nr_entries
, ret
= -1;
153 struct evlist_streams
*els
= evlist_streams__new(nr_evsel
,
159 ret
= evlist__init_callchain_streams(evlist
, els
);
161 evlist_streams__delete(els
);
168 struct evsel_streams
*evsel_streams__entry(struct evlist_streams
*els
,
171 struct evsel_streams
*es
= els
->ev_streams
;
173 for (int i
= 0; i
< els
->nr_evsel
; i
++) {
174 if (es
[i
].evsel_idx
== evsel_idx
)
181 static struct stream
*stream__callchain_match(struct stream
*base_stream
,
182 struct evsel_streams
*es_pair
)
184 for (int i
= 0; i
< es_pair
->nr_streams
; i
++) {
185 struct stream
*pair_stream
= &es_pair
->streams
[i
];
187 if (callchain_cnode_matched(base_stream
->cnode
,
188 pair_stream
->cnode
)) {
196 static struct stream
*stream__match(struct stream
*base_stream
,
197 struct evsel_streams
*es_pair
)
199 return stream__callchain_match(base_stream
, es_pair
);
202 static void stream__link(struct stream
*base_stream
, struct stream
*pair_stream
)
204 base_stream
->pair_cnode
= pair_stream
->cnode
;
205 pair_stream
->pair_cnode
= base_stream
->cnode
;
208 void evsel_streams__match(struct evsel_streams
*es_base
,
209 struct evsel_streams
*es_pair
)
211 for (int i
= 0; i
< es_base
->nr_streams
; i
++) {
212 struct stream
*base_stream
= &es_base
->streams
[i
];
213 struct stream
*pair_stream
;
215 pair_stream
= stream__match(base_stream
, es_pair
);
217 stream__link(base_stream
, pair_stream
);
221 static void print_callchain_pair(struct stream
*base_stream
, int idx
,
222 struct evsel_streams
*es_base
,
223 struct evsel_streams
*es_pair
)
225 struct callchain_node
*base_cnode
= base_stream
->cnode
;
226 struct callchain_node
*pair_cnode
= base_stream
->pair_cnode
;
227 struct callchain_list
*base_chain
, *pair_chain
;
228 char buf1
[512], buf2
[512], cbuf1
[256], cbuf2
[256];
232 printf("\nhot chain pair %d:\n", idx
);
234 pct
= (double)base_cnode
->hit
/ (double)es_base
->streams_hits
;
235 scnprintf(buf1
, sizeof(buf1
), "cycles: %ld, hits: %.2f%%",
236 callchain_avg_cycles(base_cnode
), pct
* 100.0);
238 pct
= (double)pair_cnode
->hit
/ (double)es_pair
->streams_hits
;
239 scnprintf(buf2
, sizeof(buf2
), "cycles: %ld, hits: %.2f%%",
240 callchain_avg_cycles(pair_cnode
), pct
* 100.0);
242 printf("%35s\t%35s\n", buf1
, buf2
);
244 printf("%35s\t%35s\n",
245 "---------------------------",
246 "--------------------------");
248 pair_chain
= list_first_entry(&pair_cnode
->val
,
249 struct callchain_list
,
252 list_for_each_entry(base_chain
, &base_cnode
->val
, list
) {
253 if (&pair_chain
->list
== &pair_cnode
->val
)
256 s1
= callchain_list__sym_name(base_chain
, cbuf1
, sizeof(cbuf1
),
258 s2
= callchain_list__sym_name(pair_chain
, cbuf2
, sizeof(cbuf2
),
261 scnprintf(buf1
, sizeof(buf1
), "%35s\t%35s", s1
, s2
);
262 printf("%s\n", buf1
);
263 pair_chain
= list_next_entry(pair_chain
, list
);
267 static void print_stream_callchain(struct stream
*stream
, int idx
,
268 struct evsel_streams
*es
, bool pair
)
270 struct callchain_node
*cnode
= stream
->cnode
;
271 struct callchain_list
*chain
;
272 char buf
[512], cbuf
[256], *s
;
275 printf("\nhot chain %d:\n", idx
);
277 pct
= (double)cnode
->hit
/ (double)es
->streams_hits
;
278 scnprintf(buf
, sizeof(buf
), "cycles: %ld, hits: %.2f%%",
279 callchain_avg_cycles(cnode
), pct
* 100.0);
282 printf("%35s\t%35s\n", "", buf
);
283 printf("%35s\t%35s\n",
284 "", "--------------------------");
286 printf("%35s\n", buf
);
287 printf("%35s\n", "--------------------------");
290 list_for_each_entry(chain
, &cnode
->val
, list
) {
291 s
= callchain_list__sym_name(chain
, cbuf
, sizeof(cbuf
), false);
294 scnprintf(buf
, sizeof(buf
), "%35s\t%35s", "", s
);
296 scnprintf(buf
, sizeof(buf
), "%35s", s
);
302 static void callchain_streams_report(struct evsel_streams
*es_base
,
303 struct evsel_streams
*es_pair
)
305 struct stream
*base_stream
;
308 printf("[ Matched hot streams ]\n");
309 for (i
= 0; i
< es_base
->nr_streams
; i
++) {
310 base_stream
= &es_base
->streams
[i
];
311 if (base_stream
->pair_cnode
) {
312 print_callchain_pair(base_stream
, ++idx
,
318 printf("\n[ Hot streams in old perf data only ]\n");
319 for (i
= 0; i
< es_base
->nr_streams
; i
++) {
320 base_stream
= &es_base
->streams
[i
];
321 if (!base_stream
->pair_cnode
) {
322 print_stream_callchain(base_stream
, ++idx
,
328 printf("\n[ Hot streams in new perf data only ]\n");
329 for (i
= 0; i
< es_pair
->nr_streams
; i
++) {
330 base_stream
= &es_pair
->streams
[i
];
331 if (!base_stream
->pair_cnode
) {
332 print_stream_callchain(base_stream
, ++idx
,
338 void evsel_streams__report(struct evsel_streams
*es_base
,
339 struct evsel_streams
*es_pair
)
341 return callchain_streams_report(es_base
, es_pair
);