blob: 043344a720bf3783e6ee74be251da5cebac487b2 [file] [log] [blame]
Jiri Olsa7aef3bf2016-09-22 17:36:38 +02001#include <linux/compiler.h>
2#include <linux/kernel.h>
Jiri Olsacbb88502016-09-22 17:36:48 +02003#include <linux/stringify.h>
Jiri Olsa1e181b92016-06-03 15:40:28 +02004#include <asm/bug.h>
Jiri Olsa7aef3bf2016-09-22 17:36:38 +02005#include "util.h"
6#include "debug.h"
7#include "builtin.h"
8#include <subcmd/parse-options.h>
Jiri Olsa39bcd4a2016-09-22 17:36:39 +02009#include "mem-events.h"
Jiri Olsa903a6f12016-09-22 17:36:40 +020010#include "session.h"
11#include "hist.h"
Jiri Olsacbb88502016-09-22 17:36:48 +020012#include "sort.h"
Jiri Olsa903a6f12016-09-22 17:36:40 +020013#include "tool.h"
14#include "data.h"
Jiri Olsa8d3f9382016-09-22 17:36:42 +020015#include "sort.h"
Jiri Olsa903a6f12016-09-22 17:36:40 +020016
Jiri Olsac75540e2016-09-22 17:36:41 +020017struct c2c_hists {
18 struct hists hists;
19 struct perf_hpp_list list;
Jiri Olsab2252ae2016-09-22 17:36:46 +020020 struct c2c_stats stats;
Jiri Olsac75540e2016-09-22 17:36:41 +020021};
22
Jiri Olsa92062d52016-06-05 13:40:53 +020023struct compute_stats {
24 struct stats lcl_hitm;
25 struct stats rmt_hitm;
26 struct stats load;
27};
28
Jiri Olsa78b27542016-09-22 17:36:44 +020029struct c2c_hist_entry {
30 struct c2c_hists *hists;
Jiri Olsab2252ae2016-09-22 17:36:46 +020031 struct c2c_stats stats;
Jiri Olsa1e181b92016-06-03 15:40:28 +020032 unsigned long *cpuset;
33 struct c2c_stats *node_stats;
Jiri Olsa92062d52016-06-05 13:40:53 +020034
35 struct compute_stats cstats;
36
Jiri Olsa78b27542016-09-22 17:36:44 +020037 /*
38 * must be at the end,
39 * because of its callchain dynamic entry
40 */
41 struct hist_entry he;
42};
43
Jiri Olsa903a6f12016-09-22 17:36:40 +020044struct perf_c2c {
Jiri Olsac75540e2016-09-22 17:36:41 +020045 struct perf_tool tool;
46 struct c2c_hists hists;
Jiri Olsa1e181b92016-06-03 15:40:28 +020047
48 unsigned long **nodes;
49 int nodes_cnt;
50 int cpus_cnt;
51 int *cpu2node;
52 int node_info;
Jiri Olsa903a6f12016-09-22 17:36:40 +020053};
54
55static struct perf_c2c c2c;
Jiri Olsa7aef3bf2016-09-22 17:36:38 +020056
Jiri Olsa78b27542016-09-22 17:36:44 +020057static void *c2c_he_zalloc(size_t size)
58{
59 struct c2c_hist_entry *c2c_he;
60
61 c2c_he = zalloc(size + sizeof(*c2c_he));
62 if (!c2c_he)
63 return NULL;
64
Jiri Olsa1e181b92016-06-03 15:40:28 +020065 c2c_he->cpuset = bitmap_alloc(c2c.cpus_cnt);
66 if (!c2c_he->cpuset)
67 return NULL;
68
69 c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
70 if (!c2c_he->node_stats)
71 return NULL;
72
Jiri Olsa92062d52016-06-05 13:40:53 +020073 init_stats(&c2c_he->cstats.lcl_hitm);
74 init_stats(&c2c_he->cstats.rmt_hitm);
75 init_stats(&c2c_he->cstats.load);
76
Jiri Olsa78b27542016-09-22 17:36:44 +020077 return &c2c_he->he;
78}
79
80static void c2c_he_free(void *he)
81{
82 struct c2c_hist_entry *c2c_he;
83
84 c2c_he = container_of(he, struct c2c_hist_entry, he);
85 if (c2c_he->hists) {
86 hists__delete_entries(&c2c_he->hists->hists);
87 free(c2c_he->hists);
88 }
89
Jiri Olsa1e181b92016-06-03 15:40:28 +020090 free(c2c_he->cpuset);
91 free(c2c_he->node_stats);
Jiri Olsa78b27542016-09-22 17:36:44 +020092 free(c2c_he);
93}
94
95static struct hist_entry_ops c2c_entry_ops = {
96 .new = c2c_he_zalloc,
97 .free = c2c_he_free,
98};
99
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200100static int c2c_hists__init(struct c2c_hists *hists,
101 const char *sort);
102
Jiri Olsab2252ae2016-09-22 17:36:46 +0200103static struct c2c_hists*
104he__get_c2c_hists(struct hist_entry *he,
105 const char *sort)
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200106{
107 struct c2c_hist_entry *c2c_he;
108 struct c2c_hists *hists;
109 int ret;
110
111 c2c_he = container_of(he, struct c2c_hist_entry, he);
112 if (c2c_he->hists)
Jiri Olsab2252ae2016-09-22 17:36:46 +0200113 return c2c_he->hists;
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200114
115 hists = c2c_he->hists = zalloc(sizeof(*hists));
116 if (!hists)
117 return NULL;
118
119 ret = c2c_hists__init(hists, sort);
120 if (ret) {
121 free(hists);
122 return NULL;
123 }
124
Jiri Olsab2252ae2016-09-22 17:36:46 +0200125 return hists;
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200126}
127
Jiri Olsa1e181b92016-06-03 15:40:28 +0200128static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
129 struct perf_sample *sample)
130{
131 if (WARN_ONCE(sample->cpu == (unsigned int) -1,
132 "WARNING: no sample cpu value"))
133 return;
134
135 set_bit(sample->cpu, c2c_he->cpuset);
136}
137
Jiri Olsa92062d52016-06-05 13:40:53 +0200138static void compute_stats(struct c2c_hist_entry *c2c_he,
139 struct c2c_stats *stats,
140 u64 weight)
141{
142 struct compute_stats *cstats = &c2c_he->cstats;
143
144 if (stats->rmt_hitm)
145 update_stats(&cstats->rmt_hitm, weight);
146 else if (stats->lcl_hitm)
147 update_stats(&cstats->lcl_hitm, weight);
148 else if (stats->load)
149 update_stats(&cstats->load, weight);
150}
151
Jiri Olsa78b27542016-09-22 17:36:44 +0200152static int process_sample_event(struct perf_tool *tool __maybe_unused,
153 union perf_event *event,
154 struct perf_sample *sample,
155 struct perf_evsel *evsel __maybe_unused,
156 struct machine *machine)
157{
Jiri Olsab2252ae2016-09-22 17:36:46 +0200158 struct c2c_hists *c2c_hists = &c2c.hists;
159 struct c2c_hist_entry *c2c_he;
160 struct c2c_stats stats = { .nr_entries = 0, };
Jiri Olsa78b27542016-09-22 17:36:44 +0200161 struct hist_entry *he;
162 struct addr_location al;
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200163 struct mem_info *mi, *mi_dup;
Jiri Olsa78b27542016-09-22 17:36:44 +0200164 int ret;
165
166 if (machine__resolve(machine, &al, sample) < 0) {
167 pr_debug("problem processing %d event, skipping it.\n",
168 event->header.type);
169 return -1;
170 }
171
172 mi = sample__resolve_mem(sample, &al);
173 if (mi == NULL)
174 return -ENOMEM;
175
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200176 mi_dup = memdup(mi, sizeof(*mi));
177 if (!mi_dup)
178 goto free_mi;
179
Jiri Olsab2252ae2016-09-22 17:36:46 +0200180 c2c_decode_stats(&stats, mi);
181
182 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
Jiri Olsa78b27542016-09-22 17:36:44 +0200183 &al, NULL, NULL, mi,
184 sample, true);
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200185 if (he == NULL)
186 goto free_mi_dup;
Jiri Olsa78b27542016-09-22 17:36:44 +0200187
Jiri Olsab2252ae2016-09-22 17:36:46 +0200188 c2c_he = container_of(he, struct c2c_hist_entry, he);
189 c2c_add_stats(&c2c_he->stats, &stats);
190 c2c_add_stats(&c2c_hists->stats, &stats);
191
Jiri Olsa1e181b92016-06-03 15:40:28 +0200192 c2c_he__set_cpu(c2c_he, sample);
193
Jiri Olsab2252ae2016-09-22 17:36:46 +0200194 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
Jiri Olsa78b27542016-09-22 17:36:44 +0200195 ret = hist_entry__append_callchain(he, sample);
196
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200197 if (!ret) {
Jiri Olsa1e181b92016-06-03 15:40:28 +0200198 /*
199 * There's already been warning about missing
200 * sample's cpu value. Let's account all to
201 * node 0 in this case, without any further
202 * warning.
203 *
204 * Doing node stats only for single callchain data.
205 */
206 int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
207 int node = c2c.cpu2node[cpu];
208
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200209 mi = mi_dup;
210
211 mi_dup = memdup(mi, sizeof(*mi));
212 if (!mi_dup)
213 goto free_mi;
214
Jiri Olsab2252ae2016-09-22 17:36:46 +0200215 c2c_hists = he__get_c2c_hists(he, "offset");
216 if (!c2c_hists)
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200217 goto free_mi_dup;
218
Jiri Olsab2252ae2016-09-22 17:36:46 +0200219 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200220 &al, NULL, NULL, mi,
221 sample, true);
222 if (he == NULL)
223 goto free_mi_dup;
224
Jiri Olsab2252ae2016-09-22 17:36:46 +0200225 c2c_he = container_of(he, struct c2c_hist_entry, he);
226 c2c_add_stats(&c2c_he->stats, &stats);
227 c2c_add_stats(&c2c_hists->stats, &stats);
Jiri Olsa1e181b92016-06-03 15:40:28 +0200228 c2c_add_stats(&c2c_he->node_stats[node], &stats);
229
Jiri Olsa92062d52016-06-05 13:40:53 +0200230 compute_stats(c2c_he, &stats, sample->weight);
231
Jiri Olsa1e181b92016-06-03 15:40:28 +0200232 c2c_he__set_cpu(c2c_he, sample);
Jiri Olsab2252ae2016-09-22 17:36:46 +0200233
234 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200235 ret = hist_entry__append_callchain(he, sample);
236 }
237
238out:
Jiri Olsa78b27542016-09-22 17:36:44 +0200239 addr_location__put(&al);
240 return ret;
Jiri Olsaec06f9b2016-09-22 17:36:45 +0200241
242free_mi_dup:
243 free(mi_dup);
244free_mi:
245 free(mi);
246 ret = -ENOMEM;
247 goto out;
Jiri Olsa78b27542016-09-22 17:36:44 +0200248}
249
250static struct perf_c2c c2c = {
251 .tool = {
252 .sample = process_sample_event,
253 .mmap = perf_event__process_mmap,
254 .mmap2 = perf_event__process_mmap2,
255 .comm = perf_event__process_comm,
256 .exit = perf_event__process_exit,
257 .fork = perf_event__process_fork,
258 .lost = perf_event__process_lost,
259 .ordered_events = true,
260 .ordering_requires_timestamps = true,
261 },
262};
263
Jiri Olsa7aef3bf2016-09-22 17:36:38 +0200264static const char * const c2c_usage[] = {
Jiri Olsa903a6f12016-09-22 17:36:40 +0200265 "perf c2c {record|report}",
Jiri Olsa7aef3bf2016-09-22 17:36:38 +0200266 NULL
267};
268
Jiri Olsa903a6f12016-09-22 17:36:40 +0200269static const char * const __usage_report[] = {
270 "perf c2c report",
271 NULL
272};
273
274static const char * const *report_c2c_usage = __usage_report;
275
Jiri Olsac75540e2016-09-22 17:36:41 +0200276#define C2C_HEADER_MAX 2
277
278struct c2c_header {
279 struct {
280 const char *text;
281 int span;
282 } line[C2C_HEADER_MAX];
283};
284
285struct c2c_dimension {
286 struct c2c_header header;
287 const char *name;
288 int width;
Jiri Olsa8d3f9382016-09-22 17:36:42 +0200289 struct sort_entry *se;
Jiri Olsac75540e2016-09-22 17:36:41 +0200290
291 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
292 struct hist_entry *, struct hist_entry *);
293 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
294 struct hist_entry *he);
295 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
296 struct hist_entry *he);
297};
298
299struct c2c_fmt {
300 struct perf_hpp_fmt fmt;
301 struct c2c_dimension *dim;
302};
303
304static int c2c_width(struct perf_hpp_fmt *fmt,
305 struct perf_hpp *hpp __maybe_unused,
306 struct hists *hists __maybe_unused)
307{
308 struct c2c_fmt *c2c_fmt;
Jiri Olsac75540e2016-09-22 17:36:41 +0200309 struct c2c_dimension *dim;
Jiri Olsac75540e2016-09-22 17:36:41 +0200310
311 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
312 dim = c2c_fmt->dim;
313
Jiri Olsa8d3f9382016-09-22 17:36:42 +0200314 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
315 c2c_fmt->dim->width;
316}
317
318static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
319 struct hists *hists, int line, int *span)
320{
321 struct perf_hpp_list *hpp_list = hists->hpp_list;
322 struct c2c_fmt *c2c_fmt;
323 struct c2c_dimension *dim;
324 const char *text = NULL;
325 int width = c2c_width(fmt, hpp, hists);
326
327 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
328 dim = c2c_fmt->dim;
329
330 if (dim->se) {
331 text = dim->header.line[line].text;
332 /* Use the last line from sort_entry if not defined. */
333 if (!text && (line == hpp_list->nr_header_lines - 1))
334 text = dim->se->se_header;
335 } else {
336 text = dim->header.line[line].text;
337
338 if (*span) {
339 (*span)--;
340 return 0;
341 } else {
342 *span = dim->header.line[line].span;
343 }
344 }
345
Jiri Olsac75540e2016-09-22 17:36:41 +0200346 if (text == NULL)
347 text = "";
348
Jiri Olsa8d3f9382016-09-22 17:36:42 +0200349 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
Jiri Olsac75540e2016-09-22 17:36:41 +0200350}
351
Jiri Olsacbb88502016-09-22 17:36:48 +0200352#define HEX_STR(__s, __v) \
353({ \
354 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
355 __s; \
356})
357
358static int64_t
359dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
360 struct hist_entry *left, struct hist_entry *right)
361{
362 return sort__dcacheline_cmp(left, right);
363}
364
365static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
366 struct hist_entry *he)
367{
368 uint64_t addr = 0;
369 int width = c2c_width(fmt, hpp, he->hists);
370 char buf[20];
371
372 if (he->mem_info)
373 addr = cl_address(he->mem_info->daddr.addr);
374
375 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
376}
377
Jiri Olsa48acdeb2016-04-29 14:37:06 +0200378static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
379 struct hist_entry *he)
380{
381 uint64_t addr = 0;
382 int width = c2c_width(fmt, hpp, he->hists);
383 char buf[20];
384
385 if (he->mem_info)
386 addr = cl_offset(he->mem_info->daddr.al_addr);
387
388 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
389}
390
391static int64_t
392offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
393 struct hist_entry *left, struct hist_entry *right)
394{
395 uint64_t l = 0, r = 0;
396
397 if (left->mem_info)
398 l = cl_offset(left->mem_info->daddr.addr);
399 if (right->mem_info)
400 r = cl_offset(right->mem_info->daddr.addr);
401
402 return (int64_t)(r - l);
403}
404
Jiri Olsa43575a92016-05-03 21:48:56 +0200405static int
406iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
407 struct hist_entry *he)
408{
409 uint64_t addr = 0;
410 int width = c2c_width(fmt, hpp, he->hists);
411 char buf[20];
412
413 if (he->mem_info)
414 addr = he->mem_info->iaddr.addr;
415
416 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
417}
418
419static int64_t
420iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
421 struct hist_entry *left, struct hist_entry *right)
422{
423 return sort__iaddr_cmp(left, right);
424}
425
Jiri Olsa97cb4862016-05-23 16:20:14 +0200426static int
427tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
428 struct hist_entry *he)
429{
430 struct c2c_hist_entry *c2c_he;
431 int width = c2c_width(fmt, hpp, he->hists);
432 unsigned int tot_hitm;
433
434 c2c_he = container_of(he, struct c2c_hist_entry, he);
435 tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
436
437 return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
438}
439
440static int64_t
441tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
442 struct hist_entry *left, struct hist_entry *right)
443{
444 struct c2c_hist_entry *c2c_left;
445 struct c2c_hist_entry *c2c_right;
446 unsigned int tot_hitm_left;
447 unsigned int tot_hitm_right;
448
449 c2c_left = container_of(left, struct c2c_hist_entry, he);
450 c2c_right = container_of(right, struct c2c_hist_entry, he);
451
452 tot_hitm_left = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
453 tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
454
455 return tot_hitm_left - tot_hitm_right;
456}
457
458#define STAT_FN_ENTRY(__f) \
459static int \
460__f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, \
461 struct hist_entry *he) \
462{ \
463 struct c2c_hist_entry *c2c_he; \
464 int width = c2c_width(fmt, hpp, he->hists); \
465 \
466 c2c_he = container_of(he, struct c2c_hist_entry, he); \
467 return scnprintf(hpp->buf, hpp->size, "%*u", width, \
468 c2c_he->stats.__f); \
469}
470
471#define STAT_FN_CMP(__f) \
472static int64_t \
473__f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \
474 struct hist_entry *left, struct hist_entry *right) \
475{ \
476 struct c2c_hist_entry *c2c_left, *c2c_right; \
477 \
478 c2c_left = container_of(left, struct c2c_hist_entry, he); \
479 c2c_right = container_of(right, struct c2c_hist_entry, he); \
480 return c2c_left->stats.__f - c2c_right->stats.__f; \
481}
482
483#define STAT_FN(__f) \
484 STAT_FN_ENTRY(__f) \
485 STAT_FN_CMP(__f)
486
487STAT_FN(rmt_hitm)
488STAT_FN(lcl_hitm)
Jiri Olsa0f188962016-05-04 10:10:11 +0200489STAT_FN(store)
490STAT_FN(st_l1hit)
491STAT_FN(st_l1miss)
Jiri Olsa1295f682016-05-04 10:18:24 +0200492STAT_FN(ld_fbhit)
493STAT_FN(ld_l1hit)
494STAT_FN(ld_l2hit)
Jiri Olsa4d089102016-05-04 10:27:51 +0200495STAT_FN(ld_llchit)
496STAT_FN(rmt_hit)
Jiri Olsa97cb4862016-05-23 16:20:14 +0200497
Jiri Olsa04402d22016-05-19 10:10:51 +0200498static uint64_t llc_miss(struct c2c_stats *stats)
499{
500 uint64_t llcmiss;
501
502 llcmiss = stats->lcl_dram +
503 stats->rmt_dram +
504 stats->rmt_hitm +
505 stats->rmt_hit;
506
507 return llcmiss;
508}
509
510static int
511ld_llcmiss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
512 struct hist_entry *he)
513{
514 struct c2c_hist_entry *c2c_he;
515 int width = c2c_width(fmt, hpp, he->hists);
516
517 c2c_he = container_of(he, struct c2c_hist_entry, he);
518
519 return scnprintf(hpp->buf, hpp->size, "%*lu", width,
520 llc_miss(&c2c_he->stats));
521}
522
523static int64_t
524ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
525 struct hist_entry *left, struct hist_entry *right)
526{
527 struct c2c_hist_entry *c2c_left;
528 struct c2c_hist_entry *c2c_right;
529
530 c2c_left = container_of(left, struct c2c_hist_entry, he);
531 c2c_right = container_of(right, struct c2c_hist_entry, he);
532
533 return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats);
534}
535
Jiri Olsa01b84d72016-05-04 10:35:29 +0200536static uint64_t total_records(struct c2c_stats *stats)
537{
538 uint64_t lclmiss, ldcnt, total;
539
540 lclmiss = stats->lcl_dram +
541 stats->rmt_dram +
542 stats->rmt_hitm +
543 stats->rmt_hit;
544
545 ldcnt = lclmiss +
546 stats->ld_fbhit +
547 stats->ld_l1hit +
548 stats->ld_l2hit +
549 stats->ld_llchit +
550 stats->lcl_hitm;
551
552 total = ldcnt +
553 stats->st_l1hit +
554 stats->st_l1miss;
555
556 return total;
557}
558
559static int
560tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
561 struct hist_entry *he)
562{
563 struct c2c_hist_entry *c2c_he;
564 int width = c2c_width(fmt, hpp, he->hists);
565 uint64_t tot_recs;
566
567 c2c_he = container_of(he, struct c2c_hist_entry, he);
568 tot_recs = total_records(&c2c_he->stats);
569
570 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
571}
572
573static int64_t
574tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
575 struct hist_entry *left, struct hist_entry *right)
576{
577 struct c2c_hist_entry *c2c_left;
578 struct c2c_hist_entry *c2c_right;
579 uint64_t tot_recs_left;
580 uint64_t tot_recs_right;
581
582 c2c_left = container_of(left, struct c2c_hist_entry, he);
583 c2c_right = container_of(right, struct c2c_hist_entry, he);
584
585 tot_recs_left = total_records(&c2c_left->stats);
586 tot_recs_right = total_records(&c2c_right->stats);
587
588 return tot_recs_left - tot_recs_right;
589}
590
Jiri Olsa55177c42016-05-19 09:52:37 +0200591static uint64_t total_loads(struct c2c_stats *stats)
592{
593 uint64_t lclmiss, ldcnt;
594
595 lclmiss = stats->lcl_dram +
596 stats->rmt_dram +
597 stats->rmt_hitm +
598 stats->rmt_hit;
599
600 ldcnt = lclmiss +
601 stats->ld_fbhit +
602 stats->ld_l1hit +
603 stats->ld_l2hit +
604 stats->ld_llchit +
605 stats->lcl_hitm;
606
607 return ldcnt;
608}
609
610static int
611tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
612 struct hist_entry *he)
613{
614 struct c2c_hist_entry *c2c_he;
615 int width = c2c_width(fmt, hpp, he->hists);
616 uint64_t tot_recs;
617
618 c2c_he = container_of(he, struct c2c_hist_entry, he);
619 tot_recs = total_loads(&c2c_he->stats);
620
621 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
622}
623
624static int64_t
625tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
626 struct hist_entry *left, struct hist_entry *right)
627{
628 struct c2c_hist_entry *c2c_left;
629 struct c2c_hist_entry *c2c_right;
630 uint64_t tot_recs_left;
631 uint64_t tot_recs_right;
632
633 c2c_left = container_of(left, struct c2c_hist_entry, he);
634 c2c_right = container_of(right, struct c2c_hist_entry, he);
635
636 tot_recs_left = total_loads(&c2c_left->stats);
637 tot_recs_right = total_loads(&c2c_right->stats);
638
639 return tot_recs_left - tot_recs_right;
640}
641
Jiri Olsaf0c50c12016-05-04 10:50:09 +0200642typedef double (get_percent_cb)(struct c2c_hist_entry *);
643
644static int
645percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
646 struct hist_entry *he, get_percent_cb get_percent)
647{
648 struct c2c_hist_entry *c2c_he;
649 int width = c2c_width(fmt, hpp, he->hists);
650 double per;
651
652 c2c_he = container_of(he, struct c2c_hist_entry, he);
653 per = get_percent(c2c_he);
654
655 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
656}
657
658static double percent_hitm(struct c2c_hist_entry *c2c_he)
659{
660 struct c2c_hists *hists;
661 struct c2c_stats *stats;
662 struct c2c_stats *total;
663 int tot, st;
664 double p;
665
666 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
667 stats = &c2c_he->stats;
668 total = &hists->stats;
669
670 st = stats->rmt_hitm;
671 tot = total->rmt_hitm;
672
673 p = tot ? (double) st / tot : 0;
674
675 return 100 * p;
676}
677
678#define PERC_STR(__s, __v) \
679({ \
680 scnprintf(__s, sizeof(__s), "%.2F%%", __v); \
681 __s; \
682})
683
684static int
685percent_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
686 struct hist_entry *he)
687{
688 struct c2c_hist_entry *c2c_he;
689 int width = c2c_width(fmt, hpp, he->hists);
690 char buf[10];
691 double per;
692
693 c2c_he = container_of(he, struct c2c_hist_entry, he);
694 per = percent_hitm(c2c_he);
695 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
696}
697
698static int
699percent_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
700 struct hist_entry *he)
701{
702 return percent_color(fmt, hpp, he, percent_hitm);
703}
704
705static int64_t
706percent_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
707 struct hist_entry *left, struct hist_entry *right)
708{
709 struct c2c_hist_entry *c2c_left;
710 struct c2c_hist_entry *c2c_right;
711 double per_left;
712 double per_right;
713
714 c2c_left = container_of(left, struct c2c_hist_entry, he);
715 c2c_right = container_of(right, struct c2c_hist_entry, he);
716
717 per_left = percent_hitm(c2c_left);
718 per_right = percent_hitm(c2c_right);
719
720 return per_left - per_right;
721}
722
Jiri Olsa9cb35002016-05-04 12:16:50 +0200723static struct c2c_stats *he_stats(struct hist_entry *he)
724{
725 struct c2c_hist_entry *c2c_he;
726
727 c2c_he = container_of(he, struct c2c_hist_entry, he);
728 return &c2c_he->stats;
729}
730
731static struct c2c_stats *total_stats(struct hist_entry *he)
732{
733 struct c2c_hists *hists;
734
735 hists = container_of(he->hists, struct c2c_hists, hists);
736 return &hists->stats;
737}
738
739static double percent(int st, int tot)
740{
741 return tot ? 100. * (double) st / (double) tot : 0;
742}
743
744#define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
745
746#define PERCENT_FN(__f) \
747static double percent_ ## __f(struct c2c_hist_entry *c2c_he) \
748{ \
749 struct c2c_hists *hists; \
750 \
751 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists); \
752 return percent(c2c_he->stats.__f, hists->stats.__f); \
753}
754
755PERCENT_FN(rmt_hitm)
756PERCENT_FN(lcl_hitm)
757PERCENT_FN(st_l1hit)
758PERCENT_FN(st_l1miss)
759
760static int
761percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
762 struct hist_entry *he)
763{
764 int width = c2c_width(fmt, hpp, he->hists);
765 double per = PERCENT(he, rmt_hitm);
766 char buf[10];
767
768 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
769}
770
771static int
772percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
773 struct hist_entry *he)
774{
775 return percent_color(fmt, hpp, he, percent_rmt_hitm);
776}
777
778static int64_t
779percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
780 struct hist_entry *left, struct hist_entry *right)
781{
782 double per_left;
783 double per_right;
784
785 per_left = PERCENT(left, lcl_hitm);
786 per_right = PERCENT(right, lcl_hitm);
787
788 return per_left - per_right;
789}
790
791static int
792percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
793 struct hist_entry *he)
794{
795 int width = c2c_width(fmt, hpp, he->hists);
796 double per = PERCENT(he, lcl_hitm);
797 char buf[10];
798
799 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
800}
801
802static int
803percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
804 struct hist_entry *he)
805{
806 return percent_color(fmt, hpp, he, percent_lcl_hitm);
807}
808
809static int64_t
810percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
811 struct hist_entry *left, struct hist_entry *right)
812{
813 double per_left;
814 double per_right;
815
816 per_left = PERCENT(left, lcl_hitm);
817 per_right = PERCENT(right, lcl_hitm);
818
819 return per_left - per_right;
820}
821
822static int
823percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
824 struct hist_entry *he)
825{
826 int width = c2c_width(fmt, hpp, he->hists);
827 double per = PERCENT(he, st_l1hit);
828 char buf[10];
829
830 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
831}
832
833static int
834percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
835 struct hist_entry *he)
836{
837 return percent_color(fmt, hpp, he, percent_st_l1hit);
838}
839
840static int64_t
841percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
842 struct hist_entry *left, struct hist_entry *right)
843{
844 double per_left;
845 double per_right;
846
847 per_left = PERCENT(left, st_l1hit);
848 per_right = PERCENT(right, st_l1hit);
849
850 return per_left - per_right;
851}
852
853static int
854percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
855 struct hist_entry *he)
856{
857 int width = c2c_width(fmt, hpp, he->hists);
858 double per = PERCENT(he, st_l1miss);
859 char buf[10];
860
861 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
862}
863
864static int
865percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
866 struct hist_entry *he)
867{
868 return percent_color(fmt, hpp, he, percent_st_l1miss);
869}
870
871static int64_t
872percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
873 struct hist_entry *left, struct hist_entry *right)
874{
875 double per_left;
876 double per_right;
877
878 per_left = PERCENT(left, st_l1miss);
879 per_right = PERCENT(right, st_l1miss);
880
881 return per_left - per_right;
882}
883
Jiri Olsa6c70f542016-05-28 12:30:13 +0200884STAT_FN(lcl_dram)
885STAT_FN(rmt_dram)
886
Jiri Olsa36d3deb2016-05-24 13:09:47 +0200887static int
888pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
889 struct hist_entry *he)
890{
891 int width = c2c_width(fmt, hpp, he->hists);
892
893 return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_);
894}
895
896static int64_t
897pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
898 struct hist_entry *left, struct hist_entry *right)
899{
900 return left->thread->pid_ - right->thread->pid_;
901}
902
Jiri Olsa1e181b92016-06-03 15:40:28 +0200903static int64_t
904empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
905 struct hist_entry *left __maybe_unused,
906 struct hist_entry *right __maybe_unused)
907{
908 return 0;
909}
910
911static int
912node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
913 struct hist_entry *he)
914{
915 struct c2c_hist_entry *c2c_he;
916 bool first = true;
917 int node;
918 int ret = 0;
919
920 c2c_he = container_of(he, struct c2c_hist_entry, he);
921
922 for (node = 0; node < c2c.nodes_cnt; node++) {
923 DECLARE_BITMAP(set, c2c.cpus_cnt);
924
925 bitmap_zero(set, c2c.cpus_cnt);
926 bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
927
928 if (!bitmap_weight(set, c2c.cpus_cnt)) {
929 if (c2c.node_info == 1) {
930 ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
931 advance_hpp(hpp, ret);
932 }
933 continue;
934 }
935
936 if (!first) {
937 ret = scnprintf(hpp->buf, hpp->size, " ");
938 advance_hpp(hpp, ret);
939 }
940
941 switch (c2c.node_info) {
942 case 0:
943 ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
944 advance_hpp(hpp, ret);
945 break;
946 case 1:
947 {
948 int num = bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt);
949 struct c2c_stats *stats = &c2c_he->node_stats[node];
950
951 ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
952 advance_hpp(hpp, ret);
953
954
955 if (c2c_he->stats.rmt_hitm > 0) {
956 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
957 percent(stats->rmt_hitm, c2c_he->stats.rmt_hitm));
958 } else {
959 ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
960 }
961
962 advance_hpp(hpp, ret);
963
964 if (c2c_he->stats.store > 0) {
965 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
966 percent(stats->store, c2c_he->stats.store));
967 } else {
968 ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
969 }
970
971 advance_hpp(hpp, ret);
972 break;
973 }
974 case 2:
975 ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
976 advance_hpp(hpp, ret);
977
978 ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
979 advance_hpp(hpp, ret);
980
981 ret = scnprintf(hpp->buf, hpp->size, "}");
982 advance_hpp(hpp, ret);
983 break;
984 default:
985 break;
986 }
987
988 first = false;
989 }
990
991 return 0;
992}
993
Jiri Olsa92062d52016-06-05 13:40:53 +0200994static int
995mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
996 struct hist_entry *he, double mean)
997{
998 int width = c2c_width(fmt, hpp, he->hists);
999 char buf[10];
1000
1001 scnprintf(buf, 10, "%6.0f", mean);
1002 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1003}
1004
1005#define MEAN_ENTRY(__func, __val) \
1006static int \
1007__func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) \
1008{ \
1009 struct c2c_hist_entry *c2c_he; \
1010 c2c_he = container_of(he, struct c2c_hist_entry, he); \
1011 return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val)); \
1012}
1013
1014MEAN_ENTRY(mean_rmt_entry, rmt_hitm);
1015MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
1016MEAN_ENTRY(mean_load_entry, load);
1017
Jiri Olsa600a8cf2016-09-22 17:36:47 +02001018#define HEADER_LOW(__h) \
1019 { \
1020 .line[1] = { \
1021 .text = __h, \
1022 }, \
1023 }
1024
1025#define HEADER_BOTH(__h0, __h1) \
1026 { \
1027 .line[0] = { \
1028 .text = __h0, \
1029 }, \
1030 .line[1] = { \
1031 .text = __h1, \
1032 }, \
1033 }
1034
1035#define HEADER_SPAN(__h0, __h1, __s) \
1036 { \
1037 .line[0] = { \
1038 .text = __h0, \
1039 .span = __s, \
1040 }, \
1041 .line[1] = { \
1042 .text = __h1, \
1043 }, \
1044 }
1045
1046#define HEADER_SPAN_LOW(__h) \
1047 { \
1048 .line[1] = { \
1049 .text = __h, \
1050 }, \
1051 }
1052
Jiri Olsacbb88502016-09-22 17:36:48 +02001053static struct c2c_dimension dim_dcacheline = {
1054 .header = HEADER_LOW("Cacheline"),
1055 .name = "dcacheline",
1056 .cmp = dcacheline_cmp,
1057 .entry = dcacheline_entry,
1058 .width = 18,
1059};
1060
Jiri Olsa48acdeb2016-04-29 14:37:06 +02001061static struct c2c_dimension dim_offset = {
1062 .header = HEADER_BOTH("Data address", "Offset"),
1063 .name = "offset",
1064 .cmp = offset_cmp,
1065 .entry = offset_entry,
1066 .width = 18,
1067};
1068
Jiri Olsa43575a92016-05-03 21:48:56 +02001069static struct c2c_dimension dim_iaddr = {
1070 .header = HEADER_LOW("Code address"),
1071 .name = "iaddr",
1072 .cmp = iaddr_cmp,
1073 .entry = iaddr_entry,
1074 .width = 18,
1075};
1076
Jiri Olsa97cb4862016-05-23 16:20:14 +02001077static struct c2c_dimension dim_tot_hitm = {
1078 .header = HEADER_SPAN("----- LLC Load Hitm -----", "Total", 2),
1079 .name = "tot_hitm",
1080 .cmp = tot_hitm_cmp,
1081 .entry = tot_hitm_entry,
1082 .width = 7,
1083};
1084
1085static struct c2c_dimension dim_lcl_hitm = {
1086 .header = HEADER_SPAN_LOW("Lcl"),
1087 .name = "lcl_hitm",
1088 .cmp = lcl_hitm_cmp,
1089 .entry = lcl_hitm_entry,
1090 .width = 7,
1091};
1092
1093static struct c2c_dimension dim_rmt_hitm = {
1094 .header = HEADER_SPAN_LOW("Rmt"),
1095 .name = "rmt_hitm",
1096 .cmp = rmt_hitm_cmp,
1097 .entry = rmt_hitm_entry,
1098 .width = 7,
1099};
1100
1101static struct c2c_dimension dim_cl_rmt_hitm = {
1102 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1103 .name = "cl_rmt_hitm",
1104 .cmp = rmt_hitm_cmp,
1105 .entry = rmt_hitm_entry,
1106 .width = 7,
1107};
1108
1109static struct c2c_dimension dim_cl_lcl_hitm = {
1110 .header = HEADER_SPAN_LOW("Lcl"),
1111 .name = "cl_lcl_hitm",
1112 .cmp = lcl_hitm_cmp,
1113 .entry = lcl_hitm_entry,
1114 .width = 7,
1115};
1116
Jiri Olsa0f188962016-05-04 10:10:11 +02001117static struct c2c_dimension dim_stores = {
1118 .header = HEADER_SPAN("---- Store Reference ----", "Total", 2),
1119 .name = "stores",
1120 .cmp = store_cmp,
1121 .entry = store_entry,
1122 .width = 7,
1123};
1124
1125static struct c2c_dimension dim_stores_l1hit = {
1126 .header = HEADER_SPAN_LOW("L1Hit"),
1127 .name = "stores_l1hit",
1128 .cmp = st_l1hit_cmp,
1129 .entry = st_l1hit_entry,
1130 .width = 7,
1131};
1132
1133static struct c2c_dimension dim_stores_l1miss = {
1134 .header = HEADER_SPAN_LOW("L1Miss"),
1135 .name = "stores_l1miss",
1136 .cmp = st_l1miss_cmp,
1137 .entry = st_l1miss_entry,
1138 .width = 7,
1139};
1140
1141static struct c2c_dimension dim_cl_stores_l1hit = {
1142 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1143 .name = "cl_stores_l1hit",
1144 .cmp = st_l1hit_cmp,
1145 .entry = st_l1hit_entry,
1146 .width = 7,
1147};
1148
1149static struct c2c_dimension dim_cl_stores_l1miss = {
1150 .header = HEADER_SPAN_LOW("L1 Miss"),
1151 .name = "cl_stores_l1miss",
1152 .cmp = st_l1miss_cmp,
1153 .entry = st_l1miss_entry,
1154 .width = 7,
1155};
1156
Jiri Olsa1295f682016-05-04 10:18:24 +02001157static struct c2c_dimension dim_ld_fbhit = {
1158 .header = HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1159 .name = "ld_fbhit",
1160 .cmp = ld_fbhit_cmp,
1161 .entry = ld_fbhit_entry,
1162 .width = 7,
1163};
1164
1165static struct c2c_dimension dim_ld_l1hit = {
1166 .header = HEADER_SPAN_LOW("L1"),
1167 .name = "ld_l1hit",
1168 .cmp = ld_l1hit_cmp,
1169 .entry = ld_l1hit_entry,
1170 .width = 7,
1171};
1172
1173static struct c2c_dimension dim_ld_l2hit = {
1174 .header = HEADER_SPAN_LOW("L2"),
1175 .name = "ld_l2hit",
1176 .cmp = ld_l2hit_cmp,
1177 .entry = ld_l2hit_entry,
1178 .width = 7,
1179};
1180
Jiri Olsa4d089102016-05-04 10:27:51 +02001181static struct c2c_dimension dim_ld_llchit = {
1182 .header = HEADER_SPAN("-- LLC Load Hit --", "Llc", 1),
1183 .name = "ld_lclhit",
1184 .cmp = ld_llchit_cmp,
1185 .entry = ld_llchit_entry,
1186 .width = 8,
1187};
1188
1189static struct c2c_dimension dim_ld_rmthit = {
1190 .header = HEADER_SPAN_LOW("Rmt"),
1191 .name = "ld_rmthit",
1192 .cmp = rmt_hit_cmp,
1193 .entry = rmt_hit_entry,
1194 .width = 8,
1195};
1196
Jiri Olsa04402d22016-05-19 10:10:51 +02001197static struct c2c_dimension dim_ld_llcmiss = {
1198 .header = HEADER_BOTH("LLC", "Ld Miss"),
1199 .name = "ld_llcmiss",
1200 .cmp = ld_llcmiss_cmp,
1201 .entry = ld_llcmiss_entry,
1202 .width = 7,
1203};
1204
Jiri Olsa01b84d72016-05-04 10:35:29 +02001205static struct c2c_dimension dim_tot_recs = {
1206 .header = HEADER_BOTH("Total", "records"),
1207 .name = "tot_recs",
1208 .cmp = tot_recs_cmp,
1209 .entry = tot_recs_entry,
1210 .width = 7,
1211};
1212
Jiri Olsa55177c42016-05-19 09:52:37 +02001213static struct c2c_dimension dim_tot_loads = {
1214 .header = HEADER_BOTH("Total", "Loads"),
1215 .name = "tot_loads",
1216 .cmp = tot_loads_cmp,
1217 .entry = tot_loads_entry,
1218 .width = 7,
1219};
1220
Jiri Olsaf0c50c12016-05-04 10:50:09 +02001221static struct c2c_dimension dim_percent_hitm = {
1222 .header = HEADER_LOW("%hitm"),
1223 .name = "percent_hitm",
1224 .cmp = percent_hitm_cmp,
1225 .entry = percent_hitm_entry,
1226 .color = percent_hitm_color,
1227 .width = 7,
1228};
1229
Jiri Olsa9cb35002016-05-04 12:16:50 +02001230static struct c2c_dimension dim_percent_rmt_hitm = {
1231 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1232 .name = "percent_rmt_hitm",
1233 .cmp = percent_rmt_hitm_cmp,
1234 .entry = percent_rmt_hitm_entry,
1235 .color = percent_rmt_hitm_color,
1236 .width = 7,
1237};
1238
1239static struct c2c_dimension dim_percent_lcl_hitm = {
1240 .header = HEADER_SPAN_LOW("Lcl"),
1241 .name = "percent_lcl_hitm",
1242 .cmp = percent_lcl_hitm_cmp,
1243 .entry = percent_lcl_hitm_entry,
1244 .color = percent_lcl_hitm_color,
1245 .width = 7,
1246};
1247
1248static struct c2c_dimension dim_percent_stores_l1hit = {
1249 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1250 .name = "percent_stores_l1hit",
1251 .cmp = percent_stores_l1hit_cmp,
1252 .entry = percent_stores_l1hit_entry,
1253 .color = percent_stores_l1hit_color,
1254 .width = 7,
1255};
1256
1257static struct c2c_dimension dim_percent_stores_l1miss = {
1258 .header = HEADER_SPAN_LOW("L1 Miss"),
1259 .name = "percent_stores_l1miss",
1260 .cmp = percent_stores_l1miss_cmp,
1261 .entry = percent_stores_l1miss_entry,
1262 .color = percent_stores_l1miss_color,
1263 .width = 7,
1264};
1265
Jiri Olsa6c70f542016-05-28 12:30:13 +02001266static struct c2c_dimension dim_dram_lcl = {
1267 .header = HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1268 .name = "dram_lcl",
1269 .cmp = lcl_dram_cmp,
1270 .entry = lcl_dram_entry,
1271 .width = 8,
1272};
1273
1274static struct c2c_dimension dim_dram_rmt = {
1275 .header = HEADER_SPAN_LOW("Rmt"),
1276 .name = "dram_rmt",
1277 .cmp = rmt_dram_cmp,
1278 .entry = rmt_dram_entry,
1279 .width = 8,
1280};
1281
Jiri Olsa36d3deb2016-05-24 13:09:47 +02001282static struct c2c_dimension dim_pid = {
1283 .header = HEADER_LOW("Pid"),
1284 .name = "pid",
1285 .cmp = pid_cmp,
1286 .entry = pid_entry,
1287 .width = 7,
1288};
1289
Jiri Olsae87019c2016-05-25 08:50:10 +02001290static struct c2c_dimension dim_tid = {
1291 .header = HEADER_LOW("Tid"),
1292 .name = "tid",
1293 .se = &sort_thread,
1294};
1295
Jiri Olsa51dedaa2016-05-24 23:41:52 +02001296static struct c2c_dimension dim_symbol = {
1297 .name = "symbol",
1298 .se = &sort_sym,
1299};
1300
1301static struct c2c_dimension dim_dso = {
1302 .header = HEADER_BOTH("Shared", "Object"),
1303 .name = "dso",
1304 .se = &sort_dso,
1305};
1306
Jiri Olsa1e181b92016-06-03 15:40:28 +02001307static struct c2c_header header_node[3] = {
1308 HEADER_LOW("Node"),
1309 HEADER_LOW("Node{cpus %hitms %stores}"),
1310 HEADER_LOW("Node{cpu list}"),
1311};
1312
1313static struct c2c_dimension dim_node = {
1314 .name = "node",
1315 .cmp = empty_cmp,
1316 .entry = node_entry,
1317 .width = 4,
1318};
1319
Jiri Olsa92062d52016-06-05 13:40:53 +02001320static struct c2c_dimension dim_mean_rmt = {
1321 .header = HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1322 .name = "mean_rmt",
1323 .cmp = empty_cmp,
1324 .entry = mean_rmt_entry,
1325 .width = 8,
1326};
1327
1328static struct c2c_dimension dim_mean_lcl = {
1329 .header = HEADER_SPAN_LOW("lcl hitm"),
1330 .name = "mean_lcl",
1331 .cmp = empty_cmp,
1332 .entry = mean_lcl_entry,
1333 .width = 8,
1334};
1335
1336static struct c2c_dimension dim_mean_load = {
1337 .header = HEADER_SPAN_LOW("load"),
1338 .name = "mean_load",
1339 .cmp = empty_cmp,
1340 .entry = mean_load_entry,
1341 .width = 8,
1342};
1343
Jiri Olsac75540e2016-09-22 17:36:41 +02001344static struct c2c_dimension *dimensions[] = {
Jiri Olsacbb88502016-09-22 17:36:48 +02001345 &dim_dcacheline,
Jiri Olsa48acdeb2016-04-29 14:37:06 +02001346 &dim_offset,
Jiri Olsa43575a92016-05-03 21:48:56 +02001347 &dim_iaddr,
Jiri Olsa97cb4862016-05-23 16:20:14 +02001348 &dim_tot_hitm,
1349 &dim_lcl_hitm,
1350 &dim_rmt_hitm,
1351 &dim_cl_lcl_hitm,
1352 &dim_cl_rmt_hitm,
Jiri Olsa0f188962016-05-04 10:10:11 +02001353 &dim_stores,
1354 &dim_stores_l1hit,
1355 &dim_stores_l1miss,
1356 &dim_cl_stores_l1hit,
1357 &dim_cl_stores_l1miss,
Jiri Olsa1295f682016-05-04 10:18:24 +02001358 &dim_ld_fbhit,
1359 &dim_ld_l1hit,
1360 &dim_ld_l2hit,
Jiri Olsa4d089102016-05-04 10:27:51 +02001361 &dim_ld_llchit,
1362 &dim_ld_rmthit,
Jiri Olsa04402d22016-05-19 10:10:51 +02001363 &dim_ld_llcmiss,
Jiri Olsa01b84d72016-05-04 10:35:29 +02001364 &dim_tot_recs,
Jiri Olsa55177c42016-05-19 09:52:37 +02001365 &dim_tot_loads,
Jiri Olsaf0c50c12016-05-04 10:50:09 +02001366 &dim_percent_hitm,
Jiri Olsa9cb35002016-05-04 12:16:50 +02001367 &dim_percent_rmt_hitm,
1368 &dim_percent_lcl_hitm,
1369 &dim_percent_stores_l1hit,
1370 &dim_percent_stores_l1miss,
Jiri Olsa6c70f542016-05-28 12:30:13 +02001371 &dim_dram_lcl,
1372 &dim_dram_rmt,
Jiri Olsa36d3deb2016-05-24 13:09:47 +02001373 &dim_pid,
Jiri Olsae87019c2016-05-25 08:50:10 +02001374 &dim_tid,
Jiri Olsa51dedaa2016-05-24 23:41:52 +02001375 &dim_symbol,
1376 &dim_dso,
Jiri Olsa1e181b92016-06-03 15:40:28 +02001377 &dim_node,
Jiri Olsa92062d52016-06-05 13:40:53 +02001378 &dim_mean_rmt,
1379 &dim_mean_lcl,
1380 &dim_mean_load,
Jiri Olsac75540e2016-09-22 17:36:41 +02001381 NULL,
1382};
1383
1384static void fmt_free(struct perf_hpp_fmt *fmt)
1385{
1386 struct c2c_fmt *c2c_fmt;
1387
1388 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1389 free(c2c_fmt);
1390}
1391
1392static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1393{
1394 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
1395 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
1396
1397 return c2c_a->dim == c2c_b->dim;
1398}
1399
1400static struct c2c_dimension *get_dimension(const char *name)
1401{
1402 unsigned int i;
1403
1404 for (i = 0; dimensions[i]; i++) {
1405 struct c2c_dimension *dim = dimensions[i];
1406
1407 if (!strcmp(dim->name, name))
1408 return dim;
1409 };
1410
1411 return NULL;
1412}
1413
Jiri Olsa8d3f9382016-09-22 17:36:42 +02001414static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1415 struct hist_entry *he)
1416{
1417 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1418 struct c2c_dimension *dim = c2c_fmt->dim;
1419 size_t len = fmt->user_len;
1420
1421 if (!len)
1422 len = hists__col_len(he->hists, dim->se->se_width_idx);
1423
1424 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1425}
1426
1427static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1428 struct hist_entry *a, struct hist_entry *b)
1429{
1430 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1431 struct c2c_dimension *dim = c2c_fmt->dim;
1432
1433 return dim->se->se_cmp(a, b);
1434}
1435
1436static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1437 struct hist_entry *a, struct hist_entry *b)
1438{
1439 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1440 struct c2c_dimension *dim = c2c_fmt->dim;
1441 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1442
1443 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1444 return collapse_fn(a, b);
1445}
1446
Jiri Olsac75540e2016-09-22 17:36:41 +02001447static struct c2c_fmt *get_format(const char *name)
1448{
1449 struct c2c_dimension *dim = get_dimension(name);
1450 struct c2c_fmt *c2c_fmt;
1451 struct perf_hpp_fmt *fmt;
1452
1453 if (!dim)
1454 return NULL;
1455
1456 c2c_fmt = zalloc(sizeof(*c2c_fmt));
1457 if (!c2c_fmt)
1458 return NULL;
1459
1460 c2c_fmt->dim = dim;
1461
1462 fmt = &c2c_fmt->fmt;
1463 INIT_LIST_HEAD(&fmt->list);
1464 INIT_LIST_HEAD(&fmt->sort_list);
1465
Jiri Olsa8d3f9382016-09-22 17:36:42 +02001466 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
1467 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
Jiri Olsa9cb35002016-05-04 12:16:50 +02001468 fmt->color = dim->se ? NULL : dim->color;
Jiri Olsa8d3f9382016-09-22 17:36:42 +02001469 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
Jiri Olsac75540e2016-09-22 17:36:41 +02001470 fmt->header = c2c_header;
1471 fmt->width = c2c_width;
Jiri Olsa8d3f9382016-09-22 17:36:42 +02001472 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
Jiri Olsac75540e2016-09-22 17:36:41 +02001473 fmt->equal = fmt_equal;
1474 fmt->free = fmt_free;
1475
1476 return c2c_fmt;
1477}
1478
1479static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1480{
1481 struct c2c_fmt *c2c_fmt = get_format(name);
1482
Jiri Olsa5f2eca82016-09-22 17:36:43 +02001483 if (!c2c_fmt) {
1484 reset_dimensions();
1485 return output_field_add(hpp_list, name);
1486 }
Jiri Olsac75540e2016-09-22 17:36:41 +02001487
1488 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
1489 return 0;
1490}
1491
1492static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
1493{
1494 struct c2c_fmt *c2c_fmt = get_format(name);
Jiri Olsa51dedaa2016-05-24 23:41:52 +02001495 struct c2c_dimension *dim;
Jiri Olsac75540e2016-09-22 17:36:41 +02001496
Jiri Olsa5f2eca82016-09-22 17:36:43 +02001497 if (!c2c_fmt) {
1498 reset_dimensions();
1499 return sort_dimension__add(hpp_list, name, NULL, 0);
1500 }
Jiri Olsac75540e2016-09-22 17:36:41 +02001501
Jiri Olsa51dedaa2016-05-24 23:41:52 +02001502 dim = c2c_fmt->dim;
1503 if (dim == &dim_dso)
1504 hpp_list->dso = 1;
1505
Jiri Olsac75540e2016-09-22 17:36:41 +02001506 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
1507 return 0;
1508}
1509
1510#define PARSE_LIST(_list, _fn) \
1511 do { \
1512 char *tmp, *tok; \
1513 ret = 0; \
1514 \
1515 if (!_list) \
1516 break; \
1517 \
1518 for (tok = strtok_r((char *)_list, ", ", &tmp); \
1519 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
1520 ret = _fn(hpp_list, tok); \
1521 if (ret == -EINVAL) { \
1522 error("Invalid --fields key: `%s'", tok); \
1523 break; \
1524 } else if (ret == -ESRCH) { \
1525 error("Unknown --fields key: `%s'", tok); \
1526 break; \
1527 } \
1528 } \
1529 } while (0)
1530
1531static int hpp_list__parse(struct perf_hpp_list *hpp_list,
1532 const char *output_,
1533 const char *sort_)
1534{
1535 char *output = output_ ? strdup(output_) : NULL;
1536 char *sort = sort_ ? strdup(sort_) : NULL;
1537 int ret;
1538
1539 PARSE_LIST(output, c2c_hists__init_output);
1540 PARSE_LIST(sort, c2c_hists__init_sort);
1541
1542 /* copy sort keys to output fields */
1543 perf_hpp__setup_output_field(hpp_list);
1544
1545 /*
1546 * We dont need other sorting keys other than those
1547 * we already specified. It also really slows down
1548 * the processing a lot with big number of output
1549 * fields, so switching this off for c2c.
1550 */
1551
1552#if 0
1553 /* and then copy output fields to sort keys */
1554 perf_hpp__append_sort_keys(&hists->list);
1555#endif
1556
1557 free(output);
1558 free(sort);
1559 return ret;
1560}
1561
1562static int c2c_hists__init(struct c2c_hists *hists,
1563 const char *sort)
1564{
1565 __hists__init(&hists->hists, &hists->list);
1566
1567 /*
1568 * Initialize only with sort fields, we need to resort
1569 * later anyway, and that's where we add output fields
1570 * as well.
1571 */
1572 perf_hpp_list__init(&hists->list);
1573
1574 return hpp_list__parse(&hists->list, NULL, sort);
1575}
1576
1577__maybe_unused
1578static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
1579 const char *output,
1580 const char *sort)
1581{
1582 perf_hpp__reset_output_field(&c2c_hists->list);
1583 return hpp_list__parse(&c2c_hists->list, output, sort);
1584}
1585
Jiri Olsaec06f9b2016-09-22 17:36:45 +02001586static int filter_cb(struct hist_entry *he __maybe_unused)
1587{
1588 return 0;
1589}
1590
1591static int resort_cl_cb(struct hist_entry *he)
1592{
1593 struct c2c_hist_entry *c2c_he;
1594 struct c2c_hists *c2c_hists;
1595
1596 c2c_he = container_of(he, struct c2c_hist_entry, he);
1597 c2c_hists = c2c_he->hists;
1598
1599 if (c2c_hists) {
1600 hists__collapse_resort(&c2c_hists->hists, NULL);
1601 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
1602 }
1603
1604 return 0;
1605}
1606
Jiri Olsa1e181b92016-06-03 15:40:28 +02001607static void setup_nodes_header(void)
1608{
1609 dim_node.header = header_node[c2c.node_info];
1610}
1611
1612static int setup_nodes(struct perf_session *session)
1613{
1614 struct numa_node *n;
1615 unsigned long **nodes;
1616 int node, cpu;
1617 int *cpu2node;
1618
1619 if (c2c.node_info > 2)
1620 c2c.node_info = 2;
1621
1622 c2c.nodes_cnt = session->header.env.nr_numa_nodes;
1623 c2c.cpus_cnt = session->header.env.nr_cpus_online;
1624
1625 n = session->header.env.numa_nodes;
1626 if (!n)
1627 return -EINVAL;
1628
1629 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
1630 if (!nodes)
1631 return -ENOMEM;
1632
1633 c2c.nodes = nodes;
1634
1635 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
1636 if (!cpu2node)
1637 return -ENOMEM;
1638
1639 for (cpu = 0; cpu < c2c.cpus_cnt; cpu++)
1640 cpu2node[cpu] = -1;
1641
1642 c2c.cpu2node = cpu2node;
1643
1644 for (node = 0; node < c2c.nodes_cnt; node++) {
1645 struct cpu_map *map = n[node].map;
1646 unsigned long *set;
1647
1648 set = bitmap_alloc(c2c.cpus_cnt);
1649 if (!set)
1650 return -ENOMEM;
1651
1652 for (cpu = 0; cpu < map->nr; cpu++) {
1653 set_bit(map->map[cpu], set);
1654
1655 if (WARN_ONCE(cpu2node[map->map[cpu]] != -1, "node/cpu topology bug"))
1656 return -EINVAL;
1657
1658 cpu2node[map->map[cpu]] = node;
1659 }
1660
1661 nodes[node] = set;
1662 }
1663
1664 setup_nodes_header();
1665 return 0;
1666}
1667
1668
Jiri Olsa903a6f12016-09-22 17:36:40 +02001669static int perf_c2c__report(int argc, const char **argv)
1670{
1671 struct perf_session *session;
Jiri Olsa78b27542016-09-22 17:36:44 +02001672 struct ui_progress prog;
Jiri Olsa903a6f12016-09-22 17:36:40 +02001673 struct perf_data_file file = {
1674 .mode = PERF_DATA_MODE_READ,
1675 };
1676 const struct option c2c_options[] = {
1677 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1678 "file", "vmlinux pathname"),
1679 OPT_INCR('v', "verbose", &verbose,
1680 "be more verbose (show counter open errors, etc)"),
1681 OPT_STRING('i', "input", &input_name, "file",
1682 "the input file to process"),
Jiri Olsa1e181b92016-06-03 15:40:28 +02001683 OPT_INCR('N', "node-info", &c2c.node_info,
1684 "show extra node info in report (repeat for more info)"),
Jiri Olsa903a6f12016-09-22 17:36:40 +02001685 OPT_END()
1686 };
1687 int err = 0;
1688
1689 argc = parse_options(argc, argv, c2c_options, report_c2c_usage,
1690 PARSE_OPT_STOP_AT_NON_OPTION);
Jiri Olsa78b27542016-09-22 17:36:44 +02001691 if (argc)
Jiri Olsa903a6f12016-09-22 17:36:40 +02001692 usage_with_options(report_c2c_usage, c2c_options);
1693
Jiri Olsa78b27542016-09-22 17:36:44 +02001694 if (!input_name || !strlen(input_name))
1695 input_name = "perf.data";
1696
Jiri Olsa903a6f12016-09-22 17:36:40 +02001697 file.path = input_name;
1698
Jiri Olsac75540e2016-09-22 17:36:41 +02001699 err = c2c_hists__init(&c2c.hists, "dcacheline");
1700 if (err) {
1701 pr_debug("Failed to initialize hists\n");
1702 goto out;
1703 }
1704
Jiri Olsa903a6f12016-09-22 17:36:40 +02001705 session = perf_session__new(&file, 0, &c2c.tool);
1706 if (session == NULL) {
1707 pr_debug("No memory for session\n");
1708 goto out;
1709 }
Jiri Olsa1e181b92016-06-03 15:40:28 +02001710 err = setup_nodes(session);
1711 if (err) {
1712 pr_err("Failed setup nodes\n");
1713 goto out;
1714 }
Jiri Olsa903a6f12016-09-22 17:36:40 +02001715
1716 if (symbol__init(&session->header.env) < 0)
1717 goto out_session;
1718
1719 /* No pipe support at the moment. */
1720 if (perf_data_file__is_pipe(session->file)) {
1721 pr_debug("No pipe support at the moment.\n");
1722 goto out_session;
1723 }
1724
Jiri Olsa78b27542016-09-22 17:36:44 +02001725 err = perf_session__process_events(session);
1726 if (err) {
1727 pr_err("failed to process sample\n");
1728 goto out_session;
1729 }
1730
1731 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
1732
1733 hists__collapse_resort(&c2c.hists.hists, NULL);
Jiri Olsaec06f9b2016-09-22 17:36:45 +02001734 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_cl_cb);
Jiri Olsa78b27542016-09-22 17:36:44 +02001735
1736 ui_progress__finish();
1737
Jiri Olsa903a6f12016-09-22 17:36:40 +02001738out_session:
1739 perf_session__delete(session);
1740out:
1741 return err;
1742}
1743
Jiri Olsa39bcd4a2016-09-22 17:36:39 +02001744static int parse_record_events(const struct option *opt __maybe_unused,
1745 const char *str, int unset __maybe_unused)
1746{
1747 bool *event_set = (bool *) opt->value;
1748
1749 *event_set = true;
1750 return perf_mem_events__parse(str);
1751}
1752
1753
1754static const char * const __usage_record[] = {
1755 "perf c2c record [<options>] [<command>]",
1756 "perf c2c record [<options>] -- <command> [<options>]",
1757 NULL
1758};
1759
1760static const char * const *record_mem_usage = __usage_record;
1761
1762static int perf_c2c__record(int argc, const char **argv)
1763{
1764 int rec_argc, i = 0, j;
1765 const char **rec_argv;
1766 int ret;
1767 bool all_user = false, all_kernel = false;
1768 bool event_set = false;
1769 struct option options[] = {
1770 OPT_CALLBACK('e', "event", &event_set, "event",
1771 "event selector. Use 'perf mem record -e list' to list available events",
1772 parse_record_events),
1773 OPT_INCR('v', "verbose", &verbose,
1774 "be more verbose (show counter open errors, etc)"),
1775 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
1776 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
1777 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
1778 OPT_END()
1779 };
1780
1781 if (perf_mem_events__init()) {
1782 pr_err("failed: memory events not supported\n");
1783 return -1;
1784 }
1785
1786 argc = parse_options(argc, argv, options, record_mem_usage,
1787 PARSE_OPT_KEEP_UNKNOWN);
1788
1789 rec_argc = argc + 10; /* max number of arguments */
1790 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1791 if (!rec_argv)
1792 return -1;
1793
1794 rec_argv[i++] = "record";
1795
1796 if (!event_set) {
1797 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
1798 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
1799 }
1800
1801 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
1802 rec_argv[i++] = "-W";
1803
1804 rec_argv[i++] = "-d";
1805 rec_argv[i++] = "--sample-cpu";
1806
1807 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
1808 if (!perf_mem_events[j].record)
1809 continue;
1810
1811 if (!perf_mem_events[j].supported) {
1812 pr_err("failed: event '%s' not supported\n",
1813 perf_mem_events[j].name);
1814 return -1;
1815 }
1816
1817 rec_argv[i++] = "-e";
1818 rec_argv[i++] = perf_mem_events__name(j);
1819 };
1820
1821 if (all_user)
1822 rec_argv[i++] = "--all-user";
1823
1824 if (all_kernel)
1825 rec_argv[i++] = "--all-kernel";
1826
1827 for (j = 0; j < argc; j++, i++)
1828 rec_argv[i] = argv[j];
1829
1830 if (verbose > 0) {
1831 pr_debug("calling: ");
1832
1833 j = 0;
1834
1835 while (rec_argv[j]) {
1836 pr_debug("%s ", rec_argv[j]);
1837 j++;
1838 }
1839 pr_debug("\n");
1840 }
1841
1842 ret = cmd_record(i, rec_argv, NULL);
1843 free(rec_argv);
1844 return ret;
1845}
1846
Jiri Olsa7aef3bf2016-09-22 17:36:38 +02001847int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
1848{
1849 const struct option c2c_options[] = {
1850 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
1851 OPT_END()
1852 };
1853
1854 argc = parse_options(argc, argv, c2c_options, c2c_usage,
1855 PARSE_OPT_STOP_AT_NON_OPTION);
Jiri Olsa39bcd4a2016-09-22 17:36:39 +02001856
1857 if (!argc)
1858 usage_with_options(c2c_usage, c2c_options);
1859
1860 if (!strncmp(argv[0], "rec", 3)) {
1861 return perf_c2c__record(argc, argv);
Jiri Olsa903a6f12016-09-22 17:36:40 +02001862 } else if (!strncmp(argv[0], "rep", 3)) {
1863 return perf_c2c__report(argc, argv);
Jiri Olsa39bcd4a2016-09-22 17:36:39 +02001864 } else {
1865 usage_with_options(c2c_usage, c2c_options);
1866 }
1867
Jiri Olsa7aef3bf2016-09-22 17:36:38 +02001868 return 0;
1869}