Skip to content

Commit

Permalink
Local compare mode added to CodeChecker cmd diff mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
dkrupp committed Jul 12, 2017
1 parent acdf11d commit 136ad0a
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 29 deletions.
16 changes: 14 additions & 2 deletions libcodechecker/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def __add_filtering_arguments(parser):
parser.add_argument('-s', '--suppressed',
dest="suppressed",
action='store_true',
help="Filter results to only show suppressed entries.")
help="Show only suppressed results instead of only "
"unsuppressed ones.")

parser.add_argument('--filter',
type=str,
Expand Down Expand Up @@ -155,12 +156,23 @@ def __register_diff(parser):
type=str,
dest="newname",
metavar='NEW_RUN',
required=True,
required=False,
default=argparse.SUPPRESS,
help="The 'new' (right) side of the difference: this "
"analysis run is compared to the -b/--basename "
"run.")

parser.add_argument('-r', '--reportdir',
type=str,
dest="reportdir",
metavar='REPORT_DIR',
required=False,
default=argparse.SUPPRESS,
help="The 'new' (right) side of the difference"
"given as a report directory."
"This will be compared to the baseline "
"stored on the server.")

__add_filtering_arguments(parser)

group = parser.add_argument_group("comparison modes")
Expand Down
201 changes: 174 additions & 27 deletions libcodechecker/cmd/cmd_line_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import getpass
import json
import sys
import os

from thrift.Thrift import TApplicationException

Expand All @@ -19,6 +20,8 @@
from libcodechecker import suppress_file_handler
from libcodechecker.logger import LoggerFactory
from libcodechecker.output_formatters import twodim_to_str
from libcodechecker.analyze import plist_parser
from libcodechecker.report import Report

from . import thrift_helper
from . import authentication_helper
Expand Down Expand Up @@ -259,54 +262,198 @@ def handle_list_results(args):


def handle_diff_results(args):
def printResult(getterFn, baseid, newid, suppr, output_format):
def getDiffResults(getterFn, baseid, newid, suppr):
report_filter = [
codeCheckerDBAccess.ttypes.ReportFilter(suppressed=suppr)]
add_filter_conditions(report_filter[0], args.filter)

sort_type = None
sort_mode = []
sort_mode.append(codeCheckerDBAccess.ttypes.SortMode(
codeCheckerDBAccess.ttypes.SortType.FILENAME,
codeCheckerDBAccess.ttypes.Order.ASC))
limit = codeCheckerDBAccess.constants.MAX_QUERY_SIZE
offset = 0

