273 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 NSDictionary9# 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# despite the similary to synthetic children providers, these classes are not23# trying to provide anything but the count for an NSDictionary, so they need not24# obey the interface specification for synthetic children providers25 26 27class NSCFDictionary_SummaryProvider:28 def adjust_for_architecture(self):29 pass30 31 def __init__(self, valobj, params):32 logger = lldb.formatters.Logger.Logger()33 self.valobj = valobj34 self.sys_params = params35 if not (self.sys_params.types_cache.NSUInteger):36 if self.sys_params.is_64_bit:37 self.sys_params.types_cache.NSUInteger = (38 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)39 )40 else:41 self.sys_params.types_cache.NSUInteger = (42 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)43 )44 self.update()45 46 def update(self):47 logger = lldb.formatters.Logger.Logger()48 self.adjust_for_architecture()49 50 # empirically determined on both 32 and 64bit desktop Mac OS X51 # probably boils down to 2 pointers and 4 bytes of data, but52 # the description of __CFDictionary is not readily available so most53 # of this is guesswork, plain and simple54 def offset(self):55 logger = lldb.formatters.Logger.Logger()56 if self.sys_params.is_64_bit:57 return 2058 else:59 return 1260 61 def num_children(self):62 logger = lldb.formatters.Logger.Logger()63 num_children_vo = self.valobj.CreateChildAtOffset(64 "count", self.offset(), self.sys_params.types_cache.NSUInteger65 )66 return num_children_vo.GetValueAsUnsigned(0)67 68 69class NSDictionaryI_SummaryProvider:70 def adjust_for_architecture(self):71 pass72 73 def __init__(self, valobj, params):74 logger = lldb.formatters.Logger.Logger()75 self.valobj = valobj76 self.sys_params = params77 if not (self.sys_params.types_cache.NSUInteger):78 if self.sys_params.is_64_bit:79 self.sys_params.types_cache.NSUInteger = (80 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)81 )82 else:83 self.sys_params.types_cache.NSUInteger = (84 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)85 )86 self.update()87 88 def update(self):89 logger = lldb.formatters.Logger.Logger()90 self.adjust_for_architecture()91 92 # we just need to skip the ISA and the count immediately follows93 def offset(self):94 logger = lldb.formatters.Logger.Logger()95 return self.sys_params.pointer_size96 97 def num_children(self):98 logger = lldb.formatters.Logger.Logger()99 num_children_vo = self.valobj.CreateChildAtOffset(100 "count", self.offset(), self.sys_params.types_cache.NSUInteger101 )102 value = num_children_vo.GetValueAsUnsigned(0)103 if value is not None:104 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity105 # not sure if it is a bug or some weird sort of feature, but masking that out106 # gets the count right107 if self.sys_params.is_64_bit:108 value = value & ~0xFC00000000000000109 else:110 value = value & ~0xFC000000111 return value112 113 114class NSDictionaryM_SummaryProvider:115 def adjust_for_architecture(self):116 pass117 118 def __init__(self, valobj, params):119 logger = lldb.formatters.Logger.Logger()120 self.valobj = valobj121 self.sys_params = params122 if not (self.sys_params.types_cache.NSUInteger):123 if self.sys_params.is_64_bit:124 self.sys_params.types_cache.NSUInteger = (125 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)126 )127 else:128 self.sys_params.types_cache.NSUInteger = (129 self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)130 )131 self.update()132 133 def update(self):134 logger = lldb.formatters.Logger.Logger()135 self.adjust_for_architecture()136 137 # we just need to skip the ISA and the count immediately follows138 def offset(self):139 return self.sys_params.pointer_size140 141 def num_children(self):142 logger = lldb.formatters.Logger.Logger()143 num_children_vo = self.valobj.CreateChildAtOffset(144 "count", self.offset(), self.sys_params.types_cache.NSUInteger145 )146 value = num_children_vo.GetValueAsUnsigned(0)147 if value is not None:148 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity149 # not sure if it is a bug or some weird sort of feature, but masking that out150 # gets the count right151 if self.sys_params.is_64_bit:152 value = value & ~0xFC00000000000000153 else:154 value = value & ~0xFC000000155 return value156 157 158class NSDictionaryUnknown_SummaryProvider:159 def adjust_for_architecture(self):160 pass161 162 def __init__(self, valobj, params):163 logger = lldb.formatters.Logger.Logger()164 self.valobj = valobj165 self.sys_params = params166 self.update()167 168 def update(self):169 logger = lldb.formatters.Logger.Logger()170 self.adjust_for_architecture()171 172 def num_children(self):173 logger = lldb.formatters.Logger.Logger()174 stream = lldb.SBStream()175 self.valobj.GetExpressionPath(stream)176 num_children_vo = self.valobj.CreateValueFromExpression(177 "count", "(int)[" + stream.GetData() + " count]"178 )179 if num_children_vo.IsValid():180 return num_children_vo.GetValueAsUnsigned(0)181 return "<variable is not NSDictionary>"182 183 184def GetSummary_Impl(valobj):185 logger = lldb.formatters.Logger.Logger()186 global statistics187 (188 class_data,189 wrapper,190 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(191 valobj, statistics192 )193 if wrapper:194 return wrapper195 196 name_string = class_data.class_name()197 198 logger >> "class name is: " + str(name_string)199 200 if name_string == "__NSCFDictionary":201 wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params)202 statistics.metric_hit("code_notrun", valobj)203 elif name_string == "__NSDictionaryI":204 wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params)205 statistics.metric_hit("code_notrun", valobj)206 elif name_string == "__NSDictionaryM":207 wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params)208 statistics.metric_hit("code_notrun", valobj)209 else:210 wrapper = NSDictionaryUnknown_SummaryProvider(valobj, class_data.sys_params)211 statistics.metric_hit(212 "unknown_class", valobj.GetName() + " seen as " + name_string213 )214 return wrapper215 216 217def CFDictionary_SummaryProvider(valobj, dict):218 logger = lldb.formatters.Logger.Logger()219 provider = GetSummary_Impl(valobj)220 if provider is not None:221 if isinstance(222 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description223 ):224 return provider.message()225 try:226 summary = provider.num_children()227 except:228 summary = None229 logger >> "got summary " + str(summary)230 if summary is None:231 return "<variable is not NSDictionary>"232 if isinstance(summary, str):233 return summary234 return str(summary) + (235 " key/value pairs" if summary != 1 else " key/value pair"236 )237 return "Summary Unavailable"238 239 240def CFDictionary_SummaryProvider2(valobj, dict):241 logger = lldb.formatters.Logger.Logger()242 provider = GetSummary_Impl(valobj)243 if provider is not None:244 if isinstance(245 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description246 ):247 return provider.message()248 try:249 summary = provider.num_children()250 except:251 summary = None252 logger >> "got summary " + str(summary)253 if summary is None:254 summary = "<variable is not CFDictionary>"255 if isinstance(summary, str):256 return summary257 else:258 # needed on OSX Mountain Lion259 if provider.sys_params.is_64_bit:260 summary = summary & ~0x0F1F000000000000261 summary = '@"' + str(summary) + (' entries"' if summary != 1 else ' entry"')262 return summary263 return "Summary Unavailable"264 265 266def __lldb_init_module(debugger, dict):267 debugger.HandleCommand(268 "type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary"269 )270 debugger.HandleCommand(271 "type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef"272 )273