debug.c revision 1.72 1 1.72 rillig /* $NetBSD: debug.c,v 1.72 2025/01/03 23:37:18 rillig Exp $ */
2 1.1 rillig
3 1.1 rillig /*-
4 1.1 rillig * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 1.1 rillig * All rights reserved.
6 1.1 rillig *
7 1.1 rillig * This code is derived from software contributed to The NetBSD Foundation
8 1.1 rillig * by Roland Illig <rillig (at) NetBSD.org>.
9 1.1 rillig *
10 1.1 rillig * Redistribution and use in source and binary forms, with or without
11 1.1 rillig * modification, are permitted provided that the following conditions
12 1.1 rillig * are met:
13 1.1 rillig * 1. Redistributions of source code must retain the above copyright
14 1.1 rillig * notice, this list of conditions and the following disclaimer.
15 1.1 rillig * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 rillig * notice, this list of conditions and the following disclaimer in the
17 1.1 rillig * documentation and/or other materials provided with the distribution.
18 1.1 rillig *
19 1.1 rillig * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 rillig * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 rillig * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 rillig * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 rillig * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 rillig * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 rillig * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 rillig * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 rillig * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 rillig * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 rillig * POSSIBILITY OF SUCH DAMAGE.
30 1.1 rillig */
31 1.1 rillig
32 1.1 rillig #include <sys/cdefs.h>
33 1.72 rillig __RCSID("$NetBSD: debug.c,v 1.72 2025/01/03 23:37:18 rillig Exp $");
34 1.6 rillig
35 1.6 rillig #include <stdarg.h>
36 1.47 rillig #include <string.h>
37 1.1 rillig
38 1.1 rillig #include "indent.h"
39 1.1 rillig
40 1.1 rillig #ifdef debug
41 1.7 rillig
42 1.47 rillig static struct {
43 1.58 rillig // false show only the changes to the parser state
44 1.58 rillig // true show unchanged parts of the parser state as well
45 1.47 rillig bool full_parser_state;
46 1.47 rillig } config = {
47 1.47 rillig .full_parser_state = false,
48 1.47 rillig };
49 1.7 rillig
50 1.1 rillig const char *const lsym_name[] = {
51 1.13 rillig "eof",
52 1.13 rillig "preprocessing",
53 1.13 rillig "newline",
54 1.13 rillig "comment",
55 1.26 rillig "lparen",
56 1.43 rillig "rparen",
57 1.26 rillig "lbracket",
58 1.26 rillig "rbracket",
59 1.13 rillig "lbrace",
60 1.13 rillig "rbrace",
61 1.13 rillig "period",
62 1.13 rillig "unary_op",
63 1.43 rillig "sizeof",
64 1.43 rillig "offsetof",
65 1.43 rillig "postfix_op",
66 1.13 rillig "binary_op",
67 1.13 rillig "question",
68 1.45 rillig "question_colon",
69 1.13 rillig "comma",
70 1.13 rillig "typedef",
71 1.29 rillig "modifier",
72 1.43 rillig "tag",
73 1.66 rillig "type",
74 1.43 rillig "word",
75 1.43 rillig "funcname",
76 1.45 rillig "label_colon",
77 1.45 rillig "other_colon",
78 1.43 rillig "semicolon",
79 1.30 rillig "case",
80 1.30 rillig "default",
81 1.13 rillig "do",
82 1.13 rillig "else",
83 1.13 rillig "for",
84 1.13 rillig "if",
85 1.13 rillig "switch",
86 1.13 rillig "while",
87 1.13 rillig "return",
88 1.1 rillig };
89 1.1 rillig
90 1.1 rillig const char *const psym_name[] = {
91 1.33 rillig "-",
92 1.36 rillig "{block",
93 1.36 rillig "{struct",
94 1.36 rillig "{union",
95 1.36 rillig "{enum",
96 1.36 rillig "}",
97 1.13 rillig "decl",
98 1.13 rillig "stmt",
99 1.13 rillig "for_exprs",
100 1.13 rillig "if_expr",
101 1.13 rillig "if_expr_stmt",
102 1.13 rillig "if_expr_stmt_else",
103 1.13 rillig "else",
104 1.13 rillig "switch_expr",
105 1.13 rillig "do",
106 1.13 rillig "do_stmt",
107 1.13 rillig "while_expr",
108 1.1 rillig };
109 1.1 rillig
110 1.72 rillig static const char *const newline_name[] = {
111 1.72 rillig "no",
112 1.72 rillig "no-if",
113 1.72 rillig "no-lbrace",
114 1.72 rillig "no-semicolon",
115 1.72 rillig "yes",
116 1.72 rillig };
117 1.72 rillig
118 1.9 rillig static const char *const declaration_name[] = {
119 1.13 rillig "no",
120 1.13 rillig "begin",
121 1.13 rillig "end",
122 1.3 rillig };
123 1.3 rillig
124 1.69 rillig static const char *const badp_name[] = {
125 1.69 rillig "none",
126 1.69 rillig "seen{",
127 1.69 rillig "decl",
128 1.69 rillig "seen_decl",
129 1.69 rillig "yes",
130 1.69 rillig };
131 1.69 rillig
132 1.9 rillig const char *const paren_level_cast_name[] = {
133 1.13 rillig "(unknown cast)",
134 1.13 rillig "(maybe cast)",
135 1.13 rillig "(no cast)",
136 1.9 rillig };
137 1.9 rillig
138 1.19 rillig const char *const line_kind_name[] = {
139 1.16 rillig "other",
140 1.20 rillig "blank",
141 1.16 rillig "#if",
142 1.16 rillig "#endif",
143 1.64 rillig "#other",
144 1.20 rillig "stmt head",
145 1.17 rillig "}",
146 1.18 rillig "block comment",
147 1.27 rillig "case/default",
148 1.16 rillig };
149 1.16 rillig
150 1.35 rillig static const char *const extra_expr_indent_name[] = {
151 1.35 rillig "no",
152 1.41 rillig "maybe",
153 1.35 rillig "last",
154 1.35 rillig };
155 1.35 rillig
156 1.47 rillig static struct {
157 1.47 rillig struct parser_state prev_ps;
158 1.47 rillig bool ps_first;
159 1.47 rillig const char *heading;
160 1.48 rillig unsigned wrote_newlines;
161 1.47 rillig } state = {
162 1.47 rillig .ps_first = true,
163 1.47 rillig .wrote_newlines = 1,
164 1.47 rillig };
165 1.37 rillig
166 1.6 rillig void
167 1.6 rillig debug_printf(const char *fmt, ...)
168 1.6 rillig {
169 1.13 rillig FILE *f = output == stdout ? stderr : stdout;
170 1.13 rillig va_list ap;
171 1.6 rillig
172 1.47 rillig if (state.heading != NULL) {
173 1.71 rillig (void)fprintf(f, "%s\n", state.heading);
174 1.47 rillig state.heading = NULL;
175 1.47 rillig }
176 1.13 rillig va_start(ap, fmt);
177 1.71 rillig (void)vfprintf(f, fmt, ap);
178 1.13 rillig va_end(ap);
179 1.47 rillig state.wrote_newlines = 0;
180 1.6 rillig }
181 1.6 rillig
182 1.6 rillig void
183 1.6 rillig debug_println(const char *fmt, ...)
184 1.6 rillig {
185 1.13 rillig FILE *f = output == stdout ? stderr : stdout;
186 1.13 rillig va_list ap;
187 1.6 rillig
188 1.47 rillig if (state.heading != NULL) {
189 1.71 rillig (void)fprintf(f, "%s\n", state.heading);
190 1.47 rillig state.heading = NULL;
191 1.47 rillig state.wrote_newlines = 1;
192 1.47 rillig }
193 1.13 rillig va_start(ap, fmt);
194 1.71 rillig (void)vfprintf(f, fmt, ap);
195 1.13 rillig va_end(ap);
196 1.71 rillig (void)fprintf(f, "\n");
197 1.47 rillig state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1;
198 1.22 rillig }
199 1.22 rillig
200 1.22 rillig void
201 1.22 rillig debug_blank_line(void)
202 1.22 rillig {
203 1.47 rillig while (state.wrote_newlines < 2)
204 1.22 rillig debug_println("");
205 1.6 rillig }
206 1.6 rillig
207 1.6 rillig void
208 1.58 rillig debug_vis_range(const char *s, size_t len)
209 1.6 rillig {
210 1.58 rillig debug_printf("\"");
211 1.13 rillig for (size_t i = 0; i < len; i++) {
212 1.13 rillig const char *p = s + i;
213 1.13 rillig if (*p == '\\' || *p == '"')
214 1.13 rillig debug_printf("\\%c", *p);
215 1.13 rillig else if (isprint((unsigned char)*p))
216 1.13 rillig debug_printf("%c", *p);
217 1.13 rillig else if (*p == '\n')
218 1.13 rillig debug_printf("\\n");
219 1.13 rillig else if (*p == '\t')
220 1.13 rillig debug_printf("\\t");
221 1.13 rillig else
222 1.13 rillig debug_printf("\\x%02x", (unsigned char)*p);
223 1.13 rillig }
224 1.58 rillig debug_printf("\"");
225 1.6 rillig }
226 1.6 rillig
227 1.25 rillig void
228 1.1 rillig debug_print_buf(const char *name, const struct buffer *buf)
229 1.1 rillig {
230 1.13 rillig if (buf->len > 0) {
231 1.22 rillig debug_printf(" %s ", name);
232 1.58 rillig debug_vis_range(buf->s, buf->len);
233 1.13 rillig }
234 1.1 rillig }
235 1.1 rillig
236 1.1 rillig void
237 1.1 rillig debug_buffers(void)
238 1.1 rillig {
239 1.22 rillig debug_print_buf("label", &lab);
240 1.22 rillig debug_print_buf("code", &code);
241 1.22 rillig debug_print_buf("comment", &com);
242 1.48 rillig debug_blank_line();
243 1.1 rillig }
244 1.1 rillig
245 1.47 rillig static void
246 1.58 rillig debug_ps_bool_member(const char *name, bool prev, bool curr)
247 1.47 rillig {
248 1.52 rillig if (!state.ps_first && curr != prev) {
249 1.47 rillig char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)];
250 1.47 rillig debug_println(" [%c] ps.%s", diff, name);
251 1.47 rillig } else if (config.full_parser_state || state.ps_first)
252 1.47 rillig debug_println(" [%c] ps.%s", curr ? 'x' : ' ', name);
253 1.47 rillig }
254 1.47 rillig
255 1.47 rillig static void
256 1.58 rillig debug_ps_int_member(const char *name, int prev, int curr)
257 1.47 rillig {
258 1.52 rillig if (!state.ps_first && curr != prev)
259 1.47 rillig debug_println(" %3d -> %3d ps.%s", prev, curr, name);
260 1.47 rillig else if (config.full_parser_state || state.ps_first)
261 1.47 rillig debug_println(" %3d ps.%s", curr, name);
262 1.47 rillig }
263 1.47 rillig
264 1.47 rillig static void
265 1.58 rillig debug_ps_enum_member(const char *name, const char *prev, const char *curr)
266 1.47 rillig {
267 1.52 rillig if (!state.ps_first && strcmp(prev, curr) != 0)
268 1.47 rillig debug_println(" %3s -> %3s ps.%s", prev, curr, name);
269 1.47 rillig else if (config.full_parser_state || state.ps_first)
270 1.47 rillig debug_println(" %10s ps.%s", curr, name);
271 1.47 rillig }
272 1.1 rillig
273 1.1 rillig static bool
274 1.59 rillig paren_stack_equal(const struct paren_stack *a, const struct paren_stack *b)
275 1.1 rillig {
276 1.59 rillig if (a->len != b->len)
277 1.59 rillig return false;
278 1.1 rillig
279 1.59 rillig for (size_t i = 0, n = a->len; i < n; i++)
280 1.59 rillig if (a->item[i].indent != b->item[i].indent
281 1.59 rillig || a->item[i].cast != b->item[i].cast)
282 1.68 rillig return false;
283 1.68 rillig return true;
284 1.1 rillig }
285 1.1 rillig
286 1.1 rillig static void
287 1.47 rillig debug_ps_paren(void)
288 1.1 rillig {
289 1.59 rillig if (!config.full_parser_state
290 1.59 rillig && paren_stack_equal(&state.prev_ps.paren, &ps.paren)
291 1.47 rillig && !state.ps_first)
292 1.13 rillig return;
293 1.1 rillig
294 1.36 rillig debug_printf(" ps.paren:");
295 1.59 rillig for (size_t i = 0; i < ps.paren.len; i++) {
296 1.13 rillig debug_printf(" %s%d",
297 1.59 rillig paren_level_cast_name[ps.paren.item[i].cast],
298 1.59 rillig ps.paren.item[i].indent);
299 1.13 rillig }
300 1.59 rillig if (ps.paren.len == 0)
301 1.13 rillig debug_printf(" none");
302 1.49 rillig debug_println("");
303 1.1 rillig }
304 1.1 rillig
305 1.15 rillig static bool
306 1.47 rillig ps_di_stack_has_changed(void)
307 1.15 rillig {
308 1.47 rillig if (state.prev_ps.decl_level != ps.decl_level)
309 1.15 rillig return true;
310 1.15 rillig for (int i = 0; i < ps.decl_level; i++)
311 1.47 rillig if (state.prev_ps.di_stack[i] != ps.di_stack[i])
312 1.15 rillig return true;
313 1.15 rillig return false;
314 1.15 rillig }
315 1.15 rillig
316 1.15 rillig static void
317 1.47 rillig debug_ps_di_stack(void)
318 1.15 rillig {
319 1.47 rillig bool changed = ps_di_stack_has_changed();
320 1.47 rillig if (!config.full_parser_state && !changed && !state.ps_first)
321 1.15 rillig return;
322 1.15 rillig
323 1.36 rillig debug_printf(" %s ps.di_stack:", changed ? "->" : " ");
324 1.15 rillig for (int i = 0; i < ps.decl_level; i++)
325 1.15 rillig debug_printf(" %d", ps.di_stack[i]);
326 1.15 rillig if (ps.decl_level == 0)
327 1.15 rillig debug_printf(" none");
328 1.49 rillig debug_println("");
329 1.15 rillig }
330 1.15 rillig
331 1.47 rillig #define debug_ps_bool(name) \
332 1.58 rillig debug_ps_bool_member(#name, state.prev_ps.name, ps.name)
333 1.47 rillig #define debug_ps_int(name) \
334 1.58 rillig debug_ps_int_member(#name, state.prev_ps.name, ps.name)
335 1.47 rillig #define debug_ps_enum(name, names) \
336 1.58 rillig debug_ps_enum_member(#name, (names)[state.prev_ps.name], \
337 1.58 rillig (names)[ps.name])
338 1.47 rillig
339 1.1 rillig void
340 1.22 rillig debug_parser_state(void)
341 1.1 rillig {
342 1.22 rillig debug_blank_line();
343 1.35 rillig
344 1.47 rillig state.heading = "token classification";
345 1.52 rillig debug_ps_enum(prev_lsym, lsym_name);
346 1.35 rillig debug_ps_bool(in_stmt_or_decl);
347 1.35 rillig debug_ps_bool(in_decl);
348 1.65 rillig debug_ps_bool(in_typedef_decl);
349 1.44 rillig debug_ps_bool(in_var_decl);
350 1.44 rillig debug_ps_bool(in_init);
351 1.44 rillig debug_ps_int(init_level);
352 1.46 rillig debug_ps_bool(line_has_func_def);
353 1.35 rillig debug_ps_bool(in_func_def_params);
354 1.44 rillig debug_ps_bool(line_has_decl);
355 1.44 rillig debug_ps_enum(lbrace_kind, psym_name);
356 1.44 rillig debug_ps_enum(spaced_expr_psym, psym_name);
357 1.35 rillig debug_ps_bool(seen_case);
358 1.42 rillig debug_ps_bool(prev_paren_was_cast);
359 1.44 rillig debug_ps_int(quest_level);
360 1.13 rillig
361 1.47 rillig state.heading = "indentation of statements and declarations";
362 1.13 rillig debug_ps_int(ind_level);
363 1.13 rillig debug_ps_int(ind_level_follow);
364 1.62 rillig debug_ps_bool(line_is_stmt_cont);
365 1.13 rillig debug_ps_int(decl_level);
366 1.47 rillig debug_ps_di_stack();
367 1.13 rillig debug_ps_bool(decl_indent_done);
368 1.13 rillig debug_ps_int(decl_ind);
369 1.13 rillig debug_ps_bool(tabs_to_var);
370 1.35 rillig debug_ps_enum(extra_expr_indent, extra_expr_indent_name);
371 1.13 rillig
372 1.50 rillig // The parser symbol stack is printed in debug_psyms_stack instead.
373 1.35 rillig
374 1.47 rillig state.heading = "spacing inside a statement or declaration";
375 1.35 rillig debug_ps_bool(next_unary);
376 1.35 rillig debug_ps_bool(want_blank);
377 1.53 rillig debug_ps_int(ind_paren_level);
378 1.47 rillig debug_ps_paren();
379 1.35 rillig
380 1.57 rillig state.heading = "indentation of comments";
381 1.57 rillig debug_ps_int(comment_ind);
382 1.57 rillig debug_ps_int(comment_shift);
383 1.63 rillig debug_ps_bool(comment_cont);
384 1.35 rillig
385 1.47 rillig state.heading = "vertical spacing";
386 1.35 rillig debug_ps_bool(break_after_comma);
387 1.72 rillig debug_ps_enum(newline, newline_name);
388 1.35 rillig debug_ps_enum(declaration, declaration_name);
389 1.70 rillig debug_ps_bool(blank_line_after_decl);
390 1.69 rillig debug_ps_enum(badp, badp_name);
391 1.1 rillig
392 1.47 rillig state.heading = NULL;
393 1.22 rillig debug_blank_line();
394 1.1 rillig
395 1.68 rillig parser_state_free(&state.prev_ps);
396 1.68 rillig parser_state_back_up(&state.prev_ps);
397 1.47 rillig state.ps_first = false;
398 1.1 rillig }
399 1.1 rillig
400 1.1 rillig void
401 1.50 rillig debug_psyms_stack(const char *situation)
402 1.1 rillig {
403 1.38 rillig debug_printf("parse stack %s:", situation);
404 1.39 rillig const struct psym_stack *psyms = &ps.psyms;
405 1.67 rillig for (size_t i = 0; i < psyms->len; i++)
406 1.39 rillig debug_printf(" %d %s",
407 1.39 rillig psyms->ind_level[i], psym_name[psyms->sym[i]]);
408 1.51 rillig debug_println("");
409 1.1 rillig }
410 1.1 rillig #endif
411