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 <asm/byteorder.h> 23 24 #include "debug.h" 25 #include "util.h" 26 #include "auxtrace.h" 27 #include "session.h" 28 #include "evlist.h" 29 #include "config.h" 30 #include "color.h" 31 #include "sample-raw.h" 32 #include "s390-cpumcf-kernel.h" 33 34 static size_t ctrset_size(struct cf_ctrset_entry *set) 35 { 36 return sizeof(*set) + set->ctr * sizeof(u64); 37 } 38 39 static bool ctrset_valid(struct cf_ctrset_entry *set) 40 { 41 return set->def == S390_CPUMCF_DIAG_DEF; 42 } 43 44 /* CPU Measurement Counter Facility raw data is a byte stream. It is 8 byte 45 * aligned and might have trailing padding bytes. 46 * Display the raw data on screen. 47 */ 48 static bool s390_cpumcfdg_testctr(struct perf_sample *sample) 49 { 50 size_t len = sample->raw_size, offset = 0; 51 unsigned char *buf = sample->raw_data; 52 struct cf_trailer_entry *te; 53 struct cf_ctrset_entry *cep, ce; 54 55 if (!len) 56 return false; 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:%#llx\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:%#llx" 110 " Type:%x\n\n", 111 te.progusage1, te.progusage2, te.progusage3, 112 te.tod_base, te.mach_type); 113 } 114 115 static void s390_cpumcfdg_dump(struct perf_sample *sample) 116 { 117 size_t i, len = sample->raw_size, offset = 0; 118 unsigned char *buf = sample->raw_data; 119 const char *color = PERF_COLOR_BLUE; 120 struct cf_ctrset_entry *cep, ce; 121 u64 *p; 122 123 while (offset < len) { 124 cep = (struct cf_ctrset_entry *)(buf + offset); 125 126 ce.def = be16_to_cpu(cep->def); 127 ce.set = be16_to_cpu(cep->set); 128 ce.ctr = be16_to_cpu(cep->ctr); 129 ce.res1 = be16_to_cpu(cep->res1); 130 131 if (!ctrset_valid(&ce)) { /* Print trailer */ 132 s390_cpumcfdg_dumptrail(color, offset, 133 (struct cf_trailer_entry *)cep); 134 return; 135 } 136 137 color_fprintf(stdout, color, " [%#08zx] Counterset:%d" 138 " Counters:%d\n", offset, ce.set, ce.ctr); 139 for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; i += 2, p += 2) 140 color_fprintf(stdout, color, 141 "\tCounter:%03d Value:%#018lx" 142 " Counter:%03d Value:%#018lx\n", 143 i, be64_to_cpu(*p), 144 i + 1, be64_to_cpu(*(p + 1))); 145 offset += ctrset_size(&ce); 146 } 147 } 148 149 /* S390 specific trace event function. Check for PERF_RECORD_SAMPLE events 150 * and if the event was triggered by a counter set diagnostic event display 151 * its raw data. 152 * The function is only invoked when the dump flag -D is set. 153 */ 154 void perf_evlist__s390_sample_raw(struct perf_evlist *evlist, union perf_event *event, 155 struct perf_sample *sample) 156 { 157 struct perf_evsel *ev_bc000; 158 159 if (event->header.type != PERF_RECORD_SAMPLE) 160 return; 161 162 ev_bc000 = perf_evlist__event2evsel(evlist, event); 163 if (ev_bc000 == NULL || 164 ev_bc000->attr.config != PERF_EVENT_CPUM_CF_DIAG) 165 return; 166 167 /* Display raw data on screen */ 168 if (!s390_cpumcfdg_testctr(sample)) { 169 pr_err("Invalid counter set data encountered\n"); 170 return; 171 } 172 s390_cpumcfdg_dump(sample); 173 } 174