1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright IBM Corp. 2019
4 * Author(s): Thomas Richter <tmricht@linux.ibm.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License (version 2 only)
8 * as published by the Free Software Foundation.
10 * Architecture specific trace_event function. Save event's bc000 raw data
11 * to file. File name is aux.ctr.## where ## stands for the CPU number the
12 * sample was taken from.
21 #include <linux/compiler.h>
22 #include <asm/byteorder.h>
28 #include "sample-raw.h"
29 #include "s390-cpumcf-kernel.h"
31 #include "util/sample.h"
33 static size_t ctrset_size(struct cf_ctrset_entry
*set
)
35 return sizeof(*set
) + set
->ctr
* sizeof(u64
);
38 static bool ctrset_valid(struct cf_ctrset_entry
*set
)
40 return set
->def
== S390_CPUMCF_DIAG_DEF
;
43 /* CPU Measurement Counter Facility raw data is a byte stream. It is 8 byte
44 * aligned and might have trailing padding bytes.
45 * Display the raw data on screen.
47 static bool s390_cpumcfdg_testctr(struct perf_sample
*sample
)
49 size_t len
= sample
->raw_size
, offset
= 0;
50 unsigned char *buf
= sample
->raw_data
;
51 struct cf_trailer_entry
*te
;
52 struct cf_ctrset_entry
*cep
, ce
;
54 while (offset
< len
) {
55 cep
= (struct cf_ctrset_entry
*)(buf
+ offset
);
56 ce
.def
= be16_to_cpu(cep
->def
);
57 ce
.set
= be16_to_cpu(cep
->set
);
58 ce
.ctr
= be16_to_cpu(cep
->ctr
);
59 ce
.res1
= be16_to_cpu(cep
->res1
);
61 if (!ctrset_valid(&ce
) || offset
+ ctrset_size(&ce
) > len
) {
62 /* Raw data for counter sets are always multiple of 8
63 * bytes. Prepending a 4 bytes size field to the
64 * raw data block in the sample causes the perf tool
65 * to append 4 padding bytes to make the raw data part
66 * of the sample a multiple of eight bytes again.
68 * If the last entry (trailer) is 4 bytes off the raw
69 * area data end, all is good.
71 if (len
- offset
- sizeof(*te
) == 4)
73 pr_err("Invalid counter set entry at %zd\n", offset
);
76 offset
+= ctrset_size(&ce
);
81 /* Dump event bc000 on screen, already tested on correctness. */
82 static void s390_cpumcfdg_dumptrail(const char *color
, size_t offset
,
83 struct cf_trailer_entry
*tep
)
85 struct cf_trailer_entry te
;
87 te
.flags
= be64_to_cpu(tep
->flags
);
88 te
.cfvn
= be16_to_cpu(tep
->cfvn
);
89 te
.csvn
= be16_to_cpu(tep
->csvn
);
90 te
.cpu_speed
= be32_to_cpu(tep
->cpu_speed
);
91 te
.timestamp
= be64_to_cpu(tep
->timestamp
);
92 te
.progusage1
= be64_to_cpu(tep
->progusage1
);
93 te
.progusage2
= be64_to_cpu(tep
->progusage2
);
94 te
.progusage3
= be64_to_cpu(tep
->progusage3
);
95 te
.tod_base
= be64_to_cpu(tep
->tod_base
);
96 te
.mach_type
= be16_to_cpu(tep
->mach_type
);
97 te
.res1
= be16_to_cpu(tep
->res1
);
98 te
.res2
= be32_to_cpu(tep
->res2
);
100 color_fprintf(stdout
, color
, " [%#08zx] Trailer:%c%c%c%c%c"
101 " Cfvn:%d Csvn:%d Speed:%d TOD:%#lx\n",
102 offset
, te
.clock_base
? 'T' : ' ',
103 te
.speed
? 'S' : ' ', te
.mtda
? 'M' : ' ',
104 te
.caca
? 'C' : ' ', te
.lcda
? 'L' : ' ',
105 te
.cfvn
, te
.csvn
, te
.cpu_speed
, te
.timestamp
);
106 color_fprintf(stdout
, color
, "\t\t1:%lx 2:%lx 3:%lx TOD-Base:%#lx"
108 te
.progusage1
, te
.progusage2
, te
.progusage3
,
109 te
.tod_base
, te
.mach_type
);
112 /* Return starting number of a counter set */
113 static int get_counterset_start(int setnr
)
116 case CPUMF_CTR_SET_BASIC
: /* Basic counter set */
118 case CPUMF_CTR_SET_USER
: /* Problem state counter set */
120 case CPUMF_CTR_SET_CRYPTO
: /* Crypto counter set */
122 case CPUMF_CTR_SET_EXT
: /* Extended counter set */
124 case CPUMF_CTR_SET_MT_DIAG
: /* Diagnostic counter set */
126 case PERF_EVENT_PAI_NNPA_ALL
: /* PAI NNPA counter set */
127 case PERF_EVENT_PAI_CRYPTO_ALL
: /* PAI CRYPTO counter set */
134 struct get_counter_name_data
{
139 static int get_counter_name_callback(void *vdata
, struct pmu_event_info
*info
)
141 struct get_counter_name_data
*data
= vdata
;
143 const char *event_str
;
145 if (info
->str
== NULL
)
148 event_str
= strstr(info
->str
, "event=");
152 rc
= sscanf(event_str
, "event=%x", &event_nr
);
153 if (rc
== 1 && event_nr
== data
->wanted
) {
154 data
->result
= strdup(info
->name
);
155 return 1; /* Terminate the search. */
160 /* Scan the PMU and extract the logical name of a counter from the event. Input
161 * is the counter set and counter number with in the set. Construct the event
162 * number and use this as key. If they match return the name of this counter.
163 * If no match is found a NULL pointer is returned.
165 static char *get_counter_name(int set
, int nr
, struct perf_pmu
*pmu
)
167 struct get_counter_name_data data
= {
168 .wanted
= get_counterset_start(set
) + nr
,
175 perf_pmu__for_each_event(pmu
, /*skip_duplicate_pmus=*/ true,
176 &data
, get_counter_name_callback
);
180 static void s390_cpumcfdg_dump(struct perf_pmu
*pmu
, struct perf_sample
*sample
)
182 size_t i
, len
= sample
->raw_size
, offset
= 0;
183 unsigned char *buf
= sample
->raw_data
;
184 const char *color
= PERF_COLOR_BLUE
;
185 struct cf_ctrset_entry
*cep
, ce
;
188 while (offset
< len
) {
189 cep
= (struct cf_ctrset_entry
*)(buf
+ offset
);
191 ce
.def
= be16_to_cpu(cep
->def
);
192 ce
.set
= be16_to_cpu(cep
->set
);
193 ce
.ctr
= be16_to_cpu(cep
->ctr
);
194 ce
.res1
= be16_to_cpu(cep
->res1
);
196 if (!ctrset_valid(&ce
)) { /* Print trailer */
197 s390_cpumcfdg_dumptrail(color
, offset
,
198 (struct cf_trailer_entry
*)cep
);
202 color_fprintf(stdout
, color
, " [%#08zx] Counterset:%d"
203 " Counters:%d\n", offset
, ce
.set
, ce
.ctr
);
204 for (i
= 0, p
= (u64
*)(cep
+ 1); i
< ce
.ctr
; ++i
, ++p
) {
205 char *ev_name
= get_counter_name(ce
.set
, i
, pmu
);
207 color_fprintf(stdout
, color
,
208 "\tCounter:%03zd %s Value:%#018"PRIx64
"\n", i
,
209 ev_name
?: "<unknown>", be64_to_cpu(*p
));
212 offset
+= ctrset_size(&ce
);
216 #pragma GCC diagnostic push
217 #pragma GCC diagnostic ignored "-Wpacked"
218 #pragma GCC diagnostic ignored "-Wattributes"
220 * Check for consistency of PAI_CRYPTO/PAI_NNPA raw data.
222 struct pai_data
{ /* Event number and value */
227 #pragma GCC diagnostic pop
230 * Test for valid raw data. At least one PAI event should be in the raw
233 static bool s390_pai_all_test(struct perf_sample
*sample
)
235 size_t len
= sample
->raw_size
;
242 static void s390_pai_all_dump(struct evsel
*evsel
, struct perf_sample
*sample
)
244 size_t len
= sample
->raw_size
, offset
= 0;
245 unsigned char *p
= sample
->raw_data
;
246 const char *color
= PERF_COLOR_BLUE
;
247 struct pai_data pai_data
;
250 while (offset
< len
) {
251 memcpy(&pai_data
.event_nr
, p
, sizeof(pai_data
.event_nr
));
252 pai_data
.event_nr
= be16_to_cpu(pai_data
.event_nr
);
253 p
+= sizeof(pai_data
.event_nr
);
254 offset
+= sizeof(pai_data
.event_nr
);
256 memcpy(&pai_data
.event_val
, p
, sizeof(pai_data
.event_val
));
257 pai_data
.event_val
= be64_to_cpu(pai_data
.event_val
);
258 p
+= sizeof(pai_data
.event_val
);
259 offset
+= sizeof(pai_data
.event_val
);
261 ev_name
= get_counter_name(evsel
->core
.attr
.config
,
262 pai_data
.event_nr
, evsel
->pmu
);
263 color_fprintf(stdout
, color
, "\tCounter:%03d %s Value:%#018"PRIx64
"\n",
264 pai_data
.event_nr
, ev_name
?: "<unknown>",
268 if (offset
+ 0xa > len
)
271 color_fprintf(stdout
, color
, "\n");
274 /* S390 specific trace event function. Check for PERF_RECORD_SAMPLE events
275 * and if the event was triggered by a
276 * - counter set diagnostic event
277 * - processor activity assist (PAI) crypto counter event
278 * - processor activity assist (PAI) neural network processor assist (NNPA)
280 * display its raw data.
281 * The function is only invoked when the dump flag -D is set.
283 * Function evlist__s390_sample_raw() is defined as call back after it has
284 * been verified that the perf.data file was created on s390 platform.
286 void evlist__s390_sample_raw(struct evlist
*evlist
, union perf_event
*event
,
287 struct perf_sample
*sample
)
289 const char *pai_name
;
292 if (event
->header
.type
!= PERF_RECORD_SAMPLE
)
295 evsel
= evlist__event2evsel(evlist
, event
);
299 /* Check for raw data in sample */
300 if (!sample
->raw_size
|| !sample
->raw_data
)
303 /* Display raw data on screen */
304 if (evsel
->core
.attr
.config
== PERF_EVENT_CPUM_CF_DIAG
) {
306 evsel
->pmu
= perf_pmus__find("cpum_cf");
307 if (!s390_cpumcfdg_testctr(sample
))
308 pr_err("Invalid counter set data encountered\n");
310 s390_cpumcfdg_dump(evsel
->pmu
, sample
);
314 switch (evsel
->core
.attr
.config
) {
315 case PERF_EVENT_PAI_NNPA_ALL
:
316 pai_name
= "NNPA_ALL";
318 case PERF_EVENT_PAI_CRYPTO_ALL
:
319 pai_name
= "CRYPTO_ALL";
325 if (!s390_pai_all_test(sample
)) {
326 pr_err("Invalid %s raw data encountered\n", pai_name
);
329 evsel
->pmu
= perf_pmus__find_by_type(evsel
->core
.attr
.type
);
330 s390_pai_all_dump(evsel
, sample
);