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