1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * intel_pt_log.c: Intel Processor Trace support
4  * Copyright (c) 2013-2014, Intel Corporation.
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdint.h>
10 #include <inttypes.h>
11 #include <stdarg.h>
12 #include <stdbool.h>
13 #include <string.h>
14 
15 #include <linux/zalloc.h>
16 #include <linux/kernel.h>
17 
18 #include "intel-pt-log.h"
19 #include "intel-pt-insn-decoder.h"
20 
21 #include "intel-pt-pkt-decoder.h"
22 
23 #define MAX_LOG_NAME 256
24 
25 #define DFLT_BUF_SZ	(16 * 1024)
26 
27 struct log_buf {
28 	char			*buf;
29 	size_t			buf_sz;
30 	size_t			head;
31 	bool			wrapped;
32 	FILE			*backend;
33 };
34 
35 static FILE *f;
36 static char log_name[MAX_LOG_NAME];
37 bool intel_pt_enable_logging;
38 static bool intel_pt_dump_log_on_error;
39 static unsigned int intel_pt_log_on_error_size;
40 static struct log_buf log_buf;
41 
42 void *intel_pt_log_fp(void)
43 {
44 	return f;
45 }
46 
47 void intel_pt_log_enable(bool dump_log_on_error, unsigned int log_on_error_size)
48 {
49 	intel_pt_enable_logging = true;
50 	intel_pt_dump_log_on_error = dump_log_on_error;
51 	intel_pt_log_on_error_size = log_on_error_size;
52 }
53 
54 void intel_pt_log_disable(void)
55 {
56 	if (f)
57 		fflush(f);
58 	intel_pt_enable_logging = false;
59 }
60 
61 void intel_pt_log_set_name(const char *name)
62 {
63 	strncpy(log_name, name, MAX_LOG_NAME - 5);
64 	strcat(log_name, ".log");
65 }
66 
67 static void intel_pt_print_data(const unsigned char *buf, int len, uint64_t pos,
68 				int indent)
69 {
70 	int i;
71 
72 	for (i = 0; i < indent; i++)
73 		fprintf(f, " ");
74 
75 	fprintf(f, "  %08" PRIx64 ": ", pos);
76 	for (i = 0; i < len; i++)
77 		fprintf(f, " %02x", buf[i]);
78 	for (; i < 16; i++)
79 		fprintf(f, "   ");
80 	fprintf(f, " ");
81 }
82 
83 static void intel_pt_print_no_data(uint64_t pos, int indent)
84 {
85 	int i;
86 
87 	for (i = 0; i < indent; i++)
88 		fprintf(f, " ");
89 
90 	fprintf(f, "  %08" PRIx64 ": ", pos);
91 	for (i = 0; i < 16; i++)
92 		fprintf(f, "   ");
93 	fprintf(f, " ");
94 }
95 
96 static ssize_t log_buf__write(void *cookie, const char *buf, size_t size)
97 {
98 	struct log_buf *b = cookie;
99 	size_t sz = size;
100 
101 	if (!b->buf)
102 		return size;
103 
104 	while (sz) {
105 		size_t space = b->buf_sz - b->head;
106 		size_t n = min(space, sz);
107 
108 		memcpy(b->buf + b->head, buf, n);
109 		sz -= n;
110 		buf += n;
111 		b->head += n;
112 		if (sz && b->head >= b->buf_sz) {
113 			b->head = 0;
114 			b->wrapped = true;
115 		}
116 	}
117 	return size;
118 }
119 
120 static int log_buf__close(void *cookie)
121 {
122 	struct log_buf *b = cookie;
123 
124 	zfree(&b->buf);
125 	return 0;
126 }
127 
128 static FILE *log_buf__open(struct log_buf *b, FILE *backend, unsigned int sz)
129 {
130 	cookie_io_functions_t fns = {
131 		.write = log_buf__write,
132 		.close = log_buf__close,
133 	};
134 	FILE *file;
135 
136 	memset(b, 0, sizeof(*b));
137 	b->buf_sz = sz;
138 	b->buf = malloc(b->buf_sz);
139 	b->backend = backend;
140 	file = fopencookie(b, "a", fns);
141 	if (!file)
142 		zfree(&b->buf);
143 	return file;
144 }
145 
146 static bool remove_first_line(const char **p, size_t *n)
147 {
148 	for (; *n && **p != '\n'; ++*p, --*n)
149 		;
150 	if (*n) {
151 		*p += 1;
152 		*n -= 1;
153 		return true;
154 	}
155 	return false;
156 }
157 
158 static void write_lines(const char *p, size_t n, FILE *fp, bool *remove_first)
159 {
160 	if (*remove_first)
161 		*remove_first = !remove_first_line(&p, &n);
162 	fwrite(p, n, 1, fp);
163 }
164 
165 static void log_buf__dump(struct log_buf *b)
166 {
167 	bool remove_first = false;
168 
169 	if (!b->buf)
170 		return;
171 
172 	fflush(f); /* Could update b->head and b->wrapped */
173 	fprintf(b->backend, "Dumping debug log buffer\n");
174 	if (b->wrapped) {
175 		remove_first = true;
176 		write_lines(b->buf + b->head, b->buf_sz - b->head, b->backend, &remove_first);
177 	}
178 	write_lines(b->buf, b->head, b->backend, &remove_first);
179 	fprintf(b->backend, "End of debug log buffer dump\n");
180 
181 	b->head = 0;
182 	b->wrapped = false;
183 }
184 
185 void intel_pt_log_dump_buf(void)
186 {
187 	log_buf__dump(&log_buf);
188 }
189 
190 static int intel_pt_log_open(void)
191 {
192 	if (!intel_pt_enable_logging)
193 		return -1;
194 
195 	if (f)
196 		return 0;
197 
198 	if (log_name[0])
199 		f = fopen(log_name, "w+");
200 	else
201 		f = stdout;
202 	if (f && intel_pt_dump_log_on_error)
203 		f = log_buf__open(&log_buf, f, intel_pt_log_on_error_size);
204 	if (!f) {
205 		intel_pt_enable_logging = false;
206 		return -1;
207 	}
208 
209 	return 0;
210 }
211 
212 void __intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len,
213 			   uint64_t pos, const unsigned char *buf)
214 {
215 	char desc[INTEL_PT_PKT_DESC_MAX];
216 
217 	if (intel_pt_log_open())
218 		return;
219 
220 	intel_pt_print_data(buf, pkt_len, pos, 0);
221 	intel_pt_pkt_desc(packet, desc, INTEL_PT_PKT_DESC_MAX);
222 	fprintf(f, "%s\n", desc);
223 }
224 
225 void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip)
226 {
227 	char desc[INTEL_PT_INSN_DESC_MAX];
228 	size_t len = intel_pt_insn->length;
229 
230 	if (intel_pt_log_open())
231 		return;
232 
233 	if (len > INTEL_PT_INSN_BUF_SZ)
234 		len = INTEL_PT_INSN_BUF_SZ;
235 	intel_pt_print_data(intel_pt_insn->buf, len, ip, 8);
236 	if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0)
237 		fprintf(f, "%s\n", desc);
238 	else
239 		fprintf(f, "Bad instruction!\n");
240 }
241 
242 void __intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn,
243 				 uint64_t ip)
244 {
245 	char desc[INTEL_PT_INSN_DESC_MAX];
246 
247 	if (intel_pt_log_open())
248 		return;
249 
250 	intel_pt_print_no_data(ip, 8);
251 	if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0)
252 		fprintf(f, "%s\n", desc);
253 	else
254 		fprintf(f, "Bad instruction!\n");
255 }
256 
257 void __intel_pt_log(const char *fmt, ...)
258 {
259 	va_list args;
260 
261 	if (intel_pt_log_open())
262 		return;
263 
264 	va_start(args, fmt);
265 	vfprintf(f, fmt, args);
266 	va_end(args);
267 }
268