Skip to content

Commit deb7acc

Browse files
committed
[analyzer] exploded-graph-rewriter: Implement checker messages.
They are displayed as raw lines and diffed via difflib on a per-checker basis. Differential Revision: https://reviews.llvm.org/D64100 llvm-svn: 364989
1 parent 3dbdbbe commit deb7acc

File tree

9 files changed

+239
-10
lines changed

9 files changed

+239
-10
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %exploded_graph_rewriter %s | FileCheck %s
2+
3+
// FIXME: Substitution doesn't seem to work on Windows.
4+
// UNSUPPORTED: system-windows
5+
6+
// CHECK: <b>Checker State: </b>
7+
// CHECK-SAME: <td align="left"><i>alpha.core.FooChecker</i>:</td>
8+
// CHECK-SAME: <td align="left">Foo stuff:</td>
9+
// CHECK-SAME: <td align="left">Foo: Bar</td>
10+
Node0x1 [shape=record,label=
11+
"{
12+
{ "node_id": 1,
13+
"pointer": "0x1",
14+
"state_id": 2,
15+
"program_points": [],
16+
"program_state": {
17+
"store": null,
18+
"constraints": null,
19+
"dynamic_types": null,
20+
"constructing_objects": null,
21+
"environment": null,
22+
"checker_messages": [
23+
{ "checker": "alpha.core.FooChecker", "messages": [
24+
"Foo stuff:",
25+
"Foo: Bar"
26+
]}
27+
]
28+
}
29+
}
30+
\l}"];
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %exploded_graph_rewriter -d %s | FileCheck %s
2+
3+
// FIXME: Substitution doesn't seem to work on Windows.
4+
// UNSUPPORTED: system-windows
5+
6+
Node0x1 [shape=record,label=
7+
"{
8+
{ "node_id": 1,
9+
"pointer": "0x1",
10+
"state_id": 2,
11+
"program_points": [],
12+
"program_state": {
13+
"environment": null,
14+
"store": null,
15+
"constraints": null,
16+
"dynamic_types": null,
17+
"constructing_objects": null,
18+
"checker_messages": [
19+
{ "checker": "FooChecker", "messages": [
20+
"Foo: Bar"
21+
]},
22+
{ "checker": "BarChecker", "messages": [
23+
"Bar: Foo"
24+
]}
25+
]
26+
}
27+
}
28+
\l}"];
29+
30+
Node0x1 -> Node0x4;
31+
32+
33+
// CHECK: Node0x4 [
34+
// CHECK-SAME: <tr>
35+
// CHECK-SAME: <td><font color="red">-</font></td>
36+
// CHECK-SAME: <td align="left"><i>BarChecker</i>:</td>
37+
// CHECK-SAME: </tr>
38+
// CHECK-SAME: <tr>
39+
// CHECK-SAME: <td><font color="red">-</font></td>
40+
// CHECK-SAME: <td align="left">Bar: Foo</td>
41+
// CHECK-SAME: </tr>
42+
// CHECK-SAME: <tr>
43+
// CHECK-SAME: <td></td>
44+
// CHECK-SAME: <td align="left"><i>FooChecker</i>:</td>
45+
// CHECK-SAME: </tr>
46+
// CHECK-SAME: <tr>
47+
// CHECK-SAME: <td><font color="forestgreen">+</font></td>
48+
// CHECK-SAME: <td align="left"> Bar: Foo</td>
49+
// CHECK-SAME: </tr>
50+
// CHECK-SAME: <tr>
51+
// CHECK-SAME: <td><font color="forestgreen">+</font></td>
52+
// CHECK-SAME: <td align="left"><i>DunnoWhateverSomeOtherChecker</i>:</td>
53+
// CHECK-SAME: </tr>
54+
// CHECK-SAME: <tr>
55+
// CHECK-SAME: <td><font color="forestgreen">+</font></td>
56+
// CHECK-SAME: <td align="left">Dunno, some other message.</td>
57+
// CHECK-SAME: </tr>
58+
Node0x4 [shape=record,label=
59+
"{
60+
{ "node_id": 4,
61+
"pointer": "0x4",
62+
"state_id": 5,
63+
"program_points": [],
64+
"program_state": {
65+
"environment": null,
66+
"store": null,
67+
"constraints": null,
68+
"dynamic_types": null,
69+
"constructing_objects": null,
70+
"checker_messages": [
71+
{ "checker": "FooChecker", "messages": [
72+
"Foo: Bar",
73+
"Bar: Foo"
74+
]},
75+
{ "checker": "DunnoWhateverSomeOtherChecker", "messages": [
76+
"Dunno, some other message."
77+
]}
78+
]
79+
}
80+
}
81+
\l}"];
82+
83+
Node0x4 -> Node0x6;
84+
85+
Node0x6 [shape=record,label=
86+
"{
87+
{ "node_id": 6,
88+
"pointer": "0x6",
89+
"state_id": 7,
90+
"program_points": [],
91+
"program_state": null
92+
}
93+
\l}"];

