* [PATCH v2] xen/build: Add cppcheck and cppcheck-html make rules
@ 2022-04-13 13:11 Bertrand Marquis
2022-04-15 23:42 ` Stefano Stabellini
0 siblings, 1 reply; 3+ messages in thread
From: Bertrand Marquis @ 2022-04-13 13:11 UTC (permalink / raw)
To: xen-devel
Cc: Andrew Cooper, George Dunlap, Jan Beulich, Julien Grall,
Stefano Stabellini, Wei Liu, Volodymyr Babchuk, Michal Orzel
cppcheck can be used to check Xen code quality.
To create a report do "make cppcheck" on a built tree adding any options
you added during the process you used to build xen (like CROSS_COMPILE
or XEN_TARGET_ARCH). This will generate an xml report xen-cppcheck.xml.
To create a html report do "make cppcheck-html" in the same way and a
full report to be seen in a browser will be generated in
cppcheck-htmlreport/index.html.
For better results it is recommended to build your own cppcheck from the
latest sources that you can find at [1].
Development and result analysis has been done with cppcheck 2.7.
The Makefile rule is searching for all C files which have been compiled
(ie which have a generated .o file) and is running cppcheck on all of
them using the current configuration of xen so only the code actually
compiled is checked.
A new tool is introduced to merge all cppcheck reports into one global
report including all findings and removing duplicates.
Some extra variables can be used to customize the report:
- CPPCHECK can be used to give the full path to the cppcheck binary to
use (default is to use the one from the standard path).
- CPPCHECK_HTMLREPORT can be used to give the full path to
cppcheck-htmlreport (default is to use the one from the standard path).
This has been tested on several arm configurations (x86 should work but
has not been tested).
[1] https://cppcheck.sourceforge.io/
Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
Signed-off-by: Michal Orzel <michal.orzel@arm.com>
---
Changes in v2:
- catch parsing errors during merge and tell the user to update cppcheck
- fix alignments in Makefile
- use srctree and objtree instead of BASEDIR
- remove SCTLR invalid change
- add a check of cppcheck version and mandate version 2.7 or greater
- rework the merge script to make it faster and display progress
---
.gitignore | 3 +
xen/Makefile | 84 +++++++++++++++++++++++++++-
xen/arch/arm/include/asm/processor.h | 2 +
xen/include/xen/config.h | 4 ++
xen/include/xen/kconfig.h | 5 ++
xen/tools/merge_cppcheck_reports.py | 83 +++++++++++++++++++++++++++
6 files changed, 179 insertions(+), 2 deletions(-)
create mode 100755 xen/tools/merge_cppcheck_reports.py
diff --git a/.gitignore b/.gitignore
index d425be4bd9..0d2d60b8f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.o
*.d
*.d2
+*.c.cppcheck
*.opic
*.a
*.so
@@ -296,6 +297,7 @@ xen/.banner
xen/.config
xen/.config.old
xen/.xen.elf32
+xen/xen-cppcheck.xml
xen/System.map
xen/arch/x86/boot/mkelf32
xen/arch/x86/boot/cmdline.S
@@ -316,6 +318,7 @@ xen/arch/*/efi/runtime.c
xen/arch/*/include/asm/asm-offsets.h
xen/common/config_data.S
xen/common/config.gz
+xen/cppcheck-htmlreport
xen/include/headers*.chk
xen/include/compat/*
xen/include/config/
diff --git a/xen/Makefile b/xen/Makefile
index ebc7854c08..734f9f2b4f 100644
--- a/xen/Makefile
+++ b/xen/Makefile
@@ -335,7 +335,7 @@ export CFLAGS_UBSAN
endif # need-config
-main-targets := build install uninstall clean distclean MAP
+main-targets := build install uninstall clean distclean MAP cppcheck cppcheck-html
.PHONY: $(main-targets)
ifneq ($(XEN_TARGET_ARCH),x86_32)
$(main-targets): %: _% ;
@@ -423,15 +423,17 @@ _clean:
$(Q)$(MAKE) $(clean)=tools/kconfig
find . \( -name "*.o" -o -name ".*.d" -o -name ".*.d2" \
-o -name ".*.o.tmp" -o -name "*~" -o -name "core" \
- -o -name '*.lex.c' -o -name '*.tab.[ch]' \
+ -o -name '*.lex.c' -o -name '*.tab.[ch]' -o -name '*.c.cppcheck' \
-o -name "*.gcno" -o -name ".*.cmd" -o -name "lib.a" \) -exec rm -f {} \;
rm -f include/asm $(TARGET) $(TARGET).gz $(TARGET).efi $(TARGET).efi.map $(TARGET)-syms $(TARGET)-syms.map
rm -f asm-offsets.s arch/*/include/asm/asm-offsets.h
rm -f .banner .allconfig.tmp include/xen/compile.h
+ rm -f xen-cppcheck.xml
.PHONY: _distclean
_distclean: clean
rm -f tags TAGS cscope.files cscope.in.out cscope.out cscope.po.out GTAGS GPATH GRTAGS GSYMS .config
+ rm -rf $(CPPCHECK_HTMLREPORT_OUTDIR)
$(TARGET).gz: $(TARGET)
gzip -n -f -9 < $< > $@.new
@@ -505,6 +507,84 @@ cloc:
done; \
done | cloc --list-file=-
+# What cppcheck command to use.
+# To get proper results, it is recommended to build cppcheck manually from the
+# latest source and use CPPCHECK to give the full path to the built version.
+CPPCHECK ?= cppcheck
+
+# What cppcheck-htmlreport to use.
+# If you give the full path to a self compiled cppcheck, this should be set
+# to the full path to cppcheck-html in the htmlreport directory of cppcheck.
+# On recent distribution, this is available in the standard path.
+CPPCHECK_HTMLREPORT ?= cppcheck-htmlreport
+
+# By default we generate the report in cppcheck-htmlreport directory in the
+# build directory. This can be changed by giving a directory in this variable.
+CPPCHECK_HTMLREPORT_OUTDIR ?= cppcheck-htmlreport
+
+# Compile flags to pass to cppcheck:
+# - include directories and defines Xen Makefile is passing (from CFLAGS)
+# - include config.h as this is passed directly to the compiler.
+# - define CPPCHECK as we use to disable or enable some specific part of the
+# code to solve some cppcheck issues.
+# - explicitely enable some cppcheck checks as we do not want to use "all"
+# which includes unusedFunction which gives wrong positives as we check file
+# per file.
+#
+# Compiler defines are in compiler-def.h which is included in config.h
+#
+CPPCHECKFLAGS := -DCPPCHECK --max-ctu-depth=10 \
+ --enable=style,information,missingInclude \
+ --include=$(srctree)/include/xen/config.h \
+ -I $(srctree)/xsm/flask/include \
+ -I $(srctree)/include/xen/libfdt \
+ $(filter -D% -I%,$(CFLAGS))
+
+# We need to find all C files (as we are not checking assembly files) so
+# we find all generated .o files which have a .c corresponding file.
+CPPCHECKFILES := $(wildcard $(patsubst $(objtree)/%.o,$(srctree)/%.c, \
+ $(filter-out $(objtree)/tools/%, \
+ $(shell find $(objtree) -name "*.o"))))
+
+quiet_cmd_cppcheck_xml = CPPCHECK $(patsubst $(srctree)/%,%,$<)
+cmd_cppcheck_xml = $(CPPCHECK) -v -q --xml $(CPPCHECKFLAGS) \
+ --output-file=$@ $<
+
+quiet_cmd_merge_cppcheck_reports = CPPCHECK-MERGE $@
+cmd_merge_cppcheck_reports = $(srctree)/tools/merge_cppcheck_reports.py $^ $@
+
+quiet_cmd_cppcheck_html = CPPCHECK-HTML $<
+cmd_cppcheck_html = $(CPPCHECK_HTMLREPORT) --file=$< --source-dir=$(srctree) \
+ --report-dir=$(CPPCHECK_HTMLREPORT_OUTDIR) --title=Xen
+
+PHONY += _cppcheck _cppcheck-html cppcheck-version
+
+_cppcheck-html: xen-cppcheck.xml
+ $(call if_changed,cppcheck_html)
+
+_cppcheck: xen-cppcheck.xml
+
+xen-cppcheck.xml: $(patsubst $(srctree)/%.c,$(objtree)/%.c.cppcheck,$(CPPCHECKFILES))
+ifeq ($(CPPCHECKFILES),)
+ $(error Please build Xen before running cppcheck)
+endif
+ $(call if_changed,merge_cppcheck_reports)
+
+$(objtree)/%.c.cppcheck: $(srctree)/%.c $(objtree)/include/generated/autoconf.h $(objtree)/include/generated/compiler-def.h | cppcheck-version
+ $(call if_changed,cppcheck_xml)
+
+cppcheck-version:
+ifeq ($(shell which $(CPPCHECK)),)
+ $(error Cannot find cppcheck executable: $(CPPCHECK))
+endif
+ifeq ($(shell $(CPPCHECK) --version | awk '{print ($$2 < 2.7)}'),1)
+ $(error Please upgrade your cppcheck to version 2.7 or greater)
+endif
+
+# Put this in generated headers this way it is cleaned by include/Makefile
+$(objtree)/include/generated/compiler-def.h:
+ $(Q)$(CC) -dM -E -o $@ - < /dev/null
+
endif #config-build
PHONY += FORCE
diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h
index 852b5f3c24..ef37cfa16f 100644
--- a/xen/arch/arm/include/asm/processor.h
+++ b/xen/arch/arm/include/asm/processor.h
@@ -219,9 +219,11 @@
SCTLR_Axx_ELx_A | SCTLR_Axx_ELx_C |\
SCTLR_Axx_ELx_WXN | SCTLR_Axx_ELx_EE)
+#ifndef CPPCHECK
#if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffffUL
#error "Inconsistent SCTLR_EL2 set/clear bits"
#endif
+#endif
#endif
diff --git a/xen/include/xen/config.h b/xen/include/xen/config.h
index 85c6f59be9..d888b2314d 100644
--- a/xen/include/xen/config.h
+++ b/xen/include/xen/config.h
@@ -7,6 +7,10 @@
#ifndef __XEN_CONFIG_H__
#define __XEN_CONFIG_H__
+#ifdef CPPCHECK
+#include <generated/compiler-def.h>
+#endif
+
#include <xen/kconfig.h>
#ifndef __ASSEMBLY__
diff --git a/xen/include/xen/kconfig.h b/xen/include/xen/kconfig.h
index 4d58c5bb3c..a717b0819c 100644
--- a/xen/include/xen/kconfig.h
+++ b/xen/include/xen/kconfig.h
@@ -8,6 +8,10 @@
* these only work with boolean option.
*/
+/* cppcheck is failing to parse the macro so use a dummy one */
+#ifdef CPPCHECK
+#define IS_ENABLED(option) option
+#else
/*
* Getting something that works in C and CPP for an arg that may or may
* not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1"
@@ -27,5 +31,6 @@
* otherwise.
*/
#define IS_ENABLED(option) config_enabled(option)
+#endif
#endif /* __XEN_KCONFIG_H */
diff --git a/xen/tools/merge_cppcheck_reports.py b/xen/tools/merge_cppcheck_reports.py
new file mode 100755
index 0000000000..eabf558768
--- /dev/null
+++ b/xen/tools/merge_cppcheck_reports.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+"""
+This script acts as a tool to merge XML files created by cppcheck.
+Usage:
+ merge_cppcheck_reports.py [FILES] [OUTPUT]
+
+ FILES - list of XML files with extension .cppcheck
+ OUTPUT - file to store results (with .xml extension).
+ If not specified, the script will print results to stdout.
+"""
+
+import sys
+from xml.etree import ElementTree
+
+def elements_equal(el1, el2):
+ if type(el1) != type(el2): return False
+
+ el1_location = str(el1.find('location').attrib)
+ el2_location = str(el2.find('location').attrib)
+
+ if el1_location != el2_location: return False
+
+ return True
+
+def contain_element(new, lst):
+ for elem in lst:
+ if elements_equal(new, elem):
+ return True
+ return False
+
+def merge(files):
+ try:
+ result_xml_root = ElementTree.parse(files[0]).getroot()
+ except ParseError:
+ print("Xml parsing error in %s\n" % (files[0]))
+ print("Please upgrade your cppcheck to version 2.7 or greater")
+ sys.exit(1)
+ insert_point = result_xml_root.findall("errors")[0]
+ curr = 1
+ total = len(files)
+ numelem = len(insert_point)
+ for xml_file in files[1:]:
+ try:
+ xml_root = ElementTree.parse(xml_file).getroot()
+ except ParseError:
+ print("Xml parsing error in %s\n" % (xml_file))
+ print("Please upgrade your cppcheck to version 2.7 or greater")
+ sys.exit(1)
+ curr_elem_list = list(insert_point)
+ new_elem_list = list(xml_root.findall("errors")[0])
+ for xml_error_elem in new_elem_list:
+ if not contain_element(xml_error_elem, curr_elem_list):
+ insert_point.insert(1,xml_error_elem)
+ numelem = numelem + 1
+ curr = curr + 1
+ sys.stdout.write('\r')
+ sys.stdout.write(" %d / %d" % (curr,total))
+ sys.stdout.flush()
+
+ sys.stdout.write('\r\n')
+ print("Done: %d elements" % (numelem))
+ return result_xml_root
+
+def run():
+ files = []
+ output = None
+ for i in sys.argv[1:]:
+ output = i if '.xml' in i else None
+ files.append(i) if '.cppcheck' in i else None
+
+ result = merge(files)
+
+ if result is None:
+ return
+
+ if output is not None:
+ ElementTree.ElementTree(result).write(output)
+ else:
+ print(ElementTree.tostring(result).decode('utf-8'))
+
+if __name__ == '__main__':
+ run()
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2] xen/build: Add cppcheck and cppcheck-html make rules
2022-04-13 13:11 [PATCH v2] xen/build: Add cppcheck and cppcheck-html make rules Bertrand Marquis
@ 2022-04-15 23:42 ` Stefano Stabellini
2022-04-19 14:26 ` Bertrand Marquis
0 siblings, 1 reply; 3+ messages in thread
From: Stefano Stabellini @ 2022-04-15 23:42 UTC (permalink / raw)
To: Bertrand Marquis
Cc: xen-devel, Andrew Cooper, George Dunlap, Jan Beulich,
Julien Grall, Stefano Stabellini, Wei Liu, Volodymyr Babchuk,
Michal Orzel
On Wed, 13 Apr 2022, Bertrand Marquis wrote:
> cppcheck can be used to check Xen code quality.
>
> To create a report do "make cppcheck" on a built tree adding any options
> you added during the process you used to build xen (like CROSS_COMPILE
> or XEN_TARGET_ARCH). This will generate an xml report xen-cppcheck.xml.
>
> To create a html report do "make cppcheck-html" in the same way and a
> full report to be seen in a browser will be generated in
> cppcheck-htmlreport/index.html.
>
> For better results it is recommended to build your own cppcheck from the
> latest sources that you can find at [1].
> Development and result analysis has been done with cppcheck 2.7.
>
> The Makefile rule is searching for all C files which have been compiled
> (ie which have a generated .o file) and is running cppcheck on all of
> them using the current configuration of xen so only the code actually
> compiled is checked.
>
> A new tool is introduced to merge all cppcheck reports into one global
> report including all findings and removing duplicates.
>
> Some extra variables can be used to customize the report:
> - CPPCHECK can be used to give the full path to the cppcheck binary to
> use (default is to use the one from the standard path).
> - CPPCHECK_HTMLREPORT can be used to give the full path to
> cppcheck-htmlreport (default is to use the one from the standard path).
>
> This has been tested on several arm configurations (x86 should work but
> has not been tested).
>
> [1] https://cppcheck.sourceforge.io/
>
> Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
> Signed-off-by: Michal Orzel <michal.orzel@arm.com>
This is better, thanks! I don't have a lot of comments as it looks
pretty OK to me already. Only one comment on the python script.
> ---
> Changes in v2:
> - catch parsing errors during merge and tell the user to update cppcheck
> - fix alignments in Makefile
> - use srctree and objtree instead of BASEDIR
> - remove SCTLR invalid change
> - add a check of cppcheck version and mandate version 2.7 or greater
> - rework the merge script to make it faster and display progress
> ---
> .gitignore | 3 +
> xen/Makefile | 84 +++++++++++++++++++++++++++-
> xen/arch/arm/include/asm/processor.h | 2 +
> xen/include/xen/config.h | 4 ++
> xen/include/xen/kconfig.h | 5 ++
> xen/tools/merge_cppcheck_reports.py | 83 +++++++++++++++++++++++++++
> 6 files changed, 179 insertions(+), 2 deletions(-)
> create mode 100755 xen/tools/merge_cppcheck_reports.py
>
> diff --git a/.gitignore b/.gitignore
> index d425be4bd9..0d2d60b8f1 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -7,6 +7,7 @@
> *.o
> *.d
> *.d2
> +*.c.cppcheck
> *.opic
> *.a
> *.so
> @@ -296,6 +297,7 @@ xen/.banner
> xen/.config
> xen/.config.old
> xen/.xen.elf32
> +xen/xen-cppcheck.xml
> xen/System.map
> xen/arch/x86/boot/mkelf32
> xen/arch/x86/boot/cmdline.S
> @@ -316,6 +318,7 @@ xen/arch/*/efi/runtime.c
> xen/arch/*/include/asm/asm-offsets.h
> xen/common/config_data.S
> xen/common/config.gz
> +xen/cppcheck-htmlreport
> xen/include/headers*.chk
> xen/include/compat/*
> xen/include/config/
> diff --git a/xen/Makefile b/xen/Makefile
> index ebc7854c08..734f9f2b4f 100644
> --- a/xen/Makefile
> +++ b/xen/Makefile
> @@ -335,7 +335,7 @@ export CFLAGS_UBSAN
>
> endif # need-config
>
> -main-targets := build install uninstall clean distclean MAP
> +main-targets := build install uninstall clean distclean MAP cppcheck cppcheck-html
> .PHONY: $(main-targets)
> ifneq ($(XEN_TARGET_ARCH),x86_32)
> $(main-targets): %: _% ;
> @@ -423,15 +423,17 @@ _clean:
> $(Q)$(MAKE) $(clean)=tools/kconfig
> find . \( -name "*.o" -o -name ".*.d" -o -name ".*.d2" \
> -o -name ".*.o.tmp" -o -name "*~" -o -name "core" \
> - -o -name '*.lex.c' -o -name '*.tab.[ch]' \
> + -o -name '*.lex.c' -o -name '*.tab.[ch]' -o -name '*.c.cppcheck' \
> -o -name "*.gcno" -o -name ".*.cmd" -o -name "lib.a" \) -exec rm -f {} \;
> rm -f include/asm $(TARGET) $(TARGET).gz $(TARGET).efi $(TARGET).efi.map $(TARGET)-syms $(TARGET)-syms.map
> rm -f asm-offsets.s arch/*/include/asm/asm-offsets.h
> rm -f .banner .allconfig.tmp include/xen/compile.h
> + rm -f xen-cppcheck.xml
>
> .PHONY: _distclean
> _distclean: clean
> rm -f tags TAGS cscope.files cscope.in.out cscope.out cscope.po.out GTAGS GPATH GRTAGS GSYMS .config
> + rm -rf $(CPPCHECK_HTMLREPORT_OUTDIR)
>
> $(TARGET).gz: $(TARGET)
> gzip -n -f -9 < $< > $@.new
> @@ -505,6 +507,84 @@ cloc:
> done; \
> done | cloc --list-file=-
>
> +# What cppcheck command to use.
> +# To get proper results, it is recommended to build cppcheck manually from the
> +# latest source and use CPPCHECK to give the full path to the built version.
> +CPPCHECK ?= cppcheck
> +
> +# What cppcheck-htmlreport to use.
> +# If you give the full path to a self compiled cppcheck, this should be set
> +# to the full path to cppcheck-html in the htmlreport directory of cppcheck.
> +# On recent distribution, this is available in the standard path.
> +CPPCHECK_HTMLREPORT ?= cppcheck-htmlreport
> +
> +# By default we generate the report in cppcheck-htmlreport directory in the
> +# build directory. This can be changed by giving a directory in this variable.
> +CPPCHECK_HTMLREPORT_OUTDIR ?= cppcheck-htmlreport
> +
> +# Compile flags to pass to cppcheck:
> +# - include directories and defines Xen Makefile is passing (from CFLAGS)
> +# - include config.h as this is passed directly to the compiler.
> +# - define CPPCHECK as we use to disable or enable some specific part of the
> +# code to solve some cppcheck issues.
> +# - explicitely enable some cppcheck checks as we do not want to use "all"
> +# which includes unusedFunction which gives wrong positives as we check file
> +# per file.
> +#
> +# Compiler defines are in compiler-def.h which is included in config.h
> +#
> +CPPCHECKFLAGS := -DCPPCHECK --max-ctu-depth=10 \
> + --enable=style,information,missingInclude \
> + --include=$(srctree)/include/xen/config.h \
> + -I $(srctree)/xsm/flask/include \
> + -I $(srctree)/include/xen/libfdt \
> + $(filter -D% -I%,$(CFLAGS))
> +
> +# We need to find all C files (as we are not checking assembly files) so
> +# we find all generated .o files which have a .c corresponding file.
> +CPPCHECKFILES := $(wildcard $(patsubst $(objtree)/%.o,$(srctree)/%.c, \
> + $(filter-out $(objtree)/tools/%, \
> + $(shell find $(objtree) -name "*.o"))))
> +
> +quiet_cmd_cppcheck_xml = CPPCHECK $(patsubst $(srctree)/%,%,$<)
> +cmd_cppcheck_xml = $(CPPCHECK) -v -q --xml $(CPPCHECKFLAGS) \
> + --output-file=$@ $<
> +
> +quiet_cmd_merge_cppcheck_reports = CPPCHECK-MERGE $@
> +cmd_merge_cppcheck_reports = $(srctree)/tools/merge_cppcheck_reports.py $^ $@
> +
> +quiet_cmd_cppcheck_html = CPPCHECK-HTML $<
> +cmd_cppcheck_html = $(CPPCHECK_HTMLREPORT) --file=$< --source-dir=$(srctree) \
> + --report-dir=$(CPPCHECK_HTMLREPORT_OUTDIR) --title=Xen
> +
> +PHONY += _cppcheck _cppcheck-html cppcheck-version
> +
> +_cppcheck-html: xen-cppcheck.xml
> + $(call if_changed,cppcheck_html)
> +
> +_cppcheck: xen-cppcheck.xml
> +
> +xen-cppcheck.xml: $(patsubst $(srctree)/%.c,$(objtree)/%.c.cppcheck,$(CPPCHECKFILES))
> +ifeq ($(CPPCHECKFILES),)
> + $(error Please build Xen before running cppcheck)
> +endif
> + $(call if_changed,merge_cppcheck_reports)
> +
> +$(objtree)/%.c.cppcheck: $(srctree)/%.c $(objtree)/include/generated/autoconf.h $(objtree)/include/generated/compiler-def.h | cppcheck-version
> + $(call if_changed,cppcheck_xml)
> +
> +cppcheck-version:
> +ifeq ($(shell which $(CPPCHECK)),)
> + $(error Cannot find cppcheck executable: $(CPPCHECK))
> +endif
> +ifeq ($(shell $(CPPCHECK) --version | awk '{print ($$2 < 2.7)}'),1)
> + $(error Please upgrade your cppcheck to version 2.7 or greater)
> +endif
> +
> +# Put this in generated headers this way it is cleaned by include/Makefile
> +$(objtree)/include/generated/compiler-def.h:
> + $(Q)$(CC) -dM -E -o $@ - < /dev/null
> +
> endif #config-build
>
> PHONY += FORCE
> diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h
> index 852b5f3c24..ef37cfa16f 100644
> --- a/xen/arch/arm/include/asm/processor.h
> +++ b/xen/arch/arm/include/asm/processor.h
> @@ -219,9 +219,11 @@
> SCTLR_Axx_ELx_A | SCTLR_Axx_ELx_C |\
> SCTLR_Axx_ELx_WXN | SCTLR_Axx_ELx_EE)
>
> +#ifndef CPPCHECK
> #if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffffUL
> #error "Inconsistent SCTLR_EL2 set/clear bits"
> #endif
> +#endif
>
> #endif
>
> diff --git a/xen/include/xen/config.h b/xen/include/xen/config.h
> index 85c6f59be9..d888b2314d 100644
> --- a/xen/include/xen/config.h
> +++ b/xen/include/xen/config.h
> @@ -7,6 +7,10 @@
> #ifndef __XEN_CONFIG_H__
> #define __XEN_CONFIG_H__
>
> +#ifdef CPPCHECK
> +#include <generated/compiler-def.h>
> +#endif
> +
> #include <xen/kconfig.h>
>
> #ifndef __ASSEMBLY__
> diff --git a/xen/include/xen/kconfig.h b/xen/include/xen/kconfig.h
> index 4d58c5bb3c..a717b0819c 100644
> --- a/xen/include/xen/kconfig.h
> +++ b/xen/include/xen/kconfig.h
> @@ -8,6 +8,10 @@
> * these only work with boolean option.
> */
>
> +/* cppcheck is failing to parse the macro so use a dummy one */
> +#ifdef CPPCHECK
> +#define IS_ENABLED(option) option
> +#else
> /*
> * Getting something that works in C and CPP for an arg that may or may
> * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1"
> @@ -27,5 +31,6 @@
> * otherwise.
> */
> #define IS_ENABLED(option) config_enabled(option)
> +#endif
>
> #endif /* __XEN_KCONFIG_H */
> diff --git a/xen/tools/merge_cppcheck_reports.py b/xen/tools/merge_cppcheck_reports.py
> new file mode 100755
> index 0000000000..eabf558768
> --- /dev/null
> +++ b/xen/tools/merge_cppcheck_reports.py
> @@ -0,0 +1,83 @@
> +#!/usr/bin/env python
> +
> +"""
> +This script acts as a tool to merge XML files created by cppcheck.
> +Usage:
> + merge_cppcheck_reports.py [FILES] [OUTPUT]
> +
> + FILES - list of XML files with extension .cppcheck
> + OUTPUT - file to store results (with .xml extension).
> + If not specified, the script will print results to stdout.
> +"""
> +
> +import sys
> +from xml.etree import ElementTree
> +
> +def elements_equal(el1, el2):
> + if type(el1) != type(el2): return False
> +
> + el1_location = str(el1.find('location').attrib)
> + el2_location = str(el2.find('location').attrib)
I managed to repro another stacktrace:
CPPCHECK-MERGE xen-cppcheck.xml
123 / 191Traceback (most recent call last):
File "./tools/merge_cppcheck_reports.py", line 83, in <module>
run()
File "./tools/merge_cppcheck_reports.py", line 72, in run
result = merge(files)
File "./tools/merge_cppcheck_reports.py", line 53, in merge
if not contain_element(xml_error_elem, curr_elem_list):
File "./tools/merge_cppcheck_reports.py", line 28, in contain_element
if elements_equal(new, elem):
File "./tools/merge_cppcheck_reports.py", line 19, in elements_equal
el1_location = str(el1.find('location').attrib)
AttributeError: 'NoneType' object has no attribute 'attrib'
make: *** [Makefile:685: xen-cppcheck.xml] Error 1
It must be a natural talent :-)
Should we have a try/except also here as well?
FYI I added:
if el1.find('location') is None: return False
if el2.find('location') is None: return False
And that solved the issue for me
> + if el1_location != el2_location: return False
> +
> + return True
> +
> +def contain_element(new, lst):
> + for elem in lst:
> + if elements_equal(new, elem):
> + return True
> + return False
> +
> +def merge(files):
> + try:
> + result_xml_root = ElementTree.parse(files[0]).getroot()
> + except ParseError:
> + print("Xml parsing error in %s\n" % (files[0]))
> + print("Please upgrade your cppcheck to version 2.7 or greater")
> + sys.exit(1)
> + insert_point = result_xml_root.findall("errors")[0]
> + curr = 1
> + total = len(files)
> + numelem = len(insert_point)
> + for xml_file in files[1:]:
> + try:
> + xml_root = ElementTree.parse(xml_file).getroot()
> + except ParseError:
> + print("Xml parsing error in %s\n" % (xml_file))
> + print("Please upgrade your cppcheck to version 2.7 or greater")
> + sys.exit(1)
> + curr_elem_list = list(insert_point)
> + new_elem_list = list(xml_root.findall("errors")[0])
> + for xml_error_elem in new_elem_list:
> + if not contain_element(xml_error_elem, curr_elem_list):
> + insert_point.insert(1,xml_error_elem)
> + numelem = numelem + 1
> + curr = curr + 1
> + sys.stdout.write('\r')
> + sys.stdout.write(" %d / %d" % (curr,total))
> + sys.stdout.flush()
> +
> + sys.stdout.write('\r\n')
> + print("Done: %d elements" % (numelem))
> + return result_xml_root
> +
> +def run():
> + files = []
> + output = None
> + for i in sys.argv[1:]:
> + output = i if '.xml' in i else None
> + files.append(i) if '.cppcheck' in i else None
> +
> + result = merge(files)
> +
> + if result is None:
> + return
> +
> + if output is not None:
> + ElementTree.ElementTree(result).write(output)
> + else:
> + print(ElementTree.tostring(result).decode('utf-8'))
> +
> +if __name__ == '__main__':
> + run()
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2] xen/build: Add cppcheck and cppcheck-html make rules
2022-04-15 23:42 ` Stefano Stabellini
@ 2022-04-19 14:26 ` Bertrand Marquis
0 siblings, 0 replies; 3+ messages in thread
From: Bertrand Marquis @ 2022-04-19 14:26 UTC (permalink / raw)
To: Stefano Stabellini
Cc: Xen-devel, Andrew Cooper, George Dunlap, Jan Beulich,
Julien Grall, Wei Liu, Volodymyr Babchuk, Michal Orzel
Hi Stefano,
> On 16 Apr 2022, at 00:42, Stefano Stabellini <sstabellini@kernel.org> wrote:
>
> On Wed, 13 Apr 2022, Bertrand Marquis wrote:
>> cppcheck can be used to check Xen code quality.
>>
>> To create a report do "make cppcheck" on a built tree adding any options
>> you added during the process you used to build xen (like CROSS_COMPILE
>> or XEN_TARGET_ARCH). This will generate an xml report xen-cppcheck.xml.
>>
>> To create a html report do "make cppcheck-html" in the same way and a
>> full report to be seen in a browser will be generated in
>> cppcheck-htmlreport/index.html.
>>
>> For better results it is recommended to build your own cppcheck from the
>> latest sources that you can find at [1].
>> Development and result analysis has been done with cppcheck 2.7.
>>
>> The Makefile rule is searching for all C files which have been compiled
>> (ie which have a generated .o file) and is running cppcheck on all of
>> them using the current configuration of xen so only the code actually
>> compiled is checked.
>>
>> A new tool is introduced to merge all cppcheck reports into one global
>> report including all findings and removing duplicates.
>>
>> Some extra variables can be used to customize the report:
>> - CPPCHECK can be used to give the full path to the cppcheck binary to
>> use (default is to use the one from the standard path).
>> - CPPCHECK_HTMLREPORT can be used to give the full path to
>> cppcheck-htmlreport (default is to use the one from the standard path).
>>
>> This has been tested on several arm configurations (x86 should work but
>> has not been tested).
>>
>> [1] https://cppcheck.sourceforge.io/
>>
>> Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com>
>> Signed-off-by: Michal Orzel <michal.orzel@arm.com>
>
> This is better, thanks! I don't have a lot of comments as it looks
> pretty OK to me already. Only one comment on the python script.
>
>
>> ---
>> Changes in v2:
>> - catch parsing errors during merge and tell the user to update cppcheck
>> - fix alignments in Makefile
>> - use srctree and objtree instead of BASEDIR
>> - remove SCTLR invalid change
>> - add a check of cppcheck version and mandate version 2.7 or greater
>> - rework the merge script to make it faster and display progress
>> ---
>> .gitignore | 3 +
>> xen/Makefile | 84 +++++++++++++++++++++++++++-
>> xen/arch/arm/include/asm/processor.h | 2 +
>> xen/include/xen/config.h | 4 ++
>> xen/include/xen/kconfig.h | 5 ++
>> xen/tools/merge_cppcheck_reports.py | 83 +++++++++++++++++++++++++++
>> 6 files changed, 179 insertions(+), 2 deletions(-)
>> create mode 100755 xen/tools/merge_cppcheck_reports.py
>>
>> diff --git a/.gitignore b/.gitignore
>> index d425be4bd9..0d2d60b8f1 100644
>> --- a/.gitignore
>> +++ b/.gitignore
>> @@ -7,6 +7,7 @@
>> *.o
>> *.d
>> *.d2
>> +*.c.cppcheck
>> *.opic
>> *.a
>> *.so
>> @@ -296,6 +297,7 @@ xen/.banner
>> xen/.config
>> xen/.config.old
>> xen/.xen.elf32
>> +xen/xen-cppcheck.xml
>> xen/System.map
>> xen/arch/x86/boot/mkelf32
>> xen/arch/x86/boot/cmdline.S
>> @@ -316,6 +318,7 @@ xen/arch/*/efi/runtime.c
>> xen/arch/*/include/asm/asm-offsets.h
>> xen/common/config_data.S
>> xen/common/config.gz
>> +xen/cppcheck-htmlreport
>> xen/include/headers*.chk
>> xen/include/compat/*
>> xen/include/config/
>> diff --git a/xen/Makefile b/xen/Makefile
>> index ebc7854c08..734f9f2b4f 100644
>> --- a/xen/Makefile
>> +++ b/xen/Makefile
>> @@ -335,7 +335,7 @@ export CFLAGS_UBSAN
>>
>> endif # need-config
>>
>> -main-targets := build install uninstall clean distclean MAP
>> +main-targets := build install uninstall clean distclean MAP cppcheck cppcheck-html
>> .PHONY: $(main-targets)
>> ifneq ($(XEN_TARGET_ARCH),x86_32)
>> $(main-targets): %: _% ;
>> @@ -423,15 +423,17 @@ _clean:
>> $(Q)$(MAKE) $(clean)=tools/kconfig
>> find . \( -name "*.o" -o -name ".*.d" -o -name ".*.d2" \
>> -o -name ".*.o.tmp" -o -name "*~" -o -name "core" \
>> - -o -name '*.lex.c' -o -name '*.tab.[ch]' \
>> + -o -name '*.lex.c' -o -name '*.tab.[ch]' -o -name '*.c.cppcheck' \
>> -o -name "*.gcno" -o -name ".*.cmd" -o -name "lib.a" \) -exec rm -f {} \;
>> rm -f include/asm $(TARGET) $(TARGET).gz $(TARGET).efi $(TARGET).efi.map $(TARGET)-syms $(TARGET)-syms.map
>> rm -f asm-offsets.s arch/*/include/asm/asm-offsets.h
>> rm -f .banner .allconfig.tmp include/xen/compile.h
>> + rm -f xen-cppcheck.xml
>>
>> .PHONY: _distclean
>> _distclean: clean
>> rm -f tags TAGS cscope.files cscope.in.out cscope.out cscope.po.out GTAGS GPATH GRTAGS GSYMS .config
>> + rm -rf $(CPPCHECK_HTMLREPORT_OUTDIR)
>>
>> $(TARGET).gz: $(TARGET)
>> gzip -n -f -9 < $< > $@.new
>> @@ -505,6 +507,84 @@ cloc:
>> done; \
>> done | cloc --list-file=-
>>
>> +# What cppcheck command to use.
>> +# To get proper results, it is recommended to build cppcheck manually from the
>> +# latest source and use CPPCHECK to give the full path to the built version.
>> +CPPCHECK ?= cppcheck
>> +
>> +# What cppcheck-htmlreport to use.
>> +# If you give the full path to a self compiled cppcheck, this should be set
>> +# to the full path to cppcheck-html in the htmlreport directory of cppcheck.
>> +# On recent distribution, this is available in the standard path.
>> +CPPCHECK_HTMLREPORT ?= cppcheck-htmlreport
>> +
>> +# By default we generate the report in cppcheck-htmlreport directory in the
>> +# build directory. This can be changed by giving a directory in this variable.
>> +CPPCHECK_HTMLREPORT_OUTDIR ?= cppcheck-htmlreport
>> +
>> +# Compile flags to pass to cppcheck:
>> +# - include directories and defines Xen Makefile is passing (from CFLAGS)
>> +# - include config.h as this is passed directly to the compiler.
>> +# - define CPPCHECK as we use to disable or enable some specific part of the
>> +# code to solve some cppcheck issues.
>> +# - explicitely enable some cppcheck checks as we do not want to use "all"
>> +# which includes unusedFunction which gives wrong positives as we check file
>> +# per file.
>> +#
>> +# Compiler defines are in compiler-def.h which is included in config.h
>> +#
>> +CPPCHECKFLAGS := -DCPPCHECK --max-ctu-depth=10 \
>> + --enable=style,information,missingInclude \
>> + --include=$(srctree)/include/xen/config.h \
>> + -I $(srctree)/xsm/flask/include \
>> + -I $(srctree)/include/xen/libfdt \
>> + $(filter -D% -I%,$(CFLAGS))
>> +
>> +# We need to find all C files (as we are not checking assembly files) so
>> +# we find all generated .o files which have a .c corresponding file.
>> +CPPCHECKFILES := $(wildcard $(patsubst $(objtree)/%.o,$(srctree)/%.c, \
>> + $(filter-out $(objtree)/tools/%, \
>> + $(shell find $(objtree) -name "*.o"))))
>> +
>> +quiet_cmd_cppcheck_xml = CPPCHECK $(patsubst $(srctree)/%,%,$<)
>> +cmd_cppcheck_xml = $(CPPCHECK) -v -q --xml $(CPPCHECKFLAGS) \
>> + --output-file=$@ $<
>> +
>> +quiet_cmd_merge_cppcheck_reports = CPPCHECK-MERGE $@
>> +cmd_merge_cppcheck_reports = $(srctree)/tools/merge_cppcheck_reports.py $^ $@
>> +
>> +quiet_cmd_cppcheck_html = CPPCHECK-HTML $<
>> +cmd_cppcheck_html = $(CPPCHECK_HTMLREPORT) --file=$< --source-dir=$(srctree) \
>> + --report-dir=$(CPPCHECK_HTMLREPORT_OUTDIR) --title=Xen
>> +
>> +PHONY += _cppcheck _cppcheck-html cppcheck-version
>> +
>> +_cppcheck-html: xen-cppcheck.xml
>> + $(call if_changed,cppcheck_html)
>> +
>> +_cppcheck: xen-cppcheck.xml
>> +
>> +xen-cppcheck.xml: $(patsubst $(srctree)/%.c,$(objtree)/%.c.cppcheck,$(CPPCHECKFILES))
>> +ifeq ($(CPPCHECKFILES),)
>> + $(error Please build Xen before running cppcheck)
>> +endif
>> + $(call if_changed,merge_cppcheck_reports)
>> +
>> +$(objtree)/%.c.cppcheck: $(srctree)/%.c $(objtree)/include/generated/autoconf.h $(objtree)/include/generated/compiler-def.h | cppcheck-version
>> + $(call if_changed,cppcheck_xml)
>> +
>> +cppcheck-version:
>> +ifeq ($(shell which $(CPPCHECK)),)
>> + $(error Cannot find cppcheck executable: $(CPPCHECK))
>> +endif
>> +ifeq ($(shell $(CPPCHECK) --version | awk '{print ($$2 < 2.7)}'),1)
>> + $(error Please upgrade your cppcheck to version 2.7 or greater)
>> +endif
>> +
>> +# Put this in generated headers this way it is cleaned by include/Makefile
>> +$(objtree)/include/generated/compiler-def.h:
>> + $(Q)$(CC) -dM -E -o $@ - < /dev/null
>> +
>> endif #config-build
>>
>> PHONY += FORCE
>> diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h
>> index 852b5f3c24..ef37cfa16f 100644
>> --- a/xen/arch/arm/include/asm/processor.h
>> +++ b/xen/arch/arm/include/asm/processor.h
>> @@ -219,9 +219,11 @@
>> SCTLR_Axx_ELx_A | SCTLR_Axx_ELx_C |\
>> SCTLR_Axx_ELx_WXN | SCTLR_Axx_ELx_EE)
>>
>> +#ifndef CPPCHECK
>> #if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffffUL
>> #error "Inconsistent SCTLR_EL2 set/clear bits"
>> #endif
>> +#endif
>>
>> #endif
>>
>> diff --git a/xen/include/xen/config.h b/xen/include/xen/config.h
>> index 85c6f59be9..d888b2314d 100644
>> --- a/xen/include/xen/config.h
>> +++ b/xen/include/xen/config.h
>> @@ -7,6 +7,10 @@
>> #ifndef __XEN_CONFIG_H__
>> #define __XEN_CONFIG_H__
>>
>> +#ifdef CPPCHECK
>> +#include <generated/compiler-def.h>
>> +#endif
>> +
>> #include <xen/kconfig.h>
>>
>> #ifndef __ASSEMBLY__
>> diff --git a/xen/include/xen/kconfig.h b/xen/include/xen/kconfig.h
>> index 4d58c5bb3c..a717b0819c 100644
>> --- a/xen/include/xen/kconfig.h
>> +++ b/xen/include/xen/kconfig.h
>> @@ -8,6 +8,10 @@
>> * these only work with boolean option.
>> */
>>
>> +/* cppcheck is failing to parse the macro so use a dummy one */
>> +#ifdef CPPCHECK
>> +#define IS_ENABLED(option) option
>> +#else
>> /*
>> * Getting something that works in C and CPP for an arg that may or may
>> * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1"
>> @@ -27,5 +31,6 @@
>> * otherwise.
>> */
>> #define IS_ENABLED(option) config_enabled(option)
>> +#endif
>>
>> #endif /* __XEN_KCONFIG_H */
>> diff --git a/xen/tools/merge_cppcheck_reports.py b/xen/tools/merge_cppcheck_reports.py
>> new file mode 100755
>> index 0000000000..eabf558768
>> --- /dev/null
>> +++ b/xen/tools/merge_cppcheck_reports.py
>> @@ -0,0 +1,83 @@
>> +#!/usr/bin/env python
>> +
>> +"""
>> +This script acts as a tool to merge XML files created by cppcheck.
>> +Usage:
>> + merge_cppcheck_reports.py [FILES] [OUTPUT]
>> +
>> + FILES - list of XML files with extension .cppcheck
>> + OUTPUT - file to store results (with .xml extension).
>> + If not specified, the script will print results to stdout.
>> +"""
>> +
>> +import sys
>> +from xml.etree import ElementTree
>> +
>> +def elements_equal(el1, el2):
>> + if type(el1) != type(el2): return False
>> +
>> + el1_location = str(el1.find('location').attrib)
>> + el2_location = str(el2.find('location').attrib)
>
> I managed to repro another stacktrace:
>
> CPPCHECK-MERGE xen-cppcheck.xml
> 123 / 191Traceback (most recent call last):
> File "./tools/merge_cppcheck_reports.py", line 83, in <module>
> run()
> File "./tools/merge_cppcheck_reports.py", line 72, in run
> result = merge(files)
> File "./tools/merge_cppcheck_reports.py", line 53, in merge
> if not contain_element(xml_error_elem, curr_elem_list):
> File "./tools/merge_cppcheck_reports.py", line 28, in contain_element
> if elements_equal(new, elem):
> File "./tools/merge_cppcheck_reports.py", line 19, in elements_equal
> el1_location = str(el1.find('location').attrib)
> AttributeError: 'NoneType' object has no attribute 'attrib'
> make: *** [Makefile:685: xen-cppcheck.xml] Error 1
>
> It must be a natural talent :-)
>
> Should we have a try/except also here as well?
>
> FYI I added:
>
> if el1.find('location') is None: return False
> if el2.find('location') is None: return False
>
> And that solved the issue for me
You are very talented to trigger the errors (I do not have any on my side).
Anyway I will include this and make the try/except to catch any kind of errors in v3.
Thanks a lot for the deep testing :-)
Cheers
Bertrand
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-04-19 14:27 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-13 13:11 [PATCH v2] xen/build: Add cppcheck and cppcheck-html make rules Bertrand Marquis
2022-04-15 23:42 ` Stefano Stabellini
2022-04-19 14:26 ` Bertrand Marquis
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.