Re: [PATCH] scripts: add macro_checker script to check unused parameters in macros

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jan Kara <jack@xxxxxxx> 于2024年7月24日周三 06:00写道:
>
> Hi!
>
> On Tue 23-07-24 05:11:54, Julian Sun wrote:
> > Recently, I saw a patch[1] on the ext4 mailing list regarding
> > the correction of a macro definition error. Jan mentioned
> > that "The bug in the macro is a really nasty trap...".
> > Because existing compilers are unable to detect
> > unused parameters in macro definitions. This inspired me
> > to write a script to check for unused parameters in
> > macro definitions and to run it.
> >
> > Surprisingly, the script uncovered numerous issues across
> > various subsystems, including filesystems, drivers, and sound etc.
> >
> > Some of these issues involved parameters that were accepted
> > but never used, for example:
> >       #define XFS_DAENTER_DBS(mp,w)   \
> >       (XFS_DA_NODE_MAXDEPTH + (((w) == XFS_DATA_FORK) ? 2 : 0))
> > where mp was unused.
> >
> > While others are actual bugs.
> > For example:
> >       #define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(x) \
> >               (ab->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
> >       #define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(x) \
> >               (ab->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
> >       #define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(x) \
> >               (ab->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
> >       #define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(x) \
> >               (ab->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
> > where x was entirely unused, and instead, a local variable ab was used.
> >
> > I have submitted patches[2-5] to fix some of these issues,
> > but due to the large number, many still remain unaddressed.
> > I believe that the kernel and matainers would benefit from
> > this script to check for unused parameters in macro definitions.
> >
> > It should be noted that it may cause some false positives
> > in conditional compilation scenarios, such as
> >       #ifdef DEBUG
> >       static int debug(arg) {};
> >       #else
> >       #define debug(arg)
> >       #endif
> > So the caller needs to manually verify whether it is a true
> > issue. But this should be fine, because Maintainers should only
> > need to review their own subsystems, which typically results
> > in only a few reports.
>
> Useful script! Thanks!
>
>
> > I think you could significantly reduce these false positives by checking
> > whether the macro definition ends up being empty, 0, or "do { } while (0)"
> > and in those cases don't issue a warning about unused arguments because it
> > is pretty much guaranteed the author meant it this way in these cases. You
> > seem to be already detecting the last pattern so adding the first two
> > should be easy.
Great suggestion! I will refine this script and send patch v2.
Thanks for you suggestion, Jan.
>
>                                                                 Honza
>
> >
> > [1]: https://patchwork.ozlabs.org/project/linux-ext4/patch/1717652596-58760-1-git-send-email-carrionbent@xxxxxxxxxxxxxxxxx/
> > [2]: https://lore.kernel.org/linux-xfs/20240721112701.212342-1-sunjunchao2870@xxxxxxxxx/
> > [3]: https://lore.kernel.org/linux-bcachefs/20240721123943.246705-1-sunjunchao2870@xxxxxxxxx/
> > [4]: https://sourceforge.net/p/linux-f2fs/mailman/message/58797811/
> > [5]: https://sourceforge.net/p/linux-f2fs/mailman/message/58797812/
> >
> > Signed-off-by: Julian Sun <sunjunchao2870@xxxxxxxxx>
> > ---
> >  scripts/macro_checker.py | 101 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 101 insertions(+)
> >  create mode 100755 scripts/macro_checker.py
> >
> > diff --git a/scripts/macro_checker.py b/scripts/macro_checker.py
> > new file mode 100755
> > index 000000000000..cd10c9c10d31
> > --- /dev/null
> > +++ b/scripts/macro_checker.py
> > @@ -0,0 +1,101 @@
> > +#!/usr/bin/python3
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Author: Julian Sun <sunjunchao2870@xxxxxxxxx>
> > +
> > +""" Find macro definitions with unused parameters. """
> > +
> > +import argparse
> > +import os
> > +import re
> > +
> > +macro_pattern = r"#define\s+(\w+)\(([^)]*)\)"
> > +# below two vars were used to reduce false positives
> > +do_while0_pattern = r"\s*do\s*\{\s*\}\s*while\s*\(\s*0\s*\)"
> > +correct_macros = []
> > +
> > +def check_macro(macro_line, report):
> > +    match = re.match(macro_pattern, macro_line)
> > +    if match:
> > +        macro_def = re.sub(macro_pattern, '', macro_line)
> > +        identifier = match.group(1)
> > +        content = match.group(2)
> > +        arguments = [item.strip() for item in content.split(',') if item.strip()]
> > +
> > +        if (re.match(do_while0_pattern, macro_def)):
> > +            return
> > +
> > +        for arg in arguments:
> > +            # used to reduce false positives
> > +            if "..." in arg:
> > +                continue
> > +            if not arg in macro_def and report == False:
> > +                return
> > +            if not arg in macro_def and identifier not in correct_macros:
> > +                print(f"Argument {arg} is not used in function-line macro {identifier}")
> > +                return
> > +
> > +        correct_macros.append(identifier)
> > +
> > +
> > +# remove comment and whitespace
> > +def macro_strip(macro):
> > +    comment_pattern1 = r"\/\/*"
> > +    comment_pattern2 = r"\/\**\*\/"
> > +
> > +    macro = macro.strip()
> > +    macro = re.sub(comment_pattern1, '', macro)
> > +    macro = re.sub(comment_pattern2, '', macro)
> > +
> > +    return macro
> > +
> > +def file_check_macro(file_path, report):
> > +    # only check .c and .h file
> > +    if not file_path.endswith(".c") and not file_path.endswith(".h"):
> > +        return
> > +
> > +    with open(file_path, "r") as f:
> > +        while True:
> > +            line = f.readline()
> > +            if not line:
> > +                return
> > +
> > +            macro = re.match(macro_pattern, line)
> > +            if macro:
> > +                macro = macro_strip(macro.string)
> > +                while macro[-1] == '\\':
> > +                    macro = macro[0:-1]
> > +                    macro = macro.strip()
> > +                    macro += f.readline()
> > +                    macro = macro_strip(macro)
> > +                check_macro(macro, report)
> > +
> > +def get_correct_macros(path):
> > +    file_check_macro(path, False)
> > +
> > +def dir_check_macro(dir_path):
> > +
> > +    for dentry in os.listdir(dir_path):
> > +        path = os.path.join(dir_path, dentry)
> > +        if os.path.isdir(path):
> > +            dir_check_macro(path)
> > +        elif os.path.isfile(path):
> > +            get_correct_macros(path)
> > +            file_check_macro(path, True)
> > +
> > +
> > +def main():
> > +    parser = argparse.ArgumentParser()
> > +
> > +    parser.add_argument("path", type=str, help="The file or dir path that needs check")
> > +    args = parser.parse_args()
> > +
> > +    if os.path.isfile(args.path):
> > +        get_correct_macros(args.path)
> > +        file_check_macro(args.path, True)
> > +    elif os.path.isdir(args.path):
> > +        dir_check_macro(args.path)
> > +    else:
> > +        print(f"{args.path} doesn't exit or is neither a file nor a dir")
> > +
> > +if __name__ == "__main__":
> > +    main()
> > \ No newline at end of file
> > --
> > 2.39.2
> >
> --
> Jan Kara <jack@xxxxxxxx>
> SUSE Labs, CR


Thanks,
-- 
Julian Sun <sunjunchao2870@xxxxxxxxx>





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux