mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/build: Add cppcheck and cppcheck-html make rules
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> [stefano: add in-code comment on commit] Signed-off-by: Stefano Stabellini <stefano.stabellini@xilinx.com> Tested-by: Stefano Stabellini <sstabellini@kernel.org> Acked-by: Stefano Stabellini <sstabellini@kernel.org> Acked-by: Julien Grall <jgrall@amazon.com>
This commit is contained in:
1 parent
540645212a
commit
43aa3f6e72
6 files changed
+186
-2
No files matched your search
@@ -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/
|
||||
|
||||
+82
-2
@@ -453,7 +453,7 @@ endif # need-config
|
||||
|
||||
__all: build
|
||||
|
||||
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): %: _% ;
|
||||
@@ -543,15 +543,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 source
|
||||
rm -rf $(CPPCHECK_HTMLREPORT_OUTDIR)
|
||||
|
||||
$(TARGET).gz: $(TARGET)
|
||||
gzip -n -f -9 < $< > $@.new
|
||||
@@ -625,6 +627,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
|
||||
endif # need-sub-make
|
||||
|
||||
|
||||
@@ -219,9 +219,15 @@
|
||||
SCTLR_Axx_ELx_A | SCTLR_Axx_ELx_C |\
|
||||
SCTLR_Axx_ELx_WXN | SCTLR_Axx_ELx_EE)
|
||||
|
||||
/*
|
||||
* Cppcheck preprocessor is wrongly throwing the error here so disable
|
||||
* this check for cppcheck runs.
|
||||
*/
|
||||
#ifndef CPPCHECK
|
||||
#if (SCTLR_EL2_SET ^ SCTLR_EL2_CLEAR) != 0xffffffffffffffffUL
|
||||
#error "Inconsistent SCTLR_EL2 set/clear bits"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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 */
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/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
|
||||
|
||||
if el1.find('location') is None: return False
|
||||
if el2.find('location') is None: 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:
|
||||
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:
|
||||
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()
|
||||