From 73fa8e61c56d8dbd56d6570a0d5cdc139bbd130d Mon Sep 17 00:00:00 2001 From: Julian Harbarth Date: Tue, 16 Nov 2021 18:04:52 +0100 Subject: [PATCH] Pretty Printers for Cista (#107) --- .../pretty-printers/gdb/cista_hash_storage.py | 142 ++++++++++++++++++ tools/pretty-printers/gdb/cista_pointer.py | 19 +++ tools/pretty-printers/gdb/cista_string.py | 36 +++++ tools/pretty-printers/gdb/cista_tuple.py | 29 ++++ tools/pretty-printers/gdb/cista_variant.py | 27 ++++ tools/pretty-printers/gdb/cista_vector.py | 108 +++++++++++++ tools/pretty-printers/gdb/offset_pointer.py | 24 +++ 7 files changed, 385 insertions(+) create mode 100644 tools/pretty-printers/gdb/cista_hash_storage.py create mode 100644 tools/pretty-printers/gdb/cista_pointer.py create mode 100644 tools/pretty-printers/gdb/cista_string.py create mode 100644 tools/pretty-printers/gdb/cista_tuple.py create mode 100644 tools/pretty-printers/gdb/cista_variant.py create mode 100644 tools/pretty-printers/gdb/cista_vector.py create mode 100644 tools/pretty-printers/gdb/offset_pointer.py diff --git a/tools/pretty-printers/gdb/cista_hash_storage.py b/tools/pretty-printers/gdb/cista_hash_storage.py new file mode 100644 index 00000000..02b31e14 --- /dev/null +++ b/tools/pretty-printers/gdb/cista_hash_storage.py @@ -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()) diff --git a/tools/pretty-printers/gdb/cista_pointer.py b/tools/pretty-printers/gdb/cista_pointer.py new file mode 100644 index 00000000..c532ca2c --- /dev/null +++ b/tools/pretty-printers/gdb/cista_pointer.py @@ -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) + diff --git a/tools/pretty-printers/gdb/cista_string.py b/tools/pretty-printers/gdb/cista_string.py new file mode 100644 index 00000000..ed2b1c3b --- /dev/null +++ b/tools/pretty-printers/gdb/cista_string.py @@ -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) + diff --git a/tools/pretty-printers/gdb/cista_tuple.py b/tools/pretty-printers/gdb/cista_tuple.py new file mode 100644 index 00000000..ccb41169 --- /dev/null +++ b/tools/pretty-printers/gdb/cista_tuple.py @@ -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) + diff --git a/tools/pretty-printers/gdb/cista_variant.py b/tools/pretty-printers/gdb/cista_variant.py new file mode 100644 index 00000000..389eabfc --- /dev/null +++ b/tools/pretty-printers/gdb/cista_variant.py @@ -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) + diff --git a/tools/pretty-printers/gdb/cista_vector.py b/tools/pretty-printers/gdb/cista_vector.py new file mode 100644 index 00000000..3cede374 --- /dev/null +++ b/tools/pretty-printers/gdb/cista_vector.py @@ -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()) diff --git a/tools/pretty-printers/gdb/offset_pointer.py b/tools/pretty-printers/gdb/offset_pointer.py new file mode 100644 index 00000000..950f0e62 --- /dev/null +++ b/tools/pretty-printers/gdb/offset_pointer.py @@ -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") \ No newline at end of file