]> git.saurik.com Git - apple/xnu.git/blame_incremental - tools/lldbmacros/utils.py
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tools / lldbmacros / utils.py
... / ...
CommitLineData
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"""
6import sys, re, time, getopt, shlex, os, time
7import lldb
8import struct
9from core.cvalue import *
10from core.configuration import *
11from core.lazytarget import *
12
13#DONOTTOUCHME: exclusive use for lldb_run_command only.
14lldb_run_command_state = {'active':False}
15
16def 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 else:
31 retval = "ERROR:" + res.GetError()
32 return retval
33
34def EnableLLDBAPILogging():
35 """ Enable file based logging for lldb and also provide essential information about what information
36 to include when filing a bug with lldb or xnu.
37 """
38 logfile_name = "/tmp/lldb.%d.log" % int(time.time())
39 enable_log_base_cmd = "log enable --file %s " % logfile_name
40 cmd_str = enable_log_base_cmd + ' lldb api'
41 print cmd_str
42 print lldb_run_command(cmd_str)
43 cmd_str = enable_log_base_cmd + ' gdb-remote packets'
44 print cmd_str
45 print lldb_run_command(cmd_str)
46 cmd_str = enable_log_base_cmd + ' kdp-remote packets'
47 print cmd_str
48 print lldb_run_command(cmd_str)
49 print lldb_run_command("version")
50 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
51 print "Please make sure to provide the output of 'version', 'image list' and output of command that failed."
52 return
53
54def GetConnectionProtocol():
55 """ Returns a string representing what kind of connection is used for debugging the target.
56 params: None
57 returns:
58 str - connection type. One of ("core","kdp","gdb", "unknown")
59 """
60 retval = "unknown"
61 process_plugin_name = LazyTarget.GetProcess().GetPluginName().lower()
62 if "kdp" in process_plugin_name:
63 retval = "kdp"
64 elif "gdb" in process_plugin_name:
65 retval = "gdb"
66 elif "mach-o" in process_plugin_name and "core" in process_plugin_name:
67 retval = "core"
68 return retval
69
70def SBValueToPointer(sbval):
71 """ Helper function for getting pointer value from an object of pointer type.
72 ex. void *astring = 0x12345
73 use SBValueToPointer(astring_val) to get 0x12345
74 params: sbval - value object of type '<type> *'
75 returns: int - pointer value as an int.
76 """
77 if type(sbval) == core.value:
78 sbval = sbval.GetSBValue()
79 if sbval.IsPointerType():
80 return sbval.GetValueAsUnsigned()
81 else:
82 return int(sbval.GetAddress())
83
84def ArgumentStringToInt(arg_string):
85 """ convert '1234' or '0x123' to int
86 params:
87 arg_string: str - typically string passed from commandline. ex '1234' or '0xA12CD'
88 returns:
89 int - integer representation of the string
90 """
91 arg_string = arg_string.strip()
92 if arg_string.find('0x') >=0:
93 return int(arg_string, 16)
94 else:
95 return int(arg_string)
96
97def GetLongestMatchOption(searchstr, options=[], ignore_case=True):
98 """ Get longest matched string from set of options.
99 params:
100 searchstr : string of chars to be matched
101 options : array of strings that are to be matched
102 returns:
103 [] - array of matched options. The order of options is same as the arguments.
104 empty array is returned if searchstr does not match any option.
105 example:
106 subcommand = LongestMatch('Rel', ['decode', 'enable', 'reload'], ignore_case=True)
107 print subcommand # prints ['reload']
108 """
109 if ignore_case:
110 searchstr = searchstr.lower()
111 found_options = []
112 for o in options:
113 so = o
114 if ignore_case:
115 so = o.lower()
116 if so == searchstr:
117 return [o]
118 if so.find(searchstr) >=0 :
119 found_options.append(o)
120 return found_options
121
122def GetType(target_type):
123 """ type cast an object to new type.
124 params:
125 target_type - str, ex. 'char', 'uint32_t' etc
126 returns:
127 lldb.SBType - a new Type that can be used as param to lldb.SBValue.Cast()
128 raises:
129 NameError - Incase the type is not identified
130 """
131 return gettype(target_type)
132
133
134def Cast(obj, target_type):
135 """ Type cast an object to another C type.
136 params:
137 obj - core.value object representing some C construct in lldb
138 target_type - str : ex 'char *'
139 - lldb.SBType :
140 """
141 return cast(obj, target_type)
142
143def ContainerOf(obj, target_type, field_name):
144 """ Type cast an object to another C type from a pointer to a field.
145 params:
146 obj - core.value object representing some C construct in lldb
147 target_type - str : ex 'struct thread'
148 - lldb.SBType :
149 field_name - the field name within the target_type obj is a pointer to
150 """
151 return containerof(obj, target_type, field_name)
152
153def loadLLDB():
154 """ Util function to load lldb python framework in case not available in common include paths.
155 """
156 try:
157 import lldb
158 print 'Found LLDB on path'
159 except:
160 platdir = subprocess.check_output('xcodebuild -version -sdk iphoneos PlatformPath'.split())
161 offset = platdir.find("Contents/Developer")
162 if offset == -1:
163 lldb_py = os.path.join(os.path.dirname(os.path.dirname(platdir)), 'Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python')
164 else:
165 lldb_py = os.path.join(platdir[0:offset+8], 'SharedFrameworks/LLDB.framework/Versions/A/Resources/Python')
166 if os.path.isdir(lldb_py):
167 sys.path.append(lldb_py)
168 global lldb
169 lldb = __import__('lldb')
170 print 'Found LLDB in SDK'
171 else:
172 print 'Failed to locate lldb.py from', lldb_py
173 sys.exit(-1)
174 return True
175
176class Logger():
177 """ A logging utility """
178 def __init__(self, log_file_path="/tmp/xnu.log"):
179 self.log_file_handle = open(log_file_path, "w+")
180 self.redirect_to_stdout = False
181
182 def log_debug(self, *args):
183 current_timestamp = time.time()
184 debug_line_str = "DEBUG:" + str(current_timestamp) + ":"
185 for arg in args:
186 debug_line_str += " " + str(arg).replace("\n", " ") + ", "
187
188 self.log_file_handle.write(debug_line_str + "\n")
189 if self.redirect_to_stdout :
190 print debug_line_str
191
192 def write(self, line):
193 self.log_debug(line)
194
195
196def sizeof_fmt(num, unit_str='B'):
197 """ format large number into human readable values.
198 convert any number into Kilo, Mega, Giga, Tera format for human understanding.
199 params:
200 num - int : number to be converted
201 unit_str - str : a suffix for unit. defaults to 'B' for bytes.
202 returns:
203 str - formatted string for printing.
204 """
205 for x in ['','K','M','G','T']:
206 if num < 1024.0:
207 return "%3.1f%s%s" % (num, x,unit_str)
208 num /= 1024.0
209 return "%3.1f%s%s" % (num, 'P', unit_str)
210
211def WriteStringToMemoryAddress(stringval, addr):
212 """ write a null terminated string to address.
213 params:
214 stringval: str- string to be written to memory. a '\0' will be added at the end
215 addr : int - address where data is to be written
216 returns:
217 bool - True if successfully written
218 """
219 serr = lldb.SBError()
220 length = len(stringval) + 1
221 format_string = "%ds" % length
222 sdata = struct.pack(format_string,stringval)
223 numbytes = LazyTarget.GetProcess().WriteMemory(addr, sdata, serr)
224 if numbytes == length and serr.Success():
225 return True
226 return False
227
228def WriteInt64ToMemoryAddress(intval, addr):
229 """ write a 64 bit integer at an address.
230 params:
231 intval - int - an integer value to be saved
232 addr - int - address where int is to be written
233 returns:
234 bool - True if successfully written.
235 """
236 serr = lldb.SBError()
237 sdata = struct.pack('Q', intval)
238 addr = int(hex(addr).rstrip('L'), 16)
239 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
240 if numbytes == 8 and serr.Success():
241 return True
242 return False
243
244def WritePtrDataToMemoryAddress(intval, addr):
245 """ Write data to pointer size memory.
246 This is equivalent of doing *(&((struct pmap *)addr)) = intval
247 It will identify 32/64 bit kernel and write memory accordingly.
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 if kern.ptrsize == 8:
255 return WriteInt64ToMemoryAddress(intval, addr)
256 else:
257 return WriteInt32ToMemoryAddress(intval, addr)
258
259def WriteInt32ToMemoryAddress(intval, addr):
260 """ write a 32 bit integer at an address.
261 params:
262 intval - int - an integer value to be saved
263 addr - int - address where int is to be written
264 returns:
265 bool - True if successfully written.
266 """
267 serr = lldb.SBError()
268 sdata = struct.pack('I', intval)
269 addr = int(hex(addr).rstrip('L'), 16)
270 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
271 if numbytes == 4 and serr.Success():
272 return True
273 return False
274
275def WriteInt16ToMemoryAddress(intval, addr):
276 """ write a 16 bit integer at an address.
277 params:
278 intval - int - an integer value to be saved
279 addr - int - address where int is to be written
280 returns:
281 bool - True if successfully written.
282 """
283 serr = lldb.SBError()
284 sdata = struct.pack('H', intval)
285 addr = int(hex(addr).rstrip('L'), 16)
286 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
287 if numbytes == 2 and serr.Success():
288 return True
289 return False
290
291def WriteInt8ToMemoryAddress(intval, addr):
292 """ write a 8 bit integer at an address.
293 params:
294 intval - int - an integer value to be saved
295 addr - int - address where int is to be written
296 returns:
297 bool - True if successfully written.
298 """
299 serr = lldb.SBError()
300 sdata = struct.pack('B', intval)
301 addr = int(hex(addr).rstrip('L'), 16)
302 numbytes = LazyTarget.GetProcess().WriteMemory(addr,sdata, serr)
303 if numbytes == 1 and serr.Success():
304 return True
305 return False
306
307_enum_cache = {}
308def GetEnumValue(enum_name_or_combined, member_name = None):
309 """ Finds the value of a particular enum define. Ex kdp_req_t::KDP_VERSION => 0x3
310 params:
311 enum_name_or_combined: str
312 name of an enum of the format type::name (legacy)
313 name of an enum type
314 member_name: None, or the name of an enum member
315 (then enum_name_or_combined is a type name).
316 returns:
317 int - value of the particular enum.
318 raises:
319 TypeError - if the enum is not found
320 """
321 global _enum_cache
322 if member_name is None:
323 enum_name, member_name = enum_name_or_combined.strip().split("::")
324 else:
325 enum_name = enum_name_or_combined
326
327 if enum_name not in _enum_cache:
328 ty = GetType(enum_name)
329 d = {}
330
331 for e in ty.get_enum_members_array():
332 if ty.GetTypeFlags() & lldb.eTypeIsSigned:
333 d[e.GetName()] = e.GetValueAsSigned()
334 else:
335 d[e.GetName()] = e.GetValueAsUnsigned()
336
337 _enum_cache[enum_name] = d
338
339 return _enum_cache[enum_name][member_name]
340
341def ResolveFSPath(path):
342 """ expand ~user directories and return absolute path.
343 params: path - str - eg "~rc/Software"
344 returns:
345 str - abs path with user directories and symlinks expanded.
346 str - if path resolution fails then returns the same string back
347 """
348 expanded_path = os.path.expanduser(path)
349 norm_path = os.path.normpath(expanded_path)
350 return norm_path
351
352_dsymlist = {}
353uuid_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)
354def addDSYM(uuid, info):
355 """ add a module by dsym into the target modules.
356 params: uuid - str - uuid string eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
357 info - dict - info dictionary passed from dsymForUUID
358 """
359 global _dsymlist
360 if "DBGSymbolRichExecutable" not in info:
361 print "Error: Unable to find syms for %s" % uuid
362 return False
363 if not uuid in _dsymlist:
364 # add the dsym itself
365 cmd_str = "target modules add --uuid %s" % uuid
366 debuglog(cmd_str)
367 lldb.debugger.HandleCommand(cmd_str)
368 # set up source path
369 #lldb.debugger.HandleCommand("settings append target.source-map %s %s" % (info["DBGBuildSourcePath"], info["DBGSourcePath"]))
370 # modify the list to show we loaded this
371 _dsymlist[uuid] = True
372
373def loadDSYM(uuid, load_address, sections=[]):
374 """ Load an already added symbols to a particular load address
375 params: uuid - str - uuid string
376 load_address - int - address where to load the symbols
377 returns bool:
378 True - if successful
379 False - if failed. possible because uuid is not presently loaded.
380 """
381 if uuid not in _dsymlist:
382 return False
383 if not sections:
384 cmd_str = "target modules load --uuid %s --slide %d" % ( uuid, load_address)
385 debuglog(cmd_str)
386 else:
387 cmd_str = "target modules load --uuid {} ".format(uuid)
388 sections_str = ""
389 for s in sections:
390 sections_str += " {} {:#0x} ".format(s.name, s.vmaddr)
391 cmd_str += sections_str
392 debuglog(cmd_str)
393
394 lldb.debugger.HandleCommand(cmd_str)
395 return True
396
397
398def RunShellCommand(command):
399 """ Run a shell command in subprocess.
400 params: command with arguments to run
401 returns: (exit_code, stdout, stderr)
402 """
403 import shlex, subprocess
404 cmd_args = shlex.split(command)
405 output_str = ""
406 exit_code = 0
407 try:
408 output_str = subprocess.check_output(cmd_args, stderr=subprocess.STDOUT)
409 except subprocess.CalledProcessError, e:
410 exit_code = e.returncode
411 finally:
412 return (exit_code, output_str, '')
413
414def dsymForUUID(uuid):
415 """ Get dsym informaiton by calling dsymForUUID
416 params: uuid - str - uuid string from executable. eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
417 returns:
418 {} - a dictionary holding dsym information printed by dsymForUUID.
419 None - if failed to find information
420 """
421 import subprocess
422 import plistlib
423 output = subprocess.check_output(["/usr/local/bin/dsymForUUID", "--copyExecutable", uuid])
424 if output:
425 # because of <rdar://12713712>
426 #plist = plistlib.readPlistFromString(output)
427 #beginworkaround
428 keyvalue_extract_re = re.compile("<key>(.*?)</key>\s*<string>(.*?)</string>",re.IGNORECASE|re.MULTILINE|re.DOTALL)
429 plist={}
430 plist[uuid] = {}
431 for item in keyvalue_extract_re.findall(output):
432 plist[uuid][item[0]] = item[1]
433 #endworkaround
434 if plist and plist[uuid]:
435 return plist[uuid]
436 return None
437
438def debuglog(s):
439 """ Print a object in the debug stream
440 """
441 global config
442 if config['debug']:
443 print "DEBUG:",s
444 return None
445
446def IsAppleInternal():
447 """ check if apple_internal modules are available
448 returns: True if apple_internal module is present
449 """
450 import imp
451 try:
452 imp.find_module("apple_internal")
453 retval = True
454 except ImportError:
455 retval = False
456 return retval
457
458def print_hex_data(data, begin_offset=0, desc="", marks={}):
459 """ print on stdout "hexdump -C < data" like output
460 params:
461 data - bytearray or array of int where each int < 255
462 begin_offset - int offset that should be printed in left column
463 desc - str optional description to print on the first line to describe data
464 mark - dictionary of markers
465 """
466 if desc:
467 print "{}:".format(desc)
468 index = 0
469 total_len = len(data)
470 hex_buf = ""
471 char_buf = ""
472 while index < total_len:
473 if marks.has_key(begin_offset + index):
474 hex_buf += marks[begin_offset + index]
475 hex_buf += "{:02x}".format(data[index])
476 else:
477 hex_buf += " {:02x}".format(data[index])
478 if data[index] < 0x20 or data[index] > 0x7e:
479 char_buf += "."
480 else:
481 char_buf += "{:c}".format(data[index])
482 index += 1
483 if index and index < total_len and index % 8 == 0:
484 hex_buf += " "
485 if index > 1 and index < total_len and (index % 16) == 0:
486 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
487 hex_buf = ""
488 char_buf = ""
489 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
490 return
491
492def Ones(x):
493 return (1 << x)-1
494
495def StripPAC(x, TySz):
496 sign_mask = 1 << 55
497 ptr_mask = Ones(64-TySz)
498 pac_mask = ~ptr_mask
499 sign = x & sign_mask
500 if sign:
501 return (x | pac_mask) + 2**64
502 else:
503 return x & ptr_mask