|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 1/2] xen/misra: add diff-report.py tool
On Thu, 4 May 2023, Luca Fancellu wrote:
> Add a new tool, diff-report.py that can be used to make diff between
> reports generated by xen-analysis.py tool.
> Currently this tool supports the Xen cppcheck text report format in
> its operations.
>
> The tool prints every finding that is in the report passed with -r
> (check report) which is not in the report passed with -b (baseline).
>
> Signed-off-by: Luca Fancellu <luca.fancellu@xxxxxxx>
Acked-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
Tested-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
> ---
> xen/scripts/diff-report.py | 76 ++++++++++++
> .../xen_analysis/diff_tool/__init__.py | 0
> .../xen_analysis/diff_tool/cppcheck_report.py | 41 +++++++
> xen/scripts/xen_analysis/diff_tool/debug.py | 36 ++++++
> xen/scripts/xen_analysis/diff_tool/report.py | 114 ++++++++++++++++++
> 5 files changed, 267 insertions(+)
> create mode 100755 xen/scripts/diff-report.py
> create mode 100644 xen/scripts/xen_analysis/diff_tool/__init__.py
> create mode 100644 xen/scripts/xen_analysis/diff_tool/cppcheck_report.py
> create mode 100644 xen/scripts/xen_analysis/diff_tool/debug.py
> create mode 100644 xen/scripts/xen_analysis/diff_tool/report.py
>
> diff --git a/xen/scripts/diff-report.py b/xen/scripts/diff-report.py
> new file mode 100755
> index 000000000000..4913fb43a8f9
> --- /dev/null
> +++ b/xen/scripts/diff-report.py
> @@ -0,0 +1,76 @@
> +#!/usr/bin/env python3
> +
> +import os, sys
> +from argparse import ArgumentParser
> +from xen_analysis.diff_tool.debug import Debug
> +from xen_analysis.diff_tool.report import ReportError
> +from xen_analysis.diff_tool.cppcheck_report import CppcheckReport
> +
> +
> +def log_info(text, end='\n'):
> + global args
> + global file_out
> +
> + if (args.verbose):
> + print(text, end=end, file=file_out)
> +
> +
> +def main(argv):
> + global args
> + global file_out
> +
> + parser = ArgumentParser(prog="diff-report.py")
> + parser.add_argument("-b", "--baseline", required=True, type=str,
> + help="Path to the baseline report.")
> + parser.add_argument("--debug", action='store_true',
> + help="Produce intermediate reports during
> operations.")
> + parser.add_argument("-o", "--out", default="stdout", type=str,
> + help="Where to print the tool output. Default is "
> + "stdout")
> + parser.add_argument("-r", "--report", required=True, type=str,
> + help="Path to the 'check report', the one checked "
> + "against the baseline.")
> + parser.add_argument("-v", "--verbose", action='store_true',
> + help="Print more informations during the run.")
> +
> + args = parser.parse_args()
> +
> + if args.out == "stdout":
> + file_out = sys.stdout
> + else:
> + try:
> + file_out = open(args.out, "wt")
> + except OSError as e:
> + print("ERROR: Issue opening file {}: {}".format(args.out, e))
> + sys.exit(1)
> +
> + debug = Debug(args)
> +
> + try:
> + baseline_path = os.path.realpath(args.baseline)
> + log_info("Loading baseline report {}".format(baseline_path), "")
> + baseline = CppcheckReport(baseline_path)
> + baseline.parse()
> + debug.debug_print_parsed_report(baseline)
> + log_info(" [OK]")
> + new_rep_path = os.path.realpath(args.report)
> + log_info("Loading check report {}".format(new_rep_path), "")
> + new_rep = CppcheckReport(new_rep_path)
> + new_rep.parse()
> + debug.debug_print_parsed_report(new_rep)
> + log_info(" [OK]")
> + except ReportError as e:
> + print("ERROR: {}".format(e))
> + sys.exit(1)
> +
> + output = new_rep - baseline
> + print(output, end="", file=file_out)
> +
> + if len(output) > 0:
> + sys.exit(1)
> +
> + sys.exit(0)
> +
> +
> +if __name__ == "__main__":
> + main(sys.argv[1:])
> diff --git a/xen/scripts/xen_analysis/diff_tool/__init__.py
> b/xen/scripts/xen_analysis/diff_tool/__init__.py
> new file mode 100644
> index 000000000000..e69de29bb2d1
> diff --git a/xen/scripts/xen_analysis/diff_tool/cppcheck_report.py
> b/xen/scripts/xen_analysis/diff_tool/cppcheck_report.py
> new file mode 100644
> index 000000000000..787a51aca583
> --- /dev/null
> +++ b/xen/scripts/xen_analysis/diff_tool/cppcheck_report.py
> @@ -0,0 +1,41 @@
> +#!/usr/bin/env python3
> +
> +import re
> +from .report import Report, ReportError
> +
> +
> +class CppcheckReport(Report):
> + def __init__(self, report_path: str) -> None:
> + super().__init__(report_path)
> + # This matches a string like:
> + # path/to/file.c(<line number>,<digits>):<whatever>
> + # and captures file name path and line number
> + # the last capture group is used for text substitution in __str__
> + self.__report_entry_regex = re.compile(r'^(.*)\((\d+)(,\d+\):.*)$')
> +
> + def parse(self) -> None:
> + report_path = self.get_report_path()
> + try:
> + with open(report_path, "rt") as infile:
> + report_lines = infile.readlines()
> + except OSError as e:
> + raise ReportError("Issue with reading file {}: {}"
> + .format(report_path, e))
> + for line in report_lines:
> + entry = self.__report_entry_regex.match(line)
> + if entry and entry.group(1) and entry.group(2):
> + file_path = entry.group(1)
> + line_number = int(entry.group(2))
> + self.add_entry(file_path, line_number, line)
> + else:
> + raise ReportError("Malformed report entry in file {}:\n{}"
> + .format(report_path, line))
> +
> + def __str__(self) -> str:
> + ret = ""
> + for entry in self.to_list():
> + ret += re.sub(self.__report_entry_regex,
> + r'{}({}\3'.format(entry.file_path,
> + entry.line_number),
> + entry.text)
> + return ret
> diff --git a/xen/scripts/xen_analysis/diff_tool/debug.py
> b/xen/scripts/xen_analysis/diff_tool/debug.py
> new file mode 100644
> index 000000000000..d46df3300d21
> --- /dev/null
> +++ b/xen/scripts/xen_analysis/diff_tool/debug.py
> @@ -0,0 +1,36 @@
> +#!/usr/bin/env python3
> +
> +import os
> +from .report import Report
> +
> +
> +class Debug:
> + def __init__(self, args):
> + self.args = args
> +
> + def __get_debug_out_filename(self, path: str, type: str) -> str:
> + # Take basename
> + file_name = os.path.basename(path)
> + # Split in name and extension
> + file_name = os.path.splitext(file_name)
> + if self.args.out != "stdout":
> + out_folder = os.path.dirname(self.args.out)
> + else:
> + out_folder = "./"
> + dbg_report_path = out_folder + file_name[0] + type + file_name[1]
> +
> + return dbg_report_path
> +
> + def __debug_print_report(self, report: Report, type: str) -> None:
> + report_name = self.__get_debug_out_filename(report.get_report_path(),
> + type)
> + try:
> + with open(report_name, "wt") as outfile:
> + print(report, end="", file=outfile)
> + except OSError as e:
> + print("ERROR: Issue opening file {}: {}".format(report_name, e))
> +
> + def debug_print_parsed_report(self, report: Report) -> None:
> + if not self.args.debug:
> + return
> + self.__debug_print_report(report, ".parsed")
> diff --git a/xen/scripts/xen_analysis/diff_tool/report.py
> b/xen/scripts/xen_analysis/diff_tool/report.py
> new file mode 100644
> index 000000000000..d958d1816eb4
> --- /dev/null
> +++ b/xen/scripts/xen_analysis/diff_tool/report.py
> @@ -0,0 +1,114 @@
> +#!/usr/bin/env python3
> +
> +import os
> +
> +
> +class ReportError(Exception):
> + pass
> +
> +
> +class Report:
> + class ReportEntry:
> + def __init__(self, file_path: str, line_number: int,
> + entry_text: list, line_id: int) -> None:
> + if not isinstance(line_number, int) or \
> + not isinstance(line_id, int):
> + raise ReportError("ReportEntry constructor wrong type args")
> + self.file_path = file_path
> + self.line_number = line_number
> + self.text = entry_text
> + self.line_id = line_id
> +
> + def __str__(self) -> str:
> + ret = ''
> + header = 'File path:Count\n'
> +
> + for path in self.stats:
> + ret += f'{path}: {len(self.stats[path])}\n'
> +
> + if ret == '':
> + ret += 'No new issues introduced\n'
> +
> + ret = header + ret
> +
> + return ret
> +
> + def __len__(self) -> int:
> + ret = 0
> +
> + for ln_list in self.stats.values():
> + ret += len(ln_list)
> +
> + return ret
> +
> + def __init__(self, report_path: str) -> None:
> + self.__entries = {}
> + self.__path = report_path
> + self.__last_line_order = 0
> +
> + def parse(self) -> None:
> + raise ReportError("Please create a specialised class from 'Report'.")
> +
> + def get_report_path(self) -> str:
> + return self.__path
> +
> + def get_report_entries(self) -> dict:
> + return self.__entries
> +
> + def add_entry(self, entry_path: str, entry_line_number: int,
> + entry_text: list) -> None:
> + entry = Report.ReportEntry(entry_path, entry_line_number, entry_text,
> + self.__last_line_order)
> + if entry_path in self.__entries.keys():
> + self.__entries[entry_path].append(entry)
> + else:
> + self.__entries[entry_path] = [entry]
> + self.__last_line_order += 1
> +
> + def to_list(self) -> list:
> + report_list = []
> + for _, entries in self.__entries.items():
> + for entry in entries:
> + report_list.append(entry)
> +
> + report_list.sort(key=lambda x: x.line_id)
> + return report_list
> +
> + def __str__(self) -> str:
> + ret = ""
> + for entry in self.to_list():
> + ret += entry.file_path + ":" + entry.line_number + ":" +
> entry.text
> +
> + return ret
> +
> + def __len__(self) -> int:
> + return len(self.to_list())
> +
> + def __sub__(self, report_b: 'Report') -> 'Report':
> + if self.__class__ != report_b.__class__:
> + raise ReportError("Diff of different type of report!")
> +
> + filename, file_extension = os.path.splitext(self.__path)
> + diff_report = self.__class__(filename + ".diff" + file_extension)
> + # Put in the diff report only records of this report that are not
> + # present in the report_b.
> + for file_path, entries in self.__entries.items():
> + rep_b_entries = report_b.get_report_entries()
> + if file_path in rep_b_entries.keys():
> + # File path exists in report_b, so check what entries of that
> + # file path doesn't exist in report_b and add them to the
> diff
> + rep_b_entries_num = [
> + x.line_number for x in rep_b_entries[file_path]
> + ]
> + for entry in entries:
> + if entry.line_number not in rep_b_entries_num:
> + diff_report.add_entry(file_path, entry.line_number,
> + entry.text)
> + else:
> + # File path doesn't exist in report_b, so add every entry
> + # of that file path to the diff
> + for entry in entries:
> + diff_report.add_entry(file_path, entry.line_number,
> + entry.text)
> +
> + return diff_report
> --
> 2.34.1
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |