File indexing completed on 2024-05-12 04:40:07
0001 # 0002 # LLDB data formatter helpers 0003 # 0004 # SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz> 0005 # 0006 # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 # 0008 0009 # BEGIN: Utilities for wrapping differences of Python 2.x and Python 3 0010 # Inspired by https://pythonhosted.org/six/ 0011 from __future__ import print_function 0012 import sys 0013 import lldb 0014 # Useful for very coarse version differentiation. 0015 PY2 = sys.version_info[0] == 2 0016 PY3 = sys.version_info[0] == 3 0017 0018 # create Python 2.x & 3.x compatible iterator base 0019 if PY3: 0020 Iterator = object 0021 else: 0022 class Iterator(object): 0023 0024 def next(self): 0025 return type(self).__next__(self) 0026 if PY3: 0027 unichr = chr 0028 unicode = str 0029 else: 0030 unichr = unichr 0031 # END 0032 0033 0034 def canonicalized_type_name(name): 0035 """Canonicalize the type name for FindFirstType usage. 0036 + 1 space between template arguments (after comma) 0037 + no space before pointer * 0038 otherwise FindFirstType returns None 0039 """ 0040 return name.replace(' ', '').replace(',', ', ') 0041 0042 0043 def quote(string, quote='"'): 0044 """Quote a string so it's suitable to be used in quote""" 0045 if isinstance(string, unicode): 0046 ls = [] 0047 for uch in string: 0048 code = ord(uch) 0049 if code > 255: 0050 ls += '\\u{:04x}'.format(code) 0051 elif code >= 127: 0052 ls += '\\x{:02x}'.format(code) 0053 elif uch == quote or uch == '\\': 0054 ls += '\\' + chr(code) 0055 elif code == 0: 0056 ls += '\\x00' 0057 else: 0058 ls += chr(code) 0059 return quote + ''.join(ls) + quote 0060 else: 0061 return '{q}{s}{q}'.format(s=string.replace('\\', '\\\\').replace(quote, '\\' + quote), 0062 q=quote) 0063 0064 0065 def unquote(data, quote='"'): 0066 """Unquote a string""" 0067 if data.startswith(quote) and data.endswith(quote): 0068 data = data[1:-1] 0069 ls = [] 0070 esc = False 0071 for ch in data: 0072 if esc: 0073 ls.append(ch) 0074 esc = False 0075 else: 0076 if ch == '\\': 0077 esc = True 0078 else: 0079 ls.append(ch) 0080 if esc: 0081 print('WARNING: unpaired escape') 0082 data = ''.join(ls) 0083 return data 0084 0085 0086 def invoke(val, method, args=''): 0087 """Try to invoke a method on val, args are passed in as an expression string""" 0088 # first try to get a valid frame 0089 frame = None 0090 for f in [val.frame, lldb.frame, val.process.selected_thread.GetFrameAtIndex(0)]: 0091 if f.IsValid(): 0092 frame = f 0093 break 0094 if frame is None: 0095 return lldb.SBValue() 0096 0097 # second try to get a pointer to val 0098 if val.GetType().IsPointerType(): 0099 ptype = val.GetType() 0100 addr = val.GetValueAsUnsigned(0) 0101 else: 0102 ptype = val.GetType().GetPointerType() 0103 addr = val.AddressOf().GetValueAsUnsigned(0) 0104 0105 # third, build expression 0106 expr = 'reinterpret_cast<const {}>({})->{}({})'.format(ptype.GetName(), addr, method, args) 0107 res = frame.EvaluateExpression(expr) 0108 # if not res.IsValid(): 0109 # print 'Expr {} on value {} failed'.format(expr, val.GetName()) 0110 return res 0111 0112 0113 def rename(name, val): 0114 """Rename a SBValue""" 0115 return val.CreateValueFromData(name, val.GetData(), val.GetType()) 0116 0117 0118 def toSBPointer(valobj, addr, pointee_type): 0119 """Convert a addr integer to SBValue""" 0120 addr = addr & 0xFFFFFFFFFFFFFFFF # force unsigned 0121 return valobj.CreateValueFromAddress(None, addr, pointee_type).AddressOf() 0122 0123 0124 def validAddr(valobj, addr): 0125 """Test if a address is valid""" 0126 return toSBPointer(valobj, addr, 0127 valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()).IsValid() 0128 0129 0130 def validPointer(pointer): 0131 """Test if a SBValue pointer is valid""" 0132 if not pointer.IsValid(): 0133 return False 0134 if pointer.GetValueAsUnsigned(0) == 0: 0135 return False 0136 return toSBPointer(pointer, pointer.GetValueAsUnsigned(0), pointer.GetType().GetPointeeType()).IsValid() 0137 0138 0139 class AutoCacheValue(object): 0140 """An object that can create itself when needed and cache the result""" 0141 def __init__(self, creator): 0142 super(AutoCacheValue, self).__init__() 0143 self.creator = creator 0144 self.cache = None 0145 self.cached = False 0146 0147 def get(self): 0148 if not self.cached: 0149 self.cache = self.creator() 0150 self.cached = True 0151 return self.cache 0152 0153 0154 class HiddenMemberProvider(object): 0155 """A lldb synthetic provider that can provide hidden children. 0156 Original children is exposed in this way""" 0157 0158 @staticmethod 0159 def _capping_size(): 0160 return 255 0161 0162 def __init__(self, valobj, internal_dict): 0163 self.valobj = valobj 0164 # number of normally visible children 0165 self._num_children = 0 0166 # cache for visible children 0167 self._members = [] 0168 # cache for hidden children 0169 self._hiddens = [] 0170 # child name to index 0171 self._name2idx = {} 0172 # whether to add original children 0173 self._add_original = True 0174 # some useful info 0175 process = self.valobj.GetProcess() 0176 self._endianness = process.GetByteOrder() 0177 self._pointer_size = process.GetAddressByteSize() 0178 self._char_type = valobj.GetType().GetBasicType(lldb.eBasicTypeChar) 0179 0180 def has_children(self): 0181 return self._num_children != 0 0182 0183 def num_children(self): 0184 return self._num_children 0185 0186 def get_child_index(self, name): 0187 if name in self._name2idx: 0188 return self._name2idx[name] 0189 return None 0190 0191 def get_child_at_index(self, idx): 0192 if not self.valobj.IsValid(): 0193 return None 0194 if idx < 0: 0195 return None 0196 elif idx < self._num_children: 0197 child = self._members[idx] 0198 # These are hidden children, which won't be queried by lldb, but we know 0199 # they are there, so we can use them in summary provider, to avoid another 0200 # fetch from the inferior, and don't shadow original children 0201 elif idx < self._num_children + len(self._hiddens): 0202 child = self._hiddens[idx - self._num_children] 0203 else: 0204 return None 0205 0206 if isinstance(child, AutoCacheValue): 0207 child = child.get() 0208 0209 return child 0210 0211 @staticmethod 0212 def _getName(var): 0213 if isinstance(var, lldb.SBValue): 0214 return var.GetName() 0215 else: 0216 return var[0] 0217 0218 def update(self): 0219 self._num_children = -1 0220 self._members = [] 0221 self._hiddens = [] 0222 self._name2idx = {} 0223 0224 if not self.valobj.IsValid(): 0225 return 0226 0227 # call _update on subclass 0228 self._update() 0229 0230 # add valobj's original children as hidden children, 0231 # must be called after self._update, so subclass has chance 0232 # to disable it. 0233 if self._add_original: 0234 for v in self.valobj: 0235 self._addChild(v, hidden=True) 0236 0237 # update num_children 0238 if self._num_children < 0: 0239 self._num_children = len(self._members) 0240 0241 # build name to index lookup, hidden value first, so normal value takes precedence 0242 self._name2idx = { 0243 self._getName(self._hiddens[idx]): idx + self._num_children 0244 for idx in range(0, len(self._hiddens)) 0245 } 0246 self._name2idx.update({ 0247 self._getName(self._members[idx]): idx 0248 for idx in range(0, self._num_children) 0249 }) 0250 0251 def _update(self): 0252 """override in subclass""" 0253 pass 0254 0255 def _addChild(self, var, hidden=False): 0256 if not isinstance(var, lldb.SBValue): 0257 # special handling for (name, expr) tuple of string constants 0258 if len(var) != 2: 0259 print('error, const char[] value should be a tuple with two elements, it is', var) 0260 name, content = var 0261 0262 if isinstance(content, unicode): 0263 content = content.encode() 0264 0265 try: 0266 char_arr_type = self._char_type.GetArrayType(len(content)); 0267 strdata = lldb.SBData.CreateDataFromCString(self._endianness, self._pointer_size, content) 0268 var = self.valobj.CreateValueFromData(name, strdata, char_arr_type) 0269 except: 0270 pass 0271 0272 cache = self._hiddens if hidden else self._members 0273 cache.append(var)