Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf intel-pt: Support itrace option flag d+e to log on error

Pass d+e option and log size via intel_pt_log_enable(). Allocate a buffer
for log messages and provide intel_pt_log_dump_buf() to dump and reset the
buffer upon decoder errors.

Example:

$ sudo perf record -e intel_pt// sleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.094 MB perf.data ]
$ sudo perf config itrace.debug-log-buffer-size=300
$ sudo perf script --itrace=ed+e+o | head -20
Dumping debug log buffer (first line may be sliced)
Other
ffffffff96ca22f6: 48 89 e5 Other
ffffffff96ca22f9: 65 48 8b 05 ff e0 38 69 Other
ffffffff96ca2301: 48 3d c0 a5 c1 98 Other
ffffffff96ca2307: 74 08 Jcc +8
ffffffff96ca2311: 5d Other
ffffffff96ca2312: c3 Ret
ERROR: Bad RET compression (TNT=N) at 0xffffffff96ca2312
End of debug log buffer dump
instruction trace error type 1 time 15913.537143482 cpu 5 pid 36292 tid 36292 ip 0xffffffff96ca2312 code 6: Trace doesn't match instruction
Dumping debug log buffer (first line may be sliced)
Other
ffffffff96ce7fe9: f6 47 2e 20 Other
ffffffff96ce7fed: 74 11 Jcc +17
ffffffff96ce7fef: 48 8b 87 28 0a 00 00 Other
ffffffff96ce7ff6: 5d Other
ffffffff96ce7ff7: 48 8b 40 18 Other
ffffffff96ce7ffb: c3 Ret
ERROR: Bad RET compression (TNT=N) at 0xffffffff96ce7ffb
Warning:
8 instruction trace errors

Reviewed-by: Andi Kleen <ak@linux.intel.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20220905073424.3971-6-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Adrian Hunter and committed by
Arnaldo Carvalho de Melo
65aee81a 50d7620b

