debug.c revision 1.21
1/* $NetBSD: debug.c,v 1.21 2023/05/23 06:35:01 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.21 2023/05/23 06:35:01 rillig Exp $"); 34 35#include <stdarg.h> 36 37#include "indent.h" 38 39#ifdef debug 40 41/*- 42 * false show only the changes to the parser state 43 * true show unchanged parts of the parser state as well 44 */ 45static bool debug_full_parser_state = true; 46 47const char *const lsym_name[] = { 48 "eof", 49 "preprocessing", 50 "newline", 51 "comment", 52 "lparen_or_lbracket", 53 "rparen_or_rbracket", 54 "lbrace", 55 "rbrace", 56 "period", 57 "unary_op", 58 "binary_op", 59 "postfix_op", 60 "question", 61 "colon", 62 "comma", 63 "semicolon", 64 "typedef", 65 "storage_class", 66 "type_outside_parentheses", 67 "type_in_parentheses", 68 "tag", 69 "case_label", 70 "sizeof", 71 "offsetof", 72 "word", 73 "funcname", 74 "do", 75 "else", 76 "for", 77 "if", 78 "switch", 79 "while", 80 "return", 81}; 82 83const char *const psym_name[] = { 84 "0", 85 "lbrace", 86 "rbrace", 87 "decl", 88 "stmt", 89 "stmt_list", 90 "for_exprs", 91 "if_expr", 92 "if_expr_stmt", 93 "if_expr_stmt_else", 94 "else", 95 "switch_expr", 96 "do", 97 "do_stmt", 98 "while_expr", 99}; 100 101static const char *const declaration_name[] = { 102 "no", 103 "begin", 104 "end", 105}; 106 107static const char *const in_enum_name[] = { 108 "no", 109 "enum", 110 "type", 111 "brace", 112}; 113 114const char *const paren_level_cast_name[] = { 115 "(unknown cast)", 116 "(maybe cast)", 117 "(no cast)", 118}; 119 120const char *const line_kind_name[] = { 121 "other", 122 "blank", 123 "#if", 124 "#endif", 125 "stmt head", 126 "}", 127 "block comment", 128}; 129 130static const char *const decl_ptr_name[] = { 131 "start", 132 "word", 133 "word *", 134 "other", 135}; 136 137void 138debug_printf(const char *fmt, ...) 139{ 140 FILE *f = output == stdout ? stderr : stdout; 141 va_list ap; 142 143 va_start(ap, fmt); 144 vfprintf(f, fmt, ap); 145 va_end(ap); 146} 147 148void 149debug_println(const char *fmt, ...) 150{ 151 FILE *f = output == stdout ? stderr : stdout; 152 va_list ap; 153 154 va_start(ap, fmt); 155 vfprintf(f, fmt, ap); 156 va_end(ap); 157 fprintf(f, "\n"); 158} 159 160void 161debug_vis_range(const char *prefix, const char *s, size_t len, 162 const char *suffix) 163{ 164 debug_printf("%s", prefix); 165 for (size_t i = 0; i < len; i++) { 166 const char *p = s + i; 167 if (*p == '\\' || *p == '"') 168 debug_printf("\\%c", *p); 169 else if (isprint((unsigned char)*p)) 170 debug_printf("%c", *p); 171 else if (*p == '\n') 172 debug_printf("\\n"); 173 else if (*p == '\t') 174 debug_printf("\\t"); 175 else 176 debug_printf("\\x%02x", (unsigned char)*p); 177 } 178 debug_printf("%s", suffix); 179} 180 181static void 182debug_print_buf(const char *name, const struct buffer *buf) 183{ 184 if (buf->len > 0) { 185 debug_printf("%s ", name); 186 debug_vis_range("\"", buf->st, buf->len, "\"\n"); 187 } 188} 189 190void 191debug_buffers(void) 192{ 193 if (lab.len > 0) { 194 debug_printf(" label "); 195 debug_vis_range("\"", lab.st, lab.len, "\""); 196 } 197 if (code.len > 0) { 198 debug_printf(" code "); 199 debug_vis_range("\"", code.st, code.len, "\""); 200 } 201 if (com.len > 0) { 202 debug_printf(" comment "); 203 debug_vis_range("\"", com.st, com.len, "\""); 204 } 205} 206 207#define debug_ps_bool(name) \ 208 if (ps.name != prev_ps.name) \ 209 debug_println("[%c] -> [%c] ps." #name, \ 210 prev_ps.name ? 'x' : ' ', ps.name ? 'x' : ' '); \ 211 else if (debug_full_parser_state) \ 212 debug_println(" [%c] ps." #name, ps.name ? 'x' : ' ') 213#define debug_ps_int(name) \ 214 if (ps.name != prev_ps.name) \ 215 debug_println("%3d -> %3d ps." #name, prev_ps.name, ps.name); \ 216 else if (debug_full_parser_state) \ 217 debug_println(" %3d ps." #name, ps.name) 218#define debug_ps_enum(name, names) \ 219 if (ps.name != prev_ps.name) \ 220 debug_println("%3s -> %3s ps." #name, \ 221 (names)[prev_ps.name], (names)[ps.name]); \ 222 else if (debug_full_parser_state) \ 223 debug_println("%10s ps." #name, (names)[ps.name]) 224 225static bool 226ps_paren_has_changed(const struct parser_state *prev_ps) 227{ 228 if (prev_ps->nparen != ps.nparen) 229 return true; 230 231 const paren_level_props *prev = prev_ps->paren, *curr = ps.paren; 232 for (int i = 0; i < ps.nparen; i++) 233 if (curr[i].indent != prev[i].indent 234 || curr[i].cast != prev[i].cast) 235 return true; 236 return false; 237} 238 239static void 240debug_ps_paren(const struct parser_state *prev_ps) 241{ 242 if (!debug_full_parser_state && !ps_paren_has_changed(prev_ps)) 243 return; 244 245 debug_printf(" ps.paren:"); 246 for (int i = 0; i < ps.nparen; i++) { 247 debug_printf(" %s%d", 248 paren_level_cast_name[ps.paren[i].cast], 249 ps.paren[i].indent); 250 } 251 if (ps.nparen == 0) 252 debug_printf(" none"); 253 debug_println(""); 254} 255 256static bool 257ps_di_stack_has_changed(const struct parser_state *prev_ps) 258{ 259 if (prev_ps->decl_level != ps.decl_level) 260 return true; 261 for (int i = 0; i < ps.decl_level; i++) 262 if (prev_ps->di_stack[i] != ps.di_stack[i]) 263 return true; 264 return false; 265} 266 267static void 268debug_ps_di_stack(const struct parser_state *prev_ps) 269{ 270 bool changed = ps_di_stack_has_changed(prev_ps); 271 if (!debug_full_parser_state && !changed) 272 return; 273 274 debug_printf(" %s ps.di_stack:", changed ? "->" : " "); 275 for (int i = 0; i < ps.decl_level; i++) 276 debug_printf(" %d", ps.di_stack[i]); 277 if (ps.decl_level == 0) 278 debug_printf(" none"); 279 debug_println(""); 280} 281 282void 283debug_parser_state(lexer_symbol lsym) 284{ 285 static struct parser_state prev_ps; 286 287 debug_println(""); 288 debug_printf("line %d: %s", line_no, lsym_name[lsym]); 289 debug_vis_range(" \"", token.st, token.len, "\"\n"); 290 291 debug_print_buf("label", &lab); 292 debug_print_buf("code", &code); 293 debug_print_buf("comment", &com); 294 295 debug_println(" ps.prev_token = %s", 296 lsym_name[ps.prev_token]); 297 debug_ps_bool(curr_col_1); 298 debug_ps_bool(next_col_1); 299 debug_ps_bool(next_unary); 300 debug_ps_bool(is_function_definition); 301 debug_ps_bool(want_blank); 302 debug_ps_bool(force_nl); 303 debug_ps_int(line_start_nparen); 304 debug_ps_int(nparen); 305 debug_ps_paren(&prev_ps); 306 307 debug_ps_int(comment_delta); 308 debug_ps_int(n_comment_delta); 309 debug_ps_int(com_ind); 310 311 debug_ps_bool(block_init); 312 debug_ps_int(block_init_level); 313 debug_ps_bool(init_or_struct); 314 315 debug_ps_int(ind_level); 316 debug_ps_int(ind_level_follow); 317 318 debug_ps_int(decl_level); 319 debug_ps_di_stack(&prev_ps); 320 debug_ps_bool(decl_on_line); 321 debug_ps_bool(in_decl); 322 debug_ps_enum(declaration, declaration_name); 323 debug_ps_bool(blank_line_after_decl); 324 debug_ps_bool(in_func_def_params); 325 debug_ps_enum(in_enum, in_enum_name); 326 debug_ps_enum(decl_ptr, decl_ptr_name); 327 debug_ps_bool(decl_indent_done); 328 debug_ps_int(decl_ind); 329 debug_ps_bool(tabs_to_var); 330 331 debug_ps_bool(in_stmt_or_decl); 332 debug_ps_bool(in_stmt_cont); 333 debug_ps_bool(is_case_label); 334 debug_ps_bool(seen_case); 335 336 // The debug output for the parser symbols is done in 'parse' instead. 337 338 debug_ps_enum(spaced_expr_psym, psym_name); 339 debug_ps_int(quest_level); 340 341 prev_ps = ps; 342} 343 344void 345debug_parse_stack(const char *situation) 346{ 347 printf("parse stack %s:", situation); 348 for (int i = 1; i <= ps.tos; ++i) 349 printf(" %s %d", psym_name[ps.s_sym[i]], ps.s_ind_level[i]); 350 if (ps.tos == 0) 351 printf(" empty"); 352 printf("\n"); 353} 354#endif 355