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# summary provider for NSURL 9import lldb 10import ctypes 11import lldb.runtime.objc.objc_runtime 12import lldb.formatters.metrics 13import CFString 14import lldb.formatters.Logger 15 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 not 23# trying to provide anything but a summary for an NSURL, so they need not 24# obey the interface specification for synthetic children providers 25 26 27class NSURLKnown_SummaryProvider: 28 def adjust_for_architecture(self): 29 pass 30 31 def __init__(self, valobj, params): 32 logger = lldb.formatters.Logger.Logger() 33 self.valobj = valobj 34 self.sys_params = params 35 if not (self.sys_params.types_cache.NSString): 36 self.sys_params.types_cache.NSString = ( 37 self.valobj.GetTarget().FindFirstType("NSString").GetPointerType() 38 ) 39 if not (self.sys_params.types_cache.NSURL): 40 self.sys_params.types_cache.NSURL = ( 41 self.valobj.GetTarget().FindFirstType("NSURL").GetPointerType() 42 ) 43 self.update() 44 45 def update(self): 46 logger = lldb.formatters.Logger.Logger() 47 self.adjust_for_architecture() 48 49 # one pointer is the ISA 50 # then there is one more pointer and 8 bytes of plain data 51 # (which are also present on a 32-bit system) 52 # then there is a pointer to an NSString which is the url text 53 # optionally, the next pointer is another NSURL which is the "base" 54 # of this one when doing NSURLs composition (incidentally, NSURLs can 55 # recurse the base+text mechanism to any desired depth) 56 def offset_text(self): 57 logger = lldb.formatters.Logger.Logger() 58 return 24 if self.sys_params.is_64_bit else 16 59 60 def offset_base(self): 61 logger = lldb.formatters.Logger.Logger() 62 return self.offset_text() + self.sys_params.pointer_size 63 64 def url_text(self): 65 logger = lldb.formatters.Logger.Logger() 66 text = self.valobj.CreateChildAtOffset( 67 "text", self.offset_text(), self.sys_params.types_cache.NSString 68 ) 69 base = self.valobj.CreateChildAtOffset( 70 "base", self.offset_base(), self.sys_params.types_cache.NSURL 71 ) 72 my_string = CFString.CFString_SummaryProvider(text, None) 73 if len(my_string) > 0 and base.GetValueAsUnsigned(0) != 0: 74 # remove final " from myself 75 my_string = my_string[0 : len(my_string) - 1] 76 my_string = my_string + " -- " 77 my_base_string = NSURL_SummaryProvider(base, None) 78 if len(my_base_string) > 2: 79 # remove @" marker from base URL string 80 my_base_string = my_base_string[2:] 81 my_string = my_string + my_base_string 82 return my_string 83 84 85class NSURLUnknown_SummaryProvider: 86 def adjust_for_architecture(self): 87 pass 88 89 def __init__(self, valobj, params): 90 logger = lldb.formatters.Logger.Logger() 91 self.valobj = valobj 92 self.sys_params = params 93 self.update() 94 95 def update(self): 96 logger = lldb.formatters.Logger.Logger() 97 self.adjust_for_architecture() 98 99 def url_text(self): 100 logger = lldb.formatters.Logger.Logger() 101 stream = lldb.SBStream() 102 self.valobj.GetExpressionPath(stream) 103 url_text_vo = self.valobj.CreateValueFromExpression( 104 "url", "(NSString*)[" + stream.GetData() + " description]" 105 ) 106 if url_text_vo.IsValid(): 107 return CFString.CFString_SummaryProvider(url_text_vo, None) 108 return "<variable is not NSURL>" 109 110 111def GetSummary_Impl(valobj): 112 logger = lldb.formatters.Logger.Logger() 113 global statistics 114 ( 115 class_data, 116 wrapper, 117 ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection( 118 valobj, statistics 119 ) 120 if wrapper: 121 return wrapper 122 123 name_string = class_data.class_name() 124 logger >> "class name is: " + str(name_string) 125 126 if name_string == "NSURL": 127 wrapper = NSURLKnown_SummaryProvider(valobj, class_data.sys_params) 128 statistics.metric_hit("code_notrun", valobj) 129 else: 130 wrapper = NSURLUnknown_SummaryProvider(valobj, class_data.sys_params) 131 statistics.metric_hit( 132 "unknown_class", valobj.GetName() + " seen as " + name_string 133 ) 134 return wrapper 135 136 137def NSURL_SummaryProvider(valobj, dict): 138 logger = lldb.formatters.Logger.Logger() 139 provider = GetSummary_Impl(valobj) 140 if provider is not None: 141 if isinstance( 142 provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description 143 ): 144 return provider.message() 145 try: 146 summary = provider.url_text() 147 except: 148 summary = None 149 logger >> "got summary " + str(summary) 150 if summary is None or summary == "": 151 summary = "<variable is not NSURL>" 152 return summary 153 return "Summary Unavailable" 154 155 156def __lldb_init_module(debugger, dict): 157 debugger.HandleCommand( 158 "type summary add -F NSURL.NSURL_SummaryProvider NSURL CFURLRef" 159 ) 160