Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx> writes: > 2018-06-23 4:27 GMT+09:00 Dirk Gouders <dirk@xxxxxxxxxxx>: >> Currently, we need to modify existing kconfig targets to generate debugging >> information from the parser. That information then has to be filtered >> somehow, automated tests are at least complicated to implement. >> >> Add a debugconfig target for pure debugging purposes; the initial >> version of this target dumps the menu tree and with the --debug option >> activates the parsers debugging output via it's global variable >> cdebug. The environment variable ZCONF_DEBUG can be used to generate >> even more detailed debugging information. >> >> Sample output for a simple Kconfig file: >> >> $ scripts/kconfig/dconf --debug Kconfig.dconfig >> Kconfig.dconfig:1:config a >> Kconfig.dconfig:2:type(1) >> Kconfig.dconfig:4:endconfig >> Kconfig.dconfig:5:if >> Kconfig.dconfig:5:config b >> Kconfig.dconfig:6:type(1) >> Kconfig.dconfig:7:endconfig >> Kconfig.dconfig:7:endif >> >> config a >> bool >> symbol a >> prompt "a" >> >> config b >> bool >> symbol b >> prompt "b" if a >> >> endmenu >> >> Signed-off-by: Dirk Gouders <dirk@xxxxxxxxxxx> >> Cc: Sam Ravnborg <sam@xxxxxxxxxxxx> >> --- >> scripts/kconfig/Makefile | 10 ++++++++- >> scripts/kconfig/dconf.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ >> 2 files changed, 64 insertions(+), 1 deletion(-) >> create mode 100644 scripts/kconfig/dconf.c >> >> diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile >> index a3ac2c91331c..19906ff25392 100644 >> --- a/scripts/kconfig/Makefile >> +++ b/scripts/kconfig/Makefile >> @@ -4,7 +4,7 @@ >> # These targets are used from top-level makefile >> >> PHONY += xconfig gconfig menuconfig config syncconfig \ >> - localmodconfig localyesconfig >> + localmodconfig localyesconfig debugconfig >> >> ifdef KBUILD_KCONFIG >> Kconfig := $(KBUILD_KCONFIG) >> @@ -34,6 +34,9 @@ config: $(obj)/conf >> nconfig: $(obj)/nconf >> $< $(silent) $(Kconfig) >> >> +debugconfig: $(obj)/dconf >> + $< $(silent) $(Kconfig) >> + >> # This has become an internal implementation detail and is now deprecated >> # for external use. >> syncconfig: $(obj)/conf >> @@ -149,6 +152,7 @@ help: >> @echo ' xenconfig - Enable additional options for xen dom0 and guest kernel support' >> @echo ' tinyconfig - Configure the tiniest possible kernel' >> @echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)' >> + @echo ' debugconfig - Debugging tool for developers' >> >> # =========================================================================== >> # Shared Makefile for the various kconfig executables: >> @@ -165,6 +169,10 @@ targets += zconf.lex.c >> HOSTCFLAGS_zconf.lex.o := -I$(src) >> HOSTCFLAGS_zconf.tab.o := -I$(src) >> >> +# dconf: Used for kconfig debugging >> +hostprogs-y += dconf >> +dconf-objs := dconf.o zconf.tab.o >> + >> # nconf: Used for the nconfig target based on ncurses >> hostprogs-y += nconf >> nconf-objs := nconf.o zconf.tab.o nconf.gui.o >> diff --git a/scripts/kconfig/dconf.c b/scripts/kconfig/dconf.c >> new file mode 100644 >> index 000000000000..ed1edf607d80 >> --- /dev/null >> +++ b/scripts/kconfig/dconf.c >> @@ -0,0 +1,55 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +// >> +// Copyright (C) 2018 Dirk Gouders <dirk@xxxxxxxxxxx> >> + >> +#include <stdlib.h> >> +#include <stdio.h> >> +#include <getopt.h> >> + >> +#include "lkc.h" >> + >> +extern int cdebug; >> + >> +void usage(const char *progname); >> +void usage(const char *progname) >> +{ >> + printf("Usage: %s --help | [--debug] <kconfig-file>\n", progname); >> +} >> + >> +int main(int argc, char *argv[]) >> +{ >> + int c; >> + int opt_index = 0; >> + char *filename; >> + >> + struct option long_opts[] = { >> + {"debug", no_argument, NULL, 0}, >> + {"help", no_argument, NULL, 1}, >> + {0, 0, 0, 0} >> + }; >> + >> + while (1 ) { >> + c = getopt_long(argc, argv, "", long_opts, &opt_index); >> + >> + if (c == -1) >> + break; >> + if (c == 0) { >> + cdebug = 0x02; >> + } >> + if (c == 1) { >> + usage(argv[0]); >> + exit(EXIT_SUCCESS); >> + } >> + } >> + >> + if (optind == argc) { >> + usage(argv[0]); >> + exit(EXIT_FAILURE); >> + } else >> + filename = argv[optind]; >> + >> + conf_parse(filename); >> + zconfdump(stdout); >> + >> + exit(EXIT_SUCCESS); >> +} >> -- >> 2.13.6 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > > You mentioned ZCONF_DEBUG in the log. > > So, another possibility is to use > an environment variable to turn on debug features. > > name = getenv("KCONFIG_DEBUG"); > if (name && name[0] == '1') > zconfdump(stdout); Yes, that would also be a possibility. Perhaps even more flexibility would be gained if a path is allowed for the content of the environment variable to specify where the dump should go, e.g. KCONFIG_DEBUG="./zconfdump-%d.out". This way, we could dump without disturbing std{err,out}. Since some days, I am thinking about the possibility of a "knob" for *config targets, once triggered, a dump will be produced to the specified location. But thinking of menuconfig, for example, I have not yet an idea what this "knob" could be -- we don't have many unused keys left. Perhaps a signal... The following is only meant for exchange of ideas -- I currently do some debugging this way but chances are that all of it is not thought trough, very well and too complicated: I am wondering if -- instead of providing a debugging tool -- it would be more practical to offer developers a hook to enable easy use of personal debugging tools through make(1) without disturbing git and the need to trimm the mainstream Makefile every time: 1) Add a hook to scripts/kconfig/Makefile to allow developers to add own tools if they want to: # # Include optional Makefile that developers can trimm without # disturbing git to make use of tools that are still under development # or not adequate for mainstream. # -include scripts/kconfig/Makefile.devel 2) My Makefile.devel then looks like PHONY += debugconfig debugconfig: $(obj)/dconf $< $(silent) $(dconfargs) $(Kconfig) # dconf: Used for kconfig debugging hostprogs-y += dconf dconf-objs := dconf.o zconf.tab.o 3) This allows me to call debugconfig like this: make dconfargs="--symdump=symdump" Kconfig=Kconfig_for_testing debugconfig 4) In case you are interested, my current debugging tool is attached. It grew a bit and it is bigger than it could be, because (for now) I want it to be standalone, without disturbing files that are controlled by git. The tool now also produces a dump of all symbols, a bit different compared to walking symbols via the menu. Dirk
// SPDX-License-Identifier: GPL-2.0 // // Copyright (C) 2018 Dirk Gouders <dirk@xxxxxxxxxxx> #include <stdlib.h> #include <stdio.h> #include <getopt.h> #include <string.h> #include "lkc.h" #include "expr.h" extern int cdebug; #define SYMBOL_NO_WRITE 0x1000 void usage(const char *progname); void usage(const char *progname) { printf("Usage: %s --help | [--debug] [--menudump[=file]] [--symdump[=file]] <kconfig-file>\n", progname); } static void dconf_check_types() { } static void print_quoted_string(FILE *out, const char *str) { const char *p; int len; putc('"', out); while ((p = strchr(str, '"'))) { len = p - str; if (len) fprintf(out, "%.*s", len, str); fputs("\\\"", out); str = p + 1; } fputs(str, out); putc('"', out); } static void sym_print_flags(FILE *out, struct symbol *sym) { char *space = ""; fprintf(out, "Flags: "); if (!sym->flags) { fprintf(out, "\n"); return; } if (sym->flags & SYMBOL_CONST) { fprintf(out, "CONST"); space = ", "; } if (sym->flags & SYMBOL_CHECK) { fprintf(out, "%sCHECK", space); space = ", "; } if (sym->flags & SYMBOL_CHOICE) { fprintf(out, "%sCHOICE", space); space = ", "; } if (sym->flags & SYMBOL_CHOICEVAL) { fprintf(out, "%sCHOICEVAL", space); space = ", "; } if (sym->flags & SYMBOL_VALID) { fprintf(out, "%sVALID", space); space = ", "; } if (sym->flags & SYMBOL_OPTIONAL) { fprintf(out, "%sOPTIONAL", space); space = ", "; } if (sym->flags & SYMBOL_WRITE) { fprintf(out, "%sWRITE", space); space = ", "; } if (sym->flags & SYMBOL_CHANGED) { fprintf(out, "%sCHANGED", space); space = ", "; } if (sym->flags & SYMBOL_NO_WRITE) { fprintf(out, "%sNO_WRITE", space); space = ", "; } if (sym->flags & SYMBOL_CHECKED) { fprintf(out, "%sCHECKED", space); space = ", "; } if (sym->flags & SYMBOL_WARNED) { fprintf(out, "%sWARNED", space); } fprintf(out, "\n"); } static void print_symbol(FILE *out, struct symbol *sym) { struct property *prop; if (sym_is_choice(sym)) fprintf(out, "\nchoice\n"); else fprintf(out, "\nconfig %s\n", sym->name); sym_print_flags(out, sym); switch (sym->type) { case S_BOOLEAN: fputs(" bool\n", out); break; case S_TRISTATE: fputs(" tristate\n", out); break; case S_STRING: fputs(" string\n", out); break; case S_INT: fputs(" integer\n", out); break; case S_HEX: fputs(" hex\n", out); break; default: fputs(" unknown\n", out); break; } fprintf(out, "Value: %s\n", sym_get_string_value(sym)); for (prop = sym->prop; prop; prop = prop->next) { switch (prop->type) { case P_PROMPT: fputs(" prompt ", out); print_quoted_string(out, prop->text); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_DEFAULT: fputs( " default ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_CHOICE: fputs(" #choice value\n", out); break; case P_SELECT: fputs( " select ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_IMPLY: fputs( " imply ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_RANGE: fputs( " range ", out); expr_fprint(prop->expr, out); if (!expr_is_yes(prop->visible.expr)) { fputs(" if ", out); expr_fprint(prop->visible.expr, out); } fputc('\n', out); break; case P_MENU: fputs( " menu ", out); print_quoted_string(out, prop->text); fputc('\n', out); break; case P_SYMBOL: fputs( " symbol ", out); if (prop->sym) fprintf(out, "%s (file %s:%d)\n", prop->sym->name, prop->file->name, prop->lineno); break; default: fprintf(out, " unknown prop %d!\n", prop->type); break; } } fprintf(out, " Depends on: "); if (!expr_is_yes(sym->dir_dep.expr)) expr_fprint(sym->dir_dep.expr, out); fprintf(out, "\n Selected by: "); if (!expr_is_yes(sym->rev_dep.expr)) expr_fprint(sym->rev_dep.expr, out); fprintf(out, "\n Implied by: "); if (!expr_is_yes(sym->implied.expr)) expr_fprint(sym->implied.expr, out); fputc('\n', out); } void sym_dump(FILE *); void sym_dump(FILE *out) { struct symbol *sym; int i; print_symbol(out, &symbol_yes); print_symbol(out, &symbol_mod); print_symbol(out, &symbol_no); for_all_symbols(i, sym) { print_symbol(out, sym); } } int main(int argc, char *argv[]) { int c; int opt_index = 0; char *filename; int menudump = 0; /* Print menu structure */ FILE *menudump_out; int symdump = 0; /* Print all symbols */ FILE *symdump_out; struct option long_opts[] = { {"debug", no_argument, NULL, 0}, {"help", no_argument, NULL, 1}, {"menudump", optional_argument, NULL, 2}, {"symdump", optional_argument, NULL, 3}, {0, 0, 0, 0} }; while (1 ) { c = getopt_long(argc, argv, "", long_opts, &opt_index); if (c == -1) break; if (c == 0) { cdebug = 0x02; } if (c == 1) { usage(argv[0]); exit(EXIT_SUCCESS); } if (c == 2) { menudump = 1; if (optarg) { if (!strcmp(optarg, "stdout")) menudump_out = stdout; else if (!strcmp(optarg, "stderr")) menudump_out = stderr; else { menudump_out = fopen(optarg, "w"); if (!menudump_out) { perror(optarg); } } } else menudump_out = stdout; } if (c == 3) { symdump = 1; if (optarg) { if (!strcmp(optarg, "stdout")) symdump_out = stdout; else if (!strcmp(optarg, "stderr")) symdump_out = stderr; else { symdump_out = fopen(optarg, "w"); if (!symdump_out) { perror(optarg); } } } else symdump_out = stdout; } } if (optind == argc) { usage(argv[0]); exit(EXIT_FAILURE); } else filename = argv[optind]; //getc(stdin); conf_parse(filename); //conf_read(NULL); if (menudump) zconfdump(menudump_out); if (symdump) sym_dump(symdump_out); exit(EXIT_SUCCESS); }