debug.c revision 1.59
1/* $NetBSD: debug.c,v 1.59 2023/06/14 14:11:28 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.59 2023/06/14 14:11:28 rillig Exp $"); 34 35#include <stdarg.h> 36#include <string.h> 37 38#include "indent.h" 39 40#ifdef debug 41 42static struct { 43 // false show only the changes to the parser state 44 // true show unchanged parts of the parser state as well 45 bool full_parser_state; 46} config = { 47 .full_parser_state = false, 48}; 49 50const char *const lsym_name[] = { 51 "eof", 52 "preprocessing", 53 "newline", 54 "comment", 55 "lparen", 56 "rparen", 57 "lbracket", 58 "rbracket", 59 "lbrace", 60 "rbrace", 61 "period", 62 "unary_op", 63 "sizeof", 64 "offsetof", 65 "postfix_op", 66 "binary_op", 67 "question", 68 "question_colon", 69 "comma", 70 "typedef", 71 "modifier", 72 "tag", 73 "type_outside_parentheses", 74 "type_in_parentheses", 75 "word", 76 "funcname", 77 "label_colon", 78 "other_colon", 79 "semicolon", 80 "case", 81 "default", 82 "do", 83 "else", 84 "for", 85 "if", 86 "switch", 87 "while", 88 "return", 89}; 90 91const char *const psym_name[] = { 92 "-", 93 "{block", 94 "{struct", 95 "{union", 96 "{enum", 97 "}", 98 "decl", 99 "stmt", 100 "for_exprs", 101 "if_expr", 102 "if_expr_stmt", 103 "if_expr_stmt_else", 104 "else", 105 "switch_expr", 106 "do", 107 "do_stmt", 108 "while_expr", 109}; 110 111static const char *const declaration_name[] = { 112 "no", 113 "begin", 114 "end", 115}; 116 117const char *const paren_level_cast_name[] = { 118 "(unknown cast)", 119 "(maybe cast)", 120 "(no cast)", 121}; 122 123const char *const line_kind_name[] = { 124 "other", 125 "blank", 126 "#if", 127 "#endif", 128 "stmt head", 129 "}", 130 "block comment", 131 "case/default", 132}; 133 134static const char *const extra_expr_indent_name[] = { 135 "no", 136 "maybe", 137 "last", 138}; 139 140static struct { 141 struct parser_state prev_ps; 142 bool ps_first; 143 const char *heading; 144 unsigned wrote_newlines; 145} state = { 146 .ps_first = true, 147 .wrote_newlines = 1, 148}; 149 150void 151debug_printf(const char *fmt, ...) 152{ 153 FILE *f = output == stdout ? stderr : stdout; 154 va_list ap; 155 156 if (state.heading != NULL) { 157 fprintf(f, "%s\n", state.heading); 158 state.heading = NULL; 159 } 160 va_start(ap, fmt); 161 vfprintf(f, fmt, ap); 162 va_end(ap); 163 state.wrote_newlines = 0; 164} 165 166void 167debug_println(const char *fmt, ...) 168{ 169 FILE *f = output == stdout ? stderr : stdout; 170 va_list ap; 171 172 if (state.heading != NULL) { 173 fprintf(f, "%s\n", state.heading); 174 state.heading = NULL; 175 state.wrote_newlines = 1; 176 } 177 va_start(ap, fmt); 178 vfprintf(f, fmt, ap); 179 va_end(ap); 180 fprintf(f, "\n"); 181 state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1; 182} 183 184void 185debug_blank_line(void) 186{ 187 while (state.wrote_newlines < 2) 188 debug_println(""); 189} 190 191void 192debug_vis_range(const char *s, size_t len) 193{ 194 debug_printf("\""); 195 for (size_t i = 0; i < len; i++) { 196 const char *p = s + i; 197 if (*p == '\\' || *p == '"') 198 debug_printf("\\%c", *p); 199 else if (isprint((unsigned char)*p)) 200 debug_printf("%c", *p); 201 else if (*p == '\n') 202 debug_printf("\\n"); 203 else if (*p == '\t') 204 debug_printf("\\t"); 205 else 206 debug_printf("\\x%02x", (unsigned char)*p); 207 } 208 debug_printf("\""); 209} 210 211void 212debug_print_buf(const char *name, const struct buffer *buf) 213{ 214 if (buf->len > 0) { 215 debug_printf(" %s ", name); 216 debug_vis_range(buf->s, buf->len); 217 } 218} 219 220void 221debug_buffers(void) 222{ 223 debug_print_buf("label", &lab); 224 debug_print_buf("code", &code); 225 debug_print_buf("comment", &com); 226 debug_blank_line(); 227} 228 229static void 230debug_ps_bool_member(const char *name, bool prev, bool curr) 231{ 232 if (!state.ps_first && curr != prev) { 233 char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)]; 234 debug_println(" [%c] ps.%s", diff, name); 235 } else if (config.full_parser_state || state.ps_first) 236 debug_println(" [%c] ps.%s", curr ? 'x' : ' ', name); 237} 238 239static void 240debug_ps_int_member(const char *name, int prev, int curr) 241{ 242 if (!state.ps_first && curr != prev) 243 debug_println(" %3d -> %3d ps.%s", prev, curr, name); 244 else if (config.full_parser_state || state.ps_first) 245 debug_println(" %3d ps.%s", curr, name); 246} 247 248static void 249debug_ps_enum_member(const char *name, const char *prev, const char *curr) 250{ 251 if (!state.ps_first && strcmp(prev, curr) != 0) 252 debug_println(" %3s -> %3s ps.%s", prev, curr, name); 253 else if (config.full_parser_state || state.ps_first) 254 debug_println(" %10s ps.%s", curr, name); 255} 256 257static bool 258paren_stack_equal(const struct paren_stack *a, const struct paren_stack *b) 259{ 260 if (a->len != b->len) 261 return false; 262 263 for (size_t i = 0, n = a->len; i < n; i++) 264 if (a->item[i].indent != b->item[i].indent 265 || a->item[i].cast != b->item[i].cast) 266 return true; 267 return false; 268} 269 270static void 271debug_ps_paren(void) 272{ 273 if (!config.full_parser_state 274 && paren_stack_equal(&state.prev_ps.paren, &ps.paren) 275 && !state.ps_first) 276 return; 277 278 debug_printf(" ps.paren:"); 279 for (size_t i = 0; i < ps.paren.len; i++) { 280 debug_printf(" %s%d", 281 paren_level_cast_name[ps.paren.item[i].cast], 282 ps.paren.item[i].indent); 283 } 284 if (ps.paren.len == 0) 285 debug_printf(" none"); 286 debug_println(""); 287} 288 289static bool 290ps_di_stack_has_changed(void) 291{ 292 if (state.prev_ps.decl_level != ps.decl_level) 293 return true; 294 for (int i = 0; i < ps.decl_level; i++) 295 if (state.prev_ps.di_stack[i] != ps.di_stack[i]) 296 return true; 297 return false; 298} 299 300static void 301debug_ps_di_stack(void) 302{ 303 bool changed = ps_di_stack_has_changed(); 304 if (!config.full_parser_state && !changed && !state.ps_first) 305 return; 306 307 debug_printf(" %s ps.di_stack:", changed ? "->" : " "); 308 for (int i = 0; i < ps.decl_level; i++) 309 debug_printf(" %d", ps.di_stack[i]); 310 if (ps.decl_level == 0) 311 debug_printf(" none"); 312 debug_println(""); 313} 314 315#define debug_ps_bool(name) \ 316 debug_ps_bool_member(#name, state.prev_ps.name, ps.name) 317#define debug_ps_int(name) \ 318 debug_ps_int_member(#name, state.prev_ps.name, ps.name) 319#define debug_ps_enum(name, names) \ 320 debug_ps_enum_member(#name, (names)[state.prev_ps.name], \ 321 (names)[ps.name]) 322 323void 324debug_parser_state(void) 325{ 326 debug_blank_line(); 327 328 state.heading = "token classification"; 329 debug_ps_enum(prev_lsym, lsym_name); 330 debug_ps_bool(in_stmt_or_decl); 331 debug_ps_bool(in_decl); 332 debug_ps_bool(in_var_decl); 333 debug_ps_bool(in_init); 334 debug_ps_int(init_level); 335 debug_ps_bool(line_has_func_def); 336 debug_ps_bool(in_func_def_params); 337 debug_ps_bool(line_has_decl); 338 debug_ps_enum(lbrace_kind, psym_name); 339 debug_ps_enum(spaced_expr_psym, psym_name); 340 debug_ps_bool(seen_case); 341 debug_ps_bool(prev_paren_was_cast); 342 debug_ps_int(quest_level); 343 344 state.heading = "indentation of statements and declarations"; 345 debug_ps_int(ind_level); 346 debug_ps_int(ind_level_follow); 347 debug_ps_bool(in_stmt_cont); 348 debug_ps_int(decl_level); 349 debug_ps_di_stack(); 350 debug_ps_bool(decl_indent_done); 351 debug_ps_int(decl_ind); 352 debug_ps_bool(tabs_to_var); 353 debug_ps_enum(extra_expr_indent, extra_expr_indent_name); 354 355 // The parser symbol stack is printed in debug_psyms_stack instead. 356 357 state.heading = "spacing inside a statement or declaration"; 358 debug_ps_bool(next_unary); 359 debug_ps_bool(want_blank); 360 debug_ps_int(ind_paren_level); 361 debug_ps_paren(); 362 363 state.heading = "indentation of comments"; 364 debug_ps_int(comment_ind); 365 debug_ps_int(comment_shift); 366 debug_ps_bool(comment_in_first_line); 367 368 state.heading = "vertical spacing"; 369 debug_ps_bool(break_after_comma); 370 debug_ps_bool(force_nl); 371 debug_ps_enum(declaration, declaration_name); 372 debug_ps_bool(blank_line_after_decl); 373 374 state.heading = NULL; 375 debug_blank_line(); 376 377 state.prev_ps = ps; 378 state.ps_first = false; 379} 380 381void 382debug_psyms_stack(const char *situation) 383{ 384 debug_printf("parse stack %s:", situation); 385 const struct psym_stack *psyms = &ps.psyms; 386 for (int i = 0; i <= psyms->top; ++i) 387 debug_printf(" %d %s", 388 psyms->ind_level[i], psym_name[psyms->sym[i]]); 389 debug_println(""); 390} 391#endif 392