all_results = []
results = getterFn(baseid, newid, limit, offset, sort_type,
results = getterFn(baseid, newid, limit, offset, sort_mode,
report_filter)

while results:
all_results.extend(results)
offset += limit
results = getterFn(baseid, newid, limit, offset, sort_type,
results = getterFn(baseid, newid, limit, offset, sort_mode,
report_filter)
return all_results

def getReportDirResults(reportdir):
all_reports = []
for filename in os.listdir(reportdir):
if filename.endswith(".plist"):
file_path = os.path.join(reportdir, filename)
LOG.debug("Parsing:" + file_path)
try:
files, reports = plist_parser.parse_plist(file_path)
for report in reports:
report.main['location']['file_name'] = \
files[int(report.main['location']['file'])]
all_reports.extend(reports)

except Exception as ex:
LOG.error('The generated plist is not valid!')
LOG.error(ex)
return all_reports

def getLineFromFile(filename, lineno):
with open(filename, 'r') as f:
i = 1
for line in f:
if i == lineno:
return line
i += 1
return ""

def getLineFromRemoteFile(client, fid, lineno):
# FIXME: large special file content
# cause connection abort
source = client.getSourceFileData(fid, True)
i = 1
lines = source.fileContent.split('\n')
for line in lines:
if i == lineno:
return line
i += 1
return ""

def getDiffReportDir(getterFn, baseid, report_dir, suppr, diff_type):
report_filter = [
codeCheckerDBAccess.ttypes.ReportFilter(suppressed=suppr)]
add_filter_conditions(report_filter[0], args.filter)

sort_mode = []
sort_mode.append(codeCheckerDBAccess.ttypes.SortMode(
codeCheckerDBAccess.ttypes.SortType.FILENAME,
codeCheckerDBAccess.ttypes.Order.ASC))
limit = codeCheckerDBAccess.constants.MAX_QUERY_SIZE
offset = 0

base_results = []
results = getterFn(baseid, limit, offset, sort_mode,
report_filter)
while results:
base_results.extend(results)
offset += limit
results = getterFn(baseid, limit, offset, sort_mode,
report_filter)
base_hashes = {}
for res in base_results:
base_hashes[res.bugHash] = res

filtered_reports = []
new_results = getReportDirResults(report_dir)
new_hashes = {}
for rep in new_results:
bughash = rep.main['issue_hash_content_of_line_in_context']
new_hashes[bughash] = rep
# show new reports from the report dir
# which is not present in the baseline (server)
if diff_type == 'new':
for result in new_results:
if not (result.main['issue_hash_content_of_line_in_context']
in base_hashes):
filtered_reports.append(result)
# show bugs in the baseline (server)
# which is not present in the report dir
elif diff_type == 'resolved':
for result in base_results:
if not (result.bugHash in new_hashes):
filtered_reports.append(result)
# show bugs in the report dir
# which is not suppressed in the baseline (server)
elif diff_type == 'unresolved':
for result in new_results:
new_hash = result.main['issue_hash_content_of_line_in_context']
if new_hash in base_hashes:
filtered_reports.append(result)
return filtered_reports

def printReports(client, reports, output_format):
if output_format == 'json':
print(CmdLineOutputEncoder().encode(all_results))
else:
header = ['File', 'Checker', 'Severity', 'Msg']
rows = []
for res in all_results:
bug_line = res.lastBugPosition.startLine
sev = shared.ttypes.Severity._VALUES_TO_NAMES[res.severity]
checked_file = res.checkedFile + ' @ ' + str(bug_line)
rows.append(
(checked_file, res.checkerId, sev, res.checkerMsg))
output = []
for report in reports:
if type(report) is Report:
output.append(report.main)
else:
output.append(report)
print(CmdLineOutputEncoder().encode(output))
return

header = ['File', 'Checker', 'Severity', 'Msg', 'Source']
rows = []
for report in reports:
if type(report) is Report:
bug_line = report.main['location']['line']
bug_col = report.main['location']['col']
sev = 'unknown'
checked_file = report.main['location']['file_name']\
+ ':' + str(bug_line) + ":" + str(bug_col)
check_name = report.main['check_name']
check_msg = report.main['description']
source_line =\
getLineFromFile(report.main['location']['file_name'],
bug_line)
else:
bug_line = report.lastBugPosition.startLine
bug_col = report.lastBugPosition.startCol
sev = \
shared.ttypes.Severity._VALUES_TO_NAMES[report.severity]
checked_file = report.checkedFile + ':' + str(bug_line) +\
":" + str(bug_col)
source_line =\
getLineFromRemoteFile(client, report.fileId, bug_line)
check_name = report.checkerId
check_msg = report.checkerMsg
rows.append(
(checked_file, check_name, sev, check_msg, source_line))
if output_format == 'plaintext':
for row in rows:
print("{0}: {1} [{2}]\n{3}\n".format(row[0],
row[3], row[1], row[4]))
else:
print(twodim_to_str(output_format, header, rows))

client = setup_client(args.host, args.port, '/')
run_info = check_run_names(client, [args.basename, args.newname])
if 'newname' in args:
run_info = check_run_names(client, [args.basename, args.newname])
newid = run_info[args.newname][0]
else:
run_info = check_run_names(client, [args.basename])

baseid = run_info[args.basename][0]
newid = run_info[args.newname][0]

if 'new' in args:
printResult(client.getNewResults, baseid, newid, args.suppressed,
args.output_format)
elif 'unresolved' in args:
printResult(client.getUnresolvedResults, baseid, newid,
args.suppressed, args.output_format)
elif 'resolved' in args:
printResult(client.getResolvedResults, baseid, newid, args.suppressed,
args.output_format)
results = []
if 'reportdir' in args:
if 'newname' in args:
LOG.error("Either newname or reportdir"
" should be given, but not both.")
sys.exit(1)
diff_type = 'new'
if 'unresolved' in args:
diff_type = 'unresolved'
elif 'resolved' in args:
diff_type = 'resolved'
results = getDiffReportDir(client.getRunResults, baseid,
os.path.abspath(args.reportdir),
args.suppressed, diff_type)
else:
if 'new' in args:
results = getDiffResults(client.getNewResults,
baseid, newid, args.suppressed)
elif 'unresolved' in args:
results = getDiffResults(client.getUnresolvedResults,
baseid, newid, args.suppressed)
elif 'resolved' in args:
results = getDiffResults(client.getResolvedResults,
baseid, newid, args.suppressed)

printReports(client, results, args.output_format)


def handle_list_result_types(args):
Expand Down
4 changes: 4 additions & 0 deletions libcodechecker/cmd/thrift_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ def getRunData(self):
def getRunResults(self, runId, limit, offset, sortType, reportFilters):
pass

@ThriftClientCall
def getSourceFileData(self, fileId, fileContent):
pass

@ThriftClientCall
def getRunResultCount(self, runId, reportFilters):
pass
Expand Down

0 comments on commit 136ad0a

Please sign in to comment.