]>
git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/utils.py
1 #General Utility functions for debugging or introspection
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.
6 import sys
, re
, time
, getopt
, shlex
, os
, time
9 from core
.cvalue
import *
10 from core
.configuration
import *
11 from core
.lazytarget
import *
13 #DONOTTOUCHME: exclusive use for lldb_run_command only.
14 lldb_run_command_state
= {'active':False}
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.
21 global lldb_run_command_state
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
29 retval
= res
.GetOutput()
31 retval
= "ERROR:" + res
.GetError()
34 def 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.
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'
42 print lldb_run_command(cmd_str
)
43 cmd_str
= enable_log_base_cmd
+ ' gdb-remote packets'
45 print lldb_run_command(cmd_str
)
46 cmd_str
= enable_log_base_cmd
+ ' kdp-remote packets'
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."
54 def GetConnectionProtocol():
55 """ Returns a string representing what kind of connection is used for debugging the target.
58 str - connection type. One of ("core","kdp","gdb", "unknown")
61 process_plugin_name
= LazyTarget
.GetProcess().GetPluginName().lower()
62 if "kdp" in process_plugin_name
:
64 elif "gdb" in process_plugin_name
:
66 elif "mach-o" in process_plugin_name
and "core" in process_plugin_name
:
70 def 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.
77 if type(sbval
) == core
.value
:
78 sbval
= sbval
.GetSBValue()
79 if sbval
.IsPointerType():
80 return sbval
.GetValueAsUnsigned()
82 return int(sbval
.GetAddress())
84 def ArgumentStringToInt(arg_string
):
85 """ convert '1234' or '0x123' to int
87 arg_string: str - typically string passed from commandline. ex '1234' or '0xA12CD'
89 int - integer representation of the string
91 arg_string
= arg_string
.strip()
92 if arg_string
.find('0x') >=0:
93 return int(arg_string
, 16)
95 return int(arg_string
)
97 def GetLongestMatchOption(searchstr
, options
=[], ignore_case
=True):
98 """ Get longest matched string from set of options.
100 searchstr : string of chars to be matched
101 options : array of strings that are to be matched
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.
106 subcommand = LongestMatch('Rel', ['decode', 'enable', 'reload'], ignore_case=True)
107 print subcommand # prints ['reload']
110 searchstr
= searchstr
.lower()
118 if so
.find(searchstr
) >=0 :
119 found_options
.append(o
)
122 def GetType(target_type
):
123 """ type cast an object to new type.
125 target_type - str, ex. 'char', 'uint32_t' etc
127 lldb.SBType - a new Type that can be used as param to lldb.SBValue.Cast()
129 NameError - Incase the type is not identified
131 return gettype(target_type
)
134 def Cast(obj
, target_type
):
135 """ Type cast an object to another C type.
137 obj - core.value object representing some C construct in lldb
138 target_type - str : ex 'char *'
141 return cast(obj
, target_type
)
143 def ContainerOf(obj
, target_type
, field_name
):
144 """ Type cast an object to another C type from a pointer to a field.
146 obj - core.value object representing some C construct in lldb
147 target_type - str : ex 'struct thread'
149 field_name - the field name within the target_type obj is a pointer to
151 return containerof(obj
, target_type
, field_name
)
154 """ Util function to load lldb python framework in case not available in common include paths.
158 print 'Found LLDB on path'
160 platdir
= subprocess
.check_output('xcodebuild -version -sdk iphoneos PlatformPath'.split())
161 offset
= platdir
.find("Contents/Developer")
163 lldb_py
= os
.path
.join(os
.path
.dirname(os
.path
.dirname(platdir
)), 'Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python')
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
)
169 lldb
= __import__('lldb')
170 print 'Found LLDB in SDK'
172 print 'Failed to locate lldb.py from', lldb_py
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
182 def log_debug(self
, *args
):
183 current_timestamp
= time
.time()
184 debug_line_str
= "DEBUG:" + str(current_timestamp
) + ":"
186 debug_line_str
+= " " + str(arg
).replace("\n", " ") + ", "
188 self
.log_file_handle
.write(debug_line_str
+ "\n")
189 if self
.redirect_to_stdout
:
192 def write(self
, line
):
196 def 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.
200 num - int : number to be converted
201 unit_str - str : a suffix for unit. defaults to 'B' for bytes.
203 str - formatted string for printing.
205 for x
in ['','K','M','G','T']:
207 return "%3.1f%s%s" % (num
, x
,unit_str
)
209 return "%3.1f%s%s" % (num
, 'P', unit_str
)
211 def WriteStringToMemoryAddress(stringval
, addr
):
212 """ write a null terminated string to address.
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
217 bool - True if successfully written
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():
228 def WriteInt64ToMemoryAddress(intval
, addr
):
229 """ write a 64 bit integer at an address.
231 intval - int - an integer value to be saved
232 addr - int - address where int is to be written
234 bool - True if successfully written.
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():
244 def 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.
249 intval - int - an integer value to be saved
250 addr - int - address where int is to be written
252 bool - True if successfully written.
254 if kern
.ptrsize
== 8:
255 return WriteInt64ToMemoryAddress(intval
, addr
)
257 return WriteInt32ToMemoryAddress(intval
, addr
)
259 def WriteInt32ToMemoryAddress(intval
, addr
):
260 """ write a 32 bit integer at an address.
262 intval - int - an integer value to be saved
263 addr - int - address where int is to be written
265 bool - True if successfully written.
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():
275 def WriteInt16ToMemoryAddress(intval
, addr
):
276 """ write a 16 bit integer at an address.
278 intval - int - an integer value to be saved
279 addr - int - address where int is to be written
281 bool - True if successfully written.
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():
291 def WriteInt8ToMemoryAddress(intval
, addr
):
292 """ write a 8 bit integer at an address.
294 intval - int - an integer value to be saved
295 addr - int - address where int is to be written
297 bool - True if successfully written.
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():
308 def GetEnumValue(name
):
309 """ Finds the value of a particular enum define. Ex kdp_req_t::KDP_VERSION => 0x3
311 name : str - name of enum in the format type::name
313 int - value of the particular enum.
315 TypeError - if the enum is not found
319 if name
not in _enum_cache
:
320 res
= lldb
.SBCommandReturnObject()
321 lldb
.debugger
.GetCommandInterpreter().HandleCommand("p/x (`%s`)" % name
, res
)
322 if not res
.Succeeded():
323 raise TypeError("Enum not found with name: " + name
)
324 # the result is of format '(int) $481 = 0x00000003\n'
325 _enum_cache
[name
] = int( res
.GetOutput().split('=')[-1].strip(), 16)
326 return _enum_cache
[name
]
328 def ResolveFSPath(path
):
329 """ expand ~user directories and return absolute path.
330 params: path - str - eg "~rc/Software"
332 str - abs path with user directories and symlinks expanded.
333 str - if path resolution fails then returns the same string back
335 expanded_path
= os
.path
.expanduser(path
)
336 norm_path
= os
.path
.normpath(expanded_path
)
340 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
)
341 def addDSYM(uuid
, info
):
342 """ add a module by dsym into the target modules.
343 params: uuid - str - uuid string eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
344 info - dict - info dictionary passed from dsymForUUID
347 if "DBGSymbolRichExecutable" not in info
:
348 print "Error: Unable to find syms for %s" % uuid
350 if not uuid
in _dsymlist
:
351 # add the dsym itself
352 cmd_str
= "target modules add --uuid %s" % uuid
354 lldb
.debugger
.HandleCommand(cmd_str
)
356 #lldb.debugger.HandleCommand("settings append target.source-map %s %s" % (info["DBGBuildSourcePath"], info["DBGSourcePath"]))
357 # modify the list to show we loaded this
358 _dsymlist
[uuid
] = True
360 def loadDSYM(uuid
, load_address
, sections
=[]):
361 """ Load an already added symbols to a particular load address
362 params: uuid - str - uuid string
363 load_address - int - address where to load the symbols
366 False - if failed. possible because uuid is not presently loaded.
368 if uuid
not in _dsymlist
:
371 cmd_str
= "target modules load --uuid %s --slide %d" % ( uuid
, load_address
)
374 cmd_str
= "target modules load --uuid {} ".format(uuid
)
377 sections_str
+= " {} {:#0x} ".format(s
.name
, s
.vmaddr
)
378 cmd_str
+= sections_str
381 lldb
.debugger
.HandleCommand(cmd_str
)
385 def RunShellCommand(command
):
386 """ Run a shell command in subprocess.
387 params: command with arguments to run
388 returns: (exit_code, stdout, stderr)
390 import shlex
, subprocess
391 cmd_args
= shlex
.split(command
)
395 output_str
= subprocess
.check_output(cmd_args
, stderr
=subprocess
.STDOUT
)
396 except subprocess
.CalledProcessError
, e
:
397 exit_code
= e
.returncode
399 return (exit_code
, output_str
, '')
401 def dsymForUUID(uuid
):
402 """ Get dsym informaiton by calling dsymForUUID
403 params: uuid - str - uuid string from executable. eg. 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
405 {} - a dictionary holding dsym information printed by dsymForUUID.
406 None - if failed to find information
410 output
= subprocess
.check_output(["/usr/local/bin/dsymForUUID", "--copyExecutable", uuid
])
412 # because of <rdar://12713712>
413 #plist = plistlib.readPlistFromString(output)
415 keyvalue_extract_re
= re
.compile("<key>(.*?)</key>\s*<string>(.*?)</string>",re
.IGNORECASE|re
.MULTILINE|re
.DOTALL
)
418 for item
in keyvalue_extract_re
.findall(output
):
419 plist
[uuid
][item
[0]] = item
[1]
421 if plist
and plist
[uuid
]:
426 """ Print a object in the debug stream
433 def IsAppleInternal():
434 """ check if apple_internal modules are available
435 returns: True if apple_internal module is present
439 imp
.find_module("apple_internal")
445 def print_hex_data(data
, begin_offset
=0, desc
=""):
446 """ print on stdout "hexdump -C < data" like output
448 data - bytearray or array of int where each int < 255
449 begin_offset - int offset that should be printed in left column
450 desc - str optional description to print on the first line to describe data
453 print "{}:".format(desc
)
455 total_len
= len(data
)
458 while index
< total_len
:
459 hex_buf
+= " {:02x}".format(data
[index
])
460 if data
[index
] < 0x20 or data
[index
] > 0x7e:
463 char_buf
+= "{:c}".format(data
[index
])
465 if index
and index
< total_len
and index
% 8 == 0:
467 if index
> 1 and index
< total_len
and (index
% 16) == 0:
468 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset
+ index
- 16, hex_buf
, char_buf
)
471 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset
+ index
- 16, hex_buf
, char_buf
)