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