quickerNES/extern/phmap/phmap_lldb.py

249 lines
9.5 KiB
Python
Executable File

# Python lldb formatters for parallel-hashmap
# tested witch clang10 / lldb9 & 10
# to install it, type the following command or put it in $HOME/.lldbinit:
# command script import "PATH_TO_SCRIPT/lldb_phmap.py"
import lldb
import os
import sys
import re
_MAX_CHILDREN = 250
_MAX_CTRL_INDEX = 1_000
_MODULE_NAME = os.path.basename(__file__).split(".")[0]
def _get_function_name(instance=None):
"""Return the name of the calling function"""
class_name = f"{type(instance).__name__}." if instance else ""
return class_name + sys._getframe(1).f_code.co_name
class flat_map_slot_type:
CLASS_PATTERN = "^phmap::priv::raw_hash_set<phmap::priv::FlatHashMapPolicy.*>::slot_type$"
HAS_SUMMARY = True
IS_SYNTHETIC_PROVIDER = False
@staticmethod
def summary(valobj, _):
try:
valobj = valobj.GetChildMemberWithName('value')
first = valobj.GetChildMemberWithName('first').GetSummary()
if not first: first = "{...}"
second = valobj.GetChildMemberWithName('second').GetSummary()
if not second: second = "{...}"
return f"{{{first}, {second}}}"
except BaseException as ex:
print(f"{_get_function_name()} -> {ex}")
return ""
class node_map_slot_type:
CLASS_PATTERN = r"phmap::priv::raw_hash_set<phmap::priv::NodeHashMapPolicy.*>::slot_type$"
HAS_SUMMARY = True
IS_SYNTHETIC_PROVIDER = False
@staticmethod
def summary(valobj, _):
try:
valobj = valobj.Dereference()
first = valobj.GetChildMemberWithName('first').GetSummary()
if not first: first = "{...}"
second = valobj.GetChildMemberWithName('second').GetSummary()
if not second: second = "{...}"
return f"{{{first}, {second}}}"
except BaseException as ex:
print(f"{_get_function_name()} -> {ex}")
return "{?}"
class node_set_slot_type:
CLASS_PATTERN = r"phmap::priv::raw_hash_set<phmap::priv::NodeHashSetPolicy.*>::slot_type$"
HAS_SUMMARY = True
IS_SYNTHETIC_PROVIDER = False
@staticmethod
def summary(valobj, _):
try:
summary = valobj.Dereference().GetSummary()
if not summary: summary = "{...}"
return summary
except BaseException as ex:
print(f"{_get_function_name()} -> {ex}")
return "{?}"
class flat_hash_map_or_set:
CLASS_PATTERN = "^phmap::flat_hash_(map|set)<.*>$"
HAS_SUMMARY = True
IS_SYNTHETIC_PROVIDER = True
@staticmethod
def summary(valobj, _):
try:
valobj = valobj.GetNonSyntheticValue()
size = valobj.GetChildMemberWithName('size_').GetValueAsUnsigned()
capacity = valobj.GetChildMemberWithName('capacity_').GetValueAsUnsigned()
return f"size = {size} (capacity = {capacity})"
except BaseException as ex:
print(f"{_get_function_name()} -> {ex}")
return "{?}"
def __init__(self, valobj, _):
self.valobj = valobj
self.slots_ = self.slot_type = self.ctrl_ = None
self.size_ = self.capacity_ = self.slot_size = 0
def num_children(self):
return min(self.size_, _MAX_CHILDREN)
def has_children(self):
return True
def update(self):
try:
self.size_ = self.valobj.GetChildMemberWithName('size_').GetValueAsUnsigned()
self.capacity_ = self.valobj.GetChildMemberWithName('capacity_').GetValueAsUnsigned()
self.slots_ = self.valobj.GetChildMemberWithName("slots_")
self.slot_type = self.slots_.GetType().GetPointeeType()
self.slot_size = self.slot_type.GetByteSize()
self.ctrl_ = self.valobj.GetChildMemberWithName("ctrl_")
except BaseException as ex:
print(f"{_get_function_name(self)} -> {ex}")
def get_child_index(self, name):
try:
if name in ('size_', 'capacity_'):
return -1
return int(name.lstrip('[').rstrip(']'))
except:
return -1
def get_child_at_index(self, index):
try:
if index < 0:
return None
if index >= self.size_ or index >= _MAX_CHILDREN:
return None
real_idx = -1
for idx in range(min(self.capacity_ + 3, _MAX_CTRL_INDEX)):
ctrl = self.ctrl_.GetChildAtIndex(idx).GetValueAsSigned()
if ctrl >= -1:
real_idx += 1
if real_idx == index:
return self.slots_.CreateChildAtOffset(f'[{index}]', idx * self.slot_size, self.slot_type)
except BaseException as ex:
print(f"{_get_function_name(self)} -> {ex}")
return None
class parallel_flat_or_node_map_or_set:
CLASS_PATTERN = "^phmap::parallel_(flat|node)_hash_(map|set)<.*>$"
HAS_SUMMARY = True
IS_SYNTHETIC_PROVIDER = True
REGEX_EXTRACT_ARRAY_SIZE = re.compile(r"std::array\s*<.*,\s*(\d+)\s*>")
@staticmethod
def _get_size_and_capacity(valobj):
try:
valobj = valobj.GetNonSyntheticValue()
sets = valobj.GetChildMemberWithName('sets_')
# sets is an std::array<T, SIZE>.
# It's not possible to get the size of the array with templates parameters
# "set.GetType().GetTemplateArgumentType(1)" returns an "unsigned long" type but not the value
# so we must extract it with a regex
m = parallel_flat_or_node_map_or_set.REGEX_EXTRACT_ARRAY_SIZE.match(sets.GetType().GetName())
n_buckets = int(m.group(1))
# this is dependent on the implementation of the standard library
buckets = sets.GetChildMemberWithName('_M_elems')
size = capacity = 0
for idx in range(n_buckets):
bucket = buckets.GetChildAtIndex(idx).GetChildMemberWithName('set_')
size += bucket.GetChildMemberWithName('size_').GetValueAsUnsigned()
capacity += bucket.GetChildMemberWithName('capacity_').GetValueAsUnsigned()
return size, capacity, n_buckets
except:
return '?', '?', 0
@staticmethod
def summary(valobj, _):
size, capacity, _ = parallel_flat_or_node_map_or_set._get_size_and_capacity(valobj)
return f"size = {size} (capacity = {capacity})"
def __init__(self, valobj, _):
self.valobj = valobj
self.buckets = self.slot_type = None
self.size_ = self.capacity_ = self.n_buckets_ = self.slot_type = self.ctrl_size = 0
def num_children(self):
return min(self.size_, _MAX_CHILDREN)
def has_children(self):
return True
def update(self):
try:
self.size_, self.capacity_, self.n_buckets_ = self._get_size_and_capacity(self.valobj)
self.buckets = self.valobj.GetChildMemberWithName('sets_').GetChildMemberWithName('_M_elems')
bucket0 = self.buckets.GetChildAtIndex(0).GetChildMemberWithName('set_')
self.slot_type = bucket0.GetChildMemberWithName('slots_').GetType().GetPointeeType()
self.slot_size = self.slot_type.GetByteSize()
except BaseException as ex:
print(f"{_get_function_name(self)} -> {ex}")
def get_child_index(self, name):
try:
if name in ('sets_'):
return -1
return int(name.lstrip('[').rstrip(']'))
except:
return -1
def get_child_at_index(self, index):
try:
if index < 0:
return None
if index >= self.size_ or index >= _MAX_CHILDREN:
return None
real_idx = -1
total_idx = 0
for idx in range(self.n_buckets_):
bucket = self.buckets.GetChildAtIndex(idx).GetChildMemberWithName('set_')
size = bucket.GetChildMemberWithName("size_").GetValueAsUnsigned()
if size:
slots_ = bucket.GetChildMemberWithName("slots_")
ctrl_ = bucket.GetChildMemberWithName("ctrl_")
for jdx in range(size):
ctrl = ctrl_.GetChildAtIndex(jdx).GetValueAsSigned()
if ctrl >= -1:
real_idx += 1
if real_idx == index:
return slots_.CreateChildAtOffset(f'[{index}]', jdx * self.slot_size, self.slot_type)
total_idx += size
if total_idx > _MAX_CHILDREN:
return None
except BaseException as ex:
print(f"{_get_function_name(self)} -> {ex}")
return None
def __lldb_init_module(debugger, internal_dict):
for sp in (
flat_map_slot_type,
node_map_slot_type,
node_set_slot_type,
flat_hash_map_or_set,
parallel_flat_or_node_map_or_set,
):
if sp.HAS_SUMMARY:
debugger.HandleCommand(
f'type summary add --regex "{sp.CLASS_PATTERN}" --python-function {_MODULE_NAME}.{sp.__name__}.summary '
f'--category phmap --expand')
if sp.IS_SYNTHETIC_PROVIDER:
debugger.HandleCommand(
f'type synthetic add --regex "{sp.CLASS_PATTERN}" --python-class {_MODULE_NAME}.{sp.__name__} '
f'--category phmap')
debugger.HandleCommand('type category enable phmap')