1*8af25fb9Srillig /* $NetBSD: debug.c,v 1.74 2025/01/04 21:20:59 rillig Exp $ */ 2ff5c4a3fSrillig 3ff5c4a3fSrillig /*- 4ff5c4a3fSrillig * Copyright (c) 2023 The NetBSD Foundation, Inc. 5ff5c4a3fSrillig * All rights reserved. 6ff5c4a3fSrillig * 7ff5c4a3fSrillig * This code is derived from software contributed to The NetBSD Foundation 8ff5c4a3fSrillig * by Roland Illig <rillig@NetBSD.org>. 9ff5c4a3fSrillig * 10ff5c4a3fSrillig * Redistribution and use in source and binary forms, with or without 11ff5c4a3fSrillig * modification, are permitted provided that the following conditions 12ff5c4a3fSrillig * are met: 13ff5c4a3fSrillig * 1. Redistributions of source code must retain the above copyright 14ff5c4a3fSrillig * notice, this list of conditions and the following disclaimer. 15ff5c4a3fSrillig * 2. Redistributions in binary form must reproduce the above copyright 16ff5c4a3fSrillig * notice, this list of conditions and the following disclaimer in the 17ff5c4a3fSrillig * documentation and/or other materials provided with the distribution. 18ff5c4a3fSrillig * 19ff5c4a3fSrillig * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20ff5c4a3fSrillig * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21ff5c4a3fSrillig * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22ff5c4a3fSrillig * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23ff5c4a3fSrillig * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24ff5c4a3fSrillig * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25ff5c4a3fSrillig * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26ff5c4a3fSrillig * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27ff5c4a3fSrillig * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28ff5c4a3fSrillig * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29ff5c4a3fSrillig * POSSIBILITY OF SUCH DAMAGE. 30ff5c4a3fSrillig */ 31ff5c4a3fSrillig 32ff5c4a3fSrillig #include <sys/cdefs.h> 33*8af25fb9Srillig __RCSID("$NetBSD: debug.c,v 1.74 2025/01/04 21:20:59 rillig Exp $"); 3444b10107Srillig 3544b10107Srillig #include <stdarg.h> 3689f19ae3Srillig #include <string.h> 37ff5c4a3fSrillig 38ff5c4a3fSrillig #include "indent.h" 39ff5c4a3fSrillig 40ff5c4a3fSrillig #ifdef debug 41d78c6debSrillig 4289f19ae3Srillig static struct { 43b520fe89Srillig // false show only the changes to the parser state 44b520fe89Srillig // true show unchanged parts of the parser state as well 4589f19ae3Srillig bool full_parser_state; 4689f19ae3Srillig } config = { 4789f19ae3Srillig .full_parser_state = false, 4889f19ae3Srillig }; 49d78c6debSrillig 50ff5c4a3fSrillig const char *const lsym_name[] = { 51d40c8c6bSrillig "lsym_eof", 52d40c8c6bSrillig "lsym_preprocessing", 53d40c8c6bSrillig "lsym_newline", 54d40c8c6bSrillig "lsym_comment", 55d40c8c6bSrillig "lsym_lparen", 56d40c8c6bSrillig "lsym_rparen", 57d40c8c6bSrillig "lsym_lbracket", 58d40c8c6bSrillig "lsym_rbracket", 59d40c8c6bSrillig "lsym_lbrace", 60d40c8c6bSrillig "lsym_rbrace", 61d40c8c6bSrillig "lsym_period", 62d40c8c6bSrillig "lsym_unary_op", 63d40c8c6bSrillig "lsym_sizeof", 64d40c8c6bSrillig "lsym_offsetof", 65d40c8c6bSrillig "lsym_postfix_op", 66d40c8c6bSrillig "lsym_binary_op", 67d40c8c6bSrillig "lsym_question", 68d40c8c6bSrillig "lsym_question_colon", 69d40c8c6bSrillig "lsym_comma", 70d40c8c6bSrillig "lsym_typedef", 71d40c8c6bSrillig "lsym_modifier", 72d40c8c6bSrillig "lsym_tag", 73d40c8c6bSrillig "lsym_type", 74d40c8c6bSrillig "lsym_word", 75d40c8c6bSrillig "lsym_funcname", 76d40c8c6bSrillig "lsym_label_colon", 77d40c8c6bSrillig "lsym_other_colon", 78d40c8c6bSrillig "lsym_semicolon", 79d40c8c6bSrillig "lsym_case", 80d40c8c6bSrillig "lsym_default", 81d40c8c6bSrillig "lsym_do", 82d40c8c6bSrillig "lsym_else", 83d40c8c6bSrillig "lsym_for", 84d40c8c6bSrillig "lsym_if", 85d40c8c6bSrillig "lsym_switch", 86d40c8c6bSrillig "lsym_while", 87d40c8c6bSrillig "lsym_return", 88ff5c4a3fSrillig }; 89ff5c4a3fSrillig 90ff5c4a3fSrillig const char *const psym_name[] = { 91d40c8c6bSrillig "psym_0", 92d40c8c6bSrillig "psym_lbrace_block", 93d40c8c6bSrillig "psym_lbrace_struct", 94d40c8c6bSrillig "psym_lbrace_union", 95d40c8c6bSrillig "psym_lbrace_enum", 96d40c8c6bSrillig "psym_rbrace", 97d40c8c6bSrillig "psym_decl", 98d40c8c6bSrillig "psym_stmt", 99d40c8c6bSrillig "psym_for_exprs", 100d40c8c6bSrillig "psym_if_expr", 101d40c8c6bSrillig "psym_if_expr_stmt", 102d40c8c6bSrillig "psym_if_expr_stmt_else", 103d40c8c6bSrillig "psym_else", 104d40c8c6bSrillig "psym_switch_expr", 105d40c8c6bSrillig "psym_do", 106d40c8c6bSrillig "psym_do_stmt", 107d40c8c6bSrillig "psym_while_expr", 108ff5c4a3fSrillig }; 109ff5c4a3fSrillig 110e914aa7bSrillig static const char *const newline_name[] = { 111d40c8c6bSrillig "nl_no", 112d40c8c6bSrillig "nl_unless_if", 113d40c8c6bSrillig "nl_unless_lbrace", 114d40c8c6bSrillig "nl_unless_semicolon", 115d40c8c6bSrillig "nl_yes", 116e914aa7bSrillig }; 117e914aa7bSrillig 118a0b2b7f9Srillig static const char *const declaration_name[] = { 119d40c8c6bSrillig "decl_no", 120d40c8c6bSrillig "decl_begin", 121d40c8c6bSrillig "decl_end", 122ae0013d2Srillig }; 123ae0013d2Srillig 124f73b959dSrillig static const char *const badp_name[] = { 125d40c8c6bSrillig "badp_none", 126d40c8c6bSrillig "badp_seen_lbrace", 127d40c8c6bSrillig "badp_decl", 128d40c8c6bSrillig "badp_seen_decl", 129d40c8c6bSrillig "badp_yes", 130f73b959dSrillig }; 131f73b959dSrillig 132a0b2b7f9Srillig const char *const paren_level_cast_name[] = { 133d40c8c6bSrillig "cast_unknown", 134d40c8c6bSrillig "cast_maybe", 135d40c8c6bSrillig "cast_no", 136a0b2b7f9Srillig }; 137a0b2b7f9Srillig 1384fb425a2Srillig const char *const line_kind_name[] = { 139d40c8c6bSrillig "lk_other", 140d40c8c6bSrillig "lk_blank", 141d40c8c6bSrillig "lk_pre_if", 142d40c8c6bSrillig "lk_pre_endif", 143d40c8c6bSrillig "lk_pre_other", 144d40c8c6bSrillig "lk_stmt_head", 145d40c8c6bSrillig "lk_func_end", 146d40c8c6bSrillig "lk_block_comment", 147d40c8c6bSrillig "lk_case_or_default", 1484d19140dSrillig }; 1494d19140dSrillig 1502a46ceffSrillig static const char *const extra_expr_indent_name[] = { 151d40c8c6bSrillig "eei_no", 152d40c8c6bSrillig "eei_maybe", 153d40c8c6bSrillig "eei_last" 1542a46ceffSrillig }; 1552a46ceffSrillig 15689f19ae3Srillig static struct { 15789f19ae3Srillig struct parser_state prev_ps; 15889f19ae3Srillig bool ps_first; 159d40c8c6bSrillig const char *heading1; 160d40c8c6bSrillig const char *heading2; 16189f19ae3Srillig unsigned wrote_newlines; 16289f19ae3Srillig } state = { 16389f19ae3Srillig .ps_first = true, 16489f19ae3Srillig .wrote_newlines = 1, 16589f19ae3Srillig }; 1665f6e61d7Srillig 167d40c8c6bSrillig static FILE * 168d40c8c6bSrillig debug_file(void) 169d40c8c6bSrillig { 170d40c8c6bSrillig return output == stdout ? stderr : stdout; 171d40c8c6bSrillig } 172d40c8c6bSrillig 173d40c8c6bSrillig static void 174d40c8c6bSrillig debug_print_headings(void) 175d40c8c6bSrillig { 176d40c8c6bSrillig if (state.heading1 != NULL) { 177d40c8c6bSrillig (void)fprintf(debug_file(), "%s:\n", state.heading1); 178d40c8c6bSrillig state.heading1 = NULL; 179d40c8c6bSrillig state.wrote_newlines = 1; 180d40c8c6bSrillig } 181d40c8c6bSrillig if (state.heading2 != NULL) { 182d40c8c6bSrillig (void)fprintf(debug_file(), "\t%s:\n", state.heading2); 183d40c8c6bSrillig state.heading2 = NULL; 184d40c8c6bSrillig state.wrote_newlines = 1; 185d40c8c6bSrillig } 186d40c8c6bSrillig } 187d40c8c6bSrillig 18844b10107Srillig void 18944b10107Srillig debug_printf(const char *fmt, ...) 19044b10107Srillig { 19144b10107Srillig va_list ap; 19244b10107Srillig 193d40c8c6bSrillig debug_print_headings(); 19444b10107Srillig va_start(ap, fmt); 195d40c8c6bSrillig (void)vfprintf(debug_file(), fmt, ap); 19644b10107Srillig va_end(ap); 19789f19ae3Srillig state.wrote_newlines = 0; 19844b10107Srillig } 19944b10107Srillig 20044b10107Srillig void 20144b10107Srillig debug_println(const char *fmt, ...) 20244b10107Srillig { 20344b10107Srillig va_list ap; 20444b10107Srillig 205d40c8c6bSrillig debug_print_headings(); 20644b10107Srillig va_start(ap, fmt); 207d40c8c6bSrillig (void)vfprintf(debug_file(), fmt, ap); 20844b10107Srillig va_end(ap); 209d40c8c6bSrillig (void)fprintf(debug_file(), "\n"); 21089f19ae3Srillig state.wrote_newlines = fmt[0] == '\0' ? state.wrote_newlines + 1 : 1; 211fe5b389bSrillig } 212fe5b389bSrillig 213fe5b389bSrillig void 214fe5b389bSrillig debug_blank_line(void) 215fe5b389bSrillig { 21689f19ae3Srillig while (state.wrote_newlines < 2) 217fe5b389bSrillig debug_println(""); 21844b10107Srillig } 21944b10107Srillig 22044b10107Srillig void 221b520fe89Srillig debug_vis_range(const char *s, size_t len) 22244b10107Srillig { 223b520fe89Srillig debug_printf("\""); 22444b10107Srillig for (size_t i = 0; i < len; i++) { 22544b10107Srillig const char *p = s + i; 22644b10107Srillig if (*p == '\\' || *p == '"') 22744b10107Srillig debug_printf("\\%c", *p); 22844b10107Srillig else if (isprint((unsigned char)*p)) 22944b10107Srillig debug_printf("%c", *p); 23044b10107Srillig else if (*p == '\n') 23144b10107Srillig debug_printf("\\n"); 23244b10107Srillig else if (*p == '\t') 23344b10107Srillig debug_printf("\\t"); 23444b10107Srillig else 23544b10107Srillig debug_printf("\\x%02x", (unsigned char)*p); 23644b10107Srillig } 237b520fe89Srillig debug_printf("\""); 23844b10107Srillig } 23944b10107Srillig 240ef25c88eSrillig void 241ff5c4a3fSrillig debug_print_buf(const char *name, const struct buffer *buf) 242ff5c4a3fSrillig { 2439a2185a1Srillig if (buf->len > 0) { 244ff5c4a3fSrillig debug_printf(" %s ", name); 245b520fe89Srillig debug_vis_range(buf->s, buf->len); 246ff5c4a3fSrillig } 247ff5c4a3fSrillig } 248ff5c4a3fSrillig 249ff5c4a3fSrillig void 250d40c8c6bSrillig debug_buffers(const char *descr) 251ff5c4a3fSrillig { 252d40c8c6bSrillig if (lab.len > 0 || code.len > 0 || com.len > 0) { 253d40c8c6bSrillig debug_printf("%s:", descr); 254fe5b389bSrillig debug_print_buf("label", &lab); 255fe5b389bSrillig debug_print_buf("code", &code); 256fe5b389bSrillig debug_print_buf("comment", &com); 257dd953560Srillig debug_blank_line(); 258ff5c4a3fSrillig } 259d40c8c6bSrillig } 260ff5c4a3fSrillig 26189f19ae3Srillig static void 262b520fe89Srillig debug_ps_bool_member(const char *name, bool prev, bool curr) 26389f19ae3Srillig { 2645cfa7113Srillig if (!state.ps_first && curr != prev) { 26589f19ae3Srillig char diff = " -+x"[(prev ? 1 : 0) + (curr ? 2 : 0)]; 266d40c8c6bSrillig debug_println("\t\t%s: [%c]", name, diff); 26789f19ae3Srillig } else if (config.full_parser_state || state.ps_first) 268d40c8c6bSrillig debug_println("\t\t%s: [%c]", name, curr ? 'x' : ' '); 26989f19ae3Srillig } 27089f19ae3Srillig 27189f19ae3Srillig static void 272b520fe89Srillig debug_ps_int_member(const char *name, int prev, int curr) 27389f19ae3Srillig { 2745cfa7113Srillig if (!state.ps_first && curr != prev) 275d40c8c6bSrillig debug_println("\t\t%s: %d -> %d", name, prev, curr); 27689f19ae3Srillig else if (config.full_parser_state || state.ps_first) 277d40c8c6bSrillig debug_println("\t\t%s: %d", name, curr); 27889f19ae3Srillig } 27989f19ae3Srillig 28089f19ae3Srillig static void 281b520fe89Srillig debug_ps_enum_member(const char *name, const char *prev, const char *curr) 28289f19ae3Srillig { 2835cfa7113Srillig if (!state.ps_first && strcmp(prev, curr) != 0) 284d40c8c6bSrillig debug_println("\t\t%s: %s -> %s", name, prev, curr); 28589f19ae3Srillig else if (config.full_parser_state || state.ps_first) 286d40c8c6bSrillig debug_println("\t\t%s: %s", name, curr); 28789f19ae3Srillig } 288ff5c4a3fSrillig 289*8af25fb9Srillig static void 290*8af25fb9Srillig debug_ps_str_member(const char *name, 291*8af25fb9Srillig void (*to_string)(struct buffer *, const struct parser_state *)) 292ff5c4a3fSrillig { 293*8af25fb9Srillig static struct buffer prev_buf; 294*8af25fb9Srillig static struct buffer curr_buf; 295ff5c4a3fSrillig 296*8af25fb9Srillig buf_clear(&prev_buf); 297*8af25fb9Srillig to_string(&prev_buf, &state.prev_ps); 298*8af25fb9Srillig buf_clear(&curr_buf); 299*8af25fb9Srillig to_string(&curr_buf, &ps); 300*8af25fb9Srillig 301*8af25fb9Srillig if (!state.ps_first && strcmp(prev_buf.s, curr_buf.s) != 0) 302*8af25fb9Srillig debug_println("\t\t%s: %s -> %s", 303*8af25fb9Srillig name, prev_buf.s, curr_buf.s); 304*8af25fb9Srillig else if (config.full_parser_state || state.ps_first) 305*8af25fb9Srillig debug_println("\t\t%s: %s", name, curr_buf.s); 306ff5c4a3fSrillig } 307ff5c4a3fSrillig 308ff5c4a3fSrillig static void 309*8af25fb9Srillig ps_di_stack_to_string(struct buffer *buf, const struct parser_state *s) 310ff5c4a3fSrillig { 311*8af25fb9Srillig for (int i = 0; i < s->decl_level; i++) { 312*8af25fb9Srillig char str[64]; 313*8af25fb9Srillig snprintf(str, sizeof(str), "%s%d", 314*8af25fb9Srillig i > 0 ? " " : "", s->di_stack[i]); 315*8af25fb9Srillig buf_add_str(buf, str); 316ff5c4a3fSrillig } 317*8af25fb9Srillig if (s->decl_level == 0) 318*8af25fb9Srillig buf_add_str(buf, "none"); 319e174834cSrillig } 320e174834cSrillig 321e174834cSrillig static void 322*8af25fb9Srillig ps_paren_to_string(struct buffer *buf, const struct parser_state *s) 323e174834cSrillig { 324*8af25fb9Srillig for (size_t i = 0; i < s->paren.len; i++) { 325*8af25fb9Srillig buf_add_str(buf, i > 0 ? " " : ""); 326*8af25fb9Srillig buf_add_str(buf, paren_level_cast_name[s->paren.item[i].cast]); 327*8af25fb9Srillig char str[64]; 328*8af25fb9Srillig snprintf(str, sizeof(str), " %d", s->paren.item[i].indent); 329*8af25fb9Srillig buf_add_str(buf, str); 330*8af25fb9Srillig } 331*8af25fb9Srillig if (s->paren.len == 0) 332*8af25fb9Srillig buf_add_str(buf, "none"); 333*8af25fb9Srillig } 334e174834cSrillig 335*8af25fb9Srillig void 336*8af25fb9Srillig ps_psyms_to_string(struct buffer *buf, const struct parser_state *s) 337*8af25fb9Srillig { 338*8af25fb9Srillig for (size_t i = 0; i < s->psyms.len; i++) { 339*8af25fb9Srillig char num[64]; 340*8af25fb9Srillig snprintf(num, sizeof(num), "%s%d ", 341*8af25fb9Srillig i > 0 ? " " : "", s->psyms.ind_level[i]); 342*8af25fb9Srillig buf_add_str(buf, num); 343*8af25fb9Srillig buf_add_str(buf, psym_name[s->psyms.sym[i]]); 344*8af25fb9Srillig } 345e174834cSrillig } 346e174834cSrillig 34789f19ae3Srillig #define debug_ps_bool(name) \ 348b520fe89Srillig debug_ps_bool_member(#name, state.prev_ps.name, ps.name) 34989f19ae3Srillig #define debug_ps_int(name) \ 350b520fe89Srillig debug_ps_int_member(#name, state.prev_ps.name, ps.name) 35189f19ae3Srillig #define debug_ps_enum(name, names) \ 352b520fe89Srillig debug_ps_enum_member(#name, (names)[state.prev_ps.name], \ 353b520fe89Srillig (names)[ps.name]) 354*8af25fb9Srillig #define debug_ps_str(name, to_string) \ 355*8af25fb9Srillig debug_ps_str_member((/* LINTED 129 */(void)&ps.name, #name), to_string) 35689f19ae3Srillig 357ff5c4a3fSrillig void 358fe5b389bSrillig debug_parser_state(void) 359ff5c4a3fSrillig { 360fe5b389bSrillig debug_blank_line(); 3612a46ceffSrillig 362d40c8c6bSrillig state.heading1 = "parser state"; 363d40c8c6bSrillig state.heading2 = "token classification"; 3645cfa7113Srillig debug_ps_enum(prev_lsym, lsym_name); 3652a46ceffSrillig debug_ps_bool(in_stmt_or_decl); 3662a46ceffSrillig debug_ps_bool(in_decl); 367c4d54aecSrillig debug_ps_bool(in_typedef_decl); 368338e471fSrillig debug_ps_bool(in_var_decl); 369338e471fSrillig debug_ps_bool(in_init); 370338e471fSrillig debug_ps_int(init_level); 371645d5d22Srillig debug_ps_bool(line_has_func_def); 3722a46ceffSrillig debug_ps_bool(in_func_def_params); 373338e471fSrillig debug_ps_bool(line_has_decl); 3742a46ceffSrillig debug_ps_enum(lbrace_kind, psym_name); 375338e471fSrillig debug_ps_enum(spaced_expr_psym, psym_name); 376338e471fSrillig debug_ps_bool(seen_case); 377ae40ebbcSrillig debug_ps_bool(prev_paren_was_cast); 378338e471fSrillig debug_ps_int(quest_level); 3792a46ceffSrillig 380d40c8c6bSrillig state.heading2 = "indentation of statements and declarations"; 3812a46ceffSrillig debug_ps_int(ind_level); 3822a46ceffSrillig debug_ps_int(ind_level_follow); 383*8af25fb9Srillig debug_ps_str(psyms, ps_psyms_to_string); 3845a6a9cd4Srillig debug_ps_bool(line_is_stmt_cont); 3852a46ceffSrillig debug_ps_int(decl_level); 386*8af25fb9Srillig debug_ps_str(di_stack, ps_di_stack_to_string); 3872a46ceffSrillig debug_ps_bool(decl_indent_done); 3882a46ceffSrillig debug_ps_int(decl_ind); 3892a46ceffSrillig debug_ps_bool(tabs_to_var); 3902a46ceffSrillig debug_ps_enum(extra_expr_indent, extra_expr_indent_name); 3912a46ceffSrillig 392d40c8c6bSrillig state.heading2 = "spacing inside a statement or declaration"; 3932a46ceffSrillig debug_ps_bool(next_unary); 394ff5c4a3fSrillig debug_ps_bool(want_blank); 3954e81574bSrillig debug_ps_int(ind_paren_level); 396*8af25fb9Srillig debug_ps_str(paren, ps_paren_to_string); 397ff5c4a3fSrillig 398d40c8c6bSrillig state.heading2 = "indentation of comments"; 399ed7b4359Srillig debug_ps_int(comment_ind); 400ed7b4359Srillig debug_ps_int(comment_shift); 40137a17710Srillig debug_ps_bool(comment_cont); 402ff5c4a3fSrillig 403d40c8c6bSrillig state.heading2 = "vertical spacing"; 4042a46ceffSrillig debug_ps_bool(break_after_comma); 405e914aa7bSrillig debug_ps_enum(newline, newline_name); 406ae0013d2Srillig debug_ps_enum(declaration, declaration_name); 4072261e976Srillig debug_ps_bool(blank_line_after_decl); 408f6c6195cSrillig debug_ps_enum(badp, badp_name); 409ff5c4a3fSrillig 410d40c8c6bSrillig state.heading1 = NULL; 411d40c8c6bSrillig state.heading2 = NULL; 412fe5b389bSrillig debug_blank_line(); 413ff5c4a3fSrillig 4141fe8cbaaSrillig parser_state_free(&state.prev_ps); 4151fe8cbaaSrillig parser_state_back_up(&state.prev_ps); 41689f19ae3Srillig state.ps_first = false; 417ff5c4a3fSrillig } 418ff5c4a3fSrillig #endif 419