Skip to content

Commit

Permalink
Pretty Printers for Cista (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Harbarth authored Nov 16, 2021
1 parent 7027047 commit 73fa8e6
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 0 deletions.
142 changes: 142 additions & 0 deletions tools/pretty-printers/gdb/cista_hash_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import re
import gdb.xmethod

def is_cista_hash_storage(gdb_type):
type_str = str(gdb_type.strip_typedefs())
return type_str.startswith("cista::hash_storage") and not type_str.endswith("::ctrl_t")

class CistaHashStorage:
def __init__(self, val):
self.val = val
self.entries = self.val['entries_']
self.capacity = self.val['capacity_']
self.ctrl = self.val['ctrl_']

regex = re.compile("cista::offset_ptr")
if regex.match(str(self.entries.type.strip_typedefs())):
self.entries = OffsetPointer(self.entries)
self.ctrl = OffsetPointer(self.ctrl)

def is_valid_entry(self, idx):
return (self.ctrl + idx).dereference() & int('0b10000000', 2) == 0

def __len__(self):
return int(self.val['size_'])

def __getitem__(self, key):
for i in range(self.val['capacity_']):
if not self.is_valid_entry(i):
continue

entry = (self.entries + i).dereference()
if str(entry['first']) == str(key):
return entry['second']

class CistaHashStoragePrinter:
def __init__(self, val):
self.val = CistaHashStorage(val)

def children(self):
current_idx = 0
for i in range(self.val.capacity):
if self.val.is_valid_entry(i):
yield '[' + str(current_idx) + ']', (self.val.entries + i).dereference()
current_idx += 1

def to_string(self):
return str(self.val)

def my_pp_func(val):
if is_cista_hash_storage(val.type):
return CistaHashStoragePrinter(val)

### XMethod cista::vector::operator[]

class CistaHashStorageWorker_operator_brackets(gdb.xmethod.XMethodWorker):
def __init__(self, class_type):
self.class_type = class_type

def get_arg_types(self):
return self.class_type.template_argument(0)

def get_result_type(self, obj):
return obj.type.strip_typedefs().template_argument(1)

def __call__(self, this, key):
hash_storage = CistaHashStorage(this.dereference())
return hash_storage[key]

class CistaHashStorageWorker_operator_brackets_char_ptr(gdb.xmethod.XMethodWorker):
def __init__(self, class_type):
self.class_type = class_type

def get_arg_types(self):
return gdb.lookup_type('const char* const')

def get_result_type(self, obj):
return obj.type.strip_typedefs().template_argument(1)

def __call__(self, this, key):
hash_storage = CistaHashStorage(this.dereference())
key = key.cast(gdb.lookup_type("const char* const"))
return hash_storage[str(key).split()[1]]

class CistaHashStorage_operator_brackets(gdb.xmethod.XMethod):
def __init__(self):
gdb.xmethod.XMethod.__init__(self, 'operator[]')

def get_worker(self, method_name, class_type):
worker = []
if method_name == 'operator[]':
worker.append(CistaHashStorageWorker_operator_brackets(class_type))

temp_arg = class_type.template_argument(0).name
is_string = temp_arg.startswith("std::__cxx11::basic_string") \
or temp_arg.startswith("cista::basic_string")
if method_name == 'operator[]' and is_string:
worker.append(CistaHashStorageWorker_operator_brackets_char_ptr(class_type))

return worker

### XMethod cista::vector::size

class CistaHashStorageWorker_size(gdb.xmethod.XMethodWorker):
def get_arg_types(self):
return None

def get_result_type(self):
return gdb.lookup_type('unsigned long int')

def __call__(self, this):
hash_storage = CistaHashStorage(this.dereference())
return len(hash_storage)

class CistaHashStorage_size(gdb.xmethod.XMethod):
def __init__(self):
gdb.xmethod.XMethod.__init__(self, 'size')

def get_worker(self, method_name, _):
if method_name == 'size':
return [CistaHashStorageWorker_size()]

class CistaHashStorageMatcher(gdb.xmethod.XMethodMatcher):
def __init__(self):
gdb.xmethod.XMethodMatcher.__init__(self, 'CistaHashStorageMatcher')
# List of methods 'managed' by this matcher
self.methods = [CistaHashStorage_operator_brackets(), CistaHashStorage_size()]

def match(self, class_type, method_name):
if not is_cista_hash_storage(class_type):
return None

workers = []
for method in self.methods:
if method.enabled:
worker = method.get_worker(method_name, class_type.template_argument(0))
if worker:
workers.extend(worker)

return workers

gdb.pretty_printers.append(my_pp_func)
gdb.xmethod.register_xmethod_matcher(None, CistaHashStorageMatcher())
19 changes: 19 additions & 0 deletions tools/pretty-printers/gdb/cista_pointer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import re

class CistaOffsetPointerPrinter:
def __init__(self, val):
self.val = OffsetPointer(val)

def children(self):
yield "*", self.val.dereference()

def to_string(self):
return str(self.val)

def my_pp_func(val):
regex = re.compile("cista::offset_ptr")
if regex.match(str(val.type.strip_typedefs())):
return CistaOffsetPointerPrinter(val)

gdb.pretty_printers.append(my_pp_func)

36 changes: 36 additions & 0 deletions tools/pretty-printers/gdb/cista_string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import re

class CistaString:
def __init__(self, val):
self.val = val
self.GDB_CHAR_PTR = gdb.lookup_type("char*")

def is_short(self):
return self.val['s_']['is_short_']

def data(self):
return self.val['s_']['s_'] if self.is_short() else self.val['h_']['ptr_']

def str(self):
if self.is_short():
return self.data().address.cast(self.GDB_CHAR_PTR)

if is_offset_ptr(self.data().type):
return OffsetPointer(self.data()).as_raw_ptr()

return self.data()

class CistaStringPrinter:
def __init__(self, val):
self.val = CistaString(val)

def to_string(self):
return self.val.str()

def my_pp_func(val):
regex = re.compile("cista::basic_string")
if regex.match(str(val.type.strip_typedefs())):
return CistaStringPrinter(val)

gdb.pretty_printers.append(my_pp_func)

29 changes: 29 additions & 0 deletions tools/pretty-printers/gdb/cista_tuple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import re

class CistaTuplePrinter:
def __init__(self, val):
self.val = val

def children(self):
yield '[0]', self.val['head_']

field_count = 1
local_val = self.val[self.val.type.fields()[0]]
while True:
yield '[' + str(field_count) + ']', local_val['head_']
field_count += 1
if (len(local_val.type.fields()) == 1):
return
else:
local_val = local_val[local_val.type.fields()[0]]

def to_string(self):
return str(self.val)

def my_pp_func(val):
regex = re.compile("cista::tuple")
if regex.match(str(val.type.strip_typedefs())):
return CistaTuplePrinter(val)

gdb.pretty_printers.append(my_pp_func)

27 changes: 27 additions & 0 deletions tools/pretty-printers/gdb/cista_variant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import re

class CistaVariantPrinter:
def __init__(self, val):
self.val = val

def get_actual_type(self):
return self.val.type.template_argument(self.val['idx_'])

def children(self):
for field in self.val.type.fields():
if (field.name == "storage_"):
yield field.name, self.val[field.name].cast(self.get_actual_type())
else:
yield field.name, self.val[field.name].cast(field.type)

def to_string(self):
return str(self.val)


def my_pp_func(val):
regex = re.compile("cista::variant")
if regex.match(str(val.type.strip_typedefs())):
return CistaVariantPrinter(val)

gdb.pretty_printers.append(my_pp_func)

108 changes: 108 additions & 0 deletions tools/pretty-printers/gdb/cista_vector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import re
import gdb.xmethod

def is_cista_vector(gdb_type):
return str(gdb_type.strip_typedefs()).startswith("cista::basic_vector")

def is_raw_vector(gdb_type):
return not str(gdb_type.strip_typedefs().template_argument(1)).startswith("cista::offset_ptr")

class CistaVector:
def __init__(self, val):
self.val = val
self.size = val['used_size_']
self.el = val['el_'] if is_raw_vector(val.type) else OffsetPointer(val['el_'])

def __len__(self):
return self.size

def __getitem__(self, idx):
return (self.el + idx).dereference()

def at(self, idx):
if (self.size < idx):
print("Accessing vector out of bounds")
return None

return self[idx]

class CistaVectorPrinter:
def __init__(self, val):
self.val = CistaVector(val)

def children(self):
for idx in range(len(self.val)):
yield '[' + str(idx) + ']', self.val[idx]

def to_string(self):
return str(self.val)

def my_pp_func(val):
if not is_cista_vector(val.type):
return

return CistaVectorPrinter(val)

### XMethod cista::vector::at

class CistaVectorWorker_at(gdb.xmethod.XMethodWorker):
def get_arg_types(self):
return gdb.lookup_type('unsigned long int')

def get_result_type(self, obj):
return obj.type.strip_typedefs().template_argument(0)

def __call__(self, this, idx):
vec = CistaVector(this.dereference())
return vec.at(idx)

class CistaVector_at(gdb.xmethod.XMethod):
def __init__(self):
gdb.xmethod.XMethod.__init__(self, 'at')

def get_worker(self, method_name):
if method_name == 'at':
return CistaVectorWorker_at()

### XMethod cista::vector::operator[]

class CistaVectorWorker_operator_brackets(gdb.xmethod.XMethodWorker):
def get_arg_types(self):
return gdb.lookup_type('unsigned long int')

def get_result_type(self, obj):
return obj.type.strip_typedefs().template_argument(0)

def __call__(self, this, idx):
vec = CistaVector(this.dereference())
return vec[idx]

class CistaVector_operator_brackets(gdb.xmethod.XMethod):
def __init__(self):
gdb.xmethod.XMethod.__init__(self, 'operator[]')

def get_worker(self, method_name):
if method_name == 'operator[]':
return CistaVectorWorker_operator_brackets()

class CistaVectorMatcher(gdb.xmethod.XMethodMatcher):
def __init__(self):
gdb.xmethod.XMethodMatcher.__init__(self, 'CistaVectorMatcher')
# List of methods 'managed' by this matcher
self.methods = [CistaVector_at(), CistaVector_operator_brackets()]

def match(self, class_type, method_name):
if not is_cista_vector(class_type):
return None

workers = []
for method in self.methods:
if method.enabled:
worker = method.get_worker(method_name)
if worker:
workers.append(worker)

return workers

gdb.pretty_printers.append(my_pp_func)
gdb.xmethod.register_xmethod_matcher(None, CistaVectorMatcher())
24 changes: 24 additions & 0 deletions tools/pretty-printers/gdb/offset_pointer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class OffsetPointer:
def __init__(self, val):
self.val = val
self.offset = val['offset_']
self.pointer_type = val.type.template_argument(0).pointer()

def this_as_intptr_t(self):
intptr_t = gdb.lookup_type("intptr_t")
return self.val.address.reinterpret_cast(intptr_t)

def as_raw_ptr(self):
return (self.this_as_intptr_t() + self.offset).reinterpret_cast(self.pointer_type)

def add(self, offset):
return self.as_raw_ptr() + offset

def __add__(self, o):
return self.add(o)

def dereference(self):
return self.as_raw_ptr().dereference()

def is_offset_ptr(type):
return str(type.strip_typedefs()).startswith("cista::offset_ptr")

0 comments on commit 73fa8e6

Please sign in to comment.