Skip to content

Commit 68b192a

Browse files
authored
Merge pull request #4330 from JohnDumbell/jd/enhancement/gdb_pretty_printers
Add GDB pretty-printers
2 parents 8ca4924 + 655e936 commit 68b192a

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

scripts/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
A collection of utility scripts and script applications.
2+
3+
pretty-printers
4+
------
5+
6+
GDB:
7+
8+
Pretty-printers for CBMC that enable easier debugging in IDEs and mitigate
9+
certain crashes due to the way some objects' memory is shared.
10+
11+
Currently it deals with:
12+
* irep_idt
13+
* dstring
14+
* instructiont
15+
16+
To install:
17+
18+
1. Navigate to /pretty-printers/gdb.
19+
2. Run install.py with python 3+.
20+
3. If an exception occurs, create an empty '.gdbinit' file in your home
21+
folder, and copy/paste the blob of code at the top of the install.py file.
22+
23+
The .gdbinit file is used by GDB during start-up to run any initial commands or
24+
scripts, and the code injects the pretty-printers during that.
25+
26+
Nothing else is required to get the pretty-printers to work, beside using
27+
GDB to debug the code.

scripts/pretty-printers/gdb/__init__.py

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import gdb
2+
import pretty_printers
3+
4+
5+
def build_pretty_printer_collection():
6+
7+
printers = gdb.printing.RegexpCollectionPrettyPrinter("CBMC")
8+
9+
# First argument is the name of the pretty-printer, second is a regex match for which type
10+
# it should be applied too, third is the class that should be called to pretty-print that type.
11+
printers.add_printer(
12+
'irep_idt', '^irep_idt', pretty_printers.DStringPrettyPrinter)
13+
printers.add_printer(
14+
'dstringt', '^dstringt', pretty_printers.DStringPrettyPrinter)
15+
printers.add_printer(
16+
'instructiont', '^goto_programt::instructiont', pretty_printers.InstructionPrettyPrinter)
17+
return printers
18+
19+
20+
# If you change the name of this make sure to change install.py too.
21+
def load_pretty_printers():
22+
23+
# We aren't associating with a particular object file, so pass in None instead of gdb.current_objfile()
24+
gdb.printing.register_pretty_printer(None, build_pretty_printer_collection(), replace=True)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python
2+
3+
import os
4+
5+
# This is the code that should be copied if you're applying the changes by hand.
6+
# Replace {0} with the path to this folder.
7+
file_contents = """
8+
python
9+
import sys
10+
import os
11+
12+
pretty_printer_folder = '{0}'
13+
if os.path.exists(pretty_printer_folder):
14+
sys.path.insert(1, pretty_printer_folder)
15+
import auto_load
16+
auto_load.load_pretty_printers()
17+
end
18+
"""
19+
20+
21+
def create_gdbinit_file():
22+
"""
23+
Add or append to a .gdbinit file the python code to set-up cbmc pretty-printers.
24+
"""
25+
26+
print("Attempting to enable cbmc-specific pretty-printers.")
27+
28+
home_folder = os.path.expanduser("~")
29+
if not home_folder:
30+
print(home_folder + " is an invalid home folder, please manually create a .gdbinit file and apply the code.")
31+
return
32+
33+
gdbinit_file = os.path.join(home_folder, ".gdbinit")
34+
file_write_mode = 'w'
35+
if os.path.exists(gdbinit_file):
36+
print(".gdbinit file exists at " + gdbinit_file + "."
37+
" Please type 'y' if you want to append the pretty-printer commands to it. Press any other key to exit.")
38+
while True:
39+
choice = input().lower()
40+
if choice == 'y':
41+
file_write_mode = 'a'
42+
break
43+
else:
44+
print("Not appending to file. Exiting.")
45+
return
46+
47+
if file_write_mode == 'w':
48+
print("Creating .gdbinit file.")
49+
50+
print("Adding pretty-print commands to {0}.".format(gdbinit_file))
51+
parent_directory = os.path.dirname(os.path.abspath(__file__))
52+
try:
53+
file = open(gdbinit_file, file_write_mode)
54+
file.write(file_contents.format(parent_directory))
55+
file.close()
56+
print("Commands added.")
57+
except:
58+
print("Exception occured writing to file. Please apply changes manually.")
59+
60+
61+
if __name__ == "__main__":
62+
create_gdbinit_file()
63+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import gdb
2+
3+
4+
class DStringPrettyPrinter:
5+
def __init__(self, val):
6+
self.val = val
7+
8+
def to_string(self):
9+
try:
10+
raw_address = str(self.val.address)
11+
12+
# If it's ::empty, we know it's empty without going further.
13+
if "::empty" in raw_address:
14+
return ""
15+
16+
# Split the address on the first space, return that value
17+
# Addresses are usually {address} {optional type_name}
18+
typed_pointer = '({}*){}'.format(self.val.type, raw_address.split(None, 1)[0])
19+
20+
# Check that the pointer is not null.
21+
if gdb.parse_and_eval(typed_pointer + ' == 0'):
22+
return ""
23+
24+
# If it isn't attempt to find the string.
25+
value = '(*{})'.format(typed_pointer)
26+
return gdb.parse_and_eval(value + '.c_str()')
27+
except:
28+
return ""
29+
30+
def display_hint(self):
31+
return 'string'
32+
33+
34+
class InstructionPrettyPrinter:
35+
def __init__(self, val):
36+
self.val = val
37+
38+
def to_string(self):
39+
try:
40+
raw_address = str(self.val.address)
41+
variable_accessor = '(*({}*){})'.format(self.val.type, raw_address.split(None, 1)[0])
42+
expr = '{0}.to_string()'.format(variable_accessor)
43+
return gdb.parse_and_eval(expr)
44+
except:
45+
return ""
46+
47+
def display_hint(self):
48+
return 'string'

0 commit comments

Comments
 (0)