Skip to content

Commit bfd2313

Browse files
committed
Fix & enable irept pretty-printer
1 parent d74a8d8 commit bfd2313

File tree

3 files changed

+120
-15
lines changed

3 files changed

+120
-15
lines changed

scripts/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
A collection of utility scripts and script applications.
22

3-
pretty-printers
4-
------
3+
# pretty-printers
54

65
GDB:
76

@@ -25,3 +24,14 @@ scripts, and the code injects the pretty-printers during that.
2524

2625
Nothing else is required to get the pretty-printers to work, beside using
2726
GDB to debug the code.
27+
28+
# options
29+
30+
There is an `options.json` file to control any internal options.
31+
32+
List of options.
33+
34+
`clion_pretty_printers`: Some pretty printers work differently if you're
35+
running them in CLion versus baseline GDB, and aren't very pretty if you
36+
look at them in the alternate view. Set to true if you use CLion, false if
37+
you use commandline GDB. Defaults to true.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"clion_pretty_printers": true
3+
}

scripts/pretty-printers/gdb/pretty_printers.py

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
11
import gdb
2+
import json
3+
import os
4+
from json import JSONDecodeError
5+
6+
script_directory = os.path.dirname(os.path.abspath(__file__))
7+
8+
options_path = os.path.join(script_directory, "options.json")
9+
pretty_printer_options = {}
10+
try:
11+
with open(os.path.join(script_directory, "options.json"), "r") as json_file:
12+
pretty_printer_options = json.load(json_file)
13+
except JSONDecodeError as e:
14+
print("Options file at {0} failed to load. Exception: {1}".format(options_path, e.msg))
15+
16+
17+
def get_option(value):
18+
return pretty_printer_options.get(value, None)
219

320

421
def deconstruct_dstring(val):
@@ -38,6 +55,37 @@ def deconstruct_dstring(val):
3855
except:
3956
return -1, ""
4057

58+
59+
def has_children_nodes(data_ref):
60+
has_subs = data_ref["sub"]["_M_impl"]["_M_start"] != data_ref["sub"]["_M_impl"]["_M_finish"]
61+
has_named_subs = data_ref["named_sub"]["_M_t"]["_M_impl"]["_M_node_count"] > 0
62+
return has_subs or has_named_subs
63+
64+
65+
def get_node_value(data_ref):
66+
""" If the item has children, wrap it in [...], if it's just a
67+
value wrap it in quotes to help differentiate. """
68+
has_children = has_children_nodes(data_ref)
69+
id_value = get_id(data_ref)
70+
if id_value:
71+
if has_children:
72+
return "[{0}]".format(id_value)
73+
else:
74+
return "\"{0}\"".format(id_value)
75+
elif has_children:
76+
return "[...]"
77+
78+
return "\"\""
79+
80+
81+
def get_id(data_ref):
82+
_, nested_value = deconstruct_dstring(data_ref["data"])
83+
if nested_value:
84+
return nested_value.replace("\"", "\\\"")
85+
86+
return ""
87+
88+
4189
# Class for pretty-printing dstringt
4290
class DStringPrettyPrinter:
4391
"Print a dstringt"
@@ -70,43 +118,85 @@ def find_type(type, name):
70118

71119

72120
class IrepPrettyPrinter:
73-
"Print an irept"
121+
"""
122+
Print an irept.
123+
124+
This is an array GDB type as everything in the tree is key->value, so it
125+
works better than doing a map type with individual key/value entries.
126+
"""
74127

75128
def __init__(self, val):
76129
self.val = val["data"].referenced_value()
130+
self.clion_representation = get_option("clion_pretty_printers")
77131

78132
def to_string(self):
79133
try:
80-
return "\"{}\"".format(deconstruct_dstring(self.val["data"])[1].replace("\"", "\\\""))
134+
return "\"{}\"".format(get_id(self.val))
81135
except:
82136
return "Exception pretty printing irept"
83137

