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