Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 1 | """ |
| 2 | LLDB AppKit formatters |
| 3 | |
| 4 | part of The LLVM Compiler Infrastructure |
| 5 | This file is distributed under the University of Illinois Open Source |
| 6 | License. See LICENSE.TXT for details. |
| 7 | """ |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 8 | # summary provider for NSSet |
| 9 | import lldb |
| 10 | import ctypes |
Enrico Granata | 28399ad | 2012-04-25 01:39:27 +0000 | [diff] [blame] | 11 | import lldb.runtime.objc.objc_runtime |
| 12 | import lldb.formatters.metrics |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 13 | import CFBag |
Enrico Granata | 28399ad | 2012-04-25 01:39:27 +0000 | [diff] [blame] | 14 | import lldb.formatters.Logger |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 15 | |
Enrico Granata | 28399ad | 2012-04-25 01:39:27 +0000 | [diff] [blame] | 16 | statistics = lldb.formatters.metrics.Metrics() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 17 | statistics.add_metric('invalid_isa') |
| 18 | statistics.add_metric('invalid_pointer') |
| 19 | statistics.add_metric('unknown_class') |
| 20 | statistics.add_metric('code_notrun') |
| 21 | |
| 22 | # despite the similary to synthetic children providers, these classes are not |
| 23 | # trying to provide anything but the port number of an NSMachPort, so they need not |
| 24 | # obey the interface specification for synthetic children providers |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 25 | |
| 26 | |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 27 | class NSCFSet_SummaryProvider: |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 28 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 29 | def adjust_for_architecture(self): |
| 30 | pass |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 31 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 32 | def __init__(self, valobj, params): |
| 33 | logger = lldb.formatters.Logger.Logger() |
| 34 | self.valobj = valobj |
| 35 | self.sys_params = params |
| 36 | if not(self.sys_params.types_cache.NSUInteger): |
| 37 | if self.sys_params.is_64_bit: |
| 38 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 39 | ).GetBasicType(lldb.eBasicTypeUnsignedLong) |
| 40 | else: |
| 41 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 42 | ).GetBasicType(lldb.eBasicTypeUnsignedInt) |
| 43 | self.update() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 44 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 45 | def update(self): |
| 46 | logger = lldb.formatters.Logger.Logger() |
| 47 | self.adjust_for_architecture() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 48 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 49 | # one pointer is the ISA |
| 50 | # then we have one other internal pointer, plus |
| 51 | # 4 bytes worth of flags. hence, these values |
| 52 | def offset(self): |
| 53 | logger = lldb.formatters.Logger.Logger() |
| 54 | if self.sys_params.is_64_bit: |
| 55 | return 20 |
| 56 | else: |
| 57 | return 12 |
| 58 | |
| 59 | def count(self): |
| 60 | logger = lldb.formatters.Logger.Logger() |
| 61 | vcount = self.valobj.CreateChildAtOffset( |
| 62 | "count", self.offset(), self.sys_params.types_cache.NSUInteger) |
| 63 | return vcount.GetValueAsUnsigned(0) |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 64 | |
| 65 | |
| 66 | class NSSetUnknown_SummaryProvider: |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 67 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 68 | def adjust_for_architecture(self): |
| 69 | pass |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 70 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 71 | def __init__(self, valobj, params): |
| 72 | logger = lldb.formatters.Logger.Logger() |
| 73 | self.valobj = valobj |
| 74 | self.sys_params = params |
| 75 | self.update() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 76 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 77 | def update(self): |
| 78 | logger = lldb.formatters.Logger.Logger() |
| 79 | self.adjust_for_architecture() |
| 80 | |
| 81 | def count(self): |
| 82 | logger = lldb.formatters.Logger.Logger() |
| 83 | stream = lldb.SBStream() |
| 84 | self.valobj.GetExpressionPath(stream) |
| 85 | expr = "(int)[" + stream.GetData() + " count]" |
| 86 | num_children_vo = self.valobj.CreateValueFromExpression("count", expr) |
| 87 | if num_children_vo.IsValid(): |
| 88 | return num_children_vo.GetValueAsUnsigned(0) |
| 89 | return '<variable is not NSSet>' |
| 90 | |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 91 | |
| 92 | class NSSetI_SummaryProvider: |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 93 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 94 | def adjust_for_architecture(self): |
| 95 | pass |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 96 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 97 | def __init__(self, valobj, params): |
| 98 | logger = lldb.formatters.Logger.Logger() |
| 99 | self.valobj = valobj |
| 100 | self.sys_params = params |
| 101 | if not(self.sys_params.types_cache.NSUInteger): |
| 102 | if self.sys_params.is_64_bit: |
| 103 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 104 | ).GetBasicType(lldb.eBasicTypeUnsignedLong) |
| 105 | else: |
| 106 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 107 | ).GetBasicType(lldb.eBasicTypeUnsignedInt) |
| 108 | self.update() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 109 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 110 | def update(self): |
| 111 | logger = lldb.formatters.Logger.Logger() |
| 112 | self.adjust_for_architecture() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 113 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 114 | # we just need to skip the ISA and the count immediately follows |
| 115 | def offset(self): |
| 116 | logger = lldb.formatters.Logger.Logger() |
| 117 | return self.sys_params.pointer_size |
| 118 | |
| 119 | def count(self): |
| 120 | logger = lldb.formatters.Logger.Logger() |
| 121 | num_children_vo = self.valobj.CreateChildAtOffset( |
| 122 | "count", self.offset(), self.sys_params.types_cache.NSUInteger) |
| 123 | value = num_children_vo.GetValueAsUnsigned(0) |
| 124 | if value is not None: |
| 125 | # the MSB on immutable sets seems to be taken by some other data |
| 126 | # not sure if it is a bug or some weird sort of feature, but masking it out |
| 127 | # gets the count right (unless, of course, someone's dictionaries grow |
| 128 | # too large - but I have not tested this) |
| 129 | if self.sys_params.is_64_bit: |
| 130 | value = value & ~0xFF00000000000000 |
| 131 | else: |
| 132 | value = value & ~0xFF000000 |
| 133 | return value |
| 134 | |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 135 | |
| 136 | class NSSetM_SummaryProvider: |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 137 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 138 | def adjust_for_architecture(self): |
| 139 | pass |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 140 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 141 | def __init__(self, valobj, params): |
| 142 | logger = lldb.formatters.Logger.Logger() |
| 143 | self.valobj = valobj |
| 144 | self.sys_params = params |
| 145 | if not(self.sys_params.types_cache.NSUInteger): |
| 146 | if self.sys_params.is_64_bit: |
| 147 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 148 | ).GetBasicType(lldb.eBasicTypeUnsignedLong) |
| 149 | else: |
| 150 | self.sys_params.types_cache.NSUInteger = self.valobj.GetType( |
| 151 | ).GetBasicType(lldb.eBasicTypeUnsignedInt) |
| 152 | self.update() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 153 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 154 | def update(self): |
| 155 | logger = lldb.formatters.Logger.Logger() |
| 156 | self.adjust_for_architecture() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 157 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 158 | # we just need to skip the ISA and the count immediately follows |
| 159 | def offset(self): |
| 160 | logger = lldb.formatters.Logger.Logger() |
| 161 | return self.sys_params.pointer_size |
| 162 | |
| 163 | def count(self): |
| 164 | logger = lldb.formatters.Logger.Logger() |
| 165 | num_children_vo = self.valobj.CreateChildAtOffset( |
| 166 | "count", self.offset(), self.sys_params.types_cache.NSUInteger) |
| 167 | return num_children_vo.GetValueAsUnsigned(0) |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 168 | |
| 169 | |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 170 | class NSCountedSet_SummaryProvider: |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 171 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 172 | def adjust_for_architecture(self): |
| 173 | pass |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 174 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 175 | def __init__(self, valobj, params): |
| 176 | logger = lldb.formatters.Logger.Logger() |
| 177 | self.valobj = valobj |
| 178 | self.sys_params = params |
| 179 | if not (self.sys_params.types_cache.voidptr): |
| 180 | self.sys_params.types_cache.voidptr = self.valobj.GetType( |
| 181 | ).GetBasicType(lldb.eBasicTypeVoid).GetPointerType() |
| 182 | self.update() |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 183 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 184 | def update(self): |
| 185 | logger = lldb.formatters.Logger.Logger() |
| 186 | self.adjust_for_architecture() |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 187 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 188 | # an NSCountedSet is implemented using a CFBag whose pointer just follows |
| 189 | # the ISA |
| 190 | def offset(self): |
| 191 | logger = lldb.formatters.Logger.Logger() |
| 192 | return self.sys_params.pointer_size |
| 193 | |
| 194 | def count(self): |
| 195 | logger = lldb.formatters.Logger.Logger() |
| 196 | cfbag_vo = self.valobj.CreateChildAtOffset( |
| 197 | "bag_impl", self.offset(), self.sys_params.types_cache.voidptr) |
| 198 | return CFBag.CFBagRef_SummaryProvider( |
| 199 | cfbag_vo, self.sys_params).length() |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 200 | |
| 201 | |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 202 | def GetSummary_Impl(valobj): |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 203 | logger = lldb.formatters.Logger.Logger() |
| 204 | global statistics |
| 205 | class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( |
| 206 | valobj, statistics) |
| 207 | if wrapper: |
| 208 | return wrapper |
Enrico Granata | 247bd41 | 2012-04-02 16:39:29 +0000 | [diff] [blame] | 209 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 210 | name_string = class_data.class_name() |
| 211 | logger >> "class name is: " + str(name_string) |
| 212 | |
| 213 | if name_string == '__NSCFSet': |
| 214 | wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params) |
| 215 | statistics.metric_hit('code_notrun', valobj) |
| 216 | elif name_string == '__NSSetI': |
| 217 | wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params) |
| 218 | statistics.metric_hit('code_notrun', valobj) |
| 219 | elif name_string == '__NSSetM': |
| 220 | wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params) |
| 221 | statistics.metric_hit('code_notrun', valobj) |
| 222 | elif name_string == 'NSCountedSet': |
| 223 | wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params) |
| 224 | statistics.metric_hit('code_notrun', valobj) |
| 225 | else: |
| 226 | wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params) |
| 227 | statistics.metric_hit( |
| 228 | 'unknown_class', |
| 229 | valobj.GetName() + |
| 230 | " seen as " + |
| 231 | name_string) |
| 232 | return wrapper |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 233 | |
| 234 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 235 | def NSSet_SummaryProvider(valobj, dict): |
| 236 | logger = lldb.formatters.Logger.Logger() |
| 237 | provider = GetSummary_Impl(valobj) |
| 238 | if provider is not None: |
| 239 | try: |
| 240 | summary = provider.count() |
| 241 | except: |
| 242 | summary = None |
| 243 | if summary is None: |
| 244 | summary = '<variable is not NSSet>' |
| 245 | if isinstance(summary, basestring): |
| 246 | return summary |
| 247 | else: |
| 248 | summary = str(summary) + \ |
| 249 | (' objects' if summary != 1 else ' object') |
| 250 | return summary |
| 251 | return 'Summary Unavailable' |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 252 | |
| 253 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 254 | def NSSet_SummaryProvider2(valobj, dict): |
| 255 | logger = lldb.formatters.Logger.Logger() |
| 256 | provider = GetSummary_Impl(valobj) |
| 257 | if provider is not None: |
| 258 | if isinstance( |
| 259 | provider, |
| 260 | lldb.runtime.objc.objc_runtime.SpecialSituation_Description): |
| 261 | return provider.message() |
| 262 | try: |
| 263 | summary = provider.count() |
| 264 | except: |
| 265 | summary = None |
| 266 | logger >> "got summary " + str(summary) |
| 267 | # for some reason, one needs to clear some bits for the count returned |
| 268 | # to be correct when using directly CF*SetRef as compared to NS*Set |
| 269 | # this only happens on 64bit, and the bit mask was derived through |
| 270 | # experimentation (if counts start looking weird, then most probably |
| 271 | # the mask needs to be changed) |
| 272 | if summary is None: |
| 273 | summary = '<variable is not CFSet>' |
| 274 | if isinstance(summary, basestring): |
| 275 | return summary |
| 276 | else: |
| 277 | if provider.sys_params.is_64_bit: |
| 278 | summary = summary & ~0x1fff000000000000 |
| 279 | summary = '@"' + str(summary) + \ |
| 280 | (' values"' if summary != 1 else ' value"') |
| 281 | return summary |
| 282 | return 'Summary Unavailable' |
| 283 | |
| 284 | |
| 285 | def __lldb_init_module(debugger, dict): |
| 286 | debugger.HandleCommand( |
| 287 | "type summary add -F NSSet.NSSet_SummaryProvider NSSet") |
| 288 | debugger.HandleCommand( |
| 289 | "type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef") |