235 lines · python
1"""2LLDB AppKit formatters3 4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5See https://llvm.org/LICENSE.txt for license information.6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7"""8# example summary provider for NSArray9# the real summary is now C++ code built into LLDB10import lldb11import ctypes12import lldb.runtime.objc.objc_runtime13import lldb.formatters.metrics14import lldb.formatters.Logger15 16statistics = lldb.formatters.metrics.Metrics()17statistics.add_metric("invalid_isa")18statistics.add_metric("invalid_pointer")19statistics.add_metric("unknown_class")20statistics.add_metric("code_notrun")21 22# much less functional than the other two cases below23# just runs code to get to the count and then returns24# no children25 26 27class NSArrayKVC_SynthProvider:28 def adjust_for_architecture(self):29 pass30 31 def __init__(self, valobj, dict, params):32 logger = lldb.formatters.Logger.Logger()33 self.valobj = valobj34 self.update()35 36 def update(self):37 logger = lldb.formatters.Logger.Logger()38 self.adjust_for_architecture()39 40 def num_children(self):41 logger = lldb.formatters.Logger.Logger()42 stream = lldb.SBStream()43 self.valobj.GetExpressionPath(stream)44 num_children_vo = self.valobj.CreateValueFromExpression(45 "count", "(int)[" + stream.GetData() + " count]"46 )47 if num_children_vo.IsValid():48 return num_children_vo.GetValueAsUnsigned(0)49 return "<variable is not NSArray>"50 51 52# much less functional than the other two cases below53# just runs code to get to the count and then returns54# no children55 56 57class NSArrayCF_SynthProvider:58 def adjust_for_architecture(self):59 pass60 61 def __init__(self, valobj, dict, params):62 logger = lldb.formatters.Logger.Logger()63 self.valobj = valobj64 self.sys_params = params65 if not (self.sys_params.types_cache.ulong):66 self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(67 lldb.eBasicTypeUnsignedLong68 )69 self.update()70 71 def update(self):72 logger = lldb.formatters.Logger.Logger()73 self.adjust_for_architecture()74 75 def num_children(self):76 logger = lldb.formatters.Logger.Logger()77 num_children_vo = self.valobj.CreateChildAtOffset(78 "count", self.sys_params.cfruntime_size, self.sys_params.types_cache.ulong79 )80 return num_children_vo.GetValueAsUnsigned(0)81 82 83class NSArrayI_SynthProvider:84 def adjust_for_architecture(self):85 pass86 87 def __init__(self, valobj, dict, params):88 logger = lldb.formatters.Logger.Logger()89 self.valobj = valobj90 self.sys_params = params91 if not (self.sys_params.types_cache.long):92 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(93 lldb.eBasicTypeLong94 )95 self.update()96 97 def update(self):98 logger = lldb.formatters.Logger.Logger()99 self.adjust_for_architecture()100 101 # skip the isa pointer and get at the size102 def num_children(self):103 logger = lldb.formatters.Logger.Logger()104 count = self.valobj.CreateChildAtOffset(105 "count", self.sys_params.pointer_size, self.sys_params.types_cache.long106 )107 return count.GetValueAsUnsigned(0)108 109 110class NSArrayM_SynthProvider:111 def adjust_for_architecture(self):112 pass113 114 def __init__(self, valobj, dict, params):115 logger = lldb.formatters.Logger.Logger()116 self.valobj = valobj117 self.sys_params = params118 if not (self.sys_params.types_cache.long):119 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(120 lldb.eBasicTypeLong121 )122 self.update()123 124 def update(self):125 logger = lldb.formatters.Logger.Logger()126 self.adjust_for_architecture()127 128 # skip the isa pointer and get at the size129 def num_children(self):130 logger = lldb.formatters.Logger.Logger()131 count = self.valobj.CreateChildAtOffset(132 "count", self.sys_params.pointer_size, self.sys_params.types_cache.long133 )134 return count.GetValueAsUnsigned(0)135 136 137# this is the actual synth provider, but is just a wrapper that checks138# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an139# appropriate backend layer to do the computations140 141 142class NSArray_SynthProvider:143 def adjust_for_architecture(self):144 pass145 146 def __init__(self, valobj, dict):147 logger = lldb.formatters.Logger.Logger()148 self.valobj = valobj149 self.adjust_for_architecture()150 self.error = False151 self.wrapper = self.make_wrapper()152 self.invalid = self.wrapper is None153 154 def num_children(self):155 logger = lldb.formatters.Logger.Logger()156 if self.wrapper is None:157 return 0158 return self.wrapper.num_children()159 160 def update(self):161 logger = lldb.formatters.Logger.Logger()162 if self.wrapper is None:163 return164 self.wrapper.update()165 166 # this code acts as our defense against NULL and uninitialized167 # NSArray pointers, which makes it much longer than it would be otherwise168 def make_wrapper(self):169 logger = lldb.formatters.Logger.Logger()170 if self.valobj.GetValueAsUnsigned() == 0:171 self.error = True172 return lldb.runtime.objc.objc_runtime.InvalidPointer_Description(True)173 else:174 global statistics175 (176 class_data,177 wrapper,178 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(179 self.valobj, statistics180 )181 if wrapper:182 self.error = True183 return wrapper184 185 name_string = class_data.class_name()186 187 logger >> "Class name is " + str(name_string)188 189 if name_string == "__NSArrayI":190 wrapper = NSArrayI_SynthProvider(self.valobj, dict, class_data.sys_params)191 statistics.metric_hit("code_notrun", self.valobj.GetName())192 elif name_string == "__NSArrayM":193 wrapper = NSArrayM_SynthProvider(self.valobj, dict, class_data.sys_params)194 statistics.metric_hit("code_notrun", self.valobj.GetName())195 elif name_string == "__NSCFArray":196 wrapper = NSArrayCF_SynthProvider(self.valobj, dict, class_data.sys_params)197 statistics.metric_hit("code_notrun", self.valobj.GetName())198 else:199 wrapper = NSArrayKVC_SynthProvider(self.valobj, dict, class_data.sys_params)200 statistics.metric_hit(201 "unknown_class", str(self.valobj.GetName()) + " seen as " + name_string202 )203 return wrapper204 205 206def CFArray_SummaryProvider(valobj, dict):207 logger = lldb.formatters.Logger.Logger()208 provider = NSArray_SynthProvider(valobj, dict)209 if not provider.invalid:210 if provider.error:211 return provider.wrapper.message()212 try:213 summary = int(provider.num_children())214 except:215 summary = None216 logger >> "provider gave me " + str(summary)217 if summary is None:218 summary = "<variable is not NSArray>"219 elif isinstance(summary, str):220 pass221 else:222 # we format it like it were a CFString to make it look the same as223 # the summary from Xcode224 summary = (225 '@"' + str(summary) + (" objects" if summary != 1 else " object") + '"'226 )227 return summary228 return "Summary Unavailable"229 230 231def __lldb_init_module(debugger, dict):232 debugger.HandleCommand(233 "type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef"234 )235