+117 -5
+4 -1
tools/perf/Documentation/perf-intel-pt.txt
··· 989 989 990 990 -a Suppress logging of perf events 991 991 +a Log all perf events 992 + +e Output only on decoding errors (size configurable) 992 993 +o Output to stdout instead of "intel_pt.log" 993 994 994 995 By default, logged perf events are filtered by any specified time ranges, but 995 - flag +a overrides that. 996 + flag +a overrides that. The +e flag can be useful for analyzing errors. By 997 + default, the log size in that case is 16384 bytes, but can be altered by 998 + linkperf:perf-config[1] e.g. perf config itrace.debug-log-buffer-size=30000 996 999 997 1000 In addition, the period of the "instructions" event can be specified. e.g. 998 1001
+93 -1
tools/perf/util/intel-pt-decoder/intel-pt-log.c
··· 5 5 */ 6 6 7 7 #include <stdio.h> 8 + #include <stdlib.h> 8 9 #include <stdint.h> 9 10 #include <inttypes.h> 10 11 #include <stdarg.h> 11 12 #include <stdbool.h> 12 13 #include <string.h> 14 + 15 + #include <linux/zalloc.h> 16 + #include <linux/kernel.h> 13 17 14 18 #include "intel-pt-log.h" 15 19 #include "intel-pt-insn-decoder.h" ··· 22 18 23 19 #define MAX_LOG_NAME 256 24 20 21 + #define DFLT_BUF_SZ (16 * 1024) 22 + 23 + struct log_buf { 24 + char *buf; 25 + size_t buf_sz; 26 + size_t head; 27 + bool wrapped; 28 + FILE *backend; 29 + }; 30 + 25 31 static FILE *f; 26 32 static char log_name[MAX_LOG_NAME]; 27 33 bool intel_pt_enable_logging; 34 + static bool intel_pt_dump_log_on_error; 35 + static unsigned int intel_pt_log_on_error_size; 36 + static struct log_buf log_buf; 28 37 29 38 void *intel_pt_log_fp(void) 30 39 { 31 40 return f; 32 41 } 33 42 34 - void intel_pt_log_enable(void) 43 + void intel_pt_log_enable(bool dump_log_on_error, unsigned int log_on_error_size) 35 44 { 36 45 intel_pt_enable_logging = true; 46 + intel_pt_dump_log_on_error = dump_log_on_error; 47 + intel_pt_log_on_error_size = log_on_error_size; 37 48 } 38 49 39 50 void intel_pt_log_disable(void) ··· 93 74 fprintf(f, " "); 94 75 } 95 76 77 + static ssize_t log_buf__write(void *cookie, const char *buf, size_t size) 78 + { 79 + struct log_buf *b = cookie; 80 + size_t sz = size; 81 + 82 + if (!b->buf) 83 + return size; 84 + 85 + while (sz) { 86 + size_t space = b->buf_sz - b->head; 87 + size_t n = min(space, sz); 88 + 89 + memcpy(b->buf + b->head, buf, n); 90 + sz -= n; 91 + buf += n; 92 + b->head += n; 93 + if (sz && b->head >= b->buf_sz) { 94 + b->head = 0; 95 + b->wrapped = true; 96 + } 97 + } 98 + return size; 99 + } 100 + 101 + static int log_buf__close(void *cookie) 102 + { 103 + struct log_buf *b = cookie; 104 + 105 + zfree(&b->buf); 106 + return 0; 107 + } 108 + 109 + static FILE *log_buf__open(struct log_buf *b, FILE *backend, unsigned int sz) 110 + { 111 + cookie_io_functions_t fns = { 112 + .write = log_buf__write, 113 + .close = log_buf__close, 114 + }; 115 + FILE *file; 116 + 117 + memset(b, 0, sizeof(*b)); 118 + b->buf_sz = sz; 119 + b->buf = malloc(b->buf_sz); 120 + b->backend = backend; 121 + file = fopencookie(b, "a", fns); 122 + if (!file) 123 + zfree(&b->buf); 124 + return file; 125 + } 126 + 127 + static void log_buf__dump(struct log_buf *b) 128 + { 129 + if (!b->buf) 130 + return; 131 + 132 + fflush(f); 133 + fprintf(b->backend, "Dumping debug log buffer (first line may be sliced)\n"); 134 + if (b->wrapped) 135 + fwrite(b->buf + b->head, b->buf_sz - b->head, 1, b->backend); 136 + fwrite(b->buf, b->head, 1, b->backend); 137 + fprintf(b->backend, "End of debug log buffer dump\n"); 138 + 139 + b->head = 0; 140 + b->wrapped = false; 141 + } 142 + 143 + void intel_pt_log_dump_buf(void) 144 + { 145 + log_buf__dump(&log_buf); 146 + } 147 + 96 148 static int intel_pt_log_open(void) 97 149 { 98 150 if (!intel_pt_enable_logging) ··· 176 86 f = fopen(log_name, "w+"); 177 87 else 178 88 f = stdout; 89 + if (f && intel_pt_dump_log_on_error) 90 + f = log_buf__open(&log_buf, f, intel_pt_log_on_error_size); 179 91 if (!f) { 180 92 intel_pt_enable_logging = false; 181 93 return -1;
+2 -1
tools/perf/util/intel-pt-decoder/intel-pt-log.h
··· 14 14 struct intel_pt_pkt; 15 15 16 16 void *intel_pt_log_fp(void); 17 - void intel_pt_log_enable(void); 17 + void intel_pt_log_enable(bool dump_log_on_error, unsigned int log_on_error_size); 18 18 void intel_pt_log_disable(void); 19 19 void intel_pt_log_set_name(const char *name); 20 + void intel_pt_log_dump_buf(void); 20 21 21 22 void __intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len, 22 23 uint64_t pos, const unsigned char *buf);
+18 -2
tools/perf/util/intel-pt.c
··· 2419 2419 pid_t pid, pid_t tid, u64 ip, u64 timestamp, 2420 2420 pid_t machine_pid, int vcpu) 2421 2421 { 2422 + bool dump_log_on_error = pt->synth_opts.log_plus_flags & AUXTRACE_LOG_FLG_ON_ERROR; 2423 + bool log_on_stdout = pt->synth_opts.log_plus_flags & AUXTRACE_LOG_FLG_USE_STDOUT; 2422 2424 union perf_event event; 2423 2425 char msg[MAX_AUXTRACE_ERROR_MSG]; 2424 2426 int err; ··· 2439 2437 auxtrace_synth_guest_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE, 2440 2438 code, cpu, pid, tid, ip, msg, timestamp, 2441 2439 machine_pid, vcpu); 2440 + 2441 + if (intel_pt_enable_logging && !log_on_stdout) { 2442 + FILE *fp = intel_pt_log_fp(); 2443 + 2444 + if (fp) 2445 + perf_event__fprintf_auxtrace_error(&event, fp); 2446 + } 2447 + 2448 + if (code != INTEL_PT_ERR_LOST && dump_log_on_error) 2449 + intel_pt_log_dump_buf(); 2442 2450 2443 2451 err = perf_session__deliver_synth_event(pt->session, &event, NULL); 2444 2452 if (err) ··· 4284 4272 goto err_delete_thread; 4285 4273 } 4286 4274 4287 - if (pt->synth_opts.log) 4288 - intel_pt_log_enable(); 4275 + if (pt->synth_opts.log) { 4276 + bool log_on_error = pt->synth_opts.log_plus_flags & AUXTRACE_LOG_FLG_ON_ERROR; 4277 + unsigned int log_on_error_size = pt->synth_opts.log_on_error_size; 4278 + 4279 + intel_pt_log_enable(log_on_error, log_on_error_size); 4280 + } 4289 4281 4290 4282 /* Maximum non-turbo ratio is TSC freq / 100 MHz */ 4291 4283 if (pt->tc.time_mult) {