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