Skip to content

Commit c749cd4

Browse files
committed
Fix & enable irept pretty-printer
1 parent bb6baff commit c749cd4

File tree

3 files changed

+104
-14
lines changed

3 files changed

+104
-14
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: 89 additions & 12 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):
@@ -70,10 +87,16 @@ def find_type(type, name):
7087

7188

7289
class IrepPrettyPrinter:
73-
"Print an irept"
90+
"""
91+
Print an irept.
92+
93+
This is an array GDB type as everything in the tree is key->value, so it
94+
works better than doing a map type with individual key/value entries.
95+
"""
7496

7597
def __init__(self, val):
7698
self.val = val["data"].referenced_value()
99+
self.clion_representation = get_option("clion_pretty_printers")
77100

78101
def to_string(self):
79102
try:
@@ -82,31 +105,83 @@ def to_string(self):
82105
return "Exception pretty printing irept"
83106

84107
def children(self):
108+
"""
109+
This method tells the pretty-printer what children this object can
110+
return. Because we've stated this is a array then we've also stated that
111+
irept is a container that holds other values.
112+
113+
This makes things awkward because some ireps are not actually containers
114+
of children but values themselves. It's hard to represent that, so instead
115+
we return a single child with the value of the node.
116+
"""
117+
118+
def get_node_value(data_ref):
119+
""" If the item has children, wrap it in [...], if it's just a
120+
value wrap it in quotes to help differentiate. """
121+
_, nested_value = deconstruct_dstring(data_ref["data"])
122+
if nested_value:
123+
has_subs = data_ref["sub"]["_M_impl"]["_M_start"] != data_ref["sub"]["_M_impl"]["_M_finish"]
124+
has_named_subs = data_ref["named_sub"]["_M_t"]["_M_impl"]["_M_node_count"] > 0
125+
126+
stripped_value = nested_value.replace("\"", "\\\"")
127+
if has_subs or has_named_subs:
128+
return "[{0}]".format(stripped_value)
129+
else:
130+
return "\"{0}\"".format(stripped_value)
131+
132+
return ""
133+
85134
sub = self.val["sub"]
86-
count = 0
135+
sub_count = 0
87136
item = sub["_M_impl"]["_M_start"]
88137
finish = sub["_M_impl"]["_M_finish"]
89138
while item != finish:
90-
yield "sub %d key" % count, "sub[%d]" % count
91-
yield "sub %d value" % count, item.dereference()
92-
count += 1
139+
# The original key is just the index, as that's all we have.
140+
node_key = "{}".format(sub_count)
141+
iter_item = item.dereference()
142+
143+
if self.clion_representation:
144+
nested_id = get_node_value(iter_item["data"].referenced_value())
145+
if nested_id:
146+
node_key = "{0}: {1}".format(node_key, nested_id)
147+
148+
yield node_key, iter_item
149+
else:
150+
yield "sub %d key" % sub_count, node_key
151+
yield "sub %d value" % sub_count, item.dereference()
152+
153+
sub_count += 1
93154
item += 1
94155

95156
named_sub = self.val["named_sub"]
96157
size = named_sub["_M_t"]["_M_impl"]["_M_node_count"]
97158
node = named_sub["_M_t"]["_M_impl"]["_M_header"]["_M_left"]
98-
count = 0
99-
while count != size:
159+
named_sub_count = 0
160+
while named_sub_count != size:
100161
rep_type = find_type(named_sub.type, "_Rep_type")
101162
link_type = find_type(rep_type, "_Link_type")
102163
node_type = link_type.strip_typedefs()
103164
current = node.cast(node_type).dereference()
104165
addr_type = current.type.template_argument(0).pointer()
105166
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:
167+
168+
# Get the name of the named_sub.
169+
_, sub_name = deconstruct_dstring(result["first"])
170+
node_key = sub_name.replace("\"", "\\\"")
171+
172+
iter_item = result["second"]
173+
if self.clion_representation:
174+
nested_id = get_node_value(iter_item["data"].referenced_value())
175+
if nested_id:
176+
node_key = "{0}: {1}".format(node_key, nested_id)
177+
178+
yield node_key, iter_item
179+
else:
180+
yield "named_sub %d key" % named_sub_count, node_key
181+
yield "named_sub %d value" % named_sub_count, iter_item
182+
183+
named_sub_count += 1
184+
if named_sub_count < size:
110185
# Get the next node
111186
right = node.dereference()["_M_right"]
112187
if right:
@@ -126,7 +201,7 @@ def children(self):
126201
node = parent
127202

128203
def display_hint(self):
129-
return "map"
204+
return "array" if self.clion_representation else "map"
130205

131206

132207
class InstructionPrettyPrinter:
@@ -154,5 +229,7 @@ def load_cbmc_printers():
154229
# it should be applied too, third is the class that should be called to pretty-print that type.
155230
printers.add_printer("dstringt", "^(?:dstringt|irep_idt)", DStringPrettyPrinter)
156231
printers.add_printer("instructiont", "^goto_programt::instructiont", InstructionPrettyPrinter)
232+
printers.add_printer("irept", "^irept", IrepPrettyPrinter)
233+
157234
# We aren't associating with a particular object file, so pass in None instead of gdb.current_objfile()
158235
gdb.printing.register_pretty_printer(None, printers, replace=True)

0 commit comments

Comments
 (0)