Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checker summary view #870

Merged
merged 1 commit into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the description of this command was changed, the docs/user_guide.md file should also be updated to reflect the new help.

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