Skip to content

Commit

Permalink
codechecker-parse: print analysis results to stdout
Browse files Browse the repository at this point in the history
  • Loading branch information
whisperity committed May 9, 2017
1 parent 25044fa commit 4843d1b
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 23 deletions.
21 changes: 21 additions & 0 deletions bin/codechecker-parse
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python
# -------------------------------------------------------------------------
# The CodeChecker Infrastructure
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
# -------------------------------------------------------------------------
"""
Entry point for the parse (analysis results to stdout) command.
"""

import imp
import os

THIS_PATH = os.path.dirname(os.path.abspath(__file__))
CC = os.path.join(THIS_PATH, "CodeChecker")

# Load CodeChecker from the current folder (the wrapper script (without .py))
CodeChecker = imp.load_source('CodeChecker', CC)

# Execute CC's main script with the current subcommand.
CodeChecker.main("parse")
4 changes: 3 additions & 1 deletion libcodechecker/analyze/analysis_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def check(check_data):

# The result handler for analysis is an empty result handler
# which only returns metadata, but can't process the results.
rh = analyzer_types.construct_result_callback(action,
rh = analyzer_types.construct_analyze_handler(action,
output_dir,
context.severity_map,
skip_handler)
Expand All @@ -227,6 +227,8 @@ def check(check_data):
LOG.debug_analyzer('\n' + rh.analyzer_stdout)
if rh.analyzer_stderr != '':
LOG.debug_analyzer('\n' + rh.analyzer_stderr)
rh.postprocess_result()
rh.handle_results()

LOG.info("[%d/%d] %s analyzed %s successfully." %
(progress_checked_num.value, progress_actions.value,
Expand Down
78 changes: 57 additions & 21 deletions libcodechecker/analyze/analyzers/analyzer_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,63 @@ def build_config_handlers(args, context, enabled_analyzers, connection=None):
return analyzer_config_map


def construct_analyze_handler(buildaction,
report_output,
severity_map,
skiplist_handler):
"""
Construct an empty (base) ResultHandler which is capable of returning
analyzer worker statuses to the caller method, but does not provide
actual parsing and processing of results, instead only saves the analysis
results.
"""

assert buildaction.analyzer_type in supported_analyzers, \
'Analyzer types should have been checked already.'

if buildaction.analyzer_type == CLANG_SA:
res_handler = result_handler_base.ResultHandler(buildaction,
report_output)

elif buildaction.analyzer_type == CLANG_TIDY:
res_handler = result_handler_clang_tidy.ClangTidyPlistToFile(
buildaction, report_output)

res_handler.severity_map = severity_map
res_handler.skiplist_handler = skiplist_handler
return res_handler


def construct_parse_handler(buildaction,
output,
severity_map,
suppress_handler,
print_steps):
"""
Construct a result handler for parsing results in a human-readable format.
"""
assert buildaction.analyzer_type in supported_analyzers, \
'Analyzer types should have been checked already.'

if buildaction.analyzer_type == CLANG_SA:
res_handler = result_handler_plist_to_stdout.PlistToStdout(
buildaction,
output,
None)
res_handler.print_steps = print_steps

elif buildaction.analyzer_type == CLANG_TIDY:
res_handler = result_handler_clang_tidy.ClangTidyPlistToStdout(
buildaction,
output,
None)

res_handler.severity_map = severity_map
res_handler.suppress_handler = suppress_handler
return res_handler


# TODO: This is deprecated.
def construct_result_handler(args,
buildaction,
run_id,
Expand Down Expand Up @@ -416,24 +473,3 @@ def construct_result_handler(args,
res_handler.severity_map = severity_map
res_handler.skiplist_handler = skiplist_handler
return res_handler


def construct_result_callback(buildaction,
report_output,
severity_map,
skiplist_handler):
"""
Construct an empty (base) ResultHandler which is capable of returning
analyzer worker statuses to the caller method, but does not provide
actual parsing and processing of results.
"""

assert buildaction.analyzer_type in supported_analyzers, \
'Analyzer types should have been checked already.'

res_handler = result_handler_base.ResultHandler(buildaction,
report_output)

res_handler.severity_map = severity_map
res_handler.skiplist_handler = skiplist_handler
return res_handler
18 changes: 18 additions & 0 deletions libcodechecker/analyze/analyzers/result_handler_clang_tidy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# -------------------------------------------------------------------------

from libcodechecker.analyze import tidy_output_converter
from libcodechecker.analyze.analyzers.result_handler_base \
import ResultHandler
from libcodechecker.analyze.analyzers.result_handler_plist_to_db \
import PlistToDB
from libcodechecker.analyze.analyzers.result_handler_plist_to_stdout \
Expand All @@ -28,6 +30,22 @@ def generate_plist_from_tidy_result(output_file, tidy_stdout):
plist_converter.write_to_file(output_file)


class ClangTidyPlistToFile(ResultHandler):
"""
Create a plist file from clang-tidy results.
"""

def postprocess_result(self):
"""
Generate plist file which can be parsed and processed for
results which can be stored into the database.
"""
output_file = self.analyzer_result_file
LOG.debug_analyzer(self.analyzer_stdout)
tidy_stdout = self.analyzer_stdout.splitlines()
generate_plist_from_tidy_result(output_file, tidy_stdout)


class ClangTidyPlistToDB(PlistToDB):
"""
Store clang tidy plist results to a database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self, buildaction, workspace, lock):
self.__print_steps = False
self.__output = sys.stdout
self.__lock = lock
self.suppress_handler = None

@property
def print_steps(self):
Expand Down Expand Up @@ -91,6 +92,11 @@ def __print_bugs(self, reports, files):
LOG.debug(report + ' is skipped (in ' + f_path + ")")
continue

if self.suppress_handler and \
self.suppress_handler.get_suppressed(report):
LOG.debug(report + " is suppressed by suppress file.")
continue

last_report_event = report.bug_path[-1]
source_file = files[last_report_event['location']['file']]
report_line = last_report_event['location']['line']
Expand Down
2 changes: 1 addition & 1 deletion libcodechecker/arg_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def handle_server(args):
os.makedirs(workspace)

suppress_handler = generic_package_suppress_handler.\
GenericSuppressHandler()
GenericSuppressHandler(None)
if args.suppress is None:
LOG.warning('No suppress file was given, suppressed results will '
'be only stored in the database.')
Expand Down
37 changes: 37 additions & 0 deletions libcodechecker/generic_package_suppress_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Handler for suppressing a bug.
"""

import os

from libcodechecker import suppress_file_handler
from libcodechecker import suppress_handler
from libcodechecker.logger import LoggerFactory
Expand All @@ -16,6 +18,33 @@


class GenericSuppressHandler(suppress_handler.SuppressHandler):

def __init__(self, suppress_file):
"""
Create a new suppress handler with a suppress_file as backend.
"""
super(GenericSuppressHandler, self).__init__()

self.__suppress_info = []
if suppress_file:
self.suppress_file = suppress_file
self.__have_memory_backend = True
self.__revalidate_suppress_data()
else:
self.__have_memory_backend = False

def __revalidate_suppress_data(self):
"""Reload the information in the suppress file to the memory."""

if not self.__have_memory_backend:
# Do not load and have suppress data stored in memory if not
# needed.
return

with open(self.suppress_file, 'r') as file_handle:
self.__suppress_info = suppress_file_handler.\
get_suppress_data(file_handle)

def store_suppress_bug_id(self, bug_id, file_name, comment):

if self.suppress_file is None:
Expand All @@ -25,6 +54,7 @@ def store_suppress_bug_id(self, bug_id, file_name, comment):
bug_id,
file_name,
comment)
self.__revalidate_suppress_data()
return ret

def remove_suppress_bug_id(self, bug_id, file_name):
Expand All @@ -36,4 +66,11 @@ def remove_suppress_bug_id(self, bug_id, file_name):
self.suppress_file,
bug_id,
file_name)
self.__revalidate_suppress_data()
return ret

def get_suppressed(self, bug):

return any([suppress for suppress in self.__suppress_info
if suppress[0] == bug.hash_value and
suppress[1] == os.path.basename(bug.file_path)])
Loading

0 comments on commit 4843d1b

Please sign in to comment.