File Index
Files and directories
Sign in
FILE

s390-sample-raw.c

File preview with syntax highlighting.

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