101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton#!/usr/bin/python
201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton#----------------------------------------------------------------------
401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton# Be sure to add the python path that points to the LLDB shared library.
5a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#
6a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# To use this in the embedded python interpreter using "lldb":
7a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#
8a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   cd /path/containing/crashlog.py
9a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   lldb
10a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   (lldb) script import crashlog
11a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   "crashlog" command installed, type "crashlog --help" for detailed help
12a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash
13a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#
14a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# The benefit of running the crashlog command inside lldb in the
15a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# embedded python interpreter is when the command completes, there
16a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# will be a target with all of the files loaded at the locations
17a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# described in the crash log. Only the files that have stack frames
18a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# in the backtrace will be loaded unless the "--load-all" option
19a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# has been specified. This allows users to explore the program in the
20a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton# state it was in right at crash time.
21a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#
2201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton# On MacOSX csh, tcsh:
23a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash )
24a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#
2501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton# On MacOSX sh, bash:
26a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton#   PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash
2701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton#----------------------------------------------------------------------
2801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
29a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytonimport commands
309d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonimport cmd
31ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Claytonimport datetime
329d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonimport glob
3301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport optparse
3401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport os
35ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Claytonimport platform
3601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport plistlib
373d39f83d308f4c147cff81e07c8059eb52dab89dGreg Claytonimport pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args)
3801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport re
39223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Claytonimport shlex
409d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonimport string
4101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport sys
4201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonimport time
43cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Claytonimport uuid
445ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton
455ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Claytontry:
465ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    # Just try for LLDB in case PYTHONPATH is already correctly setup
475ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    import lldb
485ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Claytonexcept ImportError:
495ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    lldb_python_dirs = list()
505ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    # lldb is not in the PYTHONPATH, try some defaults for the current platform
515ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    platform_system = platform.system()
525ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    if platform_system == 'Darwin':
535ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        # On Darwin, try the currently selected Xcode directory
545ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        xcode_dir = commands.getoutput("xcode-select --print-path")
555ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        if xcode_dir:
565ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton            lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python'))
575ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton            lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
585ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
595ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    success = False
605ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    for lldb_python_dir in lldb_python_dirs:
615ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        if os.path.exists(lldb_python_dir):
625ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton            if not (sys.path.__contains__(lldb_python_dir)):
635ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                sys.path.append(lldb_python_dir)
645ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                try:
655ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                    import lldb
665ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                except ImportError:
675ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                    pass
685ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                else:
695ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                    print 'imported lldb from: "%s"' % (lldb_python_dir)
705ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                    success = True
715ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton                    break
725ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton    if not success:
735ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
745ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton        sys.exit(1)
755ec0d02ae49a90ffc840b5f0ac9d6ba921d0942dGreg Clayton
769d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonfrom lldb.utils import symbolication
7701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
7801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg ClaytonPARSE_MODE_NORMAL = 0
7901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg ClaytonPARSE_MODE_THREAD = 1
8001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg ClaytonPARSE_MODE_IMAGES = 2
8101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg ClaytonPARSE_MODE_THREGS = 3
8201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg ClaytonPARSE_MODE_SYSTEM = 4
8301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
849d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonclass CrashLog(symbolication.Symbolicator):
8501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    """Class that does parses darwin crash logs"""
864d258c616e9166fbb8336f77b588e5e2c5332c14Sean Callanan    parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]');
8701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
8801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)')
898b4bc2b81f72f3bdc47b9d492197e5ed7718c402Jason Molenda    frame_regex = re.compile('^([0-9]+) +([^ ]+) *\t?(0x[0-9a-fA-F]+) +(.*)')
908077a5334dc4daef3a145d07307a970d00563055Greg Clayton    image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)');
918077a5334dc4daef3a145d07307a970d00563055Greg Clayton    image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)');
9201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    empty_line_regex = re.compile('^$')
9301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
9401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    class Thread:
9501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        """Class that represents a thread in a darwin crash log"""
9601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        def __init__(self, index):
9701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.index = index
9801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.frames = list()
99aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton            self.idents = list()
10001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.registers = dict()
10101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.reason = None
10201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.queue = None
10301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
10401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        def dump(self, prefix):
10501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            print "%sThread[%u] %s" % (prefix, self.index, self.reason)
10601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            if self.frames:
10701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                print "%s  Frames:" % (prefix)
10801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                for frame in self.frames:
10901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    frame.dump(prefix + '    ')
11001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            if self.registers:
11101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                print "%s  Registers:" % (prefix)
11201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                for reg in self.registers.keys():
11301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    print "%s    %-5s = %#16.16x" % (prefix, reg, self.registers[reg])
11401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
115aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton        def add_ident(self, ident):
116aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton            if not ident in self.idents:
117aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                self.idents.append(ident)
118aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton
11901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        def did_crash(self):
12001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            return self.reason != None
12101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
12201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        def __str__(self):
12301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            s = "Thread[%u]" % self.index
12401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            if self.reason:
12501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                s += ' %s' % self.reason
12601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            return s
12701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
12801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
12901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    class Frame:
13001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        """Class that represents a stack frame in a thread in a darwin crash log"""
1313d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        def __init__(self, index, pc, description):
13201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.pc = pc
1333d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            self.description = description
1343d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            self.index = index
13501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
13601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        def __str__(self):
1373d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            if self.description:
1383d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                return "[%3u] 0x%16.16x %s" % (self.index, self.pc, self.description)
1393d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            else:
1404e46867681f2a3a4587273228dbfac0030942a24Johnny Chen                return "[%3u] 0x%16.16x" % (self.index, self.pc)
1414e46867681f2a3a4587273228dbfac0030942a24Johnny Chen
1424e46867681f2a3a4587273228dbfac0030942a24Johnny Chen        def dump(self, prefix):
1434e46867681f2a3a4587273228dbfac0030942a24Johnny Chen            print "%s%s" % (prefix, str(self))
14401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
1459d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    class DarwinImage(symbolication.Image):
14601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        """Class that represents a binary images in a darwin crash log"""
1478077a5334dc4daef3a145d07307a970d00563055Greg Clayton        dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID')
148cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton        if not os.path.exists(dsymForUUIDBinary):
149cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton            dsymForUUIDBinary = commands.getoutput('which dsymForUUID')
150cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton
151cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton        dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
1528077a5334dc4daef3a145d07307a970d00563055Greg Clayton
1533d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        def __init__(self, text_addr_lo, text_addr_hi, identifier, version, uuid, path):
1549d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            symbolication.Image.__init__(self, path, uuid);
1559d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            self.add_section (symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT"))
1563d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            self.identifier = identifier
15701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            self.version = version
15801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
1593d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        def locate_module_and_debug_symbols(self):
1601b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton            # Don't load a module twice...
1611b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton            if self.resolved:
1624c983c8b32996ac4543dcd95d76a0ecbeb1c6bbdGreg Clayton                return True
1631b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton            # Mark this as resolved so we don't keep trying
1641b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton            self.resolved = True
165d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton            uuid_str = self.get_normalized_uuid_string()
166d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton            print 'Getting symbols for %s %s...' % (uuid_str, self.path),
1678077a5334dc4daef3a145d07307a970d00563055Greg Clayton            if os.path.exists(self.dsymForUUIDBinary):
168d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, uuid_str)
1698077a5334dc4daef3a145d07307a970d00563055Greg Clayton                s = commands.getoutput(dsym_for_uuid_command)
1708077a5334dc4daef3a145d07307a970d00563055Greg Clayton                if s:
1718077a5334dc4daef3a145d07307a970d00563055Greg Clayton                    plist_root = plistlib.readPlistFromString (s)
1728077a5334dc4daef3a145d07307a970d00563055Greg Clayton                    if plist_root:
173d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                        plist = plist_root[uuid_str]
174cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                        if plist:
175cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            if 'DBGArchitecture' in plist:
176cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                                self.arch = plist['DBGArchitecture']
177cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            if 'DBGDSYMPath' in plist:
1783d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                self.symfile = os.path.realpath(plist['DBGDSYMPath'])
179cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            if 'DBGSymbolRichExecutable' in plist:
180cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                                self.resolved_path = os.path.expanduser (plist['DBGSymbolRichExecutable'])
181cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton            if not self.resolved_path and os.path.exists(self.path):
182cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path)
183d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                self_uuid = self.get_uuid()
184cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                for line in dwarfdump_cmd_output.splitlines():
185cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                    match = self.dwarfdump_uuid_regex.search (line)
186cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                    if match:
187cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                        dwarf_uuid_str = match.group(1)
188cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                        dwarf_uuid = uuid.UUID(dwarf_uuid_str)
189cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                        if self_uuid == dwarf_uuid:
190cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            self.resolved_path = self.path
191cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            self.arch = match.group(2)
192cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                            break;
193cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                if not self.resolved_path:
1941b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton                    self.unavailable = True
1951b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton                    print "error\n    error: unable to locate '%s' with UUID %s" % (self.path, uuid_str)
1964c983c8b32996ac4543dcd95d76a0ecbeb1c6bbdGreg Clayton                    return False
197cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton            if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)):
198cd793121caadf8eac0b13283bc2caa4cd467aebfGreg Clayton                print 'ok'
1991dae6f39248e38ac84fc20c8b4c26e11bfcc19b7Greg Clayton                # if self.resolved_path:
2001dae6f39248e38ac84fc20c8b4c26e11bfcc19b7Greg Clayton                #     print '  exe = "%s"' % self.resolved_path
2011dae6f39248e38ac84fc20c8b4c26e11bfcc19b7Greg Clayton                # if self.symfile:
2021dae6f39248e38ac84fc20c8b4c26e11bfcc19b7Greg Clayton                #     print ' dsym = "%s"' % self.symfile
2034c983c8b32996ac4543dcd95d76a0ecbeb1c6bbdGreg Clayton                return True
2041b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton            else:
2051b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton                self.unavailable = True
2064c983c8b32996ac4543dcd95d76a0ecbeb1c6bbdGreg Clayton            return False
20701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
2083d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton
20901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
21001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    def __init__(self, path):
21101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        """CrashLog constructor that take a path to a darwin crash log file"""
2129d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        symbolication.Symbolicator.__init__(self);
213223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        self.path = os.path.expanduser(path);
21401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.info_lines = list()
21501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.system_profile = list()
21601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.threads = list()
21701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.idents = list() # A list of the required identifiers for doing all stack backtraces
21801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.crashed_thread_idx = -1
21901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.version = -1
220223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        self.error = None
22101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        # With possible initial component of ~ or ~user replaced by that user's home directory.
222223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        try:
223223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton            f = open(self.path)
224223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        except IOError:
225223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton            self.error = 'error: cannot open "%s"' % self.path
226223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton            return
227223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton
22801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        self.file_lines = f.read().splitlines()
22901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        parse_mode = PARSE_MODE_NORMAL
23001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        thread = None
23101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        for line in self.file_lines:
23201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            # print line
23301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            line_len = len(line)
23401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            if line_len == 0:
23501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                if thread:
23601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    if parse_mode == PARSE_MODE_THREAD:
23701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        if thread.index == self.crashed_thread_idx:
23801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            thread.reason = ''
23901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            if self.thread_exception:
24001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                                thread.reason += self.thread_exception
24101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            if self.thread_exception_data:
24201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                                thread.reason += " (%s)" % self.thread_exception_data
24301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        self.threads.append(thread)
24401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    thread = None
24501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                else:
24601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    # only append an extra empty line if the previous line
24701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    # in the info_lines wasn't empty
24801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    if len(self.info_lines) > 0 and len(self.info_lines[-1]):
24901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        self.info_lines.append(line)
25001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                parse_mode = PARSE_MODE_NORMAL
25101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                # print 'PARSE_MODE_NORMAL'
25201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            elif parse_mode == PARSE_MODE_NORMAL:
25301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                if line.startswith ('Process:'):
25440819bf6f7d3c2ed0c00877f9dc9efbf44beabb7Greg Clayton                    (self.process_name, pid_with_brackets) = line[8:].strip().split(' [')
25501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.process_id = pid_with_brackets.strip('[]')
25601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Path:'):
25701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.process_path = line[5:].strip()
25801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Identifier:'):
25901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.process_identifier = line[11:].strip()
26001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Version:'):
2612bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                    version_string = line[8:].strip()
2622bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                    matched_pair = re.search("(.+)\((.+)\)", version_string)
2632bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                    if matched_pair:
2642bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                        self.process_version = matched_pair.group(1)
2652bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                        self.process_compatability_version = matched_pair.group(2)
2662bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                    else:
2672bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                        self.process = version_string
2682bb4de34c39f13a0d2ec3e96bc82d804a92395a3Johnny Chen                        self.process_compatability_version = version_string
2694d258c616e9166fbb8336f77b588e5e2c5332c14Sean Callanan                elif self.parent_process_regex.search(line):
2704d258c616e9166fbb8336f77b588e5e2c5332c14Sean Callanan                    parent_process_match = self.parent_process_regex.search(line)
2714d258c616e9166fbb8336f77b588e5e2c5332c14Sean Callanan                    self.parent_process_name = parent_process_match.group(1)
2724d258c616e9166fbb8336f77b588e5e2c5332c14Sean Callanan                    self.parent_process_id = parent_process_match.group(2)
27301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Exception Type:'):
27401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.thread_exception = line[15:].strip()
27501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
27601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Exception Codes:'):
27701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.thread_exception_data = line[16:].strip()
27801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
27901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Crashed Thread:'):
28001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.crashed_thread_idx = int(line[15:].strip().split()[0])
28101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
28201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Report Version:'):
28301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.version = int(line[15:].strip())
28401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
28501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('System Profile:'):
28601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    parse_mode = PARSE_MODE_SYSTEM
28701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
28801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif (line.startswith ('Interval Since Last Report:') or
28901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                      line.startswith ('Crashes Since Last Report:') or
29001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                      line.startswith ('Per-App Interval Since Last Report:') or
29101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                      line.startswith ('Per-App Crashes Since Last Report:') or
29201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                      line.startswith ('Sleep/Wake UUID:') or
29301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                      line.startswith ('Anonymous UUID:')):
29401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    # ignore these
29501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
29601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Thread'):
29701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    thread_state_match = self.thread_state_regex.search (line)
29801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    if thread_state_match:
29901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        thread_state_match = self.thread_regex.search (line)
30001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        thread_idx = int(thread_state_match.group(1))
30101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        parse_mode = PARSE_MODE_THREGS
30201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        thread = self.threads[thread_idx]
30301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    else:
30401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        thread_match = self.thread_regex.search (line)
30501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        if thread_match:
30601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            # print 'PARSE_MODE_THREAD'
30701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            parse_mode = PARSE_MODE_THREAD
30801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            thread_idx = int(thread_match.group(1))
30901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                            thread = CrashLog.Thread(thread_idx)
31001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
31101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                elif line.startswith ('Binary Images:'):
31201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    parse_mode = PARSE_MODE_IMAGES
31301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    continue
31401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                self.info_lines.append(line.strip())
31501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            elif parse_mode == PARSE_MODE_THREAD:
316d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                if line.startswith ('Thread'):
317d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    continue
31801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                frame_match = self.frame_regex.search(line)
31901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                if frame_match:
32001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    ident = frame_match.group(2)
321aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                    thread.add_ident(ident)
32201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    if not ident in self.idents:
32301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        self.idents.append(ident)
32401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4)))
32501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                else:
3268077a5334dc4daef3a145d07307a970d00563055Greg Clayton                    print 'error: frame regex failed for line: "%s"' % line
32701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            elif parse_mode == PARSE_MODE_IMAGES:
32801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                image_match = self.image_regex_uuid.search (line)
32901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                if image_match:
3303d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                    image = CrashLog.DarwinImage (int(image_match.group(1),0),
3313d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                  int(image_match.group(2),0),
3323d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                  image_match.group(3).strip(),
3333d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                  image_match.group(4).strip(),
334d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                                                  uuid.UUID(image_match.group(5)),
3353d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                  image_match.group(6))
33601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    self.images.append (image)
33701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                else:
33801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    image_match = self.image_regex_no_uuid.search (line)
33901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    if image_match:
3403d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                        image = CrashLog.DarwinImage (int(image_match.group(1),0),
3413d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                      int(image_match.group(2),0),
3423d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                      image_match.group(3).strip(),
3433d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                      image_match.group(4).strip(),
3443d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                      None,
3453d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                                                      image_match.group(5))
34601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        self.images.append (image)
34701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    else:
34801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                        print "error: image regex failed for: %s" % line
34901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
35001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            elif parse_mode == PARSE_MODE_THREGS:
35101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                stripped_line = line.strip()
35217d4a7f296122b43fe015771fa375a52f9d97798Jason Molenda                # "r12: 0x00007fff6b5939c8  r13: 0x0000000007000006  r14: 0x0000000000002a03  r15: 0x0000000000000c00"
35317d4a7f296122b43fe015771fa375a52f9d97798Jason Molenda                reg_values = re.findall ('([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line);
35401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                for reg_value in reg_values:
355d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    #print 'reg_value = "%s"' % reg_value
35601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    (reg, value) = reg_value.split(': ')
357d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    #print 'reg = "%s"' % reg
358d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    #print 'value = "%s"' % value
35901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                    thread.registers[reg.strip()] = int(value, 0)
36001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            elif parse_mode == PARSE_MODE_SYSTEM:
36101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                self.system_profile.append(line)
36201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        f.close()
3631b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton
36401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    def dump(self):
36501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        print "Crash Log File: %s" % (self.path)
36601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        print "\nThreads:"
36701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        for thread in self.threads:
36801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            thread.dump('  ')
36901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        print "\nImages:"
37001f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        for image in self.images:
37101f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton            image.dump('  ')
37201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
3733d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton    def find_image_with_identifier(self, identifier):
37401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        for image in self.images:
3753d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            if image.identifier == identifier:
37601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton                return image
37701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        return None
37801f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
379e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton    def create_target(self):
3803d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        #print 'crashlog.create_target()...'
3819d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        target = symbolication.Symbolicator.create_target(self)
3823d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        if target:
3833d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            return target
384e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton        # We weren't able to open the main executable as, but we can still symbolicate
3853d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        print 'crashlog.create_target()...2'
386e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton        if self.idents:
387f7fb7334f7cae880fe25069921202961ffb2dc39Sean Callanan            for ident in self.idents:
388e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton                image = self.find_image_with_identifier (ident)
389e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton                if image:
3903d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                    target = image.create_target ()
3913d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                    if target:
3923d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                        return target # success
3933d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        print 'crashlog.create_target()...3'
394e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton        for image in self.images:
3953d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            target = image.create_target ()
3963d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton            if target:
3973d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton                return target # success
3983d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        print 'crashlog.create_target()...4'
3993d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        print 'error: unable to locate any executables from the crash log'
4003d39f83d308f4c147cff81e07c8059eb52dab89dGreg Clayton        return None
4011b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton
40201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
40301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytondef usage():
40401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    print "Usage: lldb-symbolicate.py [-n name] executable-image"
40501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    sys.exit(0)
40601f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
4079d01042727a9c52cbab76237c8d290f2e337067dGreg Claytonclass Interactive(cmd.Cmd):
4089d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.'''
4099d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    image_option_parser = None
4109d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4119d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def __init__(self, crash_logs):
4129d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        cmd.Cmd.__init__(self)
413dcf561419ff815256d63dedeba06d9b5c969702aGreg Clayton        self.use_rawinput = False
4149d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.'
4159d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        self.crash_logs = crash_logs
4169d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        self.prompt = '% '
4179d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4189d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def default(self, line):
4199d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        '''Catch all for unknown command, which will exit the interpreter.'''
4209d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        print "uknown command: %s" % line
4219d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        return True
4229d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4239d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def do_q(self, line):
4249d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        '''Quit command'''
4259d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        return True
4269d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4279d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def do_quit(self, line):
4289d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        '''Quit command'''
4299d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        return True
4309d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
431a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    def do_symbolicate(self, line):
432a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        description='''Symbolicate one or more darwin crash log files by index to provide source file and line information,
433a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        inlined stack frames back to the concrete functions, and disassemble the location of the crash
434a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        for the first frame of the crashed thread.'''
435a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        option_parser = CreateSymbolicateCrashLogOptions ('symbolicate', description, False)
436a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        command_args = shlex.split(line)
437a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        try:
438a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            (options, args) = option_parser.parse_args(command_args)
439a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        except:
440a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            return
441a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
442a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton        if args:
443a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton            # We have arguments, they must valid be crash log file indexes
444a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton            for idx_str in args:
445a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                idx = int(idx_str)
446a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                if idx < len(self.crash_logs):
447a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                    SymbolicateCrashLog (self.crash_logs[idx], options)
448a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                else:
449a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                    print 'error: crash log index %u is out of range' % (idx)
450a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton        else:
451a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton            # No arguments, symbolicate all crash logs using the options provided
452a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton            for idx in range(len(self.crash_logs)):
453a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                SymbolicateCrashLog (self.crash_logs[idx], options)
454a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
4559d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def do_list(self, line=None):
4569d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        '''Dump a list of all crash logs that are currently loaded.
4579d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4589d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        USAGE: list'''
4599d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        print '%u crash logs are loaded:' % len(self.crash_logs)
4609d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
4619d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            print '[%u] = %s' % (crash_log_idx, crash_log.path)
4629d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
4639d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    def do_image(self, line):
464a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton        '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.'''
4659d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        usage = "usage: %prog [options] <PATH> [PATH ...]"
466a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton        description='''Dump information about one or more images in all crash logs. The <PATH> can be a full path, image basename, or partial path. Searches are done in this order.'''
4679d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        command_args = shlex.split(line)
4689d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        if not self.image_option_parser:
4699d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            self.image_option_parser = optparse.OptionParser(description=description, prog='image',usage=usage)
4709d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            self.image_option_parser.add_option('-a', '--all', action='store_true', help='show all images', default=False)
4719d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        try:
4729d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            (options, args) = self.image_option_parser.parse_args(command_args)
4739d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        except:
4749d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            return
4759d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
476d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton        if args:
477d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton            for image_path in args:
478d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                fullpath_search = image_path[0] == '/'
479a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
480d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    matches_found = 0
481d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    for (image_idx, image) in enumerate(crash_log.images):
482d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                        if fullpath_search:
483d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                            if image.get_resolved_path() == image_path:
484d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                                matches_found += 1
485a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                                print '[%u] ' % (crash_log_idx), image
486d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                        else:
487d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                            image_basename = image.get_resolved_path_basename()
488d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                            if image_basename == image_path:
489d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                                matches_found += 1
490a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                                print '[%u] ' % (crash_log_idx), image
491d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    if matches_found == 0:
492d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                        for (image_idx, image) in enumerate(crash_log.images):
49335f62f8ff21c41cd6812704cf3c8c79f8755fef7Greg Clayton                            resolved_image_path = image.get_resolved_path()
49435f62f8ff21c41cd6812704cf3c8c79f8755fef7Greg Clayton                            if resolved_image_path and string.find(image.get_resolved_path(), image_path) >= 0:
495a838b421d5de1925ed7c1ca783796b8a0cfc5ed8Greg Clayton                                print '[%u] ' % (crash_log_idx), image
496d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton        else:
4979d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            for crash_log in self.crash_logs:
4989d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton                for (image_idx, image) in enumerate(crash_log.images):
499d8056e20e5a06bd0f7185f962fda3f5a179f71abGreg Clayton                    print '[%u] %s' % (image_idx, image)
5009d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        return False
5019d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
5029d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
5039d01042727a9c52cbab76237c8d290f2e337067dGreg Claytondef interactive_crashlogs(options, args):
5049d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    crash_log_files = list()
5059d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    for arg in args:
5069d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        for resolved_path in glob.glob(arg):
5079d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            crash_log_files.append(resolved_path)
5089d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
5099d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    crash_logs = list();
5109d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    for crash_log_file in crash_log_files:
5119d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        #print 'crash_log_file = "%s"' % crash_log_file
5129d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        crash_log = CrashLog(crash_log_file)
5139d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        if crash_log.error:
5149d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            print crash_log.error
5159d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            continue
5162b69a2bac83a9163903934eedd1a2264e8558f8bGreg Clayton        if options.debug:
5179d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            crash_log.dump()
5189d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        if not crash_log.images:
5199d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            print 'error: no images in crash log "%s"' % (crash_log)
5209d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            continue
5219d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        else:
5229d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            crash_logs.append(crash_log)
5239d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
5249d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    interpreter = Interactive(crash_logs)
5259d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    # List all crash logs that were imported
5269d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    interpreter.do_list()
5279d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton    interpreter.cmdloop()
5289d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
529ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton
530ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Claytondef save_crashlog(debugger, command, result, dict):
531ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    usage = "usage: %prog [options] <output-path>"
532ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    description='''Export the state of current target into a crashlog file'''
533ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    parser = optparse.OptionParser(description=description, prog='save_crashlog',usage=usage)
534ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
535ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    try:
536ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        (options, args) = parser.parse_args(shlex.split(command))
537ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    except:
538ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        result.PutCString ("error: invalid options");
539ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        return
540ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    if len(args) != 1:
541ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        result.PutCString ("error: invalid arguments, a single output file is the only valid argument")
542ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        return
543ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    out_file = open(args[0], 'w')
544ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    if not out_file:
545ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        result.PutCString ("error: failed to open file '%s' for writing...", args[0]);
546ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        return
547ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    if lldb.target:
548ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        identifier = lldb.target.executable.basename
549ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        if lldb.process:
550ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            pid = lldb.process.id
551ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            if pid != lldb.LLDB_INVALID_PROCESS_ID:
552ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                out_file.write('Process:         %s [%u]\n' % (identifier, pid))
553ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('Path:            %s\n' % (lldb.target.executable.fullpath))
554ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('Identifier:      %s\n' % (identifier))
555ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('\nDate/Time:       %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
556ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('OS Version:      Mac OS X %s (%s)\n' % (platform.mac_ver()[0], commands.getoutput('sysctl -n kern.osversion')));
557ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('Report Version:  9\n')
558ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        for thread_idx in range(lldb.process.num_threads):
559ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            thread = lldb.process.thread[thread_idx]
560ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            out_file.write('\nThread %u:\n' % (thread_idx))
561ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            for (frame_idx, frame) in enumerate(thread.frames):
562ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                frame_pc = frame.pc
563ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                frame_offset = 0
564ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                if frame.function:
565ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    block = frame.GetFrameBlock()
566ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    block_range = block.range[frame.addr]
567ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    if block_range:
568ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        block_start_addr = block_range[0]
569ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        frame_offset = frame_pc - block_start_addr.load_addr
570ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    else:
571ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        frame_offset = frame_pc - frame.function.addr.load_addr
572ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                elif frame.symbol:
573ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    frame_offset = frame_pc - frame.symbol.addr.load_addr
574ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                out_file.write('%-3u %-32s 0x%16.16x %s' % (frame_idx, frame.module.file.basename, frame_pc, frame.name))
575ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                if frame_offset > 0:
576ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    out_file.write(' + %u' % (frame_offset))
577ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                line_entry = frame.line_entry
578ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                if line_entry:
579ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    if options.verbose:
580ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        # This will output the fullpath + line + column
581ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        out_file.write(' %s' % (line_entry))
582ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    else:
583ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        out_file.write(' %s:%u' % (line_entry.file.basename, line_entry.line))
584ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        column = line_entry.column
585ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        if column:
586ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                            out_file.write(':%u' % (column))
587ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                out_file.write('\n')
588ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton
589ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.write('\nBinary Images:\n')
590ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        for module in lldb.target.modules:
591ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            text_segment = module.section['__TEXT']
592ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton            if text_segment:
593ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                text_segment_load_addr = text_segment.GetLoadAddress(lldb.target)
594ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS:
595ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    text_segment_end_load_addr = text_segment_load_addr + text_segment.size
596ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    identifier = module.file.basename
597ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    module_version = '???'
598ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    module_version_array = module.GetVersion()
599ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    if module_version_array:
600ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                        module_version = '.'.join(map(str,module_version_array))
601ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton                    out_file.write ('    0x%16.16x - 0x%16.16x  %s (%s - ???) <%s> %s\n' % (text_segment_load_addr, text_segment_end_load_addr, identifier, module_version, module.GetUUIDString(), module.file.fullpath))
602ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        out_file.close()
603ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    else:
604ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton        result.PutCString ("error: invalid target");
6059d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
606ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton
60701f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytondef Symbolicate(debugger, command, result, dict):
608223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton    try:
609a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        SymbolicateCrashLogs (shlex.split(command))
610223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton    except:
611223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        result.PutCString ("error: python exception %s" % sys.exc_info()[0])
612a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
613a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Claytondef SymbolicateCrashLog(crash_log, options):
614a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    if crash_log.error:
615a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        print crash_log.error
616a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        return
6172b69a2bac83a9163903934eedd1a2264e8558f8bGreg Clayton    if options.debug:
618a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        crash_log.dump()
619a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    if not crash_log.images:
620a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        print 'error: no images in crash log'
621a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        return
622a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
62349ce8969d3154e1560106cfe530444c09410f217Greg Clayton    if options.dump_image_list:
62449ce8969d3154e1560106cfe530444c09410f217Greg Clayton        print "Binary Images:"
62549ce8969d3154e1560106cfe530444c09410f217Greg Clayton        for image in crash_log.images:
62649ce8969d3154e1560106cfe530444c09410f217Greg Clayton            if options.verbose:
62749ce8969d3154e1560106cfe530444c09410f217Greg Clayton                print image.debug_dump()
62849ce8969d3154e1560106cfe530444c09410f217Greg Clayton            else:
62949ce8969d3154e1560106cfe530444c09410f217Greg Clayton                print image
63049ce8969d3154e1560106cfe530444c09410f217Greg Clayton
631a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    target = crash_log.create_target ()
632a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    if not target:
633a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        return
634a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    exe_module = target.GetModuleAtIndex(0)
635a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    images_to_load = list()
636a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    loaded_images = list()
637a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    if options.load_all_images:
638a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        # --load-all option was specified, load everything up
639a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        for image in crash_log.images:
640a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            images_to_load.append(image)
641a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    else:
642a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        # Only load the images found in stack frames for the crashed threads
643aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton        if options.crashed_only:
644aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton            for thread in crash_log.threads:
645aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                if thread.did_crash():
646aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                    for ident in thread.idents:
647aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        images = crash_log.find_images_with_identifier (ident)
648aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        if images:
649aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            for image in images:
650aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                                images_to_load.append(image)
651aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        else:
652aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            print 'error: can\'t find image for identifier "%s"' % ident
653aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton        else:
654aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton            for ident in crash_log.idents:
655aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                images = crash_log.find_images_with_identifier (ident)
656aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                if images:
657aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                    for image in images:
658aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        images_to_load.append(image)
659aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                else:
660aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                    print 'error: can\'t find image for identifier "%s"' % ident
661a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
662a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    for image in images_to_load:
663a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        if image in loaded_images:
664a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo)
665a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        else:
666a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            err = image.add_module (target)
667a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            if err:
668a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                print err
669a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            else:
670a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                #print 'loaded %s' % image
671a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                loaded_images.append(image)
672a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
673a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    for thread in crash_log.threads:
674a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        this_thread_crashed = thread.did_crash()
675a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        if options.crashed_only and this_thread_crashed == False:
676a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            continue
677a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        print "%s" % thread
678a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        #prev_frame_index = -1
679007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton        display_frame_idx = -1
680a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        for frame_idx, frame in enumerate(thread.frames):
681a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth;
682a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            if frame_idx == 0:
6834bb82ac1db085a11d22df3dff6def07119643255Greg Clayton                symbolicated_frame_addresses = crash_log.symbolicate (frame.pc & crash_log.addr_mask, options.verbose)
684a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            else:
685a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                # Any frame above frame zero and we have to subtract one to get the previous line entry
6864bb82ac1db085a11d22df3dff6def07119643255Greg Clayton                symbolicated_frame_addresses = crash_log.symbolicate ((frame.pc & crash_log.addr_mask) - 1, options.verbose)
687a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
688a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            if symbolicated_frame_addresses:
689a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                symbolicated_frame_address_idx = 0
690a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                for symbolicated_frame_address in symbolicated_frame_addresses:
691007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton                    display_frame_idx += 1
692a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                    print '[%3u] %s' % (frame_idx, symbolicated_frame_address)
693007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton                    if (options.source_all or thread.did_crash()) and display_frame_idx < options.source_frames and options.source_context:
694007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton                        source_context = options.source_context
695aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        line_entry = symbolicated_frame_address.get_symbol_context().line_entry
696aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                        if line_entry.IsValid():
697aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            strm = lldb.SBStream()
698aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            if line_entry:
699007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton                                lldb.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers(line_entry.file, line_entry.line, source_context, source_context, "->", strm)
700aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            source_text = strm.GetData()
701aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                            if source_text:
702aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                                # Indent the source a bit
703aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                                indent_str = '    '
704aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                                join_str = '\n' + indent_str
705aa7df343b30d147fe7755b407f631726ed1fe0f9Greg Clayton                                print '%s%s' % (indent_str, join_str.join(source_text.split('\n')))
706a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                    if symbolicated_frame_address_idx == 0:
707a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                        if disassemble:
708a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                            instructions = symbolicated_frame_address.get_instructions()
709a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                            if instructions:
710a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                print
711a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                symbolication.disassemble_instructions (target,
712a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                                                        instructions,
713a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                                                        frame.pc,
714a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                                                        options.disassemble_before,
715a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                                                        options.disassemble_after, frame.index > 0)
716a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                                print
717a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                    symbolicated_frame_address_idx += 1
718a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton            else:
719a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                print frame
720a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        print
721a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
722a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Claytondef CreateSymbolicateCrashLogOptions(command_name, description, add_interactive_options):
723a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton    usage = "usage: %prog [options] <FILE> [FILE ...]"
724a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    option_parser = optparse.OptionParser(description=description, prog='crashlog',usage=usage)
725007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--verbose'       , '-v', action='store_true', dest='verbose', help='display verbose debug info', default=False)
726007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--debug'         , '-g', action='store_true', dest='debug', help='display verbose debug logging', default=False)
727007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--load-all'      , '-a', action='store_true', dest='load_all_images', help='load all executable images, not just the images found in the crashed stack frames', default=False)
728007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--images'        ,       action='store_true', dest='dump_image_list', help='show image list', default=False)
729007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--debug-delay'   ,       type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0)
730007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--crashed-only'  , '-c', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False)
731007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--disasm-depth'  , '-d', type='int', dest='disassemble_depth', help='set the depth in stack frames that should be disassembled (default is 1)', default=1)
732007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--disasm-all'    , '-D',  action='store_true', dest='disassemble_all_threads', help='enabled disassembly of frames on all threads (not just the crashed thread)', default=False)
733007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--disasm-before' , '-B', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4)
734007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--disasm-after'  , '-A', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4)
735007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--source-context', '-C', type='int', metavar='NLINES', dest='source_context', help='show NLINES source lines of source context (default = 4)', default=4)
736007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--source-frames' ,       type='int', metavar='NFRAMES', dest='source_frames', help='show source for NFRAMES (default = 4)', default=4)
737007f73a55393839e9c977a4ec8c06d5721eaba06Greg Clayton    option_parser.add_option('--source-all'    ,       action='store_true', dest='source_all', help='show source for all threads, not just the crashed thread', default=False)
738a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    if add_interactive_options:
739a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        option_parser.add_option('-i', '--interactive', action='store_true', help='parse all crash logs and enter interactive mode', default=False)
740a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    return option_parser
741a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton
742a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Claytondef SymbolicateCrashLogs(command_args):
743a3698c61d6c34bab696bf82e64062d1d0438d452Greg Clayton    description='''Symbolicate one or more darwin crash log files to provide source file and line information,
744a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytoninlined stack frames back to the concrete functions, and disassemble the location of the crash
745a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytonfor the first frame of the crashed thread.
746a3698c61d6c34bab696bf82e64062d1d0438d452Greg ClaytonIf this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter
747a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytonfor use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been
748a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytoncreated that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows
749a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytonyou to explore the program as if it were stopped at the locations described in the crash log and functions can
750a3698c61d6c34bab696bf82e64062d1d0438d452Greg Claytonbe disassembled and lookups can be performed using the addresses found in the crash log.'''
751a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    option_parser = CreateSymbolicateCrashLogOptions ('crashlog', description, True)
752223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton    try:
753a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton        (options, args) = option_parser.parse_args(command_args)
754223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton    except:
755223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        return
756223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton
7572b69a2bac83a9163903934eedd1a2264e8558f8bGreg Clayton    if options.debug:
758223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        print 'command_args = %s' % command_args
75901f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        print 'options', options
760223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton        print 'args', args
761223e808ff04ddefb64861f8d8b99e1c6651a5222Greg Clayton
76201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    if options.debug_delay > 0:
76301f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        print "Waiting %u seconds for debugger to attach..." % options.debug_delay
76401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton        time.sleep(options.debug_delay)
76501f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton    error = lldb.SBError()
7669d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton
767e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton    if args:
7689d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        if options.interactive:
7699d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            interactive_crashlogs(options, args)
7709d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton        else:
7719d01042727a9c52cbab76237c8d290f2e337067dGreg Clayton            for crash_log_file in args:
7721b62f5967aa04ebde2ae163f12c739a6740596e2Greg Clayton                crash_log = CrashLog(crash_log_file)
773a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton                SymbolicateCrashLog (crash_log, options)
77401f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Claytonif __name__ == '__main__':
775e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton    # Create a new debugger instance
776e9ee550693aee8122d3d48b633fcff64e6a2f641Greg Clayton    lldb.debugger = lldb.SBDebugger.Create()
777a3368ddf9c2cc0b29fb7d329b5f6dfe5a7443b1dGreg Clayton    SymbolicateCrashLogs (sys.argv[1:])
778a889aee9354d532a250a8c5b8661a6c6f191297fJohnny Chenelif getattr(lldb, 'debugger', None):
7796f2f0ab38426bdb5ed347f057ba57248667a40c3Greg Clayton    lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.Symbolicate crashlog')
780ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog')
781ca1500f43dc65a0234dd4e8fb129ea366d512cc5Greg Clayton    print '"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help'
78201f7c96144d4604d1d120fb27643f9a29d6d1ba7Greg Clayton
783