xref: /linux/tools/perf/util/s390-sample-raw.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright IBM Corp. 2019
4  * Author(s): Thomas Richter <tmricht@linux.ibm.com>
5  *
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.
9  *
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.
13  */
14 
15 #include <inttypes.h>
16 #include <stdio.h>
17 #include <string.h>
18 
19 #include <asm/byteorder.h>
20 #include <linux/compiler.h>
21 #include <linux/err.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include "color.h"
26 #include "debug.h"
27 #include "evlist.h"
28 #include "hashmap.h"
29 #include "pmu.h"
30 #include "pmus.h"
31 #include "s390-cpumcf-kernel.h"
32 #include "sample-raw.h"
33 #include "sample.h"
34 #include "session.h"
35 
36 static size_t ctrset_size(struct cf_ctrset_entry *set)
37 {
38 	return sizeof(*set) + set->ctr * sizeof(u64);
39 }
40 
41 static bool ctrset_valid(struct cf_ctrset_entry *set)
42 {
43 	return set->def == S390_CPUMCF_DIAG_DEF;
44 }
45 
46 /* CPU Measurement Counter Facility raw data is a byte stream. It is 8 byte
47  * aligned and might have trailing padding bytes.
48  * Display the raw data on screen.
49  */
50 static bool s390_cpumcfdg_testctr(struct perf_sample *sample)
51 {
52 	size_t len = sample->raw_size, offset = 0;
53 	unsigned char *buf = sample->raw_data;
54 	struct cf_trailer_entry *te;
55 	struct cf_ctrset_entry *cep, ce;
56 
57 	while (offset < len) {
58 		cep = (struct cf_ctrset_entry *)(buf + offset);
59 		ce.def = be16_to_cpu(cep->def);
60 		ce.set = be16_to_cpu(cep->set);
61 		ce.ctr = be16_to_cpu(cep->ctr);
62 		ce.res1 = be16_to_cpu(cep->res1);
63 
64 		if (!ctrset_valid(&ce) || offset + ctrset_size(&ce) > len) {
65 			/* Raw data for counter sets are always multiple of 8
66 			 * bytes. Prepending a 4 bytes size field to the
67 			 * raw data block in the sample causes the perf tool
68 			 * to append 4 padding bytes to make the raw data part
69 			 * of the sample a multiple of eight bytes again.
70 			 *
71 			 * If the last entry (trailer) is 4 bytes off the raw
72 			 * area data end, all is good.
73 			 */
74 			if (len - offset - sizeof(*te) == 4)
75 				break;
76 			pr_err("Invalid counter set entry at %zd\n", offset);
77 			return false;
78 		}
79 		offset += ctrset_size(&ce);
80 	}
81 	return true;
82 }
83 
84 /* Dump event bc000 on screen, already tested on correctness. */
85 static void s390_cpumcfdg_dumptrail(const char *color, size_t offset,
86 				    struct cf_trailer_entry *tep)
87 {
88 	struct cf_trailer_entry  te;
89 
90 	te.flags = be64_to_cpu(tep->flags);
91 	te.cfvn = be16_to_cpu(tep->cfvn);
92 	te.csvn = be16_to_cpu(tep->csvn);
93 	te.cpu_speed = be32_to_cpu(tep->cpu_speed);
94 	te.timestamp = be64_to_cpu(tep->timestamp);
95 	te.progusage1 = be64_to_cpu(tep->progusage1);
96 	te.progusage2 = be64_to_cpu(tep->progusage2);
97 	te.progusage3 = be64_to_cpu(tep->progusage3);
98 	te.tod_base = be64_to_cpu(tep->tod_base);
99 	te.mach_type = be16_to_cpu(tep->mach_type);
100 	te.res1 = be16_to_cpu(tep->res1);
101 	te.res2 = be32_to_cpu(tep->res2);
102 
103 	color_fprintf(stdout, color, "    [%#08zx] Trailer:%c%c%c%c%c"
104 		      " Cfvn:%d Csvn:%d Speed:%d TOD:%#lx\n",
105 		      offset, te.clock_base ? 'T' : ' ',
106 		      te.speed ? 'S' : ' ', te.mtda ? 'M' : ' ',
107 		      te.caca ? 'C' : ' ', te.lcda ? 'L' : ' ',
108 		      te.cfvn, te.csvn, te.cpu_speed, te.timestamp);
109 	color_fprintf(stdout, color, "\t\t1:%lx 2:%lx 3:%lx TOD-Base:%#lx"
110 		      " Type:%x\n\n",
111 		      te.progusage1, te.progusage2, te.progusage3,
112 		      te.tod_base, te.mach_type);
113 }
114 
115 /* Return starting number of a counter set */
116 static int get_counterset_start(int setnr)
117 {
118 	switch (setnr) {
119 	case CPUMF_CTR_SET_BASIC:		/* Basic counter set */
120 		return 0;
121 	case CPUMF_CTR_SET_USER:		/* Problem state counter set */
122 		return 32;
123 	case CPUMF_CTR_SET_CRYPTO:		/* Crypto counter set */
124 		return 64;
125 	case CPUMF_CTR_SET_EXT:			/* Extended counter set */
126 		return 128;
127 	case CPUMF_CTR_SET_MT_DIAG:		/* Diagnostic counter set */
128 		return 448;
129 	case PERF_EVENT_PAI_NNPA_ALL:		/* PAI NNPA counter set */
130 	case PERF_EVENT_PAI_CRYPTO_ALL:		/* PAI CRYPTO counter set */
131 		return setnr;
132 	default:
133 		return -1;
134 	}
135 }
136 
137 struct get_counter_name_data {
138 	long wanted;
139 	const char *result;
140 };
141 
142 static int get_counter_name_callback(void *vdata, struct pmu_event_info *info)
143 {
144 	struct get_counter_name_data *data = vdata;
145 	int rc, event_nr;
146 	const char *event_str;
147 
148 	if (info->str == NULL)
149 		return 0;
150 
151 	event_str = strstr(info->str, "event=");
152 	if (!event_str)
153 		return 0;
154 
155 	rc = sscanf(event_str, "event=%x", &event_nr);
156 	if (rc == 1 && event_nr == data->wanted) {
157 		data->result = info->name;
158 		return 1; /* Terminate the search. */
159 	}
160 	return 0;
161 }
162 
163 static size_t get_counter_name_hash_fn(long key, void *ctx __maybe_unused)
164 {
165 	return key;
166 }
167 
168 static bool get_counter_name_hashmap_equal_fn(long key1, long key2, void *ctx __maybe_unused)
169 {
170 	return key1 == key2;
171 }
172 
173 /* Scan the PMU and extract the logical name of a counter from the event. Input
174  * is the counter set and counter number with in the set. Construct the event
175  * number and use this as key. If they match return the name of this counter.
176  * If no match is found a NULL pointer is returned.
177  */
178 static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
179 {
180 	static struct hashmap *cache;
181 	static struct perf_pmu *cache_pmu;
182 	long cache_key = get_counterset_start(set) + nr;
183 	struct get_counter_name_data data = {
184 		.wanted = cache_key,
185 		.result = NULL,
186 	};
187 	char *result = NULL;
188 
189 	if (!pmu)
190 		return NULL;
191 
192 	if (cache_pmu == pmu && hashmap__find(cache, cache_key, &result))
193 		return strdup(result);
194 
195 	perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ true,
196 				 &data, get_counter_name_callback);
197 
198 	result = strdup(data.result ?: "<unknown>");
199 
200 	if (cache_pmu == NULL) {
201 		struct hashmap *tmp = hashmap__new(get_counter_name_hash_fn,
202 						   get_counter_name_hashmap_equal_fn,
203 						   /*ctx=*/NULL);
204 
205 		if (!IS_ERR(tmp)) {
206 			cache = tmp;
207 			cache_pmu = pmu;
208 		}
209 	}
210 
211 	if (cache_pmu == pmu && result) {
212 		char *old_value = NULL, *new_value = strdup(result);
213 
214 		if (new_value) {
215 			hashmap__set(cache, cache_key, new_value, /*old_key=*/NULL, &old_value);
216 			/*
217 			 * Free in case of a race, but resizing would be broken
218 			 * in that case.
219 			 */
220 			free(old_value);
221 		}
222 	}
223 	return result;
224 }
225 
226 static void s390_cpumcfdg_dump(struct perf_sample *sample)
227 {
228 	struct perf_pmu *pmu = sample->evsel->pmu;
229 	size_t i, len = sample->raw_size, offset = 0;
230 	unsigned char *buf = sample->raw_data;
231 	const char *color = PERF_COLOR_BLUE;
232 	struct cf_ctrset_entry *cep, ce;
233 	u64 *p;
234 
235 	while (offset < len) {
236 		cep = (struct cf_ctrset_entry *)(buf + offset);
237 
238 		ce.def = be16_to_cpu(cep->def);
239 		ce.set = be16_to_cpu(cep->set);
240 		ce.ctr = be16_to_cpu(cep->ctr);
241 		ce.res1 = be16_to_cpu(cep->res1);
242 
243 		if (!ctrset_valid(&ce)) {	/* Print trailer */
244 			s390_cpumcfdg_dumptrail(color, offset,
245 						(struct cf_trailer_entry *)cep);
246 			return;
247 		}
248 
249 		color_fprintf(stdout, color, "    [%#08zx] Counterset:%d"
250 			      " Counters:%d\n", offset, ce.set, ce.ctr);
251 		for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) {
252 			char *ev_name = get_counter_name(ce.set, i, pmu);
253 
254 			color_fprintf(stdout, color,
255 				      "\tCounter:%03zd %s Value:%#018"PRIx64"\n", i,
256 				      ev_name ?: "<unknown>", be64_to_cpu(*p));
257 			free(ev_name);
258 		}
259 		offset += ctrset_size(&ce);
260 	}
261 }
262 
263 #pragma GCC diagnostic push
264 #pragma GCC diagnostic ignored "-Wpacked"
265 #pragma GCC diagnostic ignored "-Wattributes"
266 /*
267  * Check for consistency of PAI_CRYPTO/PAI_NNPA raw data.
268  */
269 struct pai_data {		/* Event number and value */
270 	u16 event_nr;
271 	u64 event_val;
272 } __packed;
273 
274 #pragma GCC diagnostic pop
275 
276 /*
277  * Test for valid raw data. At least one PAI event should be in the raw
278  * data section.
279  */
280 static bool s390_pai_all_test(struct perf_sample *sample)
281 {
282 	size_t len = sample->raw_size;
283 
284 	if (len < 0xa)
285 		return false;
286 	return true;
287 }
288 
289 static void s390_pai_all_dump(struct perf_sample *sample)
290 {
291 	struct evsel *evsel = sample->evsel;
292 	size_t len = sample->raw_size, offset = 0;
293 	unsigned char *p = sample->raw_data;
294 	const char *color = PERF_COLOR_BLUE;
295 	struct pai_data pai_data;
296 	char *ev_name;
297 
298 	while (offset < len) {
299 		memcpy(&pai_data.event_nr, p, sizeof(pai_data.event_nr));
300 		pai_data.event_nr = be16_to_cpu(pai_data.event_nr);
301 		p += sizeof(pai_data.event_nr);
302 		offset += sizeof(pai_data.event_nr);
303 
304 		memcpy(&pai_data.event_val, p, sizeof(pai_data.event_val));
305 		pai_data.event_val = be64_to_cpu(pai_data.event_val);
306 		p += sizeof(pai_data.event_val);
307 		offset += sizeof(pai_data.event_val);
308 
309 		ev_name = get_counter_name(evsel->core.attr.config,
310 					   pai_data.event_nr, evsel->pmu);
311 		color_fprintf(stdout, color, "\tCounter:%03d %s Value:%#018"PRIx64"\n",
312 			      pai_data.event_nr, ev_name ?: "<unknown>",
313 			      pai_data.event_val);
314 		free(ev_name);
315 
316 		if (offset + 0xa > len)
317 			break;
318 	}
319 	color_fprintf(stdout, color, "\n");
320 }
321 
322 /* S390 specific trace event function. Check for PERF_RECORD_SAMPLE events
323  * and if the event was triggered by a
324  * - counter set diagnostic event
325  * - processor activity assist (PAI) crypto counter event
326  * - processor activity assist (PAI) neural network processor assist (NNPA)
327  *   counter event
328  * display its raw data.
329  * The function is only invoked when the dump flag -D is set.
330  *
331  * Function evlist__s390_sample_raw() is defined as call back after it has
332  * been verified that the perf.data file was created on s390 platform.
333  */
334 void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event,
335 			     struct perf_sample *sample)
336 {
337 	const char *pai_name;
338 
339 	if (event->header.type != PERF_RECORD_SAMPLE)
340 		return;
341 
342 	if (!sample->evsel) {
343 		sample->evsel = evlist__event2evsel(evlist, event);
344 		if (!sample->evsel)
345 			return;
346 		evsel__get(sample->evsel);
347 	}
348 
349 	/* Check for raw data in sample */
350 	if (!sample->raw_size || !sample->raw_data)
351 		return;
352 
353 	/* Display raw data on screen */
354 	if (sample->evsel->core.attr.config == PERF_EVENT_CPUM_CF_DIAG) {
355 		if (!sample->evsel->pmu)
356 			sample->evsel->pmu = perf_pmus__find("cpum_cf");
357 		if (!s390_cpumcfdg_testctr(sample))
358 			pr_err("Invalid counter set data encountered\n");
359 		else
360 			s390_cpumcfdg_dump(sample);
361 		return;
362 	}
363 
364 	switch (sample->evsel->core.attr.config) {
365 	case PERF_EVENT_PAI_NNPA_ALL:
366 		pai_name = "NNPA_ALL";
367 		break;
368 	case PERF_EVENT_PAI_CRYPTO_ALL:
369 		pai_name = "CRYPTO_ALL";
370 		break;
371 	default:
372 		return;
373 	}
374 
375 	if (!s390_pai_all_test(sample)) {
376 		pr_err("Invalid %s raw data encountered\n", pai_name);
377 	} else {
378 		if (!sample->evsel->pmu)
379 			sample->evsel->pmu = perf_pmus__find_by_type(sample->evsel->core.attr.type);
380 		s390_pai_all_dump(sample);
381 	}
382 }
383