1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2014, Michael Ellerman, IBM Corp. |
4 | */ |
5 | |
6 | #include <errno.h> |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <sys/mman.h> |
11 | |
12 | #include "trace.h" |
13 | |
14 | |
15 | struct trace_buffer *trace_buffer_allocate(u64 size) |
16 | { |
17 | struct trace_buffer *tb; |
18 | |
19 | if (size < sizeof(*tb)) { |
20 | fprintf(stderr, "Error: trace buffer too small\n" ); |
21 | return NULL; |
22 | } |
23 | |
24 | tb = mmap(NULL, size, PROT_READ | PROT_WRITE, |
25 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
26 | if (tb == MAP_FAILED) { |
27 | perror("mmap" ); |
28 | return NULL; |
29 | } |
30 | |
31 | tb->size = size; |
32 | tb->tail = tb->data; |
33 | tb->overflow = false; |
34 | |
35 | return tb; |
36 | } |
37 | |
38 | static bool trace_check_bounds(struct trace_buffer *tb, void *p) |
39 | { |
40 | return p < ((void *)tb + tb->size); |
41 | } |
42 | |
43 | static bool trace_check_alloc(struct trace_buffer *tb, void *p) |
44 | { |
45 | /* |
46 | * If we ever overflowed don't allow any more input. This prevents us |
47 | * from dropping a large item and then later logging a small one. The |
48 | * buffer should just stop when overflow happened, not be patchy. If |
49 | * you're overflowing, make your buffer bigger. |
50 | */ |
51 | if (tb->overflow) |
52 | return false; |
53 | |
54 | if (!trace_check_bounds(tb, p)) { |
55 | tb->overflow = true; |
56 | return false; |
57 | } |
58 | |
59 | return true; |
60 | } |
61 | |
62 | static void *trace_alloc(struct trace_buffer *tb, int bytes) |
63 | { |
64 | void *p, *newtail; |
65 | |
66 | p = tb->tail; |
67 | newtail = tb->tail + bytes; |
68 | if (!trace_check_alloc(tb, newtail)) |
69 | return NULL; |
70 | |
71 | tb->tail = newtail; |
72 | |
73 | return p; |
74 | } |
75 | |
76 | static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size) |
77 | { |
78 | struct trace_entry *e; |
79 | |
80 | e = trace_alloc(tb, bytes: sizeof(*e) + payload_size); |
81 | if (e) |
82 | e->length = payload_size; |
83 | |
84 | return e; |
85 | } |
86 | |
87 | int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value) |
88 | { |
89 | struct trace_entry *e; |
90 | u64 *p; |
91 | |
92 | e = trace_alloc_entry(tb, payload_size: sizeof(reg) + sizeof(value)); |
93 | if (!e) |
94 | return -ENOSPC; |
95 | |
96 | e->type = TRACE_TYPE_REG; |
97 | p = (u64 *)e->data; |
98 | *p++ = reg; |
99 | *p++ = value; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | int trace_log_counter(struct trace_buffer *tb, u64 value) |
105 | { |
106 | struct trace_entry *e; |
107 | u64 *p; |
108 | |
109 | e = trace_alloc_entry(tb, payload_size: sizeof(value)); |
110 | if (!e) |
111 | return -ENOSPC; |
112 | |
113 | e->type = TRACE_TYPE_COUNTER; |
114 | p = (u64 *)e->data; |
115 | *p++ = value; |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | int trace_log_string(struct trace_buffer *tb, char *str) |
121 | { |
122 | struct trace_entry *e; |
123 | char *p; |
124 | int len; |
125 | |
126 | len = strlen(str); |
127 | |
128 | /* We NULL terminate to make printing easier */ |
129 | e = trace_alloc_entry(tb, payload_size: len + 1); |
130 | if (!e) |
131 | return -ENOSPC; |
132 | |
133 | e->type = TRACE_TYPE_STRING; |
134 | p = (char *)e->data; |
135 | memcpy(p, str, len); |
136 | p += len; |
137 | *p = '\0'; |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | int trace_log_indent(struct trace_buffer *tb) |
143 | { |
144 | struct trace_entry *e; |
145 | |
146 | e = trace_alloc_entry(tb, payload_size: 0); |
147 | if (!e) |
148 | return -ENOSPC; |
149 | |
150 | e->type = TRACE_TYPE_INDENT; |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | int trace_log_outdent(struct trace_buffer *tb) |
156 | { |
157 | struct trace_entry *e; |
158 | |
159 | e = trace_alloc_entry(tb, payload_size: 0); |
160 | if (!e) |
161 | return -ENOSPC; |
162 | |
163 | e->type = TRACE_TYPE_OUTDENT; |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static void (int seq, int prefix) |
169 | { |
170 | printf("%*s[%d]: " , prefix, "" , seq); |
171 | } |
172 | |
173 | static char *trace_decode_reg(int reg) |
174 | { |
175 | switch (reg) { |
176 | case 769: return "SPRN_MMCR2" ; break; |
177 | case 770: return "SPRN_MMCRA" ; break; |
178 | case 779: return "SPRN_MMCR0" ; break; |
179 | case 804: return "SPRN_EBBHR" ; break; |
180 | case 805: return "SPRN_EBBRR" ; break; |
181 | case 806: return "SPRN_BESCR" ; break; |
182 | case 800: return "SPRN_BESCRS" ; break; |
183 | case 801: return "SPRN_BESCRSU" ; break; |
184 | case 802: return "SPRN_BESCRR" ; break; |
185 | case 803: return "SPRN_BESCRRU" ; break; |
186 | case 771: return "SPRN_PMC1" ; break; |
187 | case 772: return "SPRN_PMC2" ; break; |
188 | case 773: return "SPRN_PMC3" ; break; |
189 | case 774: return "SPRN_PMC4" ; break; |
190 | case 775: return "SPRN_PMC5" ; break; |
191 | case 776: return "SPRN_PMC6" ; break; |
192 | case 780: return "SPRN_SIAR" ; break; |
193 | case 781: return "SPRN_SDAR" ; break; |
194 | case 768: return "SPRN_SIER" ; break; |
195 | } |
196 | |
197 | return NULL; |
198 | } |
199 | |
200 | static void trace_print_reg(struct trace_entry *e) |
201 | { |
202 | u64 *p, *reg, *value; |
203 | char *name; |
204 | |
205 | p = (u64 *)e->data; |
206 | reg = p++; |
207 | value = p; |
208 | |
209 | name = trace_decode_reg(*reg); |
210 | if (name) |
211 | printf("register %-10s = 0x%016llx\n" , name, *value); |
212 | else |
213 | printf("register %lld = 0x%016llx\n" , *reg, *value); |
214 | } |
215 | |
216 | static void trace_print_counter(struct trace_entry *e) |
217 | { |
218 | u64 *value; |
219 | |
220 | value = (u64 *)e->data; |
221 | printf("counter = %lld\n" , *value); |
222 | } |
223 | |
224 | static void trace_print_string(struct trace_entry *e) |
225 | { |
226 | char *str; |
227 | |
228 | str = (char *)e->data; |
229 | puts(str); |
230 | } |
231 | |
232 | #define BASE_PREFIX 2 |
233 | #define PREFIX_DELTA 8 |
234 | |
235 | static void trace_print_entry(struct trace_entry *e, int seq, int *prefix) |
236 | { |
237 | switch (e->type) { |
238 | case TRACE_TYPE_REG: |
239 | trace_print_header(seq, prefix: *prefix); |
240 | trace_print_reg(e); |
241 | break; |
242 | case TRACE_TYPE_COUNTER: |
243 | trace_print_header(seq, prefix: *prefix); |
244 | trace_print_counter(e); |
245 | break; |
246 | case TRACE_TYPE_STRING: |
247 | trace_print_header(seq, prefix: *prefix); |
248 | trace_print_string(e); |
249 | break; |
250 | case TRACE_TYPE_INDENT: |
251 | trace_print_header(seq, prefix: *prefix); |
252 | puts("{" ); |
253 | *prefix += PREFIX_DELTA; |
254 | break; |
255 | case TRACE_TYPE_OUTDENT: |
256 | *prefix -= PREFIX_DELTA; |
257 | if (*prefix < BASE_PREFIX) |
258 | *prefix = BASE_PREFIX; |
259 | trace_print_header(seq, prefix: *prefix); |
260 | puts("}" ); |
261 | break; |
262 | default: |
263 | trace_print_header(seq, prefix: *prefix); |
264 | printf("entry @ %p type %d\n" , e, e->type); |
265 | break; |
266 | } |
267 | } |
268 | |
269 | void trace_buffer_print(struct trace_buffer *tb) |
270 | { |
271 | struct trace_entry *e; |
272 | int i, prefix; |
273 | void *p; |
274 | |
275 | printf("Trace buffer dump:\n" ); |
276 | printf(" address %p \n" , tb); |
277 | printf(" tail %p\n" , tb->tail); |
278 | printf(" size %llu\n" , tb->size); |
279 | printf(" overflow %s\n" , tb->overflow ? "TRUE" : "false" ); |
280 | printf(" Content:\n" ); |
281 | |
282 | p = tb->data; |
283 | |
284 | i = 0; |
285 | prefix = BASE_PREFIX; |
286 | |
287 | while (trace_check_bounds(tb, p) && p < tb->tail) { |
288 | e = p; |
289 | |
290 | trace_print_entry(e, seq: i, prefix: &prefix); |
291 | |
292 | i++; |
293 | p = (void *)e + sizeof(*e) + e->length; |
294 | } |
295 | } |
296 | |
297 | void trace_print_location(struct trace_buffer *tb) |
298 | { |
299 | printf("Trace buffer 0x%llx bytes @ %p\n" , tb->size, tb); |
300 | } |
301 | |