0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <unistd.h>
0016 #include <stdio.h>
0017 #include <string.h>
0018 #include <inttypes.h>
0019
0020 #include <sys/stat.h>
0021 #include <linux/compiler.h>
0022 #include <asm/byteorder.h>
0023
0024 #include "debug.h"
0025 #include "session.h"
0026 #include "evlist.h"
0027 #include "color.h"
0028 #include "sample-raw.h"
0029 #include "s390-cpumcf-kernel.h"
0030 #include "pmu-events/pmu-events.h"
0031
0032 static size_t ctrset_size(struct cf_ctrset_entry *set)
0033 {
0034 return sizeof(*set) + set->ctr * sizeof(u64);
0035 }
0036
0037 static bool ctrset_valid(struct cf_ctrset_entry *set)
0038 {
0039 return set->def == S390_CPUMCF_DIAG_DEF;
0040 }
0041
0042
0043
0044
0045
0046 static bool s390_cpumcfdg_testctr(struct perf_sample *sample)
0047 {
0048 size_t len = sample->raw_size, offset = 0;
0049 unsigned char *buf = sample->raw_data;
0050 struct cf_trailer_entry *te;
0051 struct cf_ctrset_entry *cep, ce;
0052
0053 if (!len)
0054 return false;
0055 while (offset < len) {
0056 cep = (struct cf_ctrset_entry *)(buf + offset);
0057 ce.def = be16_to_cpu(cep->def);
0058 ce.set = be16_to_cpu(cep->set);
0059 ce.ctr = be16_to_cpu(cep->ctr);
0060 ce.res1 = be16_to_cpu(cep->res1);
0061
0062 if (!ctrset_valid(&ce) || offset + ctrset_size(&ce) > len) {
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072 if (len - offset - sizeof(*te) == 4)
0073 break;
0074 pr_err("Invalid counter set entry at %zd\n", offset);
0075 return false;
0076 }
0077 offset += ctrset_size(&ce);
0078 }
0079 return true;
0080 }
0081
0082
0083 static void s390_cpumcfdg_dumptrail(const char *color, size_t offset,
0084 struct cf_trailer_entry *tep)
0085 {
0086 struct cf_trailer_entry te;
0087
0088 te.flags = be64_to_cpu(tep->flags);
0089 te.cfvn = be16_to_cpu(tep->cfvn);
0090 te.csvn = be16_to_cpu(tep->csvn);
0091 te.cpu_speed = be32_to_cpu(tep->cpu_speed);
0092 te.timestamp = be64_to_cpu(tep->timestamp);
0093 te.progusage1 = be64_to_cpu(tep->progusage1);
0094 te.progusage2 = be64_to_cpu(tep->progusage2);
0095 te.progusage3 = be64_to_cpu(tep->progusage3);
0096 te.tod_base = be64_to_cpu(tep->tod_base);
0097 te.mach_type = be16_to_cpu(tep->mach_type);
0098 te.res1 = be16_to_cpu(tep->res1);
0099 te.res2 = be32_to_cpu(tep->res2);
0100
0101 color_fprintf(stdout, color, " [%#08zx] Trailer:%c%c%c%c%c"
0102 " Cfvn:%d Csvn:%d Speed:%d TOD:%#llx\n",
0103 offset, te.clock_base ? 'T' : ' ',
0104 te.speed ? 'S' : ' ', te.mtda ? 'M' : ' ',
0105 te.caca ? 'C' : ' ', te.lcda ? 'L' : ' ',
0106 te.cfvn, te.csvn, te.cpu_speed, te.timestamp);
0107 color_fprintf(stdout, color, "\t\t1:%lx 2:%lx 3:%lx TOD-Base:%#llx"
0108 " Type:%x\n\n",
0109 te.progusage1, te.progusage2, te.progusage3,
0110 te.tod_base, te.mach_type);
0111 }
0112
0113
0114 static int get_counterset_start(int setnr)
0115 {
0116 switch (setnr) {
0117 case CPUMF_CTR_SET_BASIC:
0118 return 0;
0119 case CPUMF_CTR_SET_USER:
0120 return 32;
0121 case CPUMF_CTR_SET_CRYPTO:
0122 return 64;
0123 case CPUMF_CTR_SET_EXT:
0124 return 128;
0125 case CPUMF_CTR_SET_MT_DIAG:
0126 return 448;
0127 default:
0128 return -1;
0129 }
0130 }
0131
0132 struct get_counter_name_data {
0133 int wanted;
0134 const char *result;
0135 };
0136
0137 static int get_counter_name_callback(const struct pmu_event *evp,
0138 const struct pmu_events_table *table __maybe_unused,
0139 void *vdata)
0140 {
0141 struct get_counter_name_data *data = vdata;
0142 int rc, event_nr;
0143
0144 if (evp->name == NULL || evp->event == NULL)
0145 return 0;
0146 rc = sscanf(evp->event, "event=%x", &event_nr);
0147 if (rc == 1 && event_nr == data->wanted) {
0148 data->result = evp->name;
0149 return 1;
0150 }
0151 return 0;
0152 }
0153
0154
0155
0156
0157
0158
0159
0160 static const char *get_counter_name(int set, int nr, const struct pmu_events_table *table)
0161 {
0162 struct get_counter_name_data data = {
0163 .wanted = get_counterset_start(set) + nr,
0164 .result = NULL,
0165 };
0166
0167 if (!table)
0168 return NULL;
0169
0170 pmu_events_table_for_each_event(table, get_counter_name_callback, &data);
0171 return data.result;
0172 }
0173
0174 static void s390_cpumcfdg_dump(struct perf_sample *sample)
0175 {
0176 size_t i, len = sample->raw_size, offset = 0;
0177 unsigned char *buf = sample->raw_data;
0178 const char *color = PERF_COLOR_BLUE;
0179 struct cf_ctrset_entry *cep, ce;
0180 const struct pmu_events_table *table;
0181 u64 *p;
0182
0183 table = pmu_events_table__find();
0184 while (offset < len) {
0185 cep = (struct cf_ctrset_entry *)(buf + offset);
0186
0187 ce.def = be16_to_cpu(cep->def);
0188 ce.set = be16_to_cpu(cep->set);
0189 ce.ctr = be16_to_cpu(cep->ctr);
0190 ce.res1 = be16_to_cpu(cep->res1);
0191
0192 if (!ctrset_valid(&ce)) {
0193 s390_cpumcfdg_dumptrail(color, offset,
0194 (struct cf_trailer_entry *)cep);
0195 return;
0196 }
0197
0198 color_fprintf(stdout, color, " [%#08zx] Counterset:%d"
0199 " Counters:%d\n", offset, ce.set, ce.ctr);
0200 for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) {
0201 const char *ev_name = get_counter_name(ce.set, i, table);
0202
0203 color_fprintf(stdout, color,
0204 "\tCounter:%03d %s Value:%#018lx\n", i,
0205 ev_name ?: "<unknown>", be64_to_cpu(*p));
0206 }
0207 offset += ctrset_size(&ce);
0208 }
0209 }
0210
0211
0212
0213
0214
0215
0216 void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample)
0217 {
0218 struct evsel *ev_bc000;
0219
0220 if (event->header.type != PERF_RECORD_SAMPLE)
0221 return;
0222
0223 ev_bc000 = evlist__event2evsel(evlist, event);
0224 if (ev_bc000 == NULL ||
0225 ev_bc000->core.attr.config != PERF_EVENT_CPUM_CF_DIAG)
0226 return;
0227
0228
0229 if (!s390_cpumcfdg_testctr(sample)) {
0230 pr_err("Invalid counter set data encountered\n");
0231 return;
0232 }
0233 s390_cpumcfdg_dump(sample);
0234 }