hc
2023-12-09 b22da3d8526a935aa31e086e63f60ff3246cb61c
kernel/tools/perf/util/s390-cpumsf.c
....@@ -17,8 +17,8 @@
1717 * see Documentation/perf.data-file-format.txt.
1818 * PERF_RECORD_AUXTRACE_INFO:
1919 * Defines a table of contains for PERF_RECORD_AUXTRACE records. This
20
- * record is generated during 'perf record' command. Each record contains up
21
- * to 256 entries describing offset and size of the AUXTRACE data in the
20
+ * record is generated during 'perf record' command. Each record contains
21
+ * up to 256 entries describing offset and size of the AUXTRACE data in the
2222 * perf.data file.
2323 * PERF_RECORD_AUXTRACE_ERROR:
2424 * Indicates an error during AUXTRACE collection such as buffer overflow.
....@@ -146,19 +146,23 @@
146146 #include <linux/types.h>
147147 #include <linux/bitops.h>
148148 #include <linux/log2.h>
149
+#include <linux/zalloc.h>
149150
150
-#include "cpumap.h"
151
+#include <sys/stat.h>
152
+#include <sys/types.h>
153
+
151154 #include "color.h"
152155 #include "evsel.h"
153156 #include "evlist.h"
154157 #include "machine.h"
155158 #include "session.h"
156
-#include "util.h"
157
-#include "thread.h"
159
+#include "tool.h"
158160 #include "debug.h"
159161 #include "auxtrace.h"
160162 #include "s390-cpumsf.h"
161163 #include "s390-cpumsf-kernel.h"
164
+#include "s390-cpumcf-kernel.h"
165
+#include "config.h"
162166
163167 struct s390_cpumsf {
164168 struct auxtrace auxtrace;
....@@ -170,6 +174,8 @@
170174 u32 pmu_type;
171175 u16 machine_type;
172176 bool data_queued;
177
+ bool use_logfile;
178
+ char *logdir;
173179 };
174180
175181 struct s390_cpumsf_queue {
....@@ -177,12 +183,86 @@
177183 unsigned int queue_nr;
178184 struct auxtrace_buffer *buffer;
179185 int cpu;
186
+ FILE *logfile;
187
+ FILE *logfile_ctr;
180188 };
181189
182
-/* Display s390 CPU measurement facility basic-sampling data entry */
183
-static bool s390_cpumsf_basic_show(const char *color, size_t pos,
184
- struct hws_basic_entry *basic)
190
+/* Check if the raw data should be dumped to file. If this is the case and
191
+ * the file to dump to has not been opened for writing, do so.
192
+ *
193
+ * Return 0 on success and greater zero on error so processing continues.
194
+ */
195
+static int s390_cpumcf_dumpctr(struct s390_cpumsf *sf,
196
+ struct perf_sample *sample)
185197 {
198
+ struct s390_cpumsf_queue *sfq;
199
+ struct auxtrace_queue *q;
200
+ int rc = 0;
201
+
202
+ if (!sf->use_logfile || sf->queues.nr_queues <= sample->cpu)
203
+ return rc;
204
+
205
+ q = &sf->queues.queue_array[sample->cpu];
206
+ sfq = q->priv;
207
+ if (!sfq) /* Queue not yet allocated */
208
+ return rc;
209
+
210
+ if (!sfq->logfile_ctr) {
211
+ char *name;
212
+
213
+ rc = (sf->logdir)
214
+ ? asprintf(&name, "%s/aux.ctr.%02x",
215
+ sf->logdir, sample->cpu)
216
+ : asprintf(&name, "aux.ctr.%02x", sample->cpu);
217
+ if (rc > 0)
218
+ sfq->logfile_ctr = fopen(name, "w");
219
+ if (sfq->logfile_ctr == NULL) {
220
+ pr_err("Failed to open counter set log file %s, "
221
+ "continue...\n", name);
222
+ rc = 1;
223
+ }
224
+ free(name);
225
+ }
226
+
227
+ if (sfq->logfile_ctr) {
228
+ /* See comment above for -4 */
229
+ size_t n = fwrite(sample->raw_data, sample->raw_size - 4, 1,
230
+ sfq->logfile_ctr);
231
+ if (n != 1) {
232
+ pr_err("Failed to write counter set data\n");
233
+ rc = 1;
234
+ }
235
+ }
236
+ return rc;
237
+}
238
+
239
+/* Display s390 CPU measurement facility basic-sampling data entry
240
+ * Data written on s390 in big endian byte order and contains bit
241
+ * fields across byte boundaries.
242
+ */
243
+static bool s390_cpumsf_basic_show(const char *color, size_t pos,
244
+ struct hws_basic_entry *basicp)
245
+{
246
+ struct hws_basic_entry *basic = basicp;
247
+#if __BYTE_ORDER == __LITTLE_ENDIAN
248
+ struct hws_basic_entry local;
249
+ unsigned long long word = be64toh(*(unsigned long long *)basicp);
250
+
251
+ memset(&local, 0, sizeof(local));
252
+ local.def = be16toh(basicp->def);
253
+ local.prim_asn = word & 0xffff;
254
+ local.CL = word >> 30 & 0x3;
255
+ local.I = word >> 32 & 0x1;
256
+ local.AS = word >> 33 & 0x3;
257
+ local.P = word >> 35 & 0x1;
258
+ local.W = word >> 36 & 0x1;
259
+ local.T = word >> 37 & 0x1;
260
+ local.U = word >> 40 & 0xf;
261
+ local.ia = be64toh(basicp->ia);
262
+ local.gpp = be64toh(basicp->gpp);
263
+ local.hpp = be64toh(basicp->hpp);
264
+ basic = &local;
265
+#endif
186266 if (basic->def != 1) {
187267 pr_err("Invalid AUX trace basic entry [%#08zx]\n", pos);
188268 return false;
....@@ -200,10 +280,22 @@
200280 return true;
201281 }
202282
203
-/* Display s390 CPU measurement facility diagnostic-sampling data entry */
283
+/* Display s390 CPU measurement facility diagnostic-sampling data entry.
284
+ * Data written on s390 in big endian byte order and contains bit
285
+ * fields across byte boundaries.
286
+ */
204287 static bool s390_cpumsf_diag_show(const char *color, size_t pos,
205
- struct hws_diag_entry *diag)
288
+ struct hws_diag_entry *diagp)
206289 {
290
+ struct hws_diag_entry *diag = diagp;
291
+#if __BYTE_ORDER == __LITTLE_ENDIAN
292
+ struct hws_diag_entry local;
293
+ unsigned long long word = be64toh(*(unsigned long long *)diagp);
294
+
295
+ local.def = be16toh(diagp->def);
296
+ local.I = word >> 32 & 0x1;
297
+ diag = &local;
298
+#endif
207299 if (diag->def < S390_CPUMSF_DIAG_DEF_FIRST) {
208300 pr_err("Invalid AUX trace diagnostic entry [%#08zx]\n", pos);
209301 return false;
....@@ -214,35 +306,52 @@
214306 }
215307
216308 /* Return TOD timestamp contained in an trailer entry */
217
-static unsigned long long trailer_timestamp(struct hws_trailer_entry *te)
309
+static unsigned long long trailer_timestamp(struct hws_trailer_entry *te,
310
+ int idx)
218311 {
219312 /* te->t set: TOD in STCKE format, bytes 8-15
220313 * to->t not set: TOD in STCK format, bytes 0-7
221314 */
222315 unsigned long long ts;
223316
224
- memcpy(&ts, &te->timestamp[te->t], sizeof(ts));
225
- return ts;
317
+ memcpy(&ts, &te->timestamp[idx], sizeof(ts));
318
+ return be64toh(ts);
226319 }
227320
228321 /* Display s390 CPU measurement facility trailer entry */
229322 static bool s390_cpumsf_trailer_show(const char *color, size_t pos,
230323 struct hws_trailer_entry *te)
231324 {
325
+#if __BYTE_ORDER == __LITTLE_ENDIAN
326
+ struct hws_trailer_entry local;
327
+ const unsigned long long flags = be64toh(te->flags);
328
+
329
+ memset(&local, 0, sizeof(local));
330
+ local.f = flags >> 63 & 0x1;
331
+ local.a = flags >> 62 & 0x1;
332
+ local.t = flags >> 61 & 0x1;
333
+ local.bsdes = be16toh((flags >> 16 & 0xffff));
334
+ local.dsdes = be16toh((flags & 0xffff));
335
+ memcpy(&local.timestamp, te->timestamp, sizeof(te->timestamp));
336
+ local.overflow = be64toh(te->overflow);
337
+ local.clock_base = be64toh(te->progusage[0]) >> 63 & 1;
338
+ local.progusage2 = be64toh(te->progusage2);
339
+ te = &local;
340
+#endif
232341 if (te->bsdes != sizeof(struct hws_basic_entry)) {
233342 pr_err("Invalid AUX trace trailer entry [%#08zx]\n", pos);
234343 return false;
235344 }
236345 color_fprintf(stdout, color, " [%#08zx] Trailer %c%c%c bsdes:%d"
237346 " dsdes:%d Overflow:%lld Time:%#llx\n"
238
- "\t\tC:%d TOD:%#lx 1:%#llx 2:%#llx\n",
347
+ "\t\tC:%d TOD:%#lx\n",
239348 pos,
240349 te->f ? 'F' : ' ',
241350 te->a ? 'A' : ' ',
242351 te->t ? 'T' : ' ',
243352 te->bsdes, te->dsdes, te->overflow,
244
- trailer_timestamp(te), te->clock_base, te->progusage2,
245
- te->progusage[0], te->progusage[1]);
353
+ trailer_timestamp(te, te->clock_base),
354
+ te->clock_base, te->progusage2);
246355 return true;
247356 }
248357
....@@ -269,13 +378,13 @@
269378 *dsdes = *bsdes = 0;
270379 if (len & (S390_CPUMSF_PAGESZ - 1)) /* Illegal size */
271380 return false;
272
- if (basic->def != 1) /* No basic set entry, must be first */
381
+ if (be16toh(basic->def) != 1) /* No basic set entry, must be first */
273382 return false;
274383 /* Check for trailer entry at end of SDB */
275384 te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ
276385 - sizeof(*te));
277
- *bsdes = te->bsdes;
278
- *dsdes = te->dsdes;
386
+ *bsdes = be16toh(te->bsdes);
387
+ *dsdes = be16toh(te->dsdes);
279388 if (!te->bsdes && !te->dsdes) {
280389 /* Very old hardware, use CPUID */
281390 switch (machine_type) {
....@@ -437,19 +546,27 @@
437546 static unsigned long long get_trailer_time(const unsigned char *buf)
438547 {
439548 struct hws_trailer_entry *te;
440
- unsigned long long aux_time;
549
+ unsigned long long aux_time, progusage2;
550
+ bool clock_base;
441551
442552 te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ
443553 - sizeof(*te));
444554
445
- if (!te->clock_base) /* TOD_CLOCK_BASE value missing */
555
+#if __BYTE_ORDER == __LITTLE_ENDIAN
556
+ clock_base = be64toh(te->progusage[0]) >> 63 & 0x1;
557
+ progusage2 = be64toh(te->progusage[1]);
558
+#else
559
+ clock_base = te->clock_base;
560
+ progusage2 = te->progusage2;
561
+#endif
562
+ if (!clock_base) /* TOD_CLOCK_BASE value missing */
446563 return 0;
447564
448565 /* Correct calculation to convert time stamp in trailer entry to
449566 * nano seconds (taken from arch/s390 function tod_to_ns()).
450567 * TOD_CLOCK_BASE is stored in trailer entry member progusage2.
451568 */
452
- aux_time = trailer_timestamp(te) - te->progusage2;
569
+ aux_time = trailer_timestamp(te, clock_base) - progusage2;
453570 aux_time = (aux_time >> 9) * 125 + (((aux_time & 0x1ff) * 125) >> 9);
454571 return aux_time;
455572 }
....@@ -600,6 +717,12 @@
600717 buffer->use_size = buffer->size;
601718 buffer->use_data = buffer->data;
602719 }
720
+ if (sfq->logfile) { /* Write into log file */
721
+ size_t rc = fwrite(buffer->data, buffer->size, 1,
722
+ sfq->logfile);
723
+ if (rc != 1)
724
+ pr_err("Failed to write auxiliary data\n");
725
+ }
603726 } else
604727 buffer = sfq->buffer;
605728
....@@ -611,6 +734,13 @@
611734 return -ENOMEM;
612735 buffer->use_size = buffer->size;
613736 buffer->use_data = buffer->data;
737
+
738
+ if (sfq->logfile) { /* Write into log file */
739
+ size_t rc = fwrite(buffer->data, buffer->size, 1,
740
+ sfq->logfile);
741
+ if (rc != 1)
742
+ pr_err("Failed to write auxiliary data\n");
743
+ }
614744 }
615745 pr_debug4("%s queue_nr:%d buffer:%" PRId64 " offset:%#" PRIx64 " size:%#zx rest:%#zx\n",
616746 __func__, sfq->queue_nr, buffer->buffer_nr, buffer->offset,
....@@ -625,7 +755,7 @@
625755 */
626756 if (err) {
627757 sfq->buffer = NULL;
628
- list_del(&buffer->list);
758
+ list_del_init(&buffer->list);
629759 auxtrace_buffer__free(buffer);
630760 if (err > 0) /* Buffer done, no error */
631761 err = 0;
....@@ -645,6 +775,23 @@
645775 sfq->sf = sf;
646776 sfq->queue_nr = queue_nr;
647777 sfq->cpu = -1;
778
+ if (sf->use_logfile) {
779
+ char *name;
780
+ int rc;
781
+
782
+ rc = (sf->logdir)
783
+ ? asprintf(&name, "%s/aux.smp.%02x",
784
+ sf->logdir, queue_nr)
785
+ : asprintf(&name, "aux.smp.%02x", queue_nr);
786
+ if (rc > 0)
787
+ sfq->logfile = fopen(name, "w");
788
+ if (sfq->logfile == NULL) {
789
+ pr_err("Failed to open auxiliary log file %s,"
790
+ "continue...\n", name);
791
+ sf->use_logfile = false;
792
+ }
793
+ free(name);
794
+ }
648795 return sfq;
649796 }
650797
....@@ -736,7 +883,7 @@
736883 }
737884
738885 static int s390_cpumsf_synth_error(struct s390_cpumsf *sf, int code, int cpu,
739
- pid_t pid, pid_t tid, u64 ip)
886
+ pid_t pid, pid_t tid, u64 ip, u64 timestamp)
740887 {
741888 char msg[MAX_AUXTRACE_ERROR_MSG];
742889 union perf_event event;
....@@ -744,7 +891,7 @@
744891
745892 strncpy(msg, "Lost Auxiliary Trace Buffer", sizeof(msg) - 1);
746893 auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
747
- code, cpu, pid, tid, ip, msg);
894
+ code, cpu, pid, tid, ip, msg, timestamp);
748895
749896 err = perf_session__deliver_synth_event(sf->session, &event, NULL);
750897 if (err)
....@@ -756,11 +903,12 @@
756903 static int s390_cpumsf_lost(struct s390_cpumsf *sf, struct perf_sample *sample)
757904 {
758905 return s390_cpumsf_synth_error(sf, 1, sample->cpu,
759
- sample->pid, sample->tid, 0);
906
+ sample->pid, sample->tid, 0,
907
+ sample->time);
760908 }
761909
762910 static int
763
-s390_cpumsf_process_event(struct perf_session *session __maybe_unused,
911
+s390_cpumsf_process_event(struct perf_session *session,
764912 union perf_event *event,
765913 struct perf_sample *sample,
766914 struct perf_tool *tool)
....@@ -769,6 +917,8 @@
769917 struct s390_cpumsf,
770918 auxtrace);
771919 u64 timestamp = sample->time;
920
+ struct evsel *ev_bc000;
921
+
772922 int err = 0;
773923
774924 if (dump_trace)
....@@ -777,6 +927,16 @@
777927 if (!tool->ordered_events) {
778928 pr_err("s390 Auxiliary Trace requires ordered events\n");
779929 return -EINVAL;
930
+ }
931
+
932
+ if (event->header.type == PERF_RECORD_SAMPLE &&
933
+ sample->raw_size) {
934
+ /* Handle event with raw data */
935
+ ev_bc000 = perf_evlist__event2evsel(session->evlist, event);
936
+ if (ev_bc000 &&
937
+ ev_bc000->core.attr.config == PERF_EVENT_CPUM_CF_DIAG)
938
+ err = s390_cpumcf_dumpctr(sf, sample);
939
+ return err;
780940 }
781941
782942 if (event->header.type == PERF_RECORD_AUX &&
....@@ -855,8 +1015,22 @@
8551015 struct auxtrace_queues *queues = &sf->queues;
8561016 unsigned int i;
8571017
858
- for (i = 0; i < queues->nr_queues; i++)
1018
+ for (i = 0; i < queues->nr_queues; i++) {
1019
+ struct s390_cpumsf_queue *sfq = (struct s390_cpumsf_queue *)
1020
+ queues->queue_array[i].priv;
1021
+
1022
+ if (sfq != NULL) {
1023
+ if (sfq->logfile) {
1024
+ fclose(sfq->logfile);
1025
+ sfq->logfile = NULL;
1026
+ }
1027
+ if (sfq->logfile_ctr) {
1028
+ fclose(sfq->logfile_ctr);
1029
+ sfq->logfile_ctr = NULL;
1030
+ }
1031
+ }
8591032 zfree(&queues->queue_array[i].priv);
1033
+ }
8601034 auxtrace_queues__free(queues);
8611035 }
8621036
....@@ -869,7 +1043,16 @@
8691043 auxtrace_heap__free(&sf->heap);
8701044 s390_cpumsf_free_queues(session);
8711045 session->auxtrace = NULL;
1046
+ zfree(&sf->logdir);
8721047 free(sf);
1048
+}
1049
+
1050
+static bool
1051
+s390_cpumsf_evsel_is_auxtrace(struct perf_session *session __maybe_unused,
1052
+ struct evsel *evsel)
1053
+{
1054
+ return evsel->core.attr.type == PERF_TYPE_RAW &&
1055
+ evsel->core.attr.config == PERF_EVENT_CPUM_SF_DIAG;
8731056 }
8741057
8751058 static int s390_cpumsf_get_type(const char *cpuid)
....@@ -882,25 +1065,63 @@
8821065
8831066 /* Check itrace options set on perf report command.
8841067 * Return true, if none are set or all options specified can be
885
- * handled on s390.
1068
+ * handled on s390 (currently only option 'd' for logging.
8861069 * Return false otherwise.
8871070 */
8881071 static bool check_auxtrace_itrace(struct itrace_synth_opts *itops)
8891072 {
1073
+ bool ison = false;
1074
+
8901075 if (!itops || !itops->set)
8911076 return true;
892
- pr_err("No --itrace options supported\n");
1077
+ ison = itops->inject || itops->instructions || itops->branches ||
1078
+ itops->transactions || itops->ptwrites ||
1079
+ itops->pwr_events || itops->errors ||
1080
+ itops->dont_decode || itops->calls || itops->returns ||
1081
+ itops->callchain || itops->thread_stack ||
1082
+ itops->last_branch || itops->add_callchain ||
1083
+ itops->add_last_branch;
1084
+ if (!ison)
1085
+ return true;
1086
+ pr_err("Unsupported --itrace options specified\n");
8931087 return false;
1088
+}
1089
+
1090
+/* Check for AUXTRACE dump directory if it is needed.
1091
+ * On failure print an error message but continue.
1092
+ * Return 0 on wrong keyword in config file and 1 otherwise.
1093
+ */
1094
+static int s390_cpumsf__config(const char *var, const char *value, void *cb)
1095
+{
1096
+ struct s390_cpumsf *sf = cb;
1097
+ struct stat stbuf;
1098
+ int rc;
1099
+
1100
+ if (strcmp(var, "auxtrace.dumpdir"))
1101
+ return 0;
1102
+ sf->logdir = strdup(value);
1103
+ if (sf->logdir == NULL) {
1104
+ pr_err("Failed to find auxtrace log directory %s,"
1105
+ " continue with current directory...\n", value);
1106
+ return 1;
1107
+ }
1108
+ rc = stat(sf->logdir, &stbuf);
1109
+ if (rc == -1 || !S_ISDIR(stbuf.st_mode)) {
1110
+ pr_err("Missing auxtrace log directory %s,"
1111
+ " continue with current directory...\n", value);
1112
+ zfree(&sf->logdir);
1113
+ }
1114
+ return 1;
8941115 }
8951116
8961117 int s390_cpumsf_process_auxtrace_info(union perf_event *event,
8971118 struct perf_session *session)
8981119 {
899
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
1120
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
9001121 struct s390_cpumsf *sf;
9011122 int err;
9021123
903
- if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event))
1124
+ if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info))
9041125 return -EINVAL;
9051126
9061127 sf = zalloc(sizeof(struct s390_cpumsf));
....@@ -911,6 +1132,9 @@
9111132 err = -EINVAL;
9121133 goto err_free;
9131134 }
1135
+ sf->use_logfile = session->itrace_synth_opts->log;
1136
+ if (sf->use_logfile)
1137
+ perf_config(s390_cpumsf__config, sf);
9141138
9151139 err = auxtrace_queues__init(&sf->queues);
9161140 if (err)
....@@ -927,6 +1151,7 @@
9271151 sf->auxtrace.flush_events = s390_cpumsf_flush;
9281152 sf->auxtrace.free_events = s390_cpumsf_free_events;
9291153 sf->auxtrace.free = s390_cpumsf_free;
1154
+ sf->auxtrace.evsel_is_auxtrace = s390_cpumsf_evsel_is_auxtrace;
9301155 session->auxtrace = &sf->auxtrace;
9311156
9321157 if (dump_trace)
....@@ -945,6 +1170,7 @@
9451170 auxtrace_queues__free(&sf->queues);
9461171 session->auxtrace = NULL;
9471172 err_free:
1173
+ zfree(&sf->logdir);
9481174 free(sf);
9491175 return err;
9501176 }