]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/utils.py
104a528cc8b0e56efa08dce656872c17d90f70b4
[apple/xnu.git] / tools / lldbmacros / utils.py
1 #General Utility functions for debugging or introspection
2
3 """ Please make sure you read the README file COMPLETELY BEFORE reading anything below.
4 It is very critical that you read coding guidelines in Section E in README file.
5 """
6 import sys, re, time, getopt, shlex, os, time
7 import lldb
8 import struct
9 from core.cvalue import *
10 from core.configuration import *
11 from core.lazytarget import *
12
13 #DONOTTOUCHME: exclusive use for lldb_run_command only.
14 lldb_run_command_state = {'active':False}
15
16 def lldb_run_command(cmdstring):
17 """ Run a lldb command and get the string output.
18 params: cmdstring - str : lldb command string which could be executed at (lldb) prompt. (eg. "register read")
19 returns: str - output of command. it may be "" in case if command did not return any output.
20 """
21 global lldb_run_command_state
22 retval =""
23 res = lldb.SBCommandReturnObject()
24 # set special attribute to notify xnu framework to not print on stdout
25 lldb_run_command_state['active'] = True
26 lldb.debugger.GetCommandInterpreter().HandleCommand(cmdstring, res)
27 lldb_run_command_state['active'] = False
28 if res.Succeeded():
29 retval = res.GetOutput()
30 return retval
31
32 def EnableLLDBAPILogging():
33 """ Enable file based logging for lldb and also provide essential information about what information
34 to include when filing a bug with lldb or xnu.
35 """
36 logfile_name = "/tmp/lldb.%d.log" % int(time.time())
37 enable_log_base_cmd = "log enable --file %s " % logfile_name
38 cmd_str = enable_log_base_cmd + ' lldb api'
39 print cmd_str
40 print lldb_run_command(cmd_str)
41 cmd_str = enable_log_base_cmd + ' gdb-remote packets'
42 print cmd_str
43 print lldb_run_command(cmd_str)
44 cmd_str = enable_log_base_cmd + ' kdp-remote packets'
45 print cmd_str
46 print lldb_run_command(cmd_str)
47 print lldb_run_command("verison")
48 print "Please collect the logs from %s for filing a radar. If you had encountered an exception in a lldbmacro command please re-run it." % logfile_name
49 print "Please make sure to provide the output of 'verison', 'image list' and output of command that failed."
50 return
51
52 def GetConnectionProtocol():
53 """ Returns a string representing what kind of connection is used for debugging the target.
54 params: None
55 returns:
56 str - connection type. One of ("core","kdp","gdb", "unknown")
57 """
58 retval = "unknown"
59 process_plugin_name = LazyTarget.GetProcess().GetPluginName().lower()
60 if "kdp" in process_plugin_name:
61 retval = "kdp"
62 elif "gdb" in process_plugin_name:
63 retval = "gdb"
64 elif "mach-o" in process_plugin_name and "core" in process_plugin_name:
65 retval = "core"
66 return retval
67
68 def SBValueToPointer(sbval):
69 """ Helper function for getting pointer value from an object of pointer type.
70 ex. void *astring = 0x12345
71 use SBValueToPointer(astring_val) to get 0x12345
72 params: sbval - value object of type '<type> *'
73 returns: int - pointer value as an int.
74 """
75 if type(sbval) == core.value:
76 sbval = sbval.GetSBValue()
77 if sbval.IsPointerType():
78 return sbval.GetValueAsUnsigned()
79 else:
80 return int(sbval.GetAddress())
81
82 def ArgumentStringToInt(arg_string):
83 """ convert '1234' or '0x123' to int
84 params:
85 arg_string: str - typically string passed from commandline. ex '1234' or '0xA12CD'
86 returns:
87 int - integer representation of the string
88 """
89 arg_string = arg_string.strip()
90 if arg_string.find('0x') >=0:
91 return int(arg_string, 16)
92 else:
93 return int(arg_string)
94
95 def GetLongestMatchOption(searchstr, options=[], ignore_case=True):
96 """ Get longest matched string from set of options.
97 params:
98 searchstr : string of chars to be matched
99 options : array of strings that are to be matched
100 returns:
101 [] - array of matched options. The order of options is same as the arguments.
102 empty array is returned if searchstr does not match any option.
103 example:
104 subcommand = LongestMatch('Rel', ['decode', 'enable', 'reload'], ignore_case=True)
105 print subcommand # prints ['reload']
106 """
107 if ignore_case:
108 searchstr = searchstr.lower()
109 found_options = []
110 for o in options:
111 so = o
112 if ignore_case:
113 so = o.lower()
114 if so.find(searchstr) >=0 :
115 found_options.append(o)
116 return found_options
117
118 def GetType(target_type):
119 """ type cast an object to new type.
120 params:
121 target_type - str, ex. 'char', 'uint32_t' etc
122 returns:
123 lldb.SBType - a new Type that can be used as param to lldb.SBValue.Cast()
124 raises:
125 NameError - Incase the type is not identified
126 """
127 return gettype(target_type)
128
129
130 def Cast(obj, target_type):
131 """ Type cast an object to another C type.
132 params:
133 obj - core.value object representing some C construct in lldb
134 target_type - str : ex 'char *'
135 - lldb.SBType :
136 """
137 return cast(obj, target_type)
138
139
140 def loadLLDB():
141 """ Util function to load lldb python framework in case not available in common include paths.
142 """
143 try:
144 import lldb
145 print 'Found LLDB on path'
146 except:
147 platdir = subprocess.check_output('xcodebuild -version -sdk iphoneos PlatformPath'.split())
148 offset = platdir.find("Contents/Developer")
149 if offset == -1:
150 lldb_py = os.path.join(os.path.dirname(os.path.dirname(platdir)), 'Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python')
151 else:
152 lldb_py = os.path.join(platdir[0:offset+8], 'SharedFrameworks/LLDB.framework/Versions/A/Resources/Python')
153 if os.path.isdir(lldb_py):
154 sys.path.append(lldb_py)
155 global lldb
156 lldb = __import__('lldb')
157 print 'Found LLDB in SDK'
158 else:
159 print 'Failed to locate lldb.py from', lldb_py
160 sys.exit(-1)
161 return True
162
163 class Logger():
164 """ A logging utility """
165 def __init__(self, log_file_path="/tmp/xnu.log"):
166 self.log_file_handle = open(log_file_path, "w+")
167 self.redirect_to_stdout = False
168
169 def log_debug(self, *args):
170 current_timestamp = time.time()
171 debug_line_str = "DEBUG:" + str(current_timestamp) + ":"
172 for arg in args:
173 debug_line_str += " " + str(arg).replace("\n", " ") + ", "
174
175 self.log_file_handle.write(debug_line_str + "\n")
176 if self.redirect_to_stdout :
177 print debug_line_str
178
179 def write(self, line):
180 self.log_debug(line)
181
182
183 def sizeof_fmt(num, unit_str='B'):
184 """ format large number into human readable values.
185 convert any number into Kilo, Mega, Giga, Tera format for human understanding.
186 params:
187 num - int : number to be converted
188 unit_str - str : a suffix for unit. defaults to 'B' for bytes.
189 returns:
190 str - formatted string for printing.
191 """
192 for x in ['','K','M','G','T']:
193 if num < 1024.0:
194 return "%3.1f%s%s" % (num, x,unit_str)
195 num /= 1024.0
196 return "%3.1f%s%s" % (num, 'P', unit_str)
197
198 def WriteStringToMemoryAddress(stringval, addr):
199 """ write a null terminated string to address.
200 params:
201 stringval: str- string to be written to memory. a '\0' will be added at the end
202 addr : int - address where data is to be written
203 returns:
204 bool - True if successfully written
205 """
206 serr = lldb.SBError()
207 length = len(stringval) + 1
208 format_string = "%ds" % length
209 sdata = struct.pack(format_string,stringval)
210 numbytes = LazyTarget.GetProcess().WriteMemory(addr, sdata, serr)
211 if numbytes == length and serr.Success():
212 return True
213 return False
214
215 def WriteInt64ToMemoryAddress(intval, addr):
216 """ write a 64 bit integer at an address.
217 params:
218 intval - int - an integer value to be saved
219 addr - int - address where int is to be written
220 returns:
221 bool - True if successfully written.
222 """
223 serr = lldb.SBError()
224 sdata = struct.pack('Q', intval)
225 addr = int(hex(addr).rstrip('L'), 16)
226 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
227 if numbytes == 8 and serr.Success():
228 return True
229 return False
230
231 def WritePtrDataToMemoryAddress(intval, addr):
232 """ Write data to pointer size memory.
233 This is equivalent of doing *(&((struct pmap *)addr)) = intval
234 It will identify 32/64 bit kernel and write memory accordingly.
235 params:
236 intval - int - an integer value to be saved
237 addr - int - address where int is to be written
238 returns:
239 bool - True if successfully written.
240 """
241 if kern.ptrsize == 8:
242 return WriteInt64ToMemoryAddress(intval, addr)
243 else:
244 return WriteInt32ToMemoryAddress(intval, addr)
245
246 def WriteInt32ToMemoryAddress(intval, addr):
247 """ write a 32 bit integer at an address.
248 params:
249 intval - int - an integer value to be saved
250 addr - int - address where int is to be written
251 returns:
252 bool - True if successfully written.
253 """
254 serr = lldb.SBError()
255 sdata = struct.pack('I', intval)
256 addr = int(hex(addr).rstrip('L'), 16)
257 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
258 if numbytes == 4 and serr.Success():
259 return True
260 return False
261
262 def WriteInt16ToMemoryAddress(intval, addr):
263 """ write a 16 bit integer at an address.
264 params:
265 intval - int - an integer value to be saved
266 addr - int - address where int is to be written
267 returns:
268 bool - True if successfully written.
269 """
270 serr = lldb.SBError()
271 sdata = struct.pack('H', intval)
272 addr = int(hex(addr).rstrip('L'), 16)
273 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
274 if numbytes == 2 and serr.Success():
275 return True
276 return False
277
278 def WriteInt8ToMemoryAddress(intval, addr):
279 """ write a 8 bit integer at an address.
280 params:
281 intval - int - an integer value to be saved
282 addr - int - address where int is to be written
283 returns:
284 bool - True if successfully written.
285 """
286 serr = lldb.SBError()
287 sdata = struct.pack('B', intval)
288 addr = int(hex(addr).rstrip('L'), 16)
289 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
290 if numbytes == 1 and serr.Success():
291 return True
292 return False
293
294 _enum_cache = {}
295 def GetEnumValue(name):
296 """ Finds the value of a particular enum define. Ex kdp_req_t::KDP_VERSION => 0x3
297 params:
298 name : str - name of enum in the format type::name
299 returns:
300 int - value of the particular enum.
301 raises:
302 TypeError - if the enum is not found
303 """
304 name = name.strip()
305 global _enum_cache
306 if name not in _enum_cache:
307 res = lldb.SBCommandReturnObject()
308 lldb.debugger.GetCommandInterpreter().HandleCommand("p/x (`%s`)" % name, res)
309 if not res.Succeeded():
310 raise TypeError("Enum not found with name: " + name)
311 # the result is of format '(int) $481 = 0x00000003\n'
312 _enum_cache[name] = int( res.GetOutput().split('=')[-1].strip(), 16)
313 return _enum_cache[name]
314
315 def ResolveFSPath(path):
316 """ expand ~user directories and return absolute path.
317 params: path - str - eg "~rc/Software"
318 returns:
319 str - abs path with user directories and symlinks expanded.
320 str - if path resolution fails then returns the same string back
321 """
322 expanded_path = os.path.expanduser(path)
323 norm_path = os.path.normpath(expanded_path)
324 return norm_path
325
326 _dsymlist = {}
327 uuid_regex = re.compile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}",re.IGNORECASE|re.DOTALL)
328 def addDSYM(uuid, info):
329 """ add a module by dsym into the target modules.
330 params: uuid - str - uuid string eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
331 info - dict - info dictionary passed from dsymForUUID
332 """
333 global _dsymlist
334 if "DBGSymbolRichExecutable" not in info:
335 print "Error: Unable to find syms for %s" % uuid
336 return False
337 if not uuid in _dsymlist:
338 # add the dsym itself
339 cmd_str = "target modules add --uuid %s" % uuid
340 debuglog(cmd_str)
341 lldb.debugger.HandleCommand(cmd_str)
342 # set up source path
343 #lldb.debugger.HandleCommand("settings append target.source-map %s %s" % (info["DBGBuildSourcePath"], info["DBGSourcePath"]))
344 # modify the list to show we loaded this
345 _dsymlist[uuid] = True
346
347 def loadDSYM(uuid, load_address):
348 """ Load an already added symbols to a particular load address
349 params: uuid - str - uuid string
350 load_address - int - address where to load the symbols
351 returns bool:
352 True - if successful
353 False - if failed. possible because uuid is not presently loaded.
354 """
355 if uuid not in _dsymlist:
356 return False
357 cmd_str = "target modules load --uuid %s --slide %d" % ( uuid, load_address)
358 debuglog(cmd_str)
359 lldb.debugger.HandleCommand(cmd_str)
360
361 def dsymForUUID(uuid):
362 """ Get dsym informaiton by calling dsymForUUID
363 params: uuid - str - uuid string from executable. eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
364 returns:
365 {} - a dictionary holding dsym information printed by dsymForUUID.
366 None - if failed to find information
367 """
368 import subprocess
369 import plistlib
370 output = subprocess.check_output(["/usr/local/bin/dsymForUUID", uuid])
371 if output:
372 # because of <rdar://12713712>
373 #plist = plistlib.readPlistFromString(output)
374 #beginworkaround
375 keyvalue_extract_re = re.compile("<key>(.*?)</key>\s*<string>(.*?)</string>",re.IGNORECASE|re.MULTILINE|re.DOTALL)
376 plist={}
377 plist[uuid] = {}
378 for item in keyvalue_extract_re.findall(output):
379 plist[uuid][item[0]] = item[1]
380 #endworkaround
381 if plist and plist[uuid]:
382 return plist[uuid]
383 return None
384
385 def debuglog(s):
386 """ Print a object in the debug stream
387 """
388 global config
389 if config['debug']:
390 print "DEBUG:",s
391 return None