xref: /linux/tools/perf/util/s390-sample-raw.c (revision 93115d32e8666614f3578601a0b7b3f4914509f4)
1*93115d32SThomas Richter // SPDX-License-Identifier: GPL-2.0
2*93115d32SThomas Richter /*
3*93115d32SThomas Richter  * Copyright IBM Corp. 2019
4*93115d32SThomas Richter  * Author(s): Thomas Richter <tmricht@linux.ibm.com>
5*93115d32SThomas Richter  *
6*93115d32SThomas Richter  * This program is free software; you can redistribute it and/or modify
7*93115d32SThomas Richter  * it under the terms of the GNU General Public License (version 2 only)
8*93115d32SThomas Richter  * as published by the Free Software Foundation.
9*93115d32SThomas Richter  *
10*93115d32SThomas Richter  * Architecture specific trace_event function. Save event's bc000 raw data
11*93115d32SThomas Richter  * to file. File name is aux.ctr.## where ## stands for the CPU number the
12*93115d32SThomas Richter  * sample was taken from.
13*93115d32SThomas Richter  */
14*93115d32SThomas Richter 
15*93115d32SThomas Richter #include <unistd.h>
16*93115d32SThomas Richter #include <stdio.h>
17*93115d32SThomas Richter #include <string.h>
18*93115d32SThomas Richter #include <inttypes.h>
19*93115d32SThomas Richter 
20*93115d32SThomas Richter #include <sys/stat.h>
21*93115d32SThomas Richter #include <linux/compiler.h>
22*93115d32SThomas Richter #include <asm/byteorder.h>
23*93115d32SThomas Richter 
24*93115d32SThomas Richter #include "debug.h"
25*93115d32SThomas Richter #include "util.h"
26*93115d32SThomas Richter #include "auxtrace.h"
27*93115d32SThomas Richter #include "session.h"
28*93115d32SThomas Richter #include "evlist.h"
29*93115d32SThomas Richter #include "config.h"
30*93115d32SThomas Richter #include "color.h"
31*93115d32SThomas Richter #include "sample-raw.h"
32*93115d32SThomas Richter #include "s390-cpumcf-kernel.h"
33*93115d32SThomas Richter 
34*93115d32SThomas Richter static size_t ctrset_size(struct cf_ctrset_entry *set)
35*93115d32SThomas Richter {
36*93115d32SThomas Richter 	return sizeof(*set) + set->ctr * sizeof(u64);
37*93115d32SThomas Richter }
38*93115d32SThomas Richter 
39*93115d32SThomas Richter static bool ctrset_valid(struct cf_ctrset_entry *set)
40*93115d32SThomas Richter {
41*93115d32SThomas Richter 	return set->def == S390_CPUMCF_DIAG_DEF;
42*93115d32SThomas Richter }
43*93115d32SThomas Richter 
44*93115d32SThomas Richter /* CPU Measurement Counter Facility raw data is a byte stream. It is 8 byte
45*93115d32SThomas Richter  * aligned and might have trailing padding bytes.
46*93115d32SThomas Richter  * Display the raw data on screen.
47*93115d32SThomas Richter  */
48*93115d32SThomas Richter static bool s390_cpumcfdg_testctr(struct perf_sample *sample)
49*93115d32SThomas Richter {
50*93115d32SThomas Richter 	size_t len = sample->raw_size, offset = 0;
51*93115d32SThomas Richter 	unsigned char *buf = sample->raw_data;
52*93115d32SThomas Richter 	struct cf_trailer_entry *te;
53*93115d32SThomas Richter 	struct cf_ctrset_entry *cep, ce;
54*93115d32SThomas Richter 
55*93115d32SThomas Richter 	if (!len)
56*93115d32SThomas Richter 		return false;
57*93115d32SThomas Richter 	while (offset < len) {
58*93115d32SThomas Richter 		cep = (struct cf_ctrset_entry *)(buf + offset);
59*93115d32SThomas Richter 		ce.def = be16_to_cpu(cep->def);
60*93115d32SThomas Richter 		ce.set = be16_to_cpu(cep->set);
61*93115d32SThomas Richter 		ce.ctr = be16_to_cpu(cep->ctr);
62*93115d32SThomas Richter 		ce.res1 = be16_to_cpu(cep->res1);
63*93115d32SThomas Richter 
64*93115d32SThomas Richter 		if (!ctrset_valid(&ce) || offset + ctrset_size(&ce) > len) {
65*93115d32SThomas Richter 			/* Raw data for counter sets are always multiple of 8
66*93115d32SThomas Richter 			 * bytes. Prepending a 4 bytes size field to the
67*93115d32SThomas Richter 			 * raw data block in the sample causes the perf tool
68*93115d32SThomas Richter 			 * to append 4 padding bytes to make the raw data part
69*93115d32SThomas Richter 			 * of the sample a multiple of eight bytes again.
70*93115d32SThomas Richter 			 *
71*93115d32SThomas Richter 			 * If the last entry (trailer) is 4 bytes off the raw
72*93115d32SThomas Richter 			 * area data end, all is good.
73*93115d32SThomas Richter 			 */
74*93115d32SThomas Richter 			if (len - offset - sizeof(*te) == 4)
75*93115d32SThomas Richter 				break;
76*93115d32SThomas Richter 			pr_err("Invalid counter set entry at %zd\n", offset);
77*93115d32SThomas Richter 			return false;
78*93115d32SThomas Richter 		}
79*93115d32SThomas Richter 		offset += ctrset_size(&ce);
80*93115d32SThomas Richter 	}
81*93115d32SThomas Richter 	return true;
82*93115d32SThomas Richter }
83*93115d32SThomas Richter 
84*93115d32SThomas Richter /* Dump event bc000 on screen, already tested on correctness. */
85*93115d32SThomas Richter static void s390_cpumcfdg_dumptrail(const char *color, size_t offset,
86*93115d32SThomas Richter 				    struct cf_trailer_entry *tep)
87*93115d32SThomas Richter {
88*93115d32SThomas Richter 	struct cf_trailer_entry  te;
89*93115d32SThomas Richter 
90*93115d32SThomas Richter 	te.flags = be64_to_cpu(tep->flags);
91*93115d32SThomas Richter 	te.cfvn = be16_to_cpu(tep->cfvn);
92*93115d32SThomas Richter 	te.csvn = be16_to_cpu(tep->csvn);
93*93115d32SThomas Richter 	te.cpu_speed = be32_to_cpu(tep->cpu_speed);
94*93115d32SThomas Richter 	te.timestamp = be64_to_cpu(tep->timestamp);
95*93115d32SThomas Richter 	te.progusage1 = be64_to_cpu(tep->progusage1);
96*93115d32SThomas Richter 	te.progusage2 = be64_to_cpu(tep->progusage2);
97*93115d32SThomas Richter 	te.progusage3 = be64_to_cpu(tep->progusage3);
98*93115d32SThomas Richter 	te.tod_base = be64_to_cpu(tep->tod_base);
99*93115d32SThomas Richter 	te.mach_type = be16_to_cpu(tep->mach_type);
100*93115d32SThomas Richter 	te.res1 = be16_to_cpu(tep->res1);
101*93115d32SThomas Richter 	te.res2 = be32_to_cpu(tep->res2);
102*93115d32SThomas Richter 
103*93115d32SThomas Richter 	color_fprintf(stdout, color, "    [%#08zx] Trailer:%c%c%c%c%c"
104*93115d32SThomas Richter 		      " Cfvn:%d Csvn:%d Speed:%d TOD:%#llx\n",
105*93115d32SThomas Richter 		      offset, te.clock_base ? 'T' : ' ',
106*93115d32SThomas Richter 		      te.speed ? 'S' : ' ', te.mtda ? 'M' : ' ',
107*93115d32SThomas Richter 		      te.caca ? 'C' : ' ', te.lcda ? 'L' : ' ',
108*93115d32SThomas Richter 		      te.cfvn, te.csvn, te.cpu_speed, te.timestamp);
109*93115d32SThomas Richter 	color_fprintf(stdout, color, "\t\t1:%lx 2:%lx 3:%lx TOD-Base:%#llx"
110*93115d32SThomas Richter 		      " Type:%x\n\n",
111*93115d32SThomas Richter 		      te.progusage1, te.progusage2, te.progusage3,
112*93115d32SThomas Richter 		      te.tod_base, te.mach_type);
113*93115d32SThomas Richter }
114*93115d32SThomas Richter 
115*93115d32SThomas Richter static void s390_cpumcfdg_dump(struct perf_sample *sample)
116*93115d32SThomas Richter {
117*93115d32SThomas Richter 	size_t i, len = sample->raw_size, offset = 0;
118*93115d32SThomas Richter 	unsigned char *buf = sample->raw_data;
119*93115d32SThomas Richter 	const char *color = PERF_COLOR_BLUE;
120*93115d32SThomas Richter 	struct cf_ctrset_entry *cep, ce;
121*93115d32SThomas Richter 	u64 *p;
122*93115d32SThomas Richter 
123*93115d32SThomas Richter 	while (offset < len) {
124*93115d32SThomas Richter 		cep = (struct cf_ctrset_entry *)(buf + offset);
125*93115d32SThomas Richter 
126*93115d32SThomas Richter 		ce.def = be16_to_cpu(cep->def);
127*93115d32SThomas Richter 		ce.set = be16_to_cpu(cep->set);
128*93115d32SThomas Richter 		ce.ctr = be16_to_cpu(cep->ctr);
129*93115d32SThomas Richter 		ce.res1 = be16_to_cpu(cep->res1);
130*93115d32SThomas Richter 
131*93115d32SThomas Richter 		if (!ctrset_valid(&ce)) {	/* Print trailer */
132*93115d32SThomas Richter 			s390_cpumcfdg_dumptrail(color, offset,
133*93115d32SThomas Richter 						(struct cf_trailer_entry *)cep);
134*93115d32SThomas Richter 			return;
135*93115d32SThomas Richter 		}
136*93115d32SThomas Richter 
137*93115d32SThomas Richter 		color_fprintf(stdout, color, "    [%#08zx] Counterset:%d"
138*93115d32SThomas Richter 			      " Counters:%d\n", offset, ce.set, ce.ctr);
139*93115d32SThomas Richter 		for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; i += 2, p += 2)
140*93115d32SThomas Richter 			color_fprintf(stdout, color,
141*93115d32SThomas Richter 				      "\tCounter:%03d Value:%#018lx"
142*93115d32SThomas Richter 				      " Counter:%03d Value:%#018lx\n",
143*93115d32SThomas Richter 				      i, be64_to_cpu(*p),
144*93115d32SThomas Richter 				      i + 1, be64_to_cpu(*(p + 1)));
145*93115d32SThomas Richter 		offset += ctrset_size(&ce);
146*93115d32SThomas Richter 	}
147*93115d32SThomas Richter }
148*93115d32SThomas Richter 
149*93115d32SThomas Richter /* S390 specific trace event function. Check for PERF_RECORD_SAMPLE events
150*93115d32SThomas Richter  * and if the event was triggered by a counter set diagnostic event display
151*93115d32SThomas Richter  * its raw data.
152*93115d32SThomas Richter  * The function is only invoked when the dump flag -D is set.
153*93115d32SThomas Richter  */
154*93115d32SThomas Richter void perf_evlist__s390_sample_raw(struct perf_evlist *evlist, union perf_event *event,
155*93115d32SThomas Richter 				  struct perf_sample *sample)
156*93115d32SThomas Richter {
157*93115d32SThomas Richter 	struct perf_evsel *ev_bc000;
158*93115d32SThomas Richter 
159*93115d32SThomas Richter 	if (event->header.type != PERF_RECORD_SAMPLE)
160*93115d32SThomas Richter 		return;
161*93115d32SThomas Richter 
162*93115d32SThomas Richter 	ev_bc000 = perf_evlist__event2evsel(evlist, event);
163*93115d32SThomas Richter 	if (ev_bc000 == NULL ||
164*93115d32SThomas Richter 	    ev_bc000->attr.config != PERF_EVENT_CPUM_CF_DIAG)
165*93115d32SThomas Richter 		return;
166*93115d32SThomas Richter 
167*93115d32SThomas Richter 	/* Display raw data on screen */
168*93115d32SThomas Richter 	if (!s390_cpumcfdg_testctr(sample)) {
169*93115d32SThomas Richter 		pr_err("Invalid counter set data encountered\n");
170*93115d32SThomas Richter 		return;
171*93115d32SThomas Richter 	}
172*93115d32SThomas Richter 	s390_cpumcfdg_dump(sample);
173*93115d32SThomas Richter }
174