From 1fd6761bf1dca4f7c473917b05d2253e2e1c8a06 Mon Sep 17 00:00:00 2001 From: Jay Rajput Date: Mon, 5 Oct 2020 12:24:43 +0530 Subject: [PATCH 1/5] [report-converter][cmd] Parse Smatch Output - Smatch Output is parsed and stored into the format: file:line message - Parser is called from report conveter CLI with type 'smatch' --- .../codechecker_report_converter/cli.py | 5 +- .../smatch/__init__.py | 7 +++ .../smatch/analyzer_result.py | 36 +++++++++++ .../smatch/output_parser.py | 61 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 tools/report-converter/codechecker_report_converter/smatch/__init__.py create mode 100644 tools/report-converter/codechecker_report_converter/smatch/analyzer_result.py create mode 100644 tools/report-converter/codechecker_report_converter/smatch/output_parser.py diff --git a/tools/report-converter/codechecker_report_converter/cli.py b/tools/report-converter/codechecker_report_converter/cli.py index 8a9b558ecd..9cd9a4c0b4 100755 --- a/tools/report-converter/codechecker_report_converter/cli.py +++ b/tools/report-converter/codechecker_report_converter/cli.py @@ -54,6 +54,8 @@ MarkdownlintAnalyzerResult # noqa from codechecker_report_converter.coccinelle.analyzer_result import \ CoccinelleAnalyzerResult # noqa +from codechecker_report_converter.smatch.analyzer_result import \ + SmatchAnalyzerResult # noqa LOG = logging.getLogger('ReportConverter') @@ -89,7 +91,8 @@ class RawDescriptionDefaultHelpFormatter( UBSANAnalyzerResult.TOOL_NAME: UBSANAnalyzerResult, SpotBugsAnalyzerResult.TOOL_NAME: SpotBugsAnalyzerResult, MarkdownlintAnalyzerResult.TOOL_NAME: MarkdownlintAnalyzerResult, - CoccinelleAnalyzerResult.TOOL_NAME: CoccinelleAnalyzerResult + CoccinelleAnalyzerResult.TOOL_NAME: CoccinelleAnalyzerResult, + SmatchAnalyzerResult.TOOL_NAME: SmatchAnalyzerResult } supported_metadata_keys = ["analyzer_command", "analyzer_version"] diff --git a/tools/report-converter/codechecker_report_converter/smatch/__init__.py b/tools/report-converter/codechecker_report_converter/smatch/__init__.py new file mode 100644 index 0000000000..4259749345 --- /dev/null +++ b/tools/report-converter/codechecker_report_converter/smatch/__init__.py @@ -0,0 +1,7 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- diff --git a/tools/report-converter/codechecker_report_converter/smatch/analyzer_result.py b/tools/report-converter/codechecker_report_converter/smatch/analyzer_result.py new file mode 100644 index 0000000000..feb9990687 --- /dev/null +++ b/tools/report-converter/codechecker_report_converter/smatch/analyzer_result.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- + +from codechecker_report_converter.analyzer_result import AnalyzerResult + +from .output_parser import SmatchParser +from ..plist_converter import PlistConverter + + +class SmatchAnalyzerResult(AnalyzerResult): + """ Transform analyzer result of Smatch. """ + + TOOL_NAME = 'smatch' + NAME = 'Smatch' + URL = 'https://repo.or.cz/w/smatch.git' + + def parse(self, analyzer_result): + """ Creates plist files from the given analyzer result to the given + output directory. + """ + parser = SmatchParser(analyzer_result) + + content = self._get_analyzer_result_file_content(analyzer_result) + if not content: + return + + messages = parser.parse_messages(content) + + plist_converter = PlistConverter(self.TOOL_NAME) + plist_converter.add_messages(messages) + return plist_converter.get_plist_results() diff --git a/tools/report-converter/codechecker_report_converter/smatch/output_parser.py b/tools/report-converter/codechecker_report_converter/smatch/output_parser.py new file mode 100644 index 0000000000..5e032183d4 --- /dev/null +++ b/tools/report-converter/codechecker_report_converter/smatch/output_parser.py @@ -0,0 +1,61 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- + +import logging +import os +import re + +from ..output_parser import BaseParser, Message +LOG = logging.getLogger('ReportConverter') + + +class SmatchParser(BaseParser): + """ + Parser for Smatch Output + """ + + def __init__(self, analyzer_result): + super(SmatchParser, self).__init__() + + self.analyzer_result = analyzer_result + + self.message_line_re = re.compile( + # File path followed by a ':'. + r'^(?P[\S ]+?):' + # Line number followed by a whitespace. + r'(?P\d+?)\s' + # Message. + r'(?P[\S \t]+)\s*') + + def parse_message(self, it, line): + """ + Actual Parsing function for the given line + It is expected that each line contains a seperate report + """ + match = self.message_line_re.match(line) + if match is None: + return None, next(it) + + file_path = os.path.normpath( + os.path.join(os.path.dirname(self.analyzer_result), + match.group('path'))) + + checker_name = None + column = 0 + + message = Message( + file_path, + int(match.group('line')), + column, + match.group('message').strip(), + checker_name) + + try: + return message, next(it) + except StopIteration: + return message, '' From 5a04a8e968186dbe3dc37c40777126033903ca34 Mon Sep 17 00:00:00 2001 From: Jay Rajput Date: Sun, 11 Oct 2020 18:52:32 +0530 Subject: [PATCH 2/5] [test] Test Smatch Output with sample output file - Smatch Parser is tested with sample.c and sample.out files --- .../smatch_output_test_files/files/sample.c | 5 ++ .../sample.expected.plist | 69 ++++++++++++++++ .../unit/smatch_output_test_files/sample.out | 1 + .../tests/unit/test_smatch_parser.py | 79 +++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 tools/report-converter/tests/unit/smatch_output_test_files/files/sample.c create mode 100644 tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist create mode 100644 tools/report-converter/tests/unit/smatch_output_test_files/sample.out create mode 100644 tools/report-converter/tests/unit/test_smatch_parser.py diff --git a/tools/report-converter/tests/unit/smatch_output_test_files/files/sample.c b/tools/report-converter/tests/unit/smatch_output_test_files/files/sample.c new file mode 100644 index 0000000000..38aee273fb --- /dev/null +++ b/tools/report-converter/tests/unit/smatch_output_test_files/files/sample.c @@ -0,0 +1,5 @@ +static struct parse_smatch *check_sample() { + int res; + res = 0; + return ERR_PTR(res); +} diff --git a/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist b/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist new file mode 100644 index 0000000000..d96d5997fa --- /dev/null +++ b/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist @@ -0,0 +1,69 @@ + + + + + diagnostics + + + category + unknown + check_name + smatch + description + check_sample() warn: passing zero to 'ERR_PTR' + issue_hash_content_of_line_in_context + 4b7ca471f46a9a22d294b3e6e3676131 + location + + col + 0 + file + 0 + line + 4 + + path + + + depth + 0 + kind + event + location + + col + 0 + file + 0 + line + 4 + + message + check_sample() warn: passing zero to 'ERR_PTR' + + + type + smatch + + + files + + files/sample.c + + metadata + + analyzer + + name + smatch + + generated_by + + name + report-converter + version + x.y.z + + + + diff --git a/tools/report-converter/tests/unit/smatch_output_test_files/sample.out b/tools/report-converter/tests/unit/smatch_output_test_files/sample.out new file mode 100644 index 0000000000..8b57eb8b9b --- /dev/null +++ b/tools/report-converter/tests/unit/smatch_output_test_files/sample.out @@ -0,0 +1 @@ +files/sample.c:4 check_sample() warn: passing zero to 'ERR_PTR' diff --git a/tools/report-converter/tests/unit/test_smatch_parser.py b/tools/report-converter/tests/unit/test_smatch_parser.py new file mode 100644 index 0000000000..f2936b41bc --- /dev/null +++ b/tools/report-converter/tests/unit/test_smatch_parser.py @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- + +""" +This module tests the correctness of the SmatchAnalyzerResult, which +used in sequence transform Smatch output to a plist file. +""" + + +import os +import plistlib +import shutil +import tempfile +import unittest + + +from codechecker_report_converter.smatch.analyzer_result import \ + SmatchAnalyzerResult + + +class SmatchAnalyzerResultTestCase(unittest.TestCase): + """ Test the output of the SmatchAnalyzerResult. """ + + def setUp(self): + """ Setup the test. """ + self.analyzer_result = SmatchAnalyzerResult() + self.cc_result_dir = tempfile.mkdtemp() + self.test_files = os.path.join(os.path.dirname(__file__), + 'smatch_output_test_files') + + def tearDown(self): + """ Clean temporary directory. """ + shutil.rmtree(self.cc_result_dir) + + def test_no_smatch_output_file(self): + """ Test transforming single C file. """ + analyzer_result = os.path.join(self.test_files, 'files', + 'sample.c') + + ret = self.analyzer_result.transform(analyzer_result, + self.cc_result_dir) + self.assertFalse(ret) + + def test_transform_dir(self): + """ Test transforming a directory. """ + analyzer_result = os.path.join(self.test_files) + + ret = self.analyzer_result.transform(analyzer_result, + self.cc_result_dir) + self.assertFalse(ret) + + def test_transform_single_file(self): + """ Test transforming single output file. """ + analyzer_result = os.path.join(self.test_files, 'sample.out') + self.analyzer_result.transform(analyzer_result, self.cc_result_dir) + + plist_file = os.path.join(self.cc_result_dir, + 'sample.c_smatch.plist') + + with open(plist_file, mode='rb') as pfile: + res = plistlib.load(pfile) + + # Use relative path for this test. + res['files'][0] = os.path.join('files', 'sample.c') + + self.assertTrue(res['metadata']['generated_by']['version']) + res['metadata']['generated_by']['version'] = "x.y.z" + + plist_file = os.path.join(self.test_files, + 'sample.expected.plist') + with open(plist_file, mode='rb') as pfile: + exp = plistlib.load(pfile) + + self.assertEqual(res, exp) From 7d83246318a9e0f8a396fce0a3795c65fdf9633e Mon Sep 17 00:00:00 2001 From: Jay Rajput Date: Sun, 11 Oct 2020 23:53:56 +0530 Subject: [PATCH 3/5] [docs] Explain how to use Smatch and it's converter on the kernel - Smatch documentation in report converter explains the usage of smatch tool on kernel sources - Smatch is accessible from the main readme file and supported code analyzer file - Smatch and Coccinelle updated in usage --- docs/README.md | 1 + docs/supported_code_analyzers.md | 1 + tools/report-converter/README.md | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/docs/README.md b/docs/README.md index 74e4be6541..f4c891da41 100644 --- a/docs/README.md +++ b/docs/README.md @@ -108,6 +108,7 @@ The following tools are supported: | | [Cppcheck](/tools/report-converter/README.md#cppcheck) | | | [Facebook Infer](/tools/report-converter/README.md#facebook-infer) | | | [Coccinelle](/tools/report-converter/README.md#coccinelle) | +| | [Smatch](/tools/report-converter/README.md#smatch) | | **Java** | [SpotBugs](/tools/report-converter/README.md#spotbugs) | | | [Facebook Infer](/tools/report-converter/README.md#fbinfer) | | **Python** | [Pylint](/tools/report-converter/README.md#pylint) | diff --git a/docs/supported_code_analyzers.md b/docs/supported_code_analyzers.md index 9a924d0b12..b1eddceeb6 100644 --- a/docs/supported_code_analyzers.md +++ b/docs/supported_code_analyzers.md @@ -16,6 +16,7 @@ CodeChecker result directory which can be stored to a CodeChecker server. | | [Cppcheck](/tools/report-converter/README.md#cppcheck) | ✓ | | | [Facebook Infer](/tools/report-converter/README.md#fbinfer) | ✓ | | | [Coccinelle](/tools/report-converter/README.md#coccinelle) | ✓ | +| | [Smatch](/tools/report-converter/README.md#smatch) | ✓ | | **Java** | [FindBugs](http://findbugs.sourceforge.net/) | ✗ | | | [SpotBugs](/tools/report-converter/README.md#spotbugs) | ✓ | | | [Facebook Infer](/tools/report-converter/README.md#fbinfer) | ✓ | diff --git a/tools/report-converter/README.md b/tools/report-converter/README.md index c6b1a27593..a308709286 100644 --- a/tools/report-converter/README.md +++ b/tools/report-converter/README.md @@ -21,6 +21,7 @@ a CodeChecker server. * [Pyflakes](#pyflakes) * [Markdownlint](#markdownlint) * [Coccinelle](#coccinelle) +* [Smatch](#smatch) * [License](#license) ## Install guide @@ -80,6 +81,7 @@ optional arguments: Supported analyzers: asan - AddressSanitizer, https://clang.llvm.org/docs/AddressSanitizer.html clang-tidy - Clang Tidy, https://clang.llvm.org/extra/clang-tidy + coccinelle - Coccinelle, https://github.com/coccinelle/coccinelle cppcheck - Cppcheck, http://cppcheck.sourceforge.net eslint - ESLint, https://eslint.org/ fbinfer - Facebook Infer, https://fbinfer.com @@ -88,6 +90,7 @@ Supported analyzers: msan - MemorySanitizer, https://clang.llvm.org/docs/MemorySanitizer.html pyflakes - Pyflakes, https://github.com/PyCQA/pyflakes pylint - Pylint, https://www.pylint.org + smatch - smatch, https://repo.or.cz/w/smatch.git spotbugs - spotbugs, https://spotbugs.github.io tsan - ThreadSanitizer, https://clang.llvm.org/docs/ThreadSanitizer.html tslint - TSLint, https://palantir.github.io/tslint @@ -385,6 +388,7 @@ report-converter -t mdl -o ./codechecker_mdl_reports ./mdl_reports.out # Store Markdownlint reports with CodeChecker. CodeChecker store ./codechecker_mdl_reports -n mdl +``` ## [Coccinelle](https://github.com/coccinelle/coccinelle) [Coccinelle](https://github.com/coccinelle/coccinelle) allows programmers to easily @@ -409,7 +413,30 @@ report-converter -t coccinelle -o ./codechecker_coccinelle_reports ./coccinelle_ # Store the Cocccinelle reports with CodeChecker. CodeChecker store ./codechecker_coccinelle_reports -n coccinelle +``` + +## [Smatch](https://repo.or.cz/w/smatch.git) +[Smatch](https://repo.or.cz/w/smatch.git) is a static analysis tool for C that is used on the kernel. + +The recommended way of running Smatch is to redirect the output to a file and +give this file to the report converter tool. + +The following example shows you how to run Smatch on kernel sources +and store the results found by Smatch to the CodeChecker database. +```sh +# Change Directory to your project +cd path/to/linux/kernel/repository + +# Run Smatch +# Note: The warnings will be stored by default into smatch_warns.txt after executing the following command +path/to/smatch/smatch_scripts/test_kernel.sh + +# Use 'report-converter' to create a CodeChecker report directory from the +# analyzer result of Smatch +report-converter -t smatch -o ./codechecker_smatch_reports ./smatch_warns.txt +# Store the Smatch reports with CodeChecker. +CodeChecker store ./codechecker_smatch_reports -n smatch ``` ## License From 6daa0e6d8e03eba7e671ffae4fdfccce0e65f5d5 Mon Sep 17 00:00:00 2001 From: Jay Rajput Date: Wed, 14 Oct 2020 22:37:01 +0530 Subject: [PATCH 4/5] [report-converter] Parse Smatch ouput with checker name - Checker name is included in the parsed smatch output - The message no more displays the function name --- .../codechecker_report_converter/smatch/output_parser.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/report-converter/codechecker_report_converter/smatch/output_parser.py b/tools/report-converter/codechecker_report_converter/smatch/output_parser.py index 5e032183d4..ea1c9d588a 100644 --- a/tools/report-converter/codechecker_report_converter/smatch/output_parser.py +++ b/tools/report-converter/codechecker_report_converter/smatch/output_parser.py @@ -29,6 +29,10 @@ def __init__(self, analyzer_result): r'^(?P[\S ]+?):' # Line number followed by a whitespace. r'(?P\d+?)\s' + # Function name followed by a whitespace. + r'(?P\S+)\s' + # Checker name followed by a whitespace + r'\[smatch\.(?P\S+)\]\s' # Message. r'(?P[\S \t]+)\s*') @@ -45,7 +49,6 @@ def parse_message(self, it, line): os.path.join(os.path.dirname(self.analyzer_result), match.group('path'))) - checker_name = None column = 0 message = Message( @@ -53,7 +56,7 @@ def parse_message(self, it, line): int(match.group('line')), column, match.group('message').strip(), - checker_name) + match.group('checker_name')) try: return message, next(it) From 5538d6daeefe6466faf0cd5f94a340738ddcc885 Mon Sep 17 00:00:00 2001 From: Jay Rajput Date: Wed, 14 Oct 2020 22:45:14 +0530 Subject: [PATCH 5/5] [test] Test for smatch with checker name - Smatch Parser tested with sample output containing checker name --- .../unit/smatch_output_test_files/sample.expected.plist | 8 ++++---- .../tests/unit/smatch_output_test_files/sample.out | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist b/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist index d96d5997fa..542757098a 100644 --- a/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist +++ b/tools/report-converter/tests/unit/smatch_output_test_files/sample.expected.plist @@ -8,11 +8,11 @@ category unknown check_name - smatch + check_zero_to_err_ptr description - check_sample() warn: passing zero to 'ERR_PTR' + warn: passing zero to 'ERR_PTR' issue_hash_content_of_line_in_context - 4b7ca471f46a9a22d294b3e6e3676131 + 2d8b9711e5b334b518d201a8a6799fdb location col @@ -39,7 +39,7 @@ 4 message - check_sample() warn: passing zero to 'ERR_PTR' + warn: passing zero to 'ERR_PTR' type diff --git a/tools/report-converter/tests/unit/smatch_output_test_files/sample.out b/tools/report-converter/tests/unit/smatch_output_test_files/sample.out index 8b57eb8b9b..86989adb36 100644 --- a/tools/report-converter/tests/unit/smatch_output_test_files/sample.out +++ b/tools/report-converter/tests/unit/smatch_output_test_files/sample.out @@ -1 +1 @@ -files/sample.c:4 check_sample() warn: passing zero to 'ERR_PTR' +files/sample.c:4 check_sample() [smatch.check_zero_to_err_ptr] warn: passing zero to 'ERR_PTR'