s390-sample-raw.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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. #include <unistd.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <inttypes.h>
  18. #include <sys/stat.h>
  19. #include <linux/compiler.h>
  20. #include <linux/err.h>
  21. #include <asm/byteorder.h>
  22. #include "debug.h"
  23. #include "session.h"
  24. #include "evlist.h"
  25. #include "color.h"
  26. #include "hashmap.h"
  27. #include "sample-raw.h"
  28. #include "s390-cpumcf-kernel.h"
  29. #include "util/pmu.h"
  30. #include "util/sample.h"
  31. static size_t ctrset_size(struct cf_ctrset_entry *set)
  32. {
  33. return sizeof(*set) + set->ctr * sizeof(u64);
  34. }
  35. static bool ctrset_valid(struct cf_ctrset_entry *set)
  36. {
  37. return set->def == S390_CPUMCF_DIAG_DEF;
  38. }
  39. /* CPU Measurement Counter Facility raw data is a byte stream. It is 8 byte
  40. * aligned and might have trailing padding bytes.
  41. * Display the raw data on screen.
  42. */
  43. static bool s390_cpumcfdg_testctr(struct perf_sample *sample)
  44. {
  45. size_t len = sample->raw_size, offset = 0;
  46. unsigned char *buf = sample->raw_data;
  47. struct cf_trailer_entry *te;
  48. struct cf_ctrset_entry *cep, ce;
  49. while (offset < len) {
  50. cep = (struct cf_ctrset_entry *)(buf + offset);
  51. ce.def = be16_to_cpu(cep->def);
  52. ce.set = be16_to_cpu(cep->set);
  53. ce.ctr = be16_to_cpu(cep->ctr);
  54. ce.res1 = be16_to_cpu(cep->res1);
  55. if (!ctrset_valid(&ce) || offset + ctrset_size(&ce) > len) {
  56. /* Raw data for counter sets are always multiple of 8
  57. * bytes. Prepending a 4 bytes size field to the
  58. * raw data block in the sample causes the perf tool
  59. * to append 4 padding bytes to make the raw data part
  60. * of the sample a multiple of eight bytes again.
  61. *
  62. * If the last entry (trailer) is 4 bytes off the raw
  63. * area data end, all is good.
  64. */
  65. if (len - offset - sizeof(*te) == 4)
  66. break;
  67. pr_err("Invalid counter set entry at %zd\n", offset);
  68. return false;
  69. }
  70. offset += ctrset_size(&ce);
  71. }
  72. return true;
  73. }
  74. /* Dump event bc000 on screen, already tested on correctness. */
  75. static void s390_cpumcfdg_dumptrail(const char *color, size_t offset,
  76. struct cf_trailer_entry *tep)
  77. {
  78. struct cf_trailer_entry te;
  79. te.flags = be64_to_cpu(tep->flags);
  80. te.cfvn = be16_to_cpu(tep->cfvn);
  81. te.csvn = be16_to_cpu(tep->csvn);
  82. te.cpu_speed = be32_to_cpu(tep->cpu_speed);
  83. te.timestamp = be64_to_cpu(tep->timestamp);
  84. te.progusage1 = be64_to_cpu(tep->progusage1);
  85. te.progusage2 = be64_to_cpu(tep->progusage2);
  86. te.progusage3 = be64_to_cpu(tep->progusage3);
  87. te.tod_base = be64_to_cpu(tep->tod_base);
  88. te.mach_type = be16_to_cpu(tep->mach_type);
  89. te.res1 = be16_to_cpu(tep->res1);
  90. te.res2 = be32_to_cpu(tep->res2);
  91. color_fprintf(stdout, color, " [%#08zx] Trailer:%c%c%c%c%c"
  92. " Cfvn:%d Csvn:%d Speed:%d TOD:%#lx\n",
  93. offset, te.clock_base ? 'T' : ' ',
  94. te.speed ? 'S' : ' ', te.mtda ? 'M' : ' ',
  95. te.caca ? 'C' : ' ', te.lcda ? 'L' : ' ',
  96. te.cfvn, te.csvn, te.cpu_speed, te.timestamp);
  97. color_fprintf(stdout, color, "\t\t1:%lx 2:%lx 3:%lx TOD-Base:%#lx"
  98. " Type:%x\n\n",
  99. te.progusage1, te.progusage2, te.progusage3,
  100. te.tod_base, te.mach_type);
  101. }
  102. /* Return starting number of a counter set */
  103. static int get_counterset_start(int setnr)
  104. {
  105. switch (setnr) {
  106. case CPUMF_CTR_SET_BASIC: /* Basic counter set */
  107. return 0;
  108. case CPUMF_CTR_SET_USER: /* Problem state counter set */
  109. return 32;
  110. case CPUMF_CTR_SET_CRYPTO: /* Crypto counter set */
  111. return 64;
  112. case CPUMF_CTR_SET_EXT: /* Extended counter set */
  113. return 128;
  114. case CPUMF_CTR_SET_MT_DIAG: /* Diagnostic counter set */
  115. return 448;
  116. case PERF_EVENT_PAI_NNPA_ALL: /* PAI NNPA counter set */
  117. case PERF_EVENT_PAI_CRYPTO_ALL: /* PAI CRYPTO counter set */
  118. return setnr;
  119. default:
  120. return -1;
  121. }
  122. }
  123. struct get_counter_name_data {
  124. long wanted;
  125. const char *result;
  126. };
  127. static int get_counter_name_callback(void *vdata, struct pmu_event_info *info)
  128. {
  129. struct get_counter_name_data *data = vdata;
  130. int rc, event_nr;
  131. const char *event_str;
  132. if (info->str == NULL)
  133. return 0;
  134. event_str = strstr(info->str, "event=");
  135. if (!event_str)
  136. return 0;
  137. rc = sscanf(event_str, "event=%x", &event_nr);
  138. if (rc == 1 && event_nr == data->wanted) {
  139. data->result = info->name;
  140. return 1; /* Terminate the search. */
  141. }
  142. return 0;
  143. }
  144. static size_t get_counter_name_hash_fn(long key, void *ctx __maybe_unused)
  145. {
  146. return key;
  147. }
  148. static bool get_counter_name_hashmap_equal_fn(long key1, long key2, void *ctx __maybe_unused)
  149. {
  150. return key1 == key2;
  151. }
  152. /* Scan the PMU and extract the logical name of a counter from the event. Input
  153. * is the counter set and counter number with in the set. Construct the event
  154. * number and use this as key. If they match return the name of this counter.
  155. * If no match is found a NULL pointer is returned.
  156. */
  157. static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
  158. {
  159. static struct hashmap *cache;
  160. static struct perf_pmu *cache_pmu;
  161. long cache_key = get_counterset_start(set) + nr;
  162. struct get_counter_name_data data = {
  163. .wanted = cache_key,
  164. .result = NULL,
  165. };
  166. char *result = NULL;
  167. if (!pmu)
  168. return NULL;
  169. if (cache_pmu == pmu && hashmap__find(cache, cache_key, &result))
  170. return strdup(result);
  171. perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ true,
  172. &data, get_counter_name_callback);
  173. result = strdup(data.result ?: "<unknown>");
  174. if (cache_pmu == NULL) {
  175. struct hashmap *tmp = hashmap__new(get_counter_name_hash_fn,
  176. get_counter_name_hashmap_equal_fn,
  177. /*ctx=*/NULL);
  178. if (!IS_ERR(tmp)) {
  179. cache = tmp;
  180. cache_pmu = pmu;
  181. }
  182. }
  183. if (cache_pmu == pmu && result) {
  184. char *old_value = NULL, *new_value = strdup(result);
  185. if (new_value) {
  186. hashmap__set(cache, cache_key, new_value, /*old_key=*/NULL, &old_value);
  187. /*
  188. * Free in case of a race, but resizing would be broken
  189. * in that case.
  190. */
  191. free(old_value);
  192. }
  193. }
  194. return result;
  195. }
  196. static void s390_cpumcfdg_dump(struct perf_pmu *pmu, struct perf_sample *sample)
  197. {
  198. size_t i, len = sample->raw_size, offset = 0;
  199. unsigned char *buf = sample->raw_data;
  200. const char *color = PERF_COLOR_BLUE;
  201. struct cf_ctrset_entry *cep, ce;
  202. u64 *p;
  203. while (offset < len) {
  204. cep = (struct cf_ctrset_entry *)(buf + offset);
  205. ce.def = be16_to_cpu(cep->def);
  206. ce.set = be16_to_cpu(cep->set);
  207. ce.ctr = be16_to_cpu(cep->ctr);
  208. ce.res1 = be16_to_cpu(cep->res1);
  209. if (!ctrset_valid(&ce)) { /* Print trailer */
  210. s390_cpumcfdg_dumptrail(color, offset,
  211. (struct cf_trailer_entry *)cep);
  212. return;
  213. }
  214. color_fprintf(stdout, color, " [%#08zx] Counterset:%d"
  215. " Counters:%d\n", offset, ce.set, ce.ctr);
  216. for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) {
  217. char *ev_name = get_counter_name(ce.set, i, pmu);
  218. color_fprintf(stdout, color,
  219. "\tCounter:%03zd %s Value:%#018"PRIx64"\n", i,
  220. ev_name ?: "<unknown>", be64_to_cpu(*p));
  221. free(ev_name);
  222. }
  223. offset += ctrset_size(&ce);
  224. }
  225. }
  226. #pragma GCC diagnostic push
  227. #pragma GCC diagnostic ignored "-Wpacked"
  228. #pragma GCC diagnostic ignored "-Wattributes"
  229. /*
  230. * Check for consistency of PAI_CRYPTO/PAI_NNPA raw data.
  231. */
  232. struct pai_data { /* Event number and value */
  233. u16 event_nr;
  234. u64 event_val;
  235. } __packed;
  236. #pragma GCC diagnostic pop
  237. /*
  238. * Test for valid raw data. At least one PAI event should be in the raw
  239. * data section.
  240. */
  241. static bool s390_pai_all_test(struct perf_sample *sample)
  242. {
  243. size_t len = sample->raw_size;
  244. if (len < 0xa)
  245. return false;
  246. return true;
  247. }
  248. static void s390_pai_all_dump(struct evsel *evsel, struct perf_sample *sample)
  249. {
  250. size_t len = sample->raw_size, offset = 0;
  251. unsigned char *p = sample->raw_data;
  252. const char *color = PERF_COLOR_BLUE;
  253. struct pai_data pai_data;
  254. char *ev_name;
  255. while (offset < len) {
  256. memcpy(&pai_data.event_nr, p, sizeof(pai_data.event_nr));
  257. pai_data.event_nr = be16_to_cpu(pai_data.event_nr);
  258. p += sizeof(pai_data.event_nr);
  259. offset += sizeof(pai_data.event_nr);
  260. memcpy(&pai_data.event_val, p, sizeof(pai_data.event_val));
  261. pai_data.event_val = be64_to_cpu(pai_data.event_val);
  262. p += sizeof(pai_data.event_val);
  263. offset += sizeof(pai_data.event_val);
  264. ev_name = get_counter_name(evsel->core.attr.config,
  265. pai_data.event_nr, evsel->pmu);
  266. color_fprintf(stdout, color, "\tCounter:%03d %s Value:%#018"PRIx64"\n",
  267. pai_data.event_nr, ev_name ?: "<unknown>",
  268. pai_data.event_val);
  269. free(ev_name);
  270. if (offset + 0xa > len)
  271. break;
  272. }
  273. color_fprintf(stdout, color, "\n");
  274. }
  275. /* S390 specific trace event function. Check for PERF_RECORD_SAMPLE events
  276. * and if the event was triggered by a
  277. * - counter set diagnostic event
  278. * - processor activity assist (PAI) crypto counter event
  279. * - processor activity assist (PAI) neural network processor assist (NNPA)
  280. * counter event
  281. * display its raw data.
  282. * The function is only invoked when the dump flag -D is set.
  283. *
  284. * Function evlist__s390_sample_raw() is defined as call back after it has
  285. * been verified that the perf.data file was created on s390 platform.
  286. */
  287. void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event,
  288. struct perf_sample *sample)
  289. {
  290. const char *pai_name;
  291. struct evsel *evsel;
  292. if (event->header.type != PERF_RECORD_SAMPLE)
  293. return;
  294. evsel = evlist__event2evsel(evlist, event);
  295. if (!evsel)
  296. return;
  297. /* Check for raw data in sample */
  298. if (!sample->raw_size || !sample->raw_data)
  299. return;
  300. /* Display raw data on screen */
  301. if (evsel->core.attr.config == PERF_EVENT_CPUM_CF_DIAG) {
  302. if (!evsel->pmu)
  303. evsel->pmu = perf_pmus__find("cpum_cf");
  304. if (!s390_cpumcfdg_testctr(sample))
  305. pr_err("Invalid counter set data encountered\n");
  306. else
  307. s390_cpumcfdg_dump(evsel->pmu, sample);
  308. return;
  309. }
  310. switch (evsel->core.attr.config) {
  311. case PERF_EVENT_PAI_NNPA_ALL:
  312. pai_name = "NNPA_ALL";
  313. break;
  314. case PERF_EVENT_PAI_CRYPTO_ALL:
  315. pai_name = "CRYPTO_ALL";
  316. break;
  317. default:
  318. return;
  319. }
  320. if (!s390_pai_all_test(sample)) {
  321. pr_err("Invalid %s raw data encountered\n", pai_name);
  322. } else {
  323. if (!evsel->pmu)
  324. evsel->pmu = perf_pmus__find_by_type(evsel->core.attr.type);
  325. s390_pai_all_dump(evsel, sample);
  326. }
  327. }