clang/test/Analysis/exploded-graph-rewriter/constraints.dot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Node0x1 [shape=record,label=
2121
"environment": null,
2222
"dynamic_types": null,
2323
"constructing_objects": null,
24+
"checker_messages": null,
2425
"constraints": [
2526
{ "symbol": "reg_$0<x>", "range": "{ [0, 0] }" }
2627
]

clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Node0x1 [shape=record,label=
1414
"environment": null,
1515
"dynamic_types": null,
1616
"constructing_objects": null,
17+
"checker_messages": null,
1718
"constraints": [
1819
{ "symbol": "reg_$0<x>", "range": "{ [0, 10] }" }
1920
]
@@ -45,6 +46,7 @@ Node0x3 [shape=record,label=
4546
"environment": null,
4647
"dynamic_types": null,
4748
"constructing_objects": null,
49+
"checker_messages": null,
4850
"constraints": [
4951
{ "symbol": "reg_$0<x>", "range": "{ [0, 5] }" }
5052
]
@@ -65,7 +67,8 @@ Node0x5 [shape=record,label=
6567
"environment": null,
6668
"constraints": null,
6769
"dynamic_types": null,
68-
"constructing_objects": null
70+
"constructing_objects": null,
71+
"checker_messages": null
6972
}
7073
}
7174
\l}"];

clang/test/Analysis/exploded-graph-rewriter/environment.dot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Node0x1 [shape=record,label=
3636
"constraints": null,
3737
"dynamic_types": null,
3838
"constructing_objects": null,
39+
"checker_messages": null,
3940
"environment": {
4041
"pointer": "0x2",
4142
"items": [

clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Node0x1 [shape=record,label=
1515
"constraints": null,
1616
"dynamic_types": null,
1717
"constructing_objects": null,
18+
"checker_messages": null,
1819
"environment": {
1920
"pointer": "0x2",
2021
"items": [
@@ -63,6 +64,7 @@ Node0x6 [shape=record,label=
6364
"constraints": null,
6465
"dynamic_types": null,
6566
"constructing_objects": null,
67+
"checker_messages": null,
6668
"environment": {
6769
"pointer": "0x2",
6870
"items": [
@@ -105,6 +107,7 @@ Node0x9 [shape=record,label=
105107
"constraints": null,
106108
"dynamic_types": null,
107109
"constructing_objects": null,
110+
"checker_messages": null,
108111
"environment": {
109112
"pointer": "0x2",
110113
"items": [

clang/test/Analysis/exploded-graph-rewriter/store.dot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Node0x1 [shape=record,label=
3131
"constraints": null,
3232
"dynamic_types": null,
3333
"constructing_objects": null,
34+
"checker_messages": null,
3435
"store": {
3536
"pointer": "0x2",
3637
"items": [

clang/test/Analysis/exploded-graph-rewriter/store_diff.dot

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Node0x1 [shape=record,label=
1414
"constraints": null,
1515
"dynamic_types": null,
1616
"constructing_objects": null,
17+
"checker_messages": null,
1718
"store": {
1819
"pointer": "0x2",
1920
"items": [
@@ -61,6 +62,7 @@ Node0x4 [shape=record,label=
6162
"constraints": null,
6263
"dynamic_types": null,
6364
"constructing_objects": null,
65+
"checker_messages": null,
6466
"store": {
6567
"pointer": "0x5",
6668
"items": [

clang/utils/analyzer/exploded-graph-rewriter.py

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import argparse
1515
import collections
16+
import difflib
1617
import json
1718
import logging
1819
import re
@@ -211,6 +212,41 @@ def is_different(self, prev):
211212
return len(removed) != 0 or len(added) != 0 or len(updated) != 0
212213

213214

215+
# Deserialized messages from a single checker in a single program state.
216+
# Basically a list of raw strings.
217+
class CheckerLines(object):
218+
def __init__(self, json_lines):
219+
super(CheckerLines, self).__init__()
220+
self.lines = json_lines
221+
222+
def diff_lines(self, prev):
223+
lines = difflib.ndiff(prev.lines, self.lines)
224+
return [l.strip() for l in lines
225+
if l.startswith('+') or l.startswith('-')]
226+
227+
def is_different(self, prev):
228+
return len(self.diff_lines(prev)) > 0
229+
230+
231+
# Deserialized messages of all checkers, separated by checker.
232+
class CheckerMessages(object):
233+
def __init__(self, json_m):
234+
super(CheckerMessages, self).__init__()
235+
self.items = collections.OrderedDict(
236+
[(m['checker'], CheckerLines(m['messages'])) for m in json_m])
237+
238+
def diff_messages(self, prev):
239+
removed = [k for k in prev.items if k not in self.items]
240+
added = [k for k in self.items if k not in prev.items]
241+
updated = [k for k in prev.items if k in self.items
242+
and prev.items[k].is_different(self.items[k])]
243+
return (removed, added, updated)
244+
245+
def is_different(self, prev):
246+
removed, added, updated = self.diff_messages(prev)
247+
return len(removed) != 0 or len(added) != 0 or len(updated) != 0
248+
249+
214250
# A deserialized program state.
215251
class ProgramState(object):
216252
def __init__(self, state_id, json_ps):
@@ -241,7 +277,8 @@ def __init__(self, state_id, json_ps):
241277
GenericEnvironment(json_ps['constructing_objects']) \
242278
if json_ps['constructing_objects'] is not None else None
243279

244-
# TODO: Checker messages.
280+
self.checker_messages = CheckerMessages(json_ps['checker_messages']) \
281+
if json_ps['checker_messages'] is not None else None
245282

246283

247284
# A deserialized exploded graph node. Has a default constructor because it
@@ -595,16 +632,73 @@ def visit_generic_map_in_state(self, selector, title, s, prev_s=None):
595632
if m is None:
596633
self._dump('<i> Nothing!</i>')
597634
else:
598-
if prev_s is not None:
599-
if prev_m is not None:
600-
if m.is_different(prev_m):
601-
self._dump('</td></tr><tr><td align="left">')
602-
self.visit_generic_map(m, prev_m)
603-
else:
604-
self._dump('<i> No changes!</i>')
605-
if prev_m is None:
635+
if prev_m is not None:
636+
if m.is_different(prev_m):
637+
self._dump('</td></tr><tr><td align="left">')
638+
self.visit_generic_map(m, prev_m)
639+
else:
640+
self._dump('<i> No changes!</i>')
641+
else:
606642
self._dump('</td></tr><tr><td align="left">')
607643
self.visit_generic_map(m)
644+
645+
self._dump('</td></tr>')
646+
647+
def visit_checker_messages(self, m, prev_m=None):
648+
self._dump('<table border="0">')
649+
650+
def dump_line(l, is_added=None):
651+
self._dump('<tr><td>%s</td>'
652+
'<td align="left">%s</td></tr>'
653+
% (self._diff_plus_minus(is_added), l))
654+
655+
def dump_chk(chk, is_added=None):
656+
dump_line('<i>%s</i>:' % chk, is_added)
657+
658+
if prev_m is not None:
659+
removed, added, updated = m.diff_messages(prev_m)
660+
for chk in removed:
661+
dump_chk(chk, False)
662+
for l in prev_m.items[chk].lines:
663+
dump_line(l, False)
664+
for chk in updated:
665+
dump_chk(chk)
666+
for l in m.items[chk].diff_lines(prev_m.items[chk]):
667+
dump_line(l[1:], l.startswith('+'))
668+
for chk in added:
669+
dump_chk(chk, True)
670+
for l in m.items[chk].lines:
671+
dump_line(l, True)
672+
else:
673+
for chk in m.items:
674+
dump_chk(chk)
675+
for l in m.items[chk].lines:
676+
dump_line(l)
677+
678+
self._dump('</table>')
679+
680+
def visit_checker_messages_in_state(self, s, prev_s=None):
681+
m = s.checker_messages
682+
prev_m = prev_s.checker_messages if prev_s is not None else None
683+
if m is None and prev_m is None:
684+
return
685+
686+
self._dump('<hr />')
687+
self._dump('<tr><td align="left">'
688+
'<b>Checker State: </b>')
689+
if m is None:
690+
self._dump('<i> Nothing!</i>')
691+
else:
692+
if prev_m is not None:
693+
if m.is_different(prev_m):
694+
self._dump('</td></tr><tr><td align="left">')
695+
self.visit_checker_messages(m, prev_m)
696+
else:
697+
self._dump('<i> No changes!</i>')
698+
else:
699+
self._dump('</td></tr><tr><td align="left">')
700+
self.visit_checker_messages(m)
701+
608702
self._dump('</td></tr>')
609703

610704
def visit_state(self, s, prev_s):
@@ -618,6 +712,7 @@ def visit_state(self, s, prev_s):
618712
self.visit_environment_in_state('constructing_objects',
619713
'Objects Under Construction',
620714
s, prev_s)
715+
self.visit_checker_messages_in_state(s, prev_s)
621716

622717
def visit_node(self, node):
623718
self._dump('%s [shape=record,'

0 commit comments

Comments
 (0)