1""" 2LLDB AppKit formatters 3 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-exception 7""" 8# example summary provider for NSBundle 9# the real summary is now C++ code built into LLDB 10import lldb 11import ctypes 12import lldb.runtime.objc.objc_runtime 13import lldb.formatters.metrics 14import NSURL 15import lldb.formatters.Logger 16 17statistics = lldb.formatters.metrics.Metrics() 18statistics.add_metric("invalid_isa") 19statistics.add_metric("invalid_pointer") 20statistics.add_metric("unknown_class") 21statistics.add_metric("code_notrun") 22 23# despite the similary to synthetic children providers, these classes are not 24# trying to provide anything but a summary for an NSURL, so they need not 25# obey the interface specification for synthetic children providers 26 27 28class NSBundleKnown_SummaryProvider: 29 def adjust_for_architecture(self): 30 pass 31 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.NSString): 37 self.sys_params.types_cache.NSString = ( 38 self.valobj.GetTarget().FindFirstType("NSString").GetPointerType() 39 ) 40 self.update() 41 42 def update(self): 43 logger = lldb.formatters.Logger.Logger() 44 self.adjust_for_architecture() 45 46 # we need to skip the ISA, plus four other values 47 # that are luckily each a pointer in size 48 # which makes our computation trivial :-) 49 def offset(self): 50 logger = lldb.formatters.Logger.Logger() 51 return 5 * self.sys_params.pointer_size 52 53 def url_text(self): 54 logger = lldb.formatters.Logger.Logger() 55 global statistics 56 text = self.valobj.CreateChildAtOffset( 57 "text", self.offset(), self.sys_params.types_cache.NSString 58 ) 59 my_string = text.GetSummary() 60 if (my_string is None) or (my_string == ""): 61 statistics.metric_hit( 62 "unknown_class", 63 str(self.valobj.GetName()) + " triggered unknown pointer location", 64 ) 65 return NSBundleUnknown_SummaryProvider( 66 self.valobj, self.sys_params 67 ).url_text() 68 else: 69 statistics.metric_hit("code_notrun", self.valobj) 70 return my_string 71 72 73class NSBundleUnknown_SummaryProvider: 74 def adjust_for_architecture(self): 75 pass 76 77 def __init__(self, valobj, params): 78 logger = lldb.formatters.Logger.Logger() 79 self.valobj = valobj 80 self.sys_params = params 81 self.update() 82 83 def update(self): 84 logger = lldb.formatters.Logger.Logger() 85 self.adjust_for_architecture() 86 87 def url_text(self): 88 logger = lldb.formatters.Logger.Logger() 89 stream = lldb.SBStream() 90 self.valobj.GetExpressionPath(stream) 91 expr = "(NSString*)[" + stream.GetData() + " bundlePath]" 92 url_text_vo = self.valobj.CreateValueFromExpression("path", expr) 93 if url_text_vo.IsValid(): 94 return url_text_vo.GetSummary() 95 return "<variable is not NSBundle>" 96 97 98def GetSummary_Impl(valobj): 99 logger = lldb.formatters.Logger.Logger() 100 global statistics 101 ( 102 class_data, 103 wrapper, 104 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 105 valobj, statistics 106 ) 107 if wrapper: 108 return wrapper 109 110 name_string = class_data.class_name() 111 logger >> "class name is: " + str(name_string) 112 113 if name_string == "NSBundle": 114 wrapper = NSBundleKnown_SummaryProvider(valobj, class_data.sys_params) 115 # [NSBundle mainBundle] does return an object that is 116 # not correctly filled out for our purposes, so we still 117 # end up having to run code in that case 118 # statistics.metric_hit('code_notrun',valobj) 119 else: 120 wrapper = NSBundleUnknown_SummaryProvider(valobj, class_data.sys_params) 121 statistics.metric_hit( 122 "unknown_class", valobj.GetName() + " seen as " + name_string 123 ) 124 return wrapper 125 126 127def NSBundle_SummaryProvider(valobj, dict): 128 logger = lldb.formatters.Logger.Logger() 129 provider = GetSummary_Impl(valobj) 130 if provider is not None: 131 if isinstance( 132 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description 133 ): 134 return provider.message() 135 try: 136 summary = provider.url_text() 137 except: 138 summary = None 139 logger >> "got summary " + str(summary) 140 if summary is None or summary == "": 141 summary = "<variable is not NSBundle>" 142 return summary 143 return "Summary Unavailable" 144 145 146def __lldb_init_module(debugger, dict): 147 debugger.HandleCommand( 148 "type summary add -F NSBundle.NSBundle_SummaryProvider NSBundle" 149 ) 150