84138
def children(self):
139+
"""
140+
This method tells the pretty-printer what children this object can
141+
return. Because we've stated this is a array then we've also stated that
142+
irept is a container that holds other values.
143+
144+
This makes things awkward because some ireps are not actually containers
145+
of children but values themselves. It's hard to represent that, so instead
146+
we return a single child with the value of the node.
147+
"""
148+
85149
sub = self.val["sub"]
86-
count = 0
150+
sub_count = 0
87151
item = sub["_M_impl"]["_M_start"]
88152
finish = sub["_M_impl"]["_M_finish"]
89153
while item != finish:
90-
yield "sub %d key" % count, "sub[%d]" % count
91-
yield "sub %d value" % count, item.dereference()
92-
count += 1
154+
# The original key is just the index, as that's all we have.
155+
node_key = "{}".format(sub_count)
156+
iter_item = item.dereference()
157+
158+
if self.clion_representation:
159+
nested_id = get_node_value(iter_item["data"].referenced_value())
160+
if nested_id:
161+
node_key = "{0}: {1}".format(node_key, nested_id)
162+
163+
yield node_key, iter_item
164+
else:
165+
yield "sub %d key" % sub_count, node_key
166+
yield "sub %d value" % sub_count, item.dereference()
167+
168+
sub_count += 1
93169
item += 1
94170

95171
named_sub = self.val["named_sub"]
96172
size = named_sub["_M_t"]["_M_impl"]["_M_node_count"]
97173
node = named_sub["_M_t"]["_M_impl"]["_M_header"]["_M_left"]
98-
count = 0
99-
while count != size:
174+
named_sub_count = 0
175+
while named_sub_count != size:
100176
rep_type = find_type(named_sub.type, "_Rep_type")
101177
link_type = find_type(rep_type, "_Link_type")
102178
node_type = link_type.strip_typedefs()
103179
current = node.cast(node_type).dereference()
104180
addr_type = current.type.template_argument(0).pointer()
105181
result = current["_M_storage"]["_M_storage"].address.cast(addr_type).dereference()
106-
yield "named_sub %d key" % count, "named_sub[\"%s\"]" % deconstruct_dstring(result["first"])[1].replace("\"", "\\\"")
107-
yield "named_sub %d value" % count, result["second"]
108-
count += 1
109-
if count < size:
182+
183+
# Get the name of the named_sub.
184+
_, sub_name = deconstruct_dstring(result["first"])
185+
node_key = sub_name.replace("\"", "\\\"")
186+
187+
iter_item = result["second"]
188+
if self.clion_representation:
189+
nested_id = get_node_value(iter_item["data"].referenced_value())
190+
if nested_id:
191+
node_key = "{0}: {1}".format(node_key, nested_id)
192+
193+
yield node_key, iter_item
194+
else:
195+
yield "named_sub %d key" % named_sub_count, node_key
196+
yield "named_sub %d value" % named_sub_count, iter_item
197+
198+
named_sub_count += 1
199+
if named_sub_count < size:
110200
# Get the next node
111201
right = node.dereference()["_M_right"]
112202
if right:
@@ -126,7 +216,7 @@ def children(self):
126216
node = parent
127217

128218
def display_hint(self):
129-
return "map"
219+
return "array" if self.clion_representation else "map"
130220

131221

132222
class InstructionPrettyPrinter:
@@ -153,6 +243,8 @@ def load_cbmc_printers():
153243
# First argument is the name of the pretty-printer, second is a regex match for which type
154244
# it should be applied too, third is the class that should be called to pretty-print that type.
155245
printers.add_printer("dstringt", "^(?:dstringt|irep_idt)", DStringPrettyPrinter)
246+
printers.add_printer("irept", "^irept", IrepPrettyPrinter)
156247
printers.add_printer("instructiont", "^goto_programt::instructiont", InstructionPrettyPrinter)
248+
157249
# We aren't associating with a particular object file, so pass in None instead of gdb.current_objfile()
158250
gdb.printing.register_pretty_printer(None, printers, replace=True)

0 commit comments

Comments
 (0)