Skip to content

Commit

Permalink
Checker summary view
Browse files Browse the repository at this point in the history
  • Loading branch information
csordasmarton committed Sep 7, 2017
1 parent eceaf88 commit 9b745b8
Show file tree
Hide file tree
Showing 11 changed files with 383 additions and 62 deletions.
15 changes: 11 additions & 4 deletions api/report_server.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ struct RunReportCount {
}
typedef list<RunReportCount> RunReportCounts

struct CheckerCount {
1: string name, // Name of the checker.
2: shared.Severity severity, // Severity level of the checker.
3: i64 count // Number of reports.
}
typedef list<CheckerCount> CheckerCounts

struct ReviewData {
1: shared.ReviewStatus status,
2: string comment,
Expand Down Expand Up @@ -426,10 +433,10 @@ service codeCheckerDBAccess {
// for all of the runs and in compare mode all of the runs
// will be used as a baseline excluding the runs in compare data.
// PERMISSION: PRODUCT_ACCESS
map<string, i64> getCheckerCounts(1: list<i64> runIds,
2: ReportFilter_v2 reportFilter,
3: CompareData cmpData)
throws (1: shared.RequestFailed requestError),
CheckerCounts getCheckerCounts(1: list<i64> runIds,
2: ReportFilter_v2 reportFilter,
3: CompareData cmpData)
throws (1: shared.RequestFailed requestError),


//============================================
Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,7 @@ usage: CodeChecker cmd sum [-h] (-n RUN_NAME [RUN_NAME ...] | -a) [-s]
[-o {plaintext,rows,table,csv,json}]
[--verbose {info,debug,debug_analyzer}]
Show the count of checker reports per checker for some analysis runs.
Show checker statistics for some analysis runs.
optional arguments:
-h, --help show this help message and exit
Expand Down
90 changes: 65 additions & 25 deletions libcodechecker/cmd/cmd_line_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,19 @@ def printReports(client, reports, output_format):


def handle_list_result_types(args):
client = setup_client(args.product_url)
def getStatistics(client, run_ids, field, values):
report_filter = ttypes.ReportFilter_v2()
setattr(report_filter, field, values)
checkers = client.getCheckerCounts(run_ids,
report_filter,
None)

filters = []
report_filter = ttypes.ReportFilter()
return dict((res.name, res.count) for res in checkers)

add_filter_conditions(report_filter, args.filter)
filters.append(report_filter)
def checkerCount(dict, key):
return dict[key] if key in dict else 0

client = setup_client(args.product_url)

if 'all_results' in args:
items = check_run_names(client, None).items()
Expand All @@ -372,28 +378,62 @@ def handle_list_result_types(args):
for name in args.names:
items.append((name, run_info.get(name)))

results_collector = []
for name, run_info in items:
run_id, run_date = run_info
results = client.getRunResultTypes(run_id, filters)

if args.output_format == 'json':
for res in results:
res.severity =\
shared.ttypes.Severity._VALUES_TO_NAMES[res.severity]
results_collector.append({name: results})
else: # plaintext, csv
print("Run '" + name + "', executed at '" + run_date + "'")
rows = []
header = ['Checker', 'Severity', 'Count']
for res in results:
sev = shared.ttypes.Severity._VALUES_TO_NAMES[res.severity]
rows.append((res.checkerId, sev, str(res.count)))

print(twodim_to_str(args.output_format, header, rows))
run_ids = [item[1][0] for item in items]

all_checkers_report_filter = ttypes.ReportFilter_v2()
all_checkers = client.getCheckerCounts(run_ids, all_checkers_report_filter,
None)
all_checkers_dict = dict((res.name, res) for res in all_checkers)

unrev_checkers = getStatistics(client, run_ids, 'reviewStatus',
[shared.ttypes.ReviewStatus.UNREVIEWED])

confirmed_checkers = getStatistics(client, run_ids, 'reviewStatus',
[shared.ttypes.ReviewStatus.CONFIRMED])

false_checkers = getStatistics(client, run_ids, 'reviewStatus',
[shared.ttypes.ReviewStatus.FALSE_POSITIVE])

wontfix_checkers = getStatistics(client, run_ids, 'reviewStatus',
[shared.ttypes.ReviewStatus.WONT_FIX])

resolved_checkers = getStatistics(client, run_ids, 'detectionStatus',
[shared.ttypes.DetectionStatus.RESOLVED])

all_results = []
for key, checker_data in sorted(all_checkers_dict.items(),
key=lambda x: x[1].severity,
reverse=True):
all_results.append(dict(
checker=key,
severity=shared.ttypes.Severity._VALUES_TO_NAMES[
checker_data.severity],
reports=checker_data.count,
unreviewed=checkerCount(unrev_checkers, key),
confirmed=checkerCount(confirmed_checkers, key),
false_positive=checkerCount(false_checkers, key),
wont_fix=checkerCount(wontfix_checkers, key),
resolved=checkerCount(resolved_checkers, key),
))

if args.output_format == 'json':
print(CmdLineOutputEncoder().encode(results_collector))
print(CmdLineOutputEncoder().encode(all_results))
else:
header = ['Checker', 'Severity', 'All reports', 'Resolved',
'Unreviewed', 'Confirmed', 'False positive', "Won't fix"]

rows = []
for stat in all_results:
rows.append((stat['checker'],
stat['severity'],
str(stat['reports']),
str(stat['resolved']),
str(stat['unreviewed']),
str(stat['confirmed']),
str(stat['false_positive']),
str(stat['wont_fix'])))

print(twodim_to_str(args.output_format, header, rows))


def handle_remove_run_results(args):
Expand Down
5 changes: 2 additions & 3 deletions libcodechecker/libhandlers/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,8 @@ def add_arguments_to_parser(parser):
sum_p = subcommands.add_parser(
'sum',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Show the count of checker reports per checker for some "
"analysis runs.",
help="Show number of reports per checker.")
description="Show checker statistics for some analysis runs.",
help="Show statistics of checkers.")
__register_sum(sum_p)
sum_p.set_defaults(func=cmd_line_client.handle_list_result_types)
__add_common_arguments(sum_p, has_matrix_output=True)
Expand Down
14 changes: 10 additions & 4 deletions libcodechecker/server/client_db_access_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,7 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data):
will be used as a baseline excluding the runs in compare data.
"""
self.__require_access()
results = {}
results = []
session = self.__Session()
try:

Expand All @@ -1400,7 +1400,9 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data):

count_expr = func.count(literal_column('*'))

q = session.query(Report.checker_id, count_expr) \
q = session.query(Report.checker_id,
Report.severity,
count_expr) \
.filter(Report.run_id.in_(run_ids)) \
.outerjoin(File,
Report.file_id == File.id) \
Expand All @@ -1411,9 +1413,13 @@ def getCheckerCounts(self, run_ids, report_filter, cmp_data):
if cmp_data:
q = q.filter(Report.bug_id.in_(diff_hashes))

checker_ids = q.group_by(Report.checker_id).all()
q = q.group_by(Report.checker_id, Report.severity).all()

results = dict(checker_ids)
for name, severity, count in q:
checker_count = CheckerCount(name=name,
severity=severity,
count=count)
results.append(checker_count)

except Exception as ex:
LOG.error(ex)
Expand Down
29 changes: 21 additions & 8 deletions tests/functional/diff/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,11 @@ def test_get_diff_checker_counts(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
None,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# core.CallAndMessage is the new checker.
test_res = {"core.CallAndMessage": 5}
self.assertDictEqual(diff_res, test_res)
self.assertDictEqual(diff_dict, test_res)

def test_get_diff_checker_counts_core_new(self):
"""
Expand All @@ -265,9 +266,11 @@ def test_get_diff_checker_counts_core_new(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
report_filter,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# core.CallAndMessage is the new checker.
test_res = {"core.CallAndMessage": 5}
self.assertDictEqual(diff_res, test_res)
self.assertDictEqual(diff_dict, test_res)

def test_get_diff_checker_counts_unix_resolved(self):
"""
Expand All @@ -282,9 +285,11 @@ def test_get_diff_checker_counts_unix_resolved(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
report_filter,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# Resolved core checkers.
test_res = {'core.StackAddressEscape': 3}
self.assertDictEqual(diff_res, test_res)
self.assertDictEqual(diff_dict, test_res)

def test_get_diff_checker_counts_core_unresolved(self):
"""
Expand All @@ -299,9 +304,11 @@ def test_get_diff_checker_counts_core_unresolved(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
report_filter,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# Unresolved core checkers.
test_res = {'core.NullDereference': 4, 'core.DivideZero': 5}
self.assertDictContainsSubset(test_res, diff_res)
self.assertDictContainsSubset(test_res, diff_dict)

def test_get_diff_checker_counts_all_unresolved(self):
"""
Expand All @@ -316,14 +323,16 @@ def test_get_diff_checker_counts_all_unresolved(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
None,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# All unresolved checkers.
test_res = {'core.DivideZero': 5,
'core.NullDereference': 4,
'cplusplus.NewDelete': 5,
'deadcode.DeadStores': 5,
'unix.Malloc': 1}

self.assertDictContainsSubset(diff_res, test_res)
self.assertDictContainsSubset(diff_dict, test_res)

def test_get_diff_severity_counts_all_unresolved(self):
"""
Expand Down Expand Up @@ -423,8 +432,10 @@ def test_get_diff_res_types_resolved(self):
diff_res = self._cc_client.getCheckerCounts([base_run_id],
None,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

test_res = {'core.StackAddressEscape': 3}
self.assertDictEqual(diff_res, test_res)
self.assertDictEqual(diff_dict, test_res)

def test_get_diff_res_types_unresolved(self):
"""
Expand All @@ -444,13 +455,14 @@ def test_get_diff_res_types_unresolved(self):
self._cc_client.getCheckerCounts([base_run_id],
None,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

test_res = {'cplusplus.NewDelete': 5,
'deadcode.DeadStores': 5,
'unix.Malloc': 1,
'core.NullDereference': 4,
'core.DivideZero': 5}
self.assertDictEqual(diff_res, test_res)
self.assertDictEqual(diff_dict, test_res)

def test_get_diff_res_types_unresolved_filter(self):
"""
Expand All @@ -472,9 +484,10 @@ def test_get_diff_res_types_unresolved_filter(self):
self._cc_client.getCheckerCounts([base_run_id],
checker_filter,
cmp_data)
diff_dict = dict((res.name, res.count) for res in diff_res)

# There should be only one result for each checker name.
self.assertEqual(test_result_count, diff_res[checker_name])
self.assertEqual(test_result_count, diff_dict[checker_name])

def test_local_compare_res_count_new(self):
"""
Expand Down
Loading

0 comments on commit 9b745b8

Please sign in to comment.