1 /* $NetBSD: debug.c,v 1.74 2025/01/04 21:20:59 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.74 2025/01/04 21:20:59 rillig Exp $"); 34 35 #include <stdarg.h> 36 #include <string.h> 37 38 #include "indent.h" 39 40 #ifdef debug 41 42 static 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 50 const char *const lsym_name[] = { 51 "lsym_eof", 52 "lsym_preprocessing", 53 "lsym_newline", 54 "lsym_comment", 55 "lsym_lparen", 56 "lsym_rparen", 57 "lsym_lbracket", 58 "lsym_rbracket", 59 "lsym_lbrace", 60 "lsym_rbrace", 61 "lsym_period", 62 "lsym_unary_op", 63 "lsym_sizeof", 64 "lsym_offsetof", 65 "lsym_postfix_op", 66 "lsym_binary_op", 67 "lsym_question", 68 "lsym_question_colon", 69 "lsym_comma", 70 "lsym_typedef", 71 "lsym_modifier", 72 "lsym_tag", 73 "lsym_type", 74 "lsym_word", 75 "lsym_funcname", 76 "lsym_label_colon", 77 "lsym_other_colon", 78 "lsym_semicolon", 79 "lsym_case", 80 "lsym_default", 81 "lsym_do", 82 "lsym_else", 83 "lsym_for", 84 "lsym_if", 85 "lsym_switch", 86 "lsym_while", 87 "lsym_return", 88 }; 89 90 const char *const psym_name[] = { 91 "psym_0", 92 "psym_lbrace_block", 93 "psym_lbrace_struct", 94 "psym_lbrace_union", 95 "psym_lbrace_enum", 96 "psym_rbrace", 97 "psym_decl", 98 "psym_stmt", 99 "psym_for_exprs", 100 "psym_if_expr", 101 "psym_if_expr_stmt", 102 "psym_if_expr_stmt_else", 103 "psym_else", 104 "psym_switch_expr", 105 "psym_do", 106 "psym_do_stmt", 107 "psym_while_expr", 108 }; 109 110 static const char *const newline_name[] = { 111 "nl_no", 112 "nl_unless_if", 113 "nl_unless_lbrace", 114 "nl_unless_semicolon", 115 "nl_yes", 116 }; 117 118 static const char *const declaration_name[] = { 119 "decl_no", 120 "decl_begin", 121 "decl_end", 122 }; 123 124 static const char *const badp_name[] = { 125 "badp_none", 126 "badp_seen_lbrace", 127 "badp_decl", 128 "badp_seen_decl", 129 "badp_yes", 130 }; 131 132 const char *const paren_level_cast_name[] = { 133 "cast_unknown", 134 "cast_maybe", 135 "cast_no", 136 }; 137 138 const char *const line_kind_name[] = { 139 "lk_other", 140 "lk_blank", 141 "lk_pre_if", 142 "lk_pre_endif", 143 "lk_pre_other", 144 "lk_stmt_head", 145 "lk_func_end", 146 "lk_block_comment", 147 "lk_case_or_default", 148 }; 149 150 static const char *const extra_expr_indent_name[] = { 151 "eei_no", 152 "eei_maybe", 153 "eei_last" 154 }; 155 156 static struct { 157 struct parser_state prev_ps; 158 bool ps_first; 159 const char *heading1; 160 const char *heading2; 161 unsigned wrote_newlines; 162 } state = { 163 .ps_first = true, 164 .wrote_newlines = 1, 165 }; 166 167 static FILE * 168 debug_file(void) 169 { 170 return output == stdout ? stderr : stdout; 171 } 172 173 static void 174 debug_print_headings(void) 175 { 176 if (state.heading1 != NULL) { 177 (void)fprintf(debug_file(), "%s:\n", state.heading1); 178 state.heading1 = NULL; 179 state.wrote_newlines = 1; 180 } 181 if (state.heading2 != NULL) { 182 (void)fprintf(debug_file(), "\t%s:\n", state.heading2); 183 state.heading2 = NULL; 184 state.wrote_newlines = 1; 185 } 186 } 187 188 void 189 debug_printf(const char *fmt, ...) 190 { 191 va_list ap; 192 193 debug_print_headings(); 194 va_start(ap, fmt); 195 (void)vfprintf(debug_file(), fmt, ap); 196 va_end(ap); 197 state.wrote_newlines = 0; 198 } 199 200 void 201 debug_println(const char *fmt, ...) 202 { 203 va_list ap; 204 205 debug_print_headings(); 206 va_start(ap, fmt); 207 (void)vfprintf(debug_file(), fmt, ap); 208 va_end(ap); 209 (void)fprintf(debug_file(), "\n"); 210 state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1; 211 } 212 213 void 214 debug_blank_line(void) 215 { 216 while (state.wrote_newlines < 2) 217 debug_println(""); 218 } 219 220 void 221 debug_vis_range(const char *s, size_t len) 222 { 223 debug_printf("\""); 224 for (size_t i = 0; i < len; i++) { 225 const char *p = s + i; 226 if (*p == '\\' || *p == '"') 227 debug_printf("\\%c", *p); 228 else if (isprint((unsigned char)*p)) 229 debug_printf("%c", *p); 230 else if (*p == '\n') 231 debug_printf("\\n"); 232 else if (*p == '\t') 233 debug_printf("\\t"); 234 else 235 debug_printf("\\x%02x", (unsigned char)*p); 236 } 237 debug_printf("\""); 238 } 239 240 void 241 debug_print_buf(const char *name, const struct buffer *buf) 242 { 243 if (buf->len > 0) { 244 debug_printf(" %s ", name); 245 debug_vis_range(buf->s, buf->len); 246 } 247 } 248 249 void 250 debug_buffers(const char *descr) 251 { 252 if (lab.len > 0 || code.len > 0 || com.len > 0) { 253 debug_printf("%s:", descr); 254 debug_print_buf("label", &lab); 255 debug_print_buf("code", &code); 256 debug_print_buf("comment", &com); 257 debug_blank_line(); 258 } 259 } 260 261 static void 262 debug_ps_bool_member(const char *name, bool prev, bool curr) 263 { 264 if (!state.ps_first && curr != prev) { 265 char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)]; 266 debug_println("\t\t%s: [%c]", name, diff); 267 } else if (config.full_parser_state || state.ps_first) 268 debug_println("\t\t%s: [%c]", name, curr ? 'x' : ' '); 269 } 270 271 static void 272 debug_ps_int_member(const char *name, int prev, int curr) 273 { 274 if (!state.ps_first && curr != prev) 275 debug_println("\t\t%s: %d -> %d", name, prev, curr); 276 else if (config.full_parser_state || state.ps_first) 277 debug_println("\t\t%s: %d", name, curr); 278 } 279 280 static void 281 debug_ps_enum_member(const char *name, const char *prev, const char *curr) 282 { 283 if (!state.ps_first && strcmp(prev, curr) != 0) 284 debug_println("\t\t%s: %s -> %s", name, prev, curr); 285 else if (config.full_parser_state || state.ps_first) 286 debug_println("\t\t%s: %s", name, curr); 287 } 288 289 static void 290 debug_ps_str_member(const char *name, 291 void (*to_string)(struct buffer *, const struct parser_state *)) 292 { 293 static struct buffer prev_buf; 294 static struct buffer curr_buf; 295 296 buf_clear(&prev_buf); 297 to_string(&prev_buf, &state.prev_ps); 298 buf_clear(&curr_buf); 299 to_string(&curr_buf, &ps); 300 301 if (!state.ps_first && strcmp(prev_buf.s, curr_buf.s) != 0) 302 debug_println("\t\t%s: %s -> %s", 303 name, prev_buf.s, curr_buf.s); 304 else if (config.full_parser_state || state.ps_first) 305 debug_println("\t\t%s: %s", name, curr_buf.s); 306 } 307 308 static void 309 ps_di_stack_to_string(struct buffer *buf, const struct parser_state *s) 310 { 311 for (int i = 0; i < s->decl_level; i++) { 312 char str[64]; 313 snprintf(str, sizeof(str), "%s%d", 314 i > 0 ? " " : "", s->di_stack[i]); 315 buf_add_str(buf, str); 316 } 317 if (s->decl_level == 0) 318 buf_add_str(buf, "none"); 319 } 320 321 static void 322 ps_paren_to_string(struct buffer *buf, const struct parser_state *s) 323 { 324 for (size_t i = 0; i < s->paren.len; i++) { 325 buf_add_str(buf, i > 0 ? " " : ""); 326 buf_add_str(buf, paren_level_cast_name[s->paren.item[i].cast]); 327 char str[64]; 328 snprintf(str, sizeof(str), " %d", s->paren.item[i].indent); 329 buf_add_str(buf, str); 330 } 331 if (s->paren.len == 0) 332 buf_add_str(buf, "none"); 333 } 334 335 void 336 ps_psyms_to_string(struct buffer *buf, const struct parser_state *s) 337 { 338 for (size_t i = 0; i < s->psyms.len; i++) { 339 char num[64]; 340 snprintf(num, sizeof(num), "%s%d ", 341 i > 0 ? " " : "", s->psyms.ind_level[i]); 342 buf_add_str(buf, num); 343 buf_add_str(buf, psym_name[s->psyms.sym[i]]); 344 } 345 } 346 347 #define debug_ps_bool(name) \ 348 debug_ps_bool_member(#name, state.prev_ps.name, ps.name) 349 #define debug_ps_int(name) \ 350 debug_ps_int_member(#name, state.prev_ps.name, ps.name) 351 #define debug_ps_enum(name, names) \ 352 debug_ps_enum_member(#name, (names)[state.prev_ps.name], \ 353 (names)[ps.name]) 354 #define debug_ps_str(name, to_string) \ 355 debug_ps_str_member((/* LINTED 129 */(void)&ps.name, #name), to_string) 356 357 void 358 debug_parser_state(void) 359 { 360 debug_blank_line(); 361 362 state.heading1 = "parser state"; 363 state.heading2 = "token classification"; 364 debug_ps_enum(prev_lsym, lsym_name); 365 debug_ps_bool(in_stmt_or_decl); 366 debug_ps_bool(in_decl); 367 debug_ps_bool(in_typedef_decl); 368 debug_ps_bool(in_var_decl); 369 debug_ps_bool(in_init); 370 debug_ps_int(init_level); 371 debug_ps_bool(line_has_func_def); 372 debug_ps_bool(in_func_def_params); 373 debug_ps_bool(line_has_decl); 374 debug_ps_enum(lbrace_kind, psym_name); 375 debug_ps_enum(spaced_expr_psym, psym_name); 376 debug_ps_bool(seen_case); 377 debug_ps_bool(prev_paren_was_cast); 378 debug_ps_int(quest_level); 379 380 state.heading2 = "indentation of statements and declarations"; 381 debug_ps_int(ind_level); 382 debug_ps_int(ind_level_follow); 383 debug_ps_str(psyms, ps_psyms_to_string); 384 debug_ps_bool(line_is_stmt_cont); 385 debug_ps_int(decl_level); 386 debug_ps_str(di_stack, ps_di_stack_to_string); 387 debug_ps_bool(decl_indent_done); 388 debug_ps_int(decl_ind); 389 debug_ps_bool(tabs_to_var); 390 debug_ps_enum(extra_expr_indent, extra_expr_indent_name); 391 392 state.heading2 = "spacing inside a statement or declaration"; 393 debug_ps_bool(next_unary); 394 debug_ps_bool(want_blank); 395 debug_ps_int(ind_paren_level); 396 debug_ps_str(paren, ps_paren_to_string); 397 398 state.heading2 = "indentation of comments"; 399 debug_ps_int(comment_ind); 400 debug_ps_int(comment_shift); 401 debug_ps_bool(comment_cont); 402 403 state.heading2 = "vertical spacing"; 404 debug_ps_bool(break_after_comma); 405 debug_ps_enum(newline, newline_name); 406 debug_ps_enum(declaration, declaration_name); 407 debug_ps_bool(blank_line_after_decl); 408 debug_ps_enum(badp, badp_name); 409 410 state.heading1 = NULL; 411 state.heading2 = NULL; 412 debug_blank_line(); 413 414 parser_state_free(&state.prev_ps); 415 parser_state_back_up(&state.prev_ps); 416 state.ps_first = false; 417 } 418 #endif 419