[LTP] [PATCH 1/3] lib/tst_kconfig: Rewrite the parser internals
Cyril Hrubis
chrubis@suse.cz
Wed Oct 21 16:11:57 CEST 2020
Hi!
> >> lines first to remove whitespace issues and expose the parser to all
> >> possible variable name symbols and values instead of just the ones which
> >> appear in our current tests.
> >
> > I guess that it's techincally possible to have a whitespaces there, but
> > will not happen unless you hand-edit the config file before compilation,
> > which I doubt will ever happen.
> >
>
> It can also happen if someone has their own script to modify the
> config. At any rate, if you are confident that it will never happen then
> there should be no problem failing hard if it does.
It would be probably easier to eat the whitespace around the = if
present. But still I would ignore anything that isn't correct variable
assignment, since such config would fail kernel compilation anyways.
> >> > + switch (val[0]) {
> >> > + case '=':
> >> > + break;
> >> > + case ' ':
> >> > + if (is_set(val, "is not set")) {
> >> > + vars[i].choice = 'n';
> >> > + return 1;
> >> > + }
> >>
> >> Typically such lines begin with a comment '#' and I don't see where that
> >> is handled. Possibly this will only match non standard configs?
> >
> > It does work actually, since we use strstr() to get the "CONFIG_" prefix
> > from each line of the configuration, but I guess this needs to be fixed
> > anyways since we would detect "# CONFIG_FOO=y" as enabled config feature
> > even if it's commented. Again this will not happen unless you hand-edit
> > the file, but it's probably worth fixing in a follow up patch.
>
> We don't actually use the result of strstr anymore?
Ah right, that's a bug, the cfg should be passed to the
kconfig_parse_line() instead, at least that's how the previous version
worked in order to differentiate between unset and unknown variables.
> >> > + return 1;
> >> > + /* vars[i].id may be prefix to longer config var */
> >> > + default:
> >> > + return 0;
> >> > + }
> >> >
> >> > - if (!cfg)
> >> > - return 0;
> >> > + if (is_set(val, "=y")) {
> >> > + vars[i].choice = 'y';
> >> > + return 1;
> >> > + }
> >> >
> >> > - if (strncmp(cfg, conf, match->len))
> >> > - return 0;
> >> > + if (is_set(val, "=m")) {
> >> > + vars[i].choice = 'm';
> >> > + return 1;
> >> > + }
> >> >
> >> > - const char *val = &cfg[match->len];
> >> > + vars[i].choice = 'v';
> >> > + vars[i].val = strndup(val+1, strlen(val)-2);
> >> >
> >> > - switch (cfg[match->len]) {
> >> > - case '=':
> >> > - break;
> >> > - case ' ':
> >> > - if (is_set(val, "is not set")) {
> >> > - result->match = 'n';
> >> > - goto match;
> >> > + return 1;
> >> > }
> >> > - /* fall through */
> >> > - default:
> >> > - return 0;
> >> > - }
> >> > -
> >> > - if (is_set(val, "=y")) {
> >> > - result->match = 'y';
> >> > - goto match;
> >> > }
> >> >
> >> > - if (is_set(val, "=m")) {
> >> > - result->match = 'm';
> >> > - goto match;
> >> > - }
> >> > -
> >> > - result->match = 'v';
> >> > - result->value = strndup(val+1, strlen(val)-2);
> >> > -
> >> > -match:
> >> > - match->match = 1;
> >> > - return 1;
> >> > + return 0;
> >> > }
> >> >
> >> > -void tst_kconfig_read(const char *const *kconfigs,
> >> > - struct tst_kconfig_res results[], size_t cnt)
> >> > +void tst_kconfig_read(struct tst_kconfig_var vars[], size_t vars_len)
> >> > {
> >> > - struct match matches[cnt];
> >> > - FILE *fp;
> >> > - unsigned int i, j;
> >> > - char buf[1024];
> >> > -
> >> > - for (i = 0; i < cnt; i++) {
> >> > - const char *val = strchr(kconfigs[i], '=');
> >> > -
> >> > - if (strncmp("CONFIG_", kconfigs[i], 7))
> >> > - tst_brk(TBROK, "Invalid config string '%s'", kconfigs[i]);
> >> > + char line[128];
> >> > + unsigned int vars_found = 0;
> >> >
> >> > - matches[i].match = 0;
> >> > - matches[i].len = strlen(kconfigs[i]);
> >> > -
> >> > - if (val) {
> >> > - matches[i].val = val + 1;
> >> > - matches[i].len -= strlen(val);
> >> > - }
> >> > -
> >> > - results[i].match = 0;
> >> > - results[i].value = NULL;
> >> > - }
> >> > -
> >> > - fp = open_kconfig();
> >> > + FILE *fp = open_kconfig();
> >> > if (!fp)
> >> > tst_brk(TBROK, "Cannot parse kernel .config");
> >> >
> >> > - while (fgets(buf, sizeof(buf), fp)) {
> >> > - for (i = 0; i < cnt; i++) {
> >> > - if (match(&matches[i], kconfigs[i], &results[i], buf)) {
> >> > - for (j = 0; j < cnt; j++) {
> >> > - if (matches[j].match)
> >> > - break;
> >> > - }
> >> > + while (fgets(line, sizeof(line), fp)) {
> >> > + char *cfg = strstr(line, "CONFIG_");
> >> >
> >> > - if (j == cnt)
> >> > - goto exit;
> >> > - }
> >> > - }
> >> > + if (!cfg)
> >> > + continue;
> >>
> >> This filtering seems unecessary and maybe will hide some corner cases
> >> because it reduces kconfig_parses_line's exposure. Also practically
> >> every line has 'CONFIG_' in it.
> >
> > Not really, there are empty lines and plenty of comments in the file
> > generated by kernel infrastructure.
>
> It seems about 80-90% of lines contain CONFIG_, however if you pass it
> to kconfig_parse_line then this makes more sense. Still I think with
> proper parsing this shouldn't be there.
What exactly do you mean by a proper parsing?
The file is format is very simple each line starts either with # which
is a comment or consists of 'key=val' pair and the key is by definition
prefixed with CONFIG_.
> >> > +
> >> > + if (kconfig_parse_line(line, vars, vars_len))
> >> > + vars_found++;
> >> >
> >> > + if (vars_found == vars_len)
> >> > + goto exit;
> >> > }
> >>
> >> Generally, this approach seems like to result in spurious TCONFs. We
> >> need to properly parse the file and fail if some line can't be
> >> interpreted.
> >
> > Well we do expect well formatted .config file from a start, if you hand
> > edit it and put whitespaces into unexpected places more things may
> > fail.
>
> Kernel build system seems to have no problem with it. More generally
> though we should fail hard if there is something unexpected, not produce
> TCONF which people don't check.
Even if we do I do not think that we should care about anything but
syntactically correct input, since if you modify the file after kernel
compilation you have lost anyways.
> >> I suppose most of the problems here stem from the original code, but
> >> your patch may as well clear it up :-)
> >
> > Actually the clear way how to fix this is in a separate patch so that
> > logical changes are split into different patches.
>
> I suppose that elements of the boolean parser can be used to parse the
> kconfig and it can be combined (in a later patch). It's kind of
> unecessary to parse a config file into RPN, but will work perfectly well
> so we can share some code here.
I do not get what you are trying to say. Are you saying that we should
tokenize the input? I do not think that this is necessary since the file
format is pretty simple.
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list