[Lldb-commits] [lldb] r255697 - First pass at LLDBRPC.framework

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Tue Dec 15 15:03:24 PST 2015


Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/crashlog.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/crashlog.py?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/crashlog.py (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/crashlog.py Tue Dec 15 17:03:22 2015
@@ -0,0 +1,829 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# Be sure to add the python path that points to the LLDB shared library.
+#
+# To use this in the embedded python interpreter using "lldb":
+#
+#   cd /path/containing/crashlog.py
+#   lldb
+#   (lldb) script import crashlog
+#   "crashlog" command installed, type "crashlog --help" for detailed help
+#   (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash
+#
+# The benefit of running the crashlog command inside lldb in the 
+# embedded python interpreter is when the command completes, there 
+# will be a target with all of the files loaded at the locations
+# described in the crash log. Only the files that have stack frames
+# in the backtrace will be loaded unless the "--load-all" option
+# has been specified. This allows users to explore the program in the
+# state it was in right at crash time. 
+#
+# On MacOSX csh, tcsh:
+#   ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash )
+#
+# On MacOSX sh, bash:
+#   PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash
+#----------------------------------------------------------------------
+
+import commands
+import cmd
+import datetime
+import glob
+import optparse
+import os
+import platform
+import plistlib
+import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args)
+import re
+import shlex
+import string
+import sys
+import time
+import uuid
+
+try: 
+    # Just try for LLDB in case PYTHONPATH is already correctly setup
+    import lldb
+except ImportError:
+    lldb_python_dirs = list()
+    # lldb is not in the PYTHONPATH, try some defaults for the current platform
+    platform_system = platform.system()
+    if platform_system == 'Darwin':
+        # On Darwin, try the currently selected Xcode directory
+        xcode_dir = commands.getoutput("xcode-select --print-path")
+        if xcode_dir:
+            lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python'))
+            lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
+        lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
+    success = False
+    for lldb_python_dir in lldb_python_dirs:
+        if os.path.exists(lldb_python_dir):
+            if not (sys.path.__contains__(lldb_python_dir)):
+                sys.path.append(lldb_python_dir)
+                try: 
+                    import lldb
+                except ImportError:
+                    pass
+                else:
+                    print 'imported lldb from: "%s"' % (lldb_python_dir)
+                    success = True
+                    break
+    if not success:
+        print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
+        sys.exit(1)
+
+from lldb.utils import symbolication
+
+PARSE_MODE_NORMAL = 0
+PARSE_MODE_THREAD = 1
+PARSE_MODE_IMAGES = 2
+PARSE_MODE_THREGS = 3
+PARSE_MODE_SYSTEM = 4
+
+class CrashLog(symbolication.Symbolicator):
+    """Class that does parses darwin crash logs"""
+    parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]');
+    thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
+    thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)')
+    app_backtrace_regex = re.compile('^Application Specific Backtrace ([0-9]+)([^:]*):(.*)')
+    frame_regex = re.compile('^([0-9]+)\s+([^ ]+)\s+(0x[0-9a-fA-F]+) +(.*)')
+    image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)');
+    image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)');
+    empty_line_regex = re.compile('^$')
+        
+    class Thread:
+        """Class that represents a thread in a darwin crash log"""
+        def __init__(self, index, app_specific_backtrace):
+            self.index = index
+            self.frames = list()
+            self.idents = list()
+            self.registers = dict()
+            self.reason = None
+            self.queue = None
+            self.app_specific_backtrace = app_specific_backtrace
+        
+        def dump(self, prefix):
+            if self.app_specific_backtrace:
+                print "%Application Specific Backtrace[%u] %s" % (prefix, self.index, self.reason)
+            else:
+                print "%sThread[%u] %s" % (prefix, self.index, self.reason)
+            if self.frames:
+                print "%s  Frames:" % (prefix)
+                for frame in self.frames:
+                    frame.dump(prefix + '    ')
+            if self.registers:
+                print "%s  Registers:" % (prefix)
+                for reg in self.registers.keys():
+                    print "%s    %-5s = %#16.16x" % (prefix, reg, self.registers[reg])
+        
+        def dump_symbolicated (self, crash_log, options):
+            this_thread_crashed = self.app_specific_backtrace
+            if not this_thread_crashed:
+                this_thread_crashed = self.did_crash()
+                if options.crashed_only and this_thread_crashed == False:
+                    return
+
+            print "%s" % self
+            #prev_frame_index = -1
+            display_frame_idx = -1
+            for frame_idx, frame in enumerate(self.frames):
+                disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth;
+                if frame_idx == 0:
+                    symbolicated_frame_addresses = crash_log.symbolicate (frame.pc & crash_log.addr_mask, options.verbose)
+                else:
+                    # Any frame above frame zero and we have to subtract one to get the previous line entry
+                    symbolicated_frame_addresses = crash_log.symbolicate ((frame.pc & crash_log.addr_mask) - 1, options.verbose)
+
+                if symbolicated_frame_addresses:
+                    symbolicated_frame_address_idx = 0
+                    for symbolicated_frame_address in symbolicated_frame_addresses:
+                        display_frame_idx += 1
+                        print '[%3u] %s' % (frame_idx, symbolicated_frame_address)
+                        if (options.source_all or self.did_crash()) and display_frame_idx < options.source_frames and options.source_context:
+                            source_context = options.source_context
+                            line_entry = symbolicated_frame_address.get_symbol_context().line_entry
+                            if line_entry.IsValid():
+                                strm = lldb.SBStream()
+                                if line_entry:
+                                    lldb.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers(line_entry.file, line_entry.line, source_context, source_context, "->", strm)
+                                source_text = strm.GetData()
+                                if source_text:
+                                    # Indent the source a bit
+                                    indent_str = '    '
+                                    join_str = '\n' + indent_str
+                                    print '%s%s' % (indent_str, join_str.join(source_text.split('\n')))
+                        if symbolicated_frame_address_idx == 0:
+                            if disassemble:
+                                instructions = symbolicated_frame_address.get_instructions()
+                                if instructions:
+                                    print
+                                    symbolication.disassemble_instructions (crash_log.get_target(), 
+                                                                            instructions, 
+                                                                            frame.pc, 
+                                                                            options.disassemble_before, 
+                                                                            options.disassemble_after, frame.index > 0)
+                                    print
+                        symbolicated_frame_address_idx += 1
+                else:
+                    print frame
+            
+        def add_ident(self, ident):
+            if not ident in self.idents:
+                self.idents.append(ident)
+            
+        def did_crash(self):
+            return self.reason != None
+        
+        def __str__(self):
+            if self.app_specific_backtrace:
+                s = "Application Specific Backtrace[%u]" % self.index
+            else:
+                s = "Thread[%u]" % self.index
+            if self.reason:
+                s += ' %s' % self.reason
+            return s
+        
+    
+    class Frame:
+        """Class that represents a stack frame in a thread in a darwin crash log"""
+        def __init__(self, index, pc, description):
+            self.pc = pc
+            self.description = description
+            self.index = index
+        
+        def __str__(self):
+            if self.description:
+                return "[%3u] 0x%16.16x %s" % (self.index, self.pc, self.description)
+            else:
+                return "[%3u] 0x%16.16x" % (self.index, self.pc)
+
+        def dump(self, prefix):
+            print "%s%s" % (prefix, str(self))
+    
+    class DarwinImage(symbolication.Image):
+        """Class that represents a binary images in a darwin crash log"""
+        dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID')
+        if not os.path.exists(dsymForUUIDBinary):
+            dsymForUUIDBinary = commands.getoutput('which dsymForUUID')
+            
+        dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
+        
+        def __init__(self, text_addr_lo, text_addr_hi, identifier, version, uuid, path):
+            symbolication.Image.__init__(self, path, uuid);
+            self.add_section (symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT"))
+            self.identifier = identifier
+            self.version = version
+        
+        def locate_module_and_debug_symbols(self):
+            # Don't load a module twice...
+            if self.resolved:
+                return True
+            # Mark this as resolved so we don't keep trying
+            self.resolved = True
+            uuid_str = self.get_normalized_uuid_string()
+            print 'Getting symbols for %s %s...' % (uuid_str, self.path),
+            if os.path.exists(self.dsymForUUIDBinary):
+                dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, uuid_str)
+                s = commands.getoutput(dsym_for_uuid_command)
+                if s:
+                    plist_root = plistlib.readPlistFromString (s)
+                    if plist_root:
+                        plist = plist_root[uuid_str]
+                        if plist:
+                            if 'DBGArchitecture' in plist:
+                                self.arch = plist['DBGArchitecture']
+                            if 'DBGDSYMPath' in plist:
+                                self.symfile = os.path.realpath(plist['DBGDSYMPath'])
+                            if 'DBGSymbolRichExecutable' in plist:
+                                self.path = os.path.expanduser (plist['DBGSymbolRichExecutable'])
+                                self.resolved_path = self.path
+            if not self.resolved_path and os.path.exists(self.path):
+                dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path)
+                self_uuid = self.get_uuid()
+                for line in dwarfdump_cmd_output.splitlines():
+                    match = self.dwarfdump_uuid_regex.search (line)
+                    if match:
+                        dwarf_uuid_str = match.group(1)
+                        dwarf_uuid = uuid.UUID(dwarf_uuid_str)
+                        if self_uuid == dwarf_uuid:
+                            self.resolved_path = self.path
+                            self.arch = match.group(2)
+                            break;
+                if not self.resolved_path:
+                    self.unavailable = True
+                    print "error\n    error: unable to locate '%s' with UUID %s" % (self.path, uuid_str)
+                    return False
+            if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)):
+                print 'ok'
+                # if self.resolved_path:
+                #     print '  exe = "%s"' % self.resolved_path 
+                # if self.symfile:
+                #     print ' dsym = "%s"' % self.symfile
+                return True
+            else:
+                self.unavailable = True
+            return False
+        
+    
+        
+    def __init__(self, path):
+        """CrashLog constructor that take a path to a darwin crash log file"""
+        symbolication.Symbolicator.__init__(self);
+        self.path = os.path.expanduser(path);
+        self.info_lines = list()
+        self.system_profile = list()
+        self.threads = list()
+        self.backtraces = list() # For application specific backtraces
+        self.idents = list() # A list of the required identifiers for doing all stack backtraces
+        self.crashed_thread_idx = -1
+        self.version = -1
+        self.error = None
+        self.target = None
+        # With possible initial component of ~ or ~user replaced by that user's home directory.
+        try:
+            f = open(self.path)
+        except IOError:
+            self.error = 'error: cannot open "%s"' % self.path
+            return
+
+        self.file_lines = f.read().splitlines()
+        parse_mode = PARSE_MODE_NORMAL
+        thread = None
+        app_specific_backtrace = False
+        for line in self.file_lines:
+            # print line
+            line_len = len(line)
+            if line_len == 0:
+                if thread:
+                    if parse_mode == PARSE_MODE_THREAD:
+                        if thread.index == self.crashed_thread_idx:
+                            thread.reason = ''
+                            if self.thread_exception:
+                                thread.reason += self.thread_exception
+                            if self.thread_exception_data:
+                                thread.reason += " (%s)" % self.thread_exception_data
+                        if app_specific_backtrace:
+                            self.backtraces.append(thread)
+                        else:
+                            self.threads.append(thread)
+                    thread = None
+                else:
+                    # only append an extra empty line if the previous line 
+                    # in the info_lines wasn't empty
+                    if len(self.info_lines) > 0 and len(self.info_lines[-1]):
+                        self.info_lines.append(line)
+                parse_mode = PARSE_MODE_NORMAL
+                # print 'PARSE_MODE_NORMAL'
+            elif parse_mode == PARSE_MODE_NORMAL:
+                if line.startswith ('Process:'):
+                    (self.process_name, pid_with_brackets) = line[8:].strip().split(' [')
+                    self.process_id = pid_with_brackets.strip('[]')
+                elif line.startswith ('Path:'):
+                    self.process_path = line[5:].strip()
+                elif line.startswith ('Identifier:'):
+                    self.process_identifier = line[11:].strip()
+                elif line.startswith ('Version:'):
+                    version_string = line[8:].strip()
+                    matched_pair = re.search("(.+)\((.+)\)", version_string)
+                    if matched_pair:
+                        self.process_version = matched_pair.group(1)
+                        self.process_compatability_version = matched_pair.group(2)
+                    else:
+                        self.process = version_string
+                        self.process_compatability_version = version_string
+                elif self.parent_process_regex.search(line):
+                    parent_process_match = self.parent_process_regex.search(line)
+                    self.parent_process_name = parent_process_match.group(1)
+                    self.parent_process_id = parent_process_match.group(2)
+                elif line.startswith ('Exception Type:'):
+                    self.thread_exception = line[15:].strip()
+                    continue
+                elif line.startswith ('Exception Codes:'):
+                    self.thread_exception_data = line[16:].strip()
+                    continue
+                elif line.startswith ('Crashed Thread:'):
+                    self.crashed_thread_idx = int(line[15:].strip().split()[0])
+                    continue
+                elif line.startswith ('Report Version:'):
+                    self.version = int(line[15:].strip())
+                    continue
+                elif line.startswith ('System Profile:'):
+                    parse_mode = PARSE_MODE_SYSTEM
+                    continue
+                elif (line.startswith ('Interval Since Last Report:') or
+                      line.startswith ('Crashes Since Last Report:') or
+                      line.startswith ('Per-App Interval Since Last Report:') or
+                      line.startswith ('Per-App Crashes Since Last Report:') or
+                      line.startswith ('Sleep/Wake UUID:') or
+                      line.startswith ('Anonymous UUID:')):
+                    # ignore these
+                    continue  
+                elif line.startswith ('Thread'):
+                    thread_state_match = self.thread_state_regex.search (line)
+                    if thread_state_match:
+                        app_specific_backtrace = False
+                        thread_state_match = self.thread_regex.search (line)
+                        thread_idx = int(thread_state_match.group(1))
+                        parse_mode = PARSE_MODE_THREGS
+                        thread = self.threads[thread_idx]
+                    else:
+                        thread_match = self.thread_regex.search (line)
+                        if thread_match:
+                            app_specific_backtrace = False
+                            parse_mode = PARSE_MODE_THREAD
+                            thread_idx = int(thread_match.group(1))
+                            thread = CrashLog.Thread(thread_idx, False)
+                    continue
+                elif line.startswith ('Binary Images:'):
+                    parse_mode = PARSE_MODE_IMAGES
+                    continue
+                elif line.startswith ('Application Specific Backtrace'):
+                    app_backtrace_match = self.app_backtrace_regex.search (line)
+                    if app_backtrace_match:
+                        parse_mode = PARSE_MODE_THREAD
+                        app_specific_backtrace = True
+                        idx = int(app_backtrace_match.group(1))
+                        thread = CrashLog.Thread(idx, True)
+                self.info_lines.append(line.strip())
+            elif parse_mode == PARSE_MODE_THREAD:
+                if line.startswith ('Thread'):
+                    continue
+                frame_match = self.frame_regex.search(line)
+                if frame_match:
+                    ident = frame_match.group(2)
+                    thread.add_ident(ident)
+                    if not ident in self.idents:
+                        self.idents.append(ident)
+                    thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4)))
+                else:
+                    print 'error: frame regex failed for line: "%s"' % line
+            elif parse_mode == PARSE_MODE_IMAGES:
+                image_match = self.image_regex_uuid.search (line)
+                if image_match:
+                    image = CrashLog.DarwinImage (int(image_match.group(1),0), 
+                                                  int(image_match.group(2),0), 
+                                                  image_match.group(3).strip(), 
+                                                  image_match.group(4).strip(), 
+                                                  uuid.UUID(image_match.group(5)), 
+                                                  image_match.group(6))
+                    self.images.append (image)
+                else:
+                    image_match = self.image_regex_no_uuid.search (line)
+                    if image_match:
+                        image = CrashLog.DarwinImage (int(image_match.group(1),0), 
+                                                      int(image_match.group(2),0), 
+                                                      image_match.group(3).strip(), 
+                                                      image_match.group(4).strip(), 
+                                                      None,
+                                                      image_match.group(5))
+                        self.images.append (image)
+                    else:
+                        print "error: image regex failed for: %s" % line
+
+            elif parse_mode == PARSE_MODE_THREGS:
+                stripped_line = line.strip()
+                # "r12: 0x00007fff6b5939c8  r13: 0x0000000007000006  r14: 0x0000000000002a03  r15: 0x0000000000000c00"
+                reg_values = re.findall ('([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line);
+                for reg_value in reg_values:
+                    #print 'reg_value = "%s"' % reg_value
+                    (reg, value) = reg_value.split(': ')
+                    #print 'reg = "%s"' % reg
+                    #print 'value = "%s"' % value
+                    thread.registers[reg.strip()] = int(value, 0)
+            elif parse_mode == PARSE_MODE_SYSTEM:
+                self.system_profile.append(line)
+        f.close()
+    
+    def dump(self):
+        print "Crash Log File: %s" % (self.path)
+        if self.backtraces:
+            print "\nApplication Specific Backtraces:"
+            for thread in self.backtraces:
+                thread.dump('  ')
+        print "\nThreads:"
+        for thread in self.threads:
+            thread.dump('  ')
+        print "\nImages:"
+        for image in self.images:
+            image.dump('  ')
+    
+    def find_image_with_identifier(self, identifier):
+        for image in self.images:
+            if image.identifier == identifier:
+                return image            
+        regex_text = '^.*\.%s$' % (identifier)
+        regex = re.compile(regex_text)
+        for image in self.images:
+            if regex.match(image.identifier):
+                return image
+        return None
+    
+    def create_target(self):
+        #print 'crashlog.create_target()...'
+        if self.target is None:
+            self.target = symbolication.Symbolicator.create_target(self)
+            if self.target:
+                return self.target
+            # We weren't able to open the main executable as, but we can still symbolicate
+            print 'crashlog.create_target()...2'
+            if self.idents:
+                for ident in self.idents:
+                    image = self.find_image_with_identifier (ident)
+                    if image:
+                        self.target = image.create_target ()
+                        if self.target:
+                            return self.target # success
+            print 'crashlog.create_target()...3'
+            for image in self.images:
+                self.target = image.create_target ()
+                if self.target:
+                    return self.target # success
+            print 'crashlog.create_target()...4'
+            print 'error: unable to locate any executables from the crash log'
+        return self.target
+    
+    def get_target(self):
+        return self.target
+
+def usage():
+    print "Usage: lldb-symbolicate.py [-n name] executable-image"
+    sys.exit(0)
+
+class Interactive(cmd.Cmd):
+    '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.'''
+    image_option_parser = None
+    
+    def __init__(self, crash_logs):
+        cmd.Cmd.__init__(self)
+        self.use_rawinput = False
+        self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.'
+        self.crash_logs = crash_logs
+        self.prompt = '% '
+
+    def default(self, line):
+        '''Catch all for unknown command, which will exit the interpreter.'''
+        print "uknown command: %s" % line
+        return True
+
+    def do_q(self, line):
+        '''Quit command'''
+        return True
+
+    def do_quit(self, line):
+        '''Quit command'''
+        return True
+
+    def do_symbolicate(self, line):
+        description='''Symbolicate one or more darwin crash log files by index to provide source file and line information,
+        inlined stack frames back to the concrete functions, and disassemble the location of the crash
+        for the first frame of the crashed thread.'''
+        option_parser = CreateSymbolicateCrashLogOptions ('symbolicate', description, False)
+        command_args = shlex.split(line)
+        try:
+            (options, args) = option_parser.parse_args(command_args)
+        except:
+            return
+
+        if args:
+            # We have arguments, they must valid be crash log file indexes
+            for idx_str in args:
+                idx = int(idx_str)
+                if idx < len(self.crash_logs):
+                    SymbolicateCrashLog (self.crash_logs[idx], options)
+                else:
+                    print 'error: crash log index %u is out of range' % (idx)
+        else:
+            # No arguments, symbolicate all crash logs using the options provided
+            for idx in range(len(self.crash_logs)):
+                SymbolicateCrashLog (self.crash_logs[idx], options)                
+    
+    def do_list(self, line=None):
+        '''Dump a list of all crash logs that are currently loaded.
+        
+        USAGE: list'''
+        print '%u crash logs are loaded:' % len(self.crash_logs)
+        for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
+            print '[%u] = %s' % (crash_log_idx, crash_log.path)
+
+    def do_image(self, line):
+        '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.'''
+        usage = "usage: %prog [options] <PATH> [PATH ...]"
+        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.'''
+        command_args = shlex.split(line)
+        if not self.image_option_parser:
+            self.image_option_parser = optparse.OptionParser(description=description, prog='image',usage=usage)
+            self.image_option_parser.add_option('-a', '--all', action='store_true', help='show all images', default=False)
+        try:
+            (options, args) = self.image_option_parser.parse_args(command_args)
+        except:
+            return
+        
+        if args:
+            for image_path in args:
+                fullpath_search = image_path[0] == '/'
+                for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
+                    matches_found = 0
+                    for (image_idx, image) in enumerate(crash_log.images):
+                        if fullpath_search:
+                            if image.get_resolved_path() == image_path:
+                                matches_found += 1
+                                print '[%u] ' % (crash_log_idx), image
+                        else:
+                            image_basename = image.get_resolved_path_basename()
+                            if image_basename == image_path:
+                                matches_found += 1
+                                print '[%u] ' % (crash_log_idx), image
+                    if matches_found == 0:
+                        for (image_idx, image) in enumerate(crash_log.images):
+                            resolved_image_path = image.get_resolved_path()
+                            if resolved_image_path and string.find(image.get_resolved_path(), image_path) >= 0:
+                                print '[%u] ' % (crash_log_idx), image
+        else:
+            for crash_log in self.crash_logs:
+                for (image_idx, image) in enumerate(crash_log.images):
+                    print '[%u] %s' % (image_idx, image)            
+        return False
+
+
+def interactive_crashlogs(options, args):
+    crash_log_files = list()
+    for arg in args:
+        for resolved_path in glob.glob(arg):
+            crash_log_files.append(resolved_path)
+    
+    crash_logs = list();
+    for crash_log_file in crash_log_files:
+        #print 'crash_log_file = "%s"' % crash_log_file
+        crash_log = CrashLog(crash_log_file)
+        if crash_log.error:
+            print crash_log.error
+            continue
+        if options.debug:
+            crash_log.dump()
+        if not crash_log.images:
+            print 'error: no images in crash log "%s"' % (crash_log)
+            continue
+        else:
+            crash_logs.append(crash_log)
+    
+    interpreter = Interactive(crash_logs)
+    # List all crash logs that were imported
+    interpreter.do_list()
+    interpreter.cmdloop()
+    
+
+def save_crashlog(debugger, command, result, dict):
+    usage = "usage: %prog [options] <output-path>"
+    description='''Export the state of current target into a crashlog file'''
+    parser = optparse.OptionParser(description=description, prog='save_crashlog',usage=usage)
+    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+    try:
+        (options, args) = parser.parse_args(shlex.split(command))
+    except:
+        result.PutCString ("error: invalid options");
+        return
+    if len(args) != 1:
+        result.PutCString ("error: invalid arguments, a single output file is the only valid argument")
+        return
+    out_file = open(args[0], 'w')
+    if not out_file:
+        result.PutCString ("error: failed to open file '%s' for writing...", args[0]);
+        return
+    target = debugger.GetSelectedTarget()
+    if target:
+        identifier = target.executable.basename
+        if lldb.process:
+            pid = lldb.process.id
+            if pid != lldb.LLDB_INVALID_PROCESS_ID:
+                out_file.write('Process:         %s [%u]\n' % (identifier, pid))
+        out_file.write('Path:            %s\n' % (target.executable.fullpath))
+        out_file.write('Identifier:      %s\n' % (identifier))
+        out_file.write('\nDate/Time:       %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
+        out_file.write('OS Version:      Mac OS X %s (%s)\n' % (platform.mac_ver()[0], commands.getoutput('sysctl -n kern.osversion')));
+        out_file.write('Report Version:  9\n')
+        for thread_idx in range(lldb.process.num_threads):
+            thread = lldb.process.thread[thread_idx]
+            out_file.write('\nThread %u:\n' % (thread_idx))
+            for (frame_idx, frame) in enumerate(thread.frames):
+                frame_pc = frame.pc
+                frame_offset = 0
+                if frame.function:
+                    block = frame.GetFrameBlock()
+                    block_range = block.range[frame.addr]
+                    if block_range:
+                        block_start_addr = block_range[0]
+                        frame_offset = frame_pc - block_start_addr.load_addr
+                    else:
+                        frame_offset = frame_pc - frame.function.addr.load_addr
+                elif frame.symbol:
+                    frame_offset = frame_pc - frame.symbol.addr.load_addr
+                out_file.write('%-3u %-32s 0x%16.16x %s' % (frame_idx, frame.module.file.basename, frame_pc, frame.name))
+                if frame_offset > 0: 
+                    out_file.write(' + %u' % (frame_offset))
+                line_entry = frame.line_entry
+                if line_entry:
+                    if options.verbose:
+                        # This will output the fullpath + line + column
+                        out_file.write(' %s' % (line_entry))
+                    else:
+                        out_file.write(' %s:%u' % (line_entry.file.basename, line_entry.line))
+                        column = line_entry.column
+                        if column: 
+                            out_file.write(':%u' % (column))
+                out_file.write('\n')
+                
+        out_file.write('\nBinary Images:\n')
+        for module in target.modules:
+            text_segment = module.section['__TEXT']
+            if text_segment:
+                text_segment_load_addr = text_segment.GetLoadAddress(target)
+                if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS:
+                    text_segment_end_load_addr = text_segment_load_addr + text_segment.size
+                    identifier = module.file.basename
+                    module_version = '???'
+                    module_version_array = module.GetVersion()
+                    if module_version_array:
+                        module_version = '.'.join(map(str,module_version_array))
+                    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))
+        out_file.close()
+    else:
+        result.PutCString ("error: invalid target");
+        
+    
+def Symbolicate(debugger, command, result, dict):
+    try:
+        SymbolicateCrashLogs (shlex.split(command))
+    except:
+        result.PutCString ("error: python exception %s" % sys.exc_info()[0])
+
+def SymbolicateCrashLog(crash_log, options):
+    if crash_log.error:
+        print crash_log.error
+        return
+    if options.debug:
+        crash_log.dump()
+    if not crash_log.images:
+        print 'error: no images in crash log'
+        return
+
+    if options.dump_image_list:
+        print "Binary Images:"
+        for image in crash_log.images:
+            if options.verbose:
+                print image.debug_dump()
+            else:
+                print image
+
+    target = crash_log.create_target ()
+    if not target:
+        return
+    exe_module = target.GetModuleAtIndex(0)
+    images_to_load = list()
+    loaded_images = list()
+    if options.load_all_images:
+        # --load-all option was specified, load everything up
+        for image in crash_log.images:
+            images_to_load.append(image)
+    else:
+        # Only load the images found in stack frames for the crashed threads
+        if options.crashed_only:
+            for thread in crash_log.threads:
+                if thread.did_crash():
+                    for ident in thread.idents:
+                        images = crash_log.find_images_with_identifier (ident)
+                        if images:
+                            for image in images:
+                                images_to_load.append(image)
+                        else:
+                            print 'error: can\'t find image for identifier "%s"' % ident
+        else:
+            for ident in crash_log.idents:
+                images = crash_log.find_images_with_identifier (ident)
+                if images:
+                    for image in images:
+                        images_to_load.append(image)
+                else:
+                    print 'error: can\'t find image for identifier "%s"' % ident
+
+    for image in images_to_load:
+        if not image in loaded_images:
+            err = image.add_module (target)
+            if err:
+                print err
+            else:
+                #print 'loaded %s' % image
+                loaded_images.append(image)
+
+    if crash_log.backtraces:
+        for thread in crash_log.backtraces:
+            thread.dump_symbolicated (crash_log, options)
+            print                
+
+    for thread in crash_log.threads:
+        thread.dump_symbolicated (crash_log, options)
+        print                
+
+        
+def CreateSymbolicateCrashLogOptions(command_name, description, add_interactive_options):
+    usage = "usage: %prog [options] <FILE> [FILE ...]"
+    option_parser = optparse.OptionParser(description=description, prog='crashlog',usage=usage)
+    option_parser.add_option('--verbose'       , '-v', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+    option_parser.add_option('--debug'         , '-g', action='store_true', dest='debug', help='display verbose debug logging', default=False)
+    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)
+    option_parser.add_option('--images'        ,       action='store_true', dest='dump_image_list', help='show image list', default=False)
+    option_parser.add_option('--debug-delay'   ,       type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0)
+    option_parser.add_option('--crashed-only'  , '-c', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False)
+    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)
+    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)
+    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)
+    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)
+    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)
+    option_parser.add_option('--source-frames' ,       type='int', metavar='NFRAMES', dest='source_frames', help='show source for NFRAMES (default = 4)', default=4)
+    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)
+    if add_interactive_options:
+        option_parser.add_option('-i', '--interactive', action='store_true', help='parse all crash logs and enter interactive mode', default=False)
+    return option_parser
+    
+def SymbolicateCrashLogs(command_args):
+    description='''Symbolicate one or more darwin crash log files to provide source file and line information,
+inlined stack frames back to the concrete functions, and disassemble the location of the crash
+for the first frame of the crashed thread.
+If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter
+for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been
+created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows
+you to explore the program as if it were stopped at the locations described in the crash log and functions can 
+be disassembled and lookups can be performed using the addresses found in the crash log.'''
+    option_parser = CreateSymbolicateCrashLogOptions ('crashlog', description, True)
+    try:
+        (options, args) = option_parser.parse_args(command_args)
+    except:
+        return
+        
+    if options.debug:
+        print 'command_args = %s' % command_args
+        print 'options', options
+        print 'args', args
+        
+    if options.debug_delay > 0:
+        print "Waiting %u seconds for debugger to attach..." % options.debug_delay
+        time.sleep(options.debug_delay)
+    error = lldb.SBError()
+        
+    if args:
+        if options.interactive:
+            interactive_crashlogs(options, args)
+        else:
+            for crash_log_file in args:
+                crash_log = CrashLog(crash_log_file)
+                SymbolicateCrashLog (crash_log, options)
+if __name__ == '__main__':
+    # Create a new debugger instance
+    lldb.debugger = lldb.SBDebugger.Create()
+    SymbolicateCrashLogs (sys.argv[1:])
+    lldb.SBDebugger.Destroy (lldb.debugger)
+elif getattr(lldb, 'debugger', None):
+    lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.Symbolicate crashlog')
+    lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog')
+    print '"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help'
+

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/crashlog.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap.py?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap.py (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap.py Tue Dec 15 17:03:22 2015
@@ -0,0 +1,1244 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# This module is designed to live inside the "lldb" python package
+# in the "lldb.macosx" package. To use this in the embedded python
+# interpreter using "lldb" just import it:
+#
+#   (lldb) script import lldb.macosx.heap
+#----------------------------------------------------------------------
+
+import lldb
+import commands
+import optparse
+import os
+import os.path
+import re
+import shlex
+import string
+import sys
+import tempfile
+import lldb.utils.symbolication
+
+g_libheap_dylib_dir = None
+g_libheap_dylib_dict = dict()
+
+def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
+    expr = '''
+typedef unsigned natural_t;
+typedef uintptr_t vm_size_t;
+typedef uintptr_t vm_address_t;
+typedef natural_t task_t;
+typedef int kern_return_t;
+#define KERN_SUCCESS 0
+typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
+''';
+    if options.search_vm_regions:
+        expr += '''
+typedef int vm_prot_t;
+typedef unsigned int vm_inherit_t;
+typedef unsigned long long	memory_object_offset_t;
+typedef unsigned int boolean_t;
+typedef int vm_behavior_t;
+typedef uint32_t vm32_object_id_t;
+typedef natural_t mach_msg_type_number_t;
+typedef uint64_t mach_vm_address_t;
+typedef uint64_t mach_vm_offset_t;
+typedef uint64_t mach_vm_size_t;
+typedef uint64_t vm_map_offset_t;
+typedef uint64_t vm_map_address_t;
+typedef uint64_t vm_map_size_t;
+#define	VM_PROT_NONE ((vm_prot_t) 0x00)
+#define VM_PROT_READ ((vm_prot_t) 0x01)
+#define VM_PROT_WRITE ((vm_prot_t) 0x02)
+#define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
+typedef struct vm_region_submap_short_info_data_64_t {
+    vm_prot_t protection;
+    vm_prot_t max_protection;
+    vm_inherit_t inheritance;
+    memory_object_offset_t offset;		// offset into object/map
+    unsigned int user_tag;	// user tag on map entry
+    unsigned int ref_count;	 // obj/map mappers, etc
+    unsigned short shadow_depth; 	// only for obj
+    unsigned char external_pager;  // only for obj
+    unsigned char share_mode;	// see enumeration
+    boolean_t is_submap;	// submap vs obj
+    vm_behavior_t behavior;	// access behavior hint
+    vm32_object_id_t object_id;	// obj/map name, not a handle
+    unsigned short user_wired_count; 
+} vm_region_submap_short_info_data_64_t;
+#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))''';
+        if user_init_code:
+            expr += user_init_code;
+        expr += '''
+task_t task = (task_t)mach_task_self();
+mach_vm_address_t vm_region_base_addr;
+mach_vm_size_t vm_region_size;
+natural_t vm_region_depth;
+vm_region_submap_short_info_data_64_t vm_region_info;
+kern_return_t err;
+for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
+{
+    mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+    err = (kern_return_t)mach_vm_region_recurse (task,
+                                                 &vm_region_base_addr,
+                                                 &vm_region_size,
+                                                 &vm_region_depth,
+                                                 &vm_region_info,
+                                                 &vm_region_info_size);
+    if (err)
+        break;
+    // Check all read + write regions. This will cover the thread stacks 
+    // and any regions of memory like __DATA segments, that might contain
+    // data we are looking for
+    if (vm_region_info.protection & VM_PROT_WRITE && 
+        vm_region_info.protection & VM_PROT_READ)
+    {
+        baton.callback (task, 
+                        &baton, 
+                        64, 
+                        vm_region_base_addr, 
+                        vm_region_size);
+    }
+}'''
+    else:
+        if options.search_stack:
+            expr += get_thread_stack_ranges_struct (process)
+        if options.search_segments:
+            expr += get_sections_ranges_struct (process)
+        if user_init_code:
+            expr += user_init_code
+        if options.search_heap:
+            expr += '''
+#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
+typedef struct vm_range_t {
+    vm_address_t	address;
+    vm_size_t		size;
+} vm_range_t;
+typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
+typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
+typedef struct malloc_introspection_t {
+    kern_return_t (*enumerator)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); /* enumerates all the malloc pointers in use */
+} malloc_introspection_t;
+typedef struct malloc_zone_t {
+    void *reserved1[12];
+    struct malloc_introspection_t	*introspect;
+} malloc_zone_t;
+memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
+    *local_memory = (void*) remote_address;
+    return KERN_SUCCESS;
+};
+vm_address_t *zones = 0;
+unsigned int num_zones = 0;task_t task = 0;
+kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
+if (KERN_SUCCESS == err)
+{
+    for (unsigned int i=0; i<num_zones; ++i)
+    {
+        const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
+        if (zone && zone->introspect)
+            zone->introspect->enumerator (task, 
+                                          &baton, 
+                                          MALLOC_PTR_IN_USE_RANGE_TYPE, 
+                                          (vm_address_t)zone, 
+                                          task_peek, 
+                                          [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
+                                          {
+                                              range_callback_t callback = ((callback_baton_t *)baton)->callback;
+                                              for (unsigned i=0; i<size; ++i)
+                                              {
+                                                  callback (task, baton, type, ranges[i].address, ranges[i].size);
+                                              }
+                                          });    
+    }
+}'''
+
+        if options.search_stack:
+            expr += '''
+#ifdef NUM_STACKS
+// Call the callback for the thread stack ranges
+for (uint32_t i=0; i<NUM_STACKS; ++i) {
+    range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
+    if (STACK_RED_ZONE_SIZE > 0) {
+        range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
+    }
+}
+#endif'''
+    
+        if options.search_segments:
+            expr += '''
+#ifdef NUM_SEGMENTS
+// Call the callback for all segments
+for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
+    range_callback(task, &baton, 32, segments[i].base, segments[i].size);
+#endif'''
+
+    if user_return_code:
+        expr += "\n%s" % (user_return_code,)
+    
+    return expr
+
+def get_member_types_for_offset(value_type, offset, member_list):
+    member = value_type.GetFieldAtIndex(0)
+    search_bases = False
+    if member:
+        if member.GetOffsetInBytes() <= offset:
+            for field_idx in range (value_type.GetNumberOfFields()):
+                member = value_type.GetFieldAtIndex(field_idx)
+                member_byte_offset = member.GetOffsetInBytes()
+                member_end_byte_offset = member_byte_offset + member.type.size
+                if member_byte_offset <= offset and offset < member_end_byte_offset:
+                    member_list.append(member)
+                    get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+                    return
+        else:
+            search_bases = True
+    else:
+        search_bases = True
+    if search_bases:
+        for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
+            member = value_type.GetDirectBaseClassAtIndex(field_idx)
+            member_byte_offset = member.GetOffsetInBytes()
+            member_end_byte_offset = member_byte_offset + member.type.size
+            if member_byte_offset <= offset and offset < member_end_byte_offset:
+                member_list.append(member)
+                get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+                return
+        for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
+            member = value_type.GetVirtualBaseClassAtIndex(field_idx)
+            member_byte_offset = member.GetOffsetInBytes()
+            member_end_byte_offset = member_byte_offset + member.type.size
+            if member_byte_offset <= offset and offset < member_end_byte_offset:
+                member_list.append(member)
+                get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+                return
+
+def append_regex_callback(option, opt, value, parser):
+    try:
+        ivar_regex = re.compile(value)
+        parser.values.ivar_regex_blacklist.append(ivar_regex)
+    except:
+        print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
+    
+def add_common_options(parser):
+    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+    parser.add_option('-t', '--type', action='store_true', dest='print_type', help='print the full value of the type for each matching malloc block', default=False)
+    parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
+    parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
+    parser.add_option('-r', '--range', action='store_true', dest='show_range', help='print the allocation address range instead of just the allocation base address', default=False)
+    parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
+    parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
+    parser.add_option('-I', '--omit-ivar-regex', type='string', action='callback', callback=append_regex_callback, dest='ivar_regex_blacklist', default=[], help='specify one or more regular expressions used to backlist any matches that are in ivars')
+    parser.add_option('-s', '--stack', action='store_true', dest='stack', help='gets the stack that allocated each malloc block if MallocStackLogging is enabled', default=False)
+    parser.add_option('-S', '--stack-history', action='store_true', dest='stack_history', help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled', default=False)
+    parser.add_option('-F', '--max-frames', type='int', dest='max_frames', help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)', default=128)
+    parser.add_option('-H', '--max-history', type='int', dest='max_history', help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)', default=16)
+    parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=32)
+    parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
+    parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True)
+    parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True)
+    parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True)
+    parser.add_option('-V', '--vm-regions', action='store_true', dest='search_vm_regions', help='Check all VM regions instead of searching the heap, stack and segments', default=False)
+
+def type_flags_to_string(type_flags):
+    if type_flags == 0:
+        type_str = 'free'
+    elif type_flags & 2:
+        type_str = 'malloc'
+    elif type_flags & 4:
+        type_str = 'free'
+    elif type_flags & 1:
+        type_str = 'generic'
+    elif type_flags & 8:
+        type_str = 'stack'
+    elif type_flags & 16:
+        type_str = 'stack (red zone)'
+    elif type_flags & 32:
+        type_str = 'segment'
+    elif type_flags & 64:
+        type_str = 'vm_region'
+    else:
+        type_str = hex(type_flags)
+    return type_str
+
+def find_variable_containing_address(verbose, frame, match_addr):
+    variables = frame.GetVariables(True,True,True,True)
+    matching_var = None
+    for var in variables:
+        var_addr = var.GetLoadAddress()
+        if var_addr != lldb.LLDB_INVALID_ADDRESS:
+            byte_size = var.GetType().GetByteSize()
+            if verbose:
+                print 'frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name)
+            if var_addr == match_addr:
+                if verbose:
+                    print 'match'
+                return var
+            else:
+                if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
+                    if verbose:
+                        print 'match'
+                    return var
+    return None
+    
+def find_frame_for_stack_address(process, addr):
+    closest_delta = sys.maxint
+    closest_frame = None
+    #print 'find_frame_for_stack_address(%#x)' % (addr)
+    for thread in process:
+        prev_sp = lldb.LLDB_INVALID_ADDRESS
+        for frame in thread:
+            cfa = frame.GetCFA()
+            #print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
+            if addr < cfa:
+                delta = cfa - addr
+                #print '%#x < %#x, delta = %i' % (addr, cfa, delta)
+                if delta < closest_delta:
+                    #print 'closest'
+                    closest_delta = delta
+                    closest_frame = frame
+                # else:
+                #     print 'delta >= closest_delta'
+    return closest_frame
+            
+def type_flags_to_description(process, type_flags, ptr_addr, ptr_size, offset, match_addr):
+    show_offset = False
+    if type_flags == 0 or type_flags & 4:
+        type_str = 'free(%#x)' % (ptr_addr,)
+    elif type_flags & 2 or type_flags & 1:
+        type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
+        show_offset = True
+    elif type_flags & 8:
+        type_str = 'stack'
+        frame = find_frame_for_stack_address(process, match_addr)
+        if frame:
+            type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
+        variables = frame.GetVariables(True,True,True,True)
+        matching_var = None
+        for var in variables:
+            var_addr = var.GetLoadAddress()
+            if var_addr != lldb.LLDB_INVALID_ADDRESS:
+                #print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr, match_addr)
+                if var_addr == match_addr:
+                    matching_var = var
+                    break
+                else:
+                    byte_size = var.GetType().GetByteSize()
+                    if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
+                        matching_var = var
+                        break
+        if matching_var:
+            type_str += ' in variable at %#x:\n    %s' % (matching_var.GetLoadAddress(), matching_var)
+    elif type_flags & 16:
+        type_str = 'stack (red zone)'
+    elif type_flags & 32:
+        sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
+        type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
+    elif type_flags & 64:
+        sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
+        type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
+    else:
+        type_str = '%#x' % (ptr_addr,)
+        show_offset = True
+    if show_offset and offset != 0:
+        type_str += ' + %-6u' % (offset,)
+    return type_str
+    
+def dump_stack_history_entry(options, result, stack_history_entry, idx):
+    address = int(stack_history_entry.address)
+    if address:
+        type_flags = int(stack_history_entry.type_flags)
+        symbolicator = lldb.utils.symbolication.Symbolicator()
+        symbolicator.target = lldb.debugger.GetSelectedTarget()
+        type_str = type_flags_to_string(type_flags)
+        result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
+        frame_idx = 0
+        idx = 0
+        pc = int(stack_history_entry.frames[idx])
+        while pc != 0:
+            if pc >= 0x1000:
+                frames = symbolicator.symbolicate(pc)
+                if frames:
+                    for frame in frames:
+                        result.AppendMessage('     [%u] %s' % (frame_idx, frame))
+                        frame_idx += 1
+                else:
+                    result.AppendMessage('     [%u] 0x%x' % (frame_idx, pc))
+                    frame_idx += 1
+                idx = idx + 1
+                pc = int(stack_history_entry.frames[idx])
+            else:
+                pc = 0
+        if idx >= options.max_frames:
+            result.AppendMessage('warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' % (options.max_frames))
+            
+        result.AppendMessage('')
+            
+def dump_stack_history_entries(options, result, addr, history):
+    # malloc_stack_entry *get_stack_history_for_address (const void * addr)
+    expr_prefix = '''
+typedef int kern_return_t;
+typedef struct $malloc_stack_entry {
+    uint64_t address;
+    uint64_t argument;
+    uint32_t type_flags;
+    uint32_t num_frames;
+    uint64_t frames[512];
+    kern_return_t err;
+} $malloc_stack_entry;
+'''
+    single_expr = '''
+#define MAX_FRAMES %u
+typedef unsigned task_t;
+$malloc_stack_entry stack;
+stack.address = 0x%x;
+stack.type_flags = 2;
+stack.num_frames = 0;
+stack.frames[0] = 0;
+uint32_t max_stack_frames = MAX_FRAMES;
+stack.err = (kern_return_t)__mach_stack_logging_get_frames (
+    (task_t)mach_task_self(), 
+    stack.address, 
+    &stack.frames[0], 
+    max_stack_frames, 
+    &stack.num_frames);
+if (stack.num_frames < MAX_FRAMES)
+    stack.frames[stack.num_frames] = 0;
+else
+    stack.frames[MAX_FRAMES-1] = 0;
+stack''' % (options.max_frames, addr);
+
+    history_expr = '''
+typedef int kern_return_t;
+typedef unsigned task_t;
+#define MAX_FRAMES %u
+#define MAX_HISTORY %u
+typedef struct mach_stack_logging_record_t {
+	uint32_t type_flags;
+	uint64_t stack_identifier;
+	uint64_t argument;
+	uint64_t address;
+} mach_stack_logging_record_t;
+typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
+typedef struct malloc_stack_entry {
+    uint64_t address;
+    uint64_t argument;
+    uint32_t type_flags;
+    uint32_t num_frames;
+    uint64_t frames[MAX_FRAMES];
+    kern_return_t frames_err;    
+} malloc_stack_entry;
+typedef struct $malloc_stack_history {
+    task_t task;
+    unsigned idx;
+    malloc_stack_entry entries[MAX_HISTORY];
+} $malloc_stack_history;
+$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
+uint32_t max_stack_frames = MAX_FRAMES;
+enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
+    $malloc_stack_history *info = ($malloc_stack_history *)baton;
+    if (info->idx < MAX_HISTORY) {
+        malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
+        stack_entry->address = stack_record.address;
+        stack_entry->type_flags = stack_record.type_flags;
+        stack_entry->argument = stack_record.argument;
+        stack_entry->num_frames = 0;
+        stack_entry->frames[0] = 0;
+        stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
+            info->task, 
+            stack_record.stack_identifier,
+            stack_entry->frames,
+            (uint32_t)MAX_FRAMES,
+            &stack_entry->num_frames);
+        // Terminate the frames with zero if there is room
+        if (stack_entry->num_frames < MAX_FRAMES)
+            stack_entry->frames[stack_entry->num_frames] = 0; 
+    }
+    ++info->idx;
+};
+(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
+info''' % (options.max_frames, options.max_history, addr);
+
+    frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
+    if history:
+        expr = history_expr
+    else:
+        expr = single_expr
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(True);
+    expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout
+    expr_options.SetTryAllThreads (True)
+    expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+    expr_options.SetPrefix(expr_prefix)
+    expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
+    if options.verbose:
+        print "expression:"
+        print expr
+        print "expression result:"
+        print expr_sbvalue
+    if expr_sbvalue.error.Success():
+        if history:
+            malloc_stack_history = lldb.value(expr_sbvalue)
+            num_stacks = int(malloc_stack_history.idx)
+            if num_stacks <= options.max_history:
+                i_max = num_stacks
+            else:
+                i_max = options.max_history
+            for i in range(i_max):
+                stack_history_entry = malloc_stack_history.entries[i]
+                dump_stack_history_entry(options, result, stack_history_entry, i)
+            if num_stacks > options.max_history:
+                result.AppendMessage('warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' % (options.max_history, num_stacks))
+        else:
+            stack_history_entry = lldb.value(expr_sbvalue)
+            dump_stack_history_entry(options, result, stack_history_entry, 0)
+            
+    else:
+        result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
+
+
+def display_match_results (process, result, options, arg_str_description, expr, print_no_matches, expr_prefix = None):
+    frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
+    if not frame:
+        result.AppendMessage('error: invalid frame')
+        return 0
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(True);
+    expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues);
+    expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout
+    expr_options.SetTryAllThreads (False)
+    expr_options.SetLanguage (lldb.eLanguageTypeObjC_plus_plus)
+    if expr_prefix:
+        expr_options.SetPrefix (expr_prefix)
+    expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
+    if options.verbose:
+        print "expression:"
+        print expr
+        print "expression result:"
+        print expr_sbvalue
+    if expr_sbvalue.error.Success():
+        match_value = lldb.value(expr_sbvalue)  
+        i = 0
+        match_idx = 0
+        while 1:
+            print_entry = True
+            match_entry = match_value[i]; i += 1
+            if i > options.max_matches:
+                result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
+                break
+            malloc_addr = match_entry.addr.sbvalue.unsigned
+            if malloc_addr == 0:
+                break
+            malloc_size = int(match_entry.size)
+            offset = int(match_entry.offset)
+            
+            if options.offset >= 0 and options.offset != offset:
+                print_entry = False
+            else:                    
+                match_addr = malloc_addr + offset
+                type_flags = int(match_entry.type)
+                #result.AppendMessage (hex(malloc_addr + offset))
+                if type_flags == 64:
+                    search_stack_old = options.search_stack
+                    search_segments_old = options.search_segments
+                    search_heap_old = options.search_heap
+                    search_vm_regions = options.search_vm_regions
+                    options.search_stack = True
+                    options.search_segments = True
+                    options.search_heap = True
+                    options.search_vm_regions = False
+                    if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]):
+                        print_entry = False
+                    options.search_stack = search_stack_old
+                    options.search_segments = search_segments_old
+                    options.search_heap = search_heap_old
+                    options.search_vm_regions = search_vm_regions
+                if print_entry:
+                    description = '%#16.16x: %s' % (match_addr, type_flags_to_description(process, type_flags, malloc_addr, malloc_size, offset, match_addr))
+                    if options.show_size:
+                        description += ' <%5u>' % (malloc_size)
+                    if options.show_range:
+                        description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
+                    derefed_dynamic_value = None
+                    dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
+                    if dynamic_value.type.name == 'void *':
+                        if options.type == 'pointer' and malloc_size == 4096:
+                            error = lldb.SBError()
+                            process = expr_sbvalue.GetProcess()
+                            target = expr_sbvalue.GetTarget()
+                            data = bytearray(process.ReadMemory(malloc_addr, 16, error))
+                            if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
+                                ptr_size = target.addr_size
+                                thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
+                                #   4 bytes  0xa1a1a1a1
+                                #  12 bytes  'AUTORELEASE!'
+                                # ptr bytes  autorelease insertion point
+                                # ptr bytes  pthread_t
+                                # ptr bytes  next colder page
+                                # ptr bytes  next hotter page
+                                #   4 bytes  this page's depth in the list
+                                #   4 bytes  high-water mark
+                                description += ' AUTORELEASE! for pthread_t %#x' % (thread)
+                        #     else:
+                        #         description += 'malloc(%u)' % (malloc_size)
+                        # else:
+                        #     description += 'malloc(%u)' % (malloc_size)
+                    else:
+                        derefed_dynamic_value = dynamic_value.deref
+                        if derefed_dynamic_value:                        
+                            derefed_dynamic_type = derefed_dynamic_value.type
+                            derefed_dynamic_type_size = derefed_dynamic_type.size
+                            derefed_dynamic_type_name = derefed_dynamic_type.name
+                            description += ' '
+                            description += derefed_dynamic_type_name
+                            if offset < derefed_dynamic_type_size:
+                                member_list = list();
+                                get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
+                                if member_list:
+                                    member_path = ''
+                                    for member in member_list:
+                                        member_name = member.name
+                                        if member_name: 
+                                            if member_path:
+                                                member_path += '.'
+                                            member_path += member_name
+                                    if member_path:
+                                        if options.ivar_regex_blacklist:
+                                            for ivar_regex in options.ivar_regex_blacklist:
+                                                if ivar_regex.match(member_path):
+                                                    print_entry = False
+                                        description += '.%s' % (member_path)
+                            else:
+                                description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
+                        else:
+                            # strip the "*" from the end of the name since we were unable to dereference this
+                            description += dynamic_value.type.name[0:-1]
+            if print_entry:
+                match_idx += 1
+                result_output = ''
+                if description:
+                    result_output += description
+                    if options.print_type and derefed_dynamic_value:
+                        result_output += ' %s' % (derefed_dynamic_value)
+                    if options.print_object_description and dynamic_value:
+                        desc = dynamic_value.GetObjectDescription()
+                        if desc:
+                            result_output += '\n%s' % (desc)
+                if result_output:
+                    result.AppendMessage(result_output)
+                if options.memory:
+                    cmd_result = lldb.SBCommandReturnObject()
+                    if options.format == None:
+                        memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
+                    else:
+                        memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
+                    if options.verbose:
+                        result.AppendMessage(memory_command)
+                    lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
+                    result.AppendMessage(cmd_result.GetOutput())
+                if options.stack_history:
+                    dump_stack_history_entries(options, result, malloc_addr, 1)
+                elif options.stack:
+                    dump_stack_history_entries(options, result, malloc_addr, 0)
+        return i
+    else:
+        result.AppendMessage(str(expr_sbvalue.error))
+    return 0
+    
+def get_ptr_refs_options ():
+    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+    description='''Searches all allocations on the heap for pointer values on 
+darwin user space programs. Any matches that were found will dump the malloc 
+blocks that contain the pointers and might be able to print what kind of 
+objects the pointers are contained in using dynamic type information in the
+program.'''
+    parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
+    add_common_options(parser)
+    return parser
+    
+def find_variable(debugger, command, result, dict):
+    usage = "usage: %prog [options] <ADDR> [ADDR ...]"
+    description='''Searches for a local variable in all frames that contains a hex ADDR.'''
+    command_args = shlex.split(command)
+    parser = optparse.OptionParser(description=description, prog='find_variable',usage=usage)
+    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+    
+    process = debugger.GetSelectedTarget().GetProcess()
+    if not process:
+        result.AppendMessage('error: invalid process')
+        return
+    
+    for arg in args:
+        var_addr = int(arg, 16)
+        print >>result, "Finding a variable with address %#x..." % (var_addr)
+        done = False
+        for thread in process:
+            for frame in thread:
+                var = find_variable_containing_address(options.verbose, frame, var_addr)
+                if var:
+                    print var
+                    done = True
+                    break
+            if done:
+                break
+                
+def ptr_refs(debugger, command, result, dict):
+    command_args = shlex.split(command)
+    parser = get_ptr_refs_options()
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+
+    process = debugger.GetSelectedTarget().GetProcess()
+    if not process:
+        result.AppendMessage('error: invalid process')
+        return
+    frame = process.GetSelectedThread().GetSelectedFrame()
+    if not frame:
+        result.AppendMessage('error: invalid frame')
+        return
+
+    options.type = 'pointer'
+    if options.format == None: 
+        options.format = "A" # 'A' is "address" format
+
+    if args:
+        # When we initialize the expression, we must define any types that
+        # we will need when looking at every allocation. We must also define
+        # a type named callback_baton_t and make an instance named "baton" 
+        # and initialize it how ever we want to. The address of "baton" will
+        # be passed into our range callback. callback_baton_t must contain
+        # a member named "callback" whose type is "range_callback_t". This
+        # will be used by our zone callbacks to call the range callback for
+        # each malloc range.
+        expr_prefix = '''
+struct $malloc_match {
+    void *addr;
+    uintptr_t size;
+    uintptr_t offset;
+    uintptr_t type;
+};
+'''        
+        user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef struct callback_baton_t {
+    range_callback_t callback;
+    unsigned num_matches;
+    $malloc_match matches[MAX_MATCHES];
+    void *ptr;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+    callback_baton_t *info = (callback_baton_t *)baton;
+    typedef void* T;
+    const unsigned size = sizeof(T);
+    T *array = (T*)ptr_addr;
+    for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {  
+        if (array[idx] == info->ptr) {
+            if (info->num_matches < MAX_MATCHES) {
+                info->matches[info->num_matches].addr = (void*)ptr_addr;
+                info->matches[info->num_matches].size = ptr_size;
+                info->matches[info->num_matches].offset = idx*sizeof(T);
+                info->matches[info->num_matches].type = type;
+                ++info->num_matches;
+            }
+        }
+    }
+};
+callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
+'''
+        # We must also define a snippet of code to be run that returns
+        # the result of the expression we run.
+        # Here we return NULL if our pointer was not found in any malloc blocks,
+        # and we return the address of the matches array so we can then access
+        # the matching results
+        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+baton.matches'''
+        # Iterate through all of our pointer expressions and display the results
+        for ptr_expr in args:
+            user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
+            expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)          
+            arg_str_description = 'malloc block containing pointer %s' % ptr_expr
+            display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+    else:
+        result.AppendMessage('error: no pointer arguments were given')
+
+def get_cstr_refs_options():
+    usage = "usage: %prog [options] <CSTR> [CSTR ...]"
+    description='''Searches all allocations on the heap for C string values on
+darwin user space programs. Any matches that were found will dump the malloc
+blocks that contain the C strings and might be able to print what kind of
+objects the pointers are contained in using dynamic type information in the
+program.'''
+    parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
+    add_common_options(parser)
+    return parser
+
+def cstr_refs(debugger, command, result, dict):
+    command_args = shlex.split(command)
+    parser = get_cstr_refs_options();
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+
+    process = debugger.GetSelectedTarget().GetProcess()
+    if not process:
+        result.AppendMessage('error: invalid process')
+        return
+    frame = process.GetSelectedThread().GetSelectedFrame()
+    if not frame:
+        result.AppendMessage('error: invalid frame')
+        return
+
+
+    options.type = 'cstr'
+    if options.format == None: 
+        options.format = "Y" # 'Y' is "bytes with ASCII" format
+
+    if args:
+        # When we initialize the expression, we must define any types that
+        # we will need when looking at every allocation. We must also define
+        # a type named callback_baton_t and make an instance named "baton" 
+        # and initialize it how ever we want to. The address of "baton" will
+        # be passed into our range callback. callback_baton_t must contain
+        # a member named "callback" whose type is "range_callback_t". This
+        # will be used by our zone callbacks to call the range callback for
+        # each malloc range.
+        expr_prefix = '''
+struct $malloc_match {
+    void *addr;
+    uintptr_t size;
+    uintptr_t offset;
+    uintptr_t type;
+};
+'''        
+        user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef struct callback_baton_t {
+    range_callback_t callback;
+    unsigned num_matches;
+    $malloc_match matches[MAX_MATCHES];
+    const char *cstr;
+    unsigned cstr_len;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+    callback_baton_t *info = (callback_baton_t *)baton;
+    if (info->cstr_len < ptr_size) {
+        const char *begin = (const char *)ptr_addr;
+        const char *end = begin + ptr_size - info->cstr_len;
+        for (const char *s = begin; s < end; ++s) {
+            if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
+                if (info->num_matches < MAX_MATCHES) {
+                    info->matches[info->num_matches].addr = (void*)ptr_addr;
+                    info->matches[info->num_matches].size = ptr_size;
+                    info->matches[info->num_matches].offset = s - begin;
+                    info->matches[info->num_matches].type = type;
+                    ++info->num_matches;
+                }
+            }
+        }
+    }
+};
+const char *cstr = "%s";
+callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
+        # We must also define a snippet of code to be run that returns
+        # the result of the expression we run.
+        # Here we return NULL if our pointer was not found in any malloc blocks,
+        # and we return the address of the matches array so we can then access
+        # the matching results
+        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+baton.matches'''
+        # Iterate through all of our pointer expressions and display the results
+        for cstr in args:
+            user_init_code = user_init_code_format % (options.max_matches, cstr)
+            expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)          
+            arg_str_description = 'malloc block containing "%s"' % cstr            
+            display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+    else:
+        result.AppendMessage('error: command takes one or more C string arguments')
+
+
+def get_malloc_info_options():
+    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+    description='''Searches the heap a malloc block that contains the addresses
+specified as one or more address expressions. Any matches that were found will
+dump the malloc blocks that match or contain the specified address. The matching
+blocks might be able to show what kind of objects they are using dynamic type
+information in the program.'''
+    parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
+    add_common_options(parser)
+    return parser
+
+def malloc_info(debugger, command, result, dict):
+    command_args = shlex.split(command)
+    parser = get_malloc_info_options()
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+    malloc_info_impl (debugger, result, options, args)
+
+def malloc_info_impl (debugger, result, options, args):
+    # We are specifically looking for something on the heap only
+    options.type = 'malloc_info'
+
+    process = debugger.GetSelectedTarget().GetProcess()
+    if not process:
+        result.AppendMessage('error: invalid process')
+        return
+    frame = process.GetSelectedThread().GetSelectedFrame()
+    if not frame:
+        result.AppendMessage('error: invalid frame')
+        return
+    expr_prefix = '''
+struct $malloc_match {
+    void *addr;
+    uintptr_t size;
+    uintptr_t offset;
+    uintptr_t type;
+};
+'''        
+    
+    user_init_code_format = '''
+typedef struct callback_baton_t {
+    range_callback_t callback;
+    unsigned num_matches;
+    $malloc_match matches[2]; // Two items so they can be NULL terminated
+    void *ptr;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+    callback_baton_t *info = (callback_baton_t *)baton;
+    if (info->num_matches == 0) {
+        uint8_t *p = (uint8_t *)info->ptr;
+        uint8_t *lo = (uint8_t *)ptr_addr;
+        uint8_t *hi = lo + ptr_size;
+        if (lo <= p && p < hi) {
+            info->matches[info->num_matches].addr = (void*)ptr_addr;
+            info->matches[info->num_matches].size = ptr_size;
+            info->matches[info->num_matches].offset = p - lo;
+            info->matches[info->num_matches].type = type;
+            info->num_matches = 1;
+        }
+    }
+};
+callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
+baton.matches[0].addr = 0;
+baton.matches[1].addr = 0;'''
+    if args:
+        total_matches = 0
+        for ptr_expr in args:
+            user_init_code = user_init_code_format % (ptr_expr)
+            expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches')          
+            arg_str_description = 'malloc block that contains %s' % ptr_expr
+            total_matches += display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+        return total_matches
+    else:
+        result.AppendMessage('error: command takes one or more pointer expressions')
+        return 0
+
+def get_thread_stack_ranges_struct (process):
+    '''Create code that defines a structure that represents threads stack bounds
+    for all  threads. It returns a static sized array initialized with all of
+    the tid, base, size structs for all the threads.'''
+    stack_dicts = list()
+    if process:
+        i = 0;
+        for thread in process:
+            min_sp = thread.frame[0].sp
+            max_sp = min_sp
+            for frame in thread.frames:
+                sp = frame.sp
+                if sp < min_sp: min_sp = sp
+                if sp > max_sp: max_sp = sp
+            if min_sp < max_sp:
+                stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp  , 'size' : max_sp-min_sp, 'index' : i })
+                i += 1
+    stack_dicts_len = len(stack_dicts)
+    if stack_dicts_len > 0:
+        result = '''
+#define NUM_STACKS %u
+#define STACK_RED_ZONE_SIZE %u
+typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
+thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
+        for stack_dict in stack_dicts:
+            result += '''
+stacks[%(index)u].tid  = 0x%(tid)x;
+stacks[%(index)u].base = 0x%(base)x;
+stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
+        return result
+    else:
+        return ''
+
+def get_sections_ranges_struct (process):
+    '''Create code that defines a structure that represents all segments that
+    can contain data for all images in "target". It returns a static sized 
+    array initialized with all of base, size structs for all the threads.'''
+    target = process.target
+    segment_dicts = list()
+    for (module_idx, module) in enumerate(target.modules):
+        for sect_idx in range(module.GetNumSections()):
+            section = module.GetSectionAtIndex(sect_idx)
+            if not section:
+                break
+            name = section.name
+            if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
+                base = section.GetLoadAddress(target)
+                size = section.GetByteSize()
+                if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
+                    segment_dicts.append ({ 'base' : base, 'size' : size })
+    segment_dicts_len = len(segment_dicts)
+    if segment_dicts_len > 0:
+        result = '''
+#define NUM_SEGMENTS %u
+typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
+segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
+        for (idx, segment_dict) in enumerate(segment_dicts):
+            segment_dict['index'] = idx
+            result += '''
+segments[%(index)u].base = 0x%(base)x;
+segments[%(index)u].size = 0x%(size)x;''' % segment_dict
+        return result
+    else:
+        return ''
+
+def section_ptr_refs(debugger, command, result, dict):
+    command_args = shlex.split(command)
+    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+    description='''Searches section contents for pointer values in darwin user space programs.'''
+    parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
+    add_common_options(parser)
+    parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+
+    options.type = 'pointer'
+
+    sections = list()
+    section_modules = list()
+    if not options.section_names:
+        result.AppendMessage('error: at least one section must be specified with the --section option')
+        return
+
+    target = debugger.GetSelectedTarget()
+    for module in target.modules:
+        for section_name in options.section_names:
+            section = module.section[section_name]
+            if section:
+                sections.append (section)
+                section_modules.append (module)
+    if sections:
+        dylid_load_err = load_dylib()
+        if dylid_load_err:
+            result.AppendMessage(dylid_load_err)
+            return
+        frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
+        for expr_str in args:
+            for (idx, section) in enumerate(sections):
+                expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
+                arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
+                num_matches = display_match_results (target.GetProcess(), result, options, arg_str_description, expr, False)
+                if num_matches:
+                    if num_matches < options.max_matches:
+                        options.max_matches = options.max_matches - num_matches
+                    else:
+                        options.max_matches = 0
+                if options.max_matches == 0:
+                    return
+    else:
+        result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
+
+def get_objc_refs_options():
+    usage = "usage: %prog [options] <CLASS> [CLASS ...]"
+    description='''Searches all allocations on the heap for instances of 
+objective C classes, or any classes that inherit from the specified classes
+in darwin user space programs. Any matches that were found will dump the malloc
+blocks that contain the C strings and might be able to print what kind of
+objects the pointers are contained in using dynamic type information in the
+program.'''
+    parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
+    add_common_options(parser)
+    return parser
+
+def objc_refs(debugger, command, result, dict):
+    command_args = shlex.split(command)
+    parser = get_objc_refs_options()
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+
+    process = debugger.GetSelectedTarget().GetProcess()
+    if not process:
+        result.AppendMessage('error: invalid process')
+        return
+    frame = process.GetSelectedThread().GetSelectedFrame()
+    if not frame:
+        result.AppendMessage('error: invalid frame')
+        return
+
+    options.type = 'isa'
+    if options.format == None: 
+        options.format = "A" # 'A' is "address" format
+
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(True);
+    expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout
+    expr_options.SetTryAllThreads (True)
+    expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+    num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options)
+    if not num_objc_classes_value.error.Success():
+        result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
+        return
+    
+    num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
+    if num_objc_classes == 0:
+        result.AppendMessage('error: no objective C classes in program')
+        return
+        
+    if args:
+        # When we initialize the expression, we must define any types that
+        # we will need when looking at every allocation. We must also define
+        # a type named callback_baton_t and make an instance named "baton" 
+        # and initialize it how ever we want to. The address of "baton" will
+        # be passed into our range callback. callback_baton_t must contain
+        # a member named "callback" whose type is "range_callback_t". This
+        # will be used by our zone callbacks to call the range callback for
+        # each malloc range.
+        expr_prefix = '''
+struct $malloc_match {
+    void *addr;
+    uintptr_t size;
+    uintptr_t offset;
+    uintptr_t type;
+};
+'''        
+
+        user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef int (*compare_callback_t)(const void *a, const void *b);
+typedef struct callback_baton_t {
+    range_callback_t callback;
+    compare_callback_t compare_callback;
+    unsigned num_matches;
+    $malloc_match matches[MAX_MATCHES];
+    void *isa;
+    Class classes[%u];
+} callback_baton_t;
+compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
+     Class a_ptr = *(Class *)a;
+     Class b_ptr = *(Class *)b;
+     if (a_ptr < b_ptr) return -1;
+     if (a_ptr > b_ptr) return +1;
+     return 0;
+};
+typedef Class (*class_getSuperclass_type)(void *isa);
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+    class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
+    callback_baton_t *info = (callback_baton_t *)baton;
+    if (sizeof(Class) <= ptr_size) {
+        Class *curr_class_ptr = (Class *)ptr_addr;
+        Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr, 
+                                                      (const void *)info->classes, 
+                                                      sizeof(info->classes)/sizeof(Class), 
+                                                      sizeof(Class), 
+                                                      info->compare_callback);
+        if (matching_class_ptr) {
+            bool match = false;
+            if (info->isa) {
+                Class isa = *curr_class_ptr;
+                if (info->isa == isa)
+                    match = true;
+                else { // if (info->objc.match_superclasses) {
+                    Class super = class_getSuperclass_impl(isa);
+                    while (super) {
+                        if (super == info->isa) {
+                            match = true;
+                            break;
+                        }
+                        super = class_getSuperclass_impl(super);
+                    }
+                }
+            }
+            else
+                match = true;
+            if (match) {
+                if (info->num_matches < MAX_MATCHES) {
+                    info->matches[info->num_matches].addr = (void*)ptr_addr;
+                    info->matches[info->num_matches].size = ptr_size;
+                    info->matches[info->num_matches].offset = 0;
+                    info->matches[info->num_matches].type = type;
+                    ++info->num_matches;
+                }
+            }
+        }
+    }
+};
+callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
+int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
+(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
+        # We must also define a snippet of code to be run that returns
+        # the result of the expression we run.
+        # Here we return NULL if our pointer was not found in any malloc blocks,
+        # and we return the address of the matches array so we can then access
+        # the matching results
+        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+        baton.matches'''
+        # Iterate through all of our ObjC class name arguments
+        for class_name in args:
+            addr_expr_str = "(void *)[%s class]" % class_name
+            expr_options = lldb.SBExpressionOptions()
+            expr_options.SetIgnoreBreakpoints(True);
+            expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout
+            expr_options.SetTryAllThreads (True)
+            expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+            expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options)
+            if expr_sbvalue.error.Success():
+                isa = expr_sbvalue.unsigned
+                if isa:
+                    options.type = 'isa'
+                    result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
+                    user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
+                    expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)          
+                    arg_str_description = 'objective C classes with isa 0x%x' % isa
+                    display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+                else:
+                    result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
+            else:
+                result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
+    else:
+        result.AppendMessage('error: command takes one or more C string arguments');
+
+if __name__ == '__main__':
+    lldb.debugger = lldb.SBDebugger.Create()
+
+# Make the options so we can generate the help text for the new LLDB 
+# command line command prior to registering it with LLDB below. This way
+# if clients in LLDB type "help malloc_info", they will see the exact same
+# output as typing "malloc_info --help".
+ptr_refs.__doc__ = get_ptr_refs_options().format_help()
+cstr_refs.__doc__ = get_cstr_refs_options().format_help()
+malloc_info.__doc__ = get_malloc_info_options().format_help()
+objc_refs.__doc__ = get_objc_refs_options().format_help()
+lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.find_variable find_variable' % __name__)
+# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
+# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
+# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
+lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
+print '"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
+
+
+
+

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/Makefile?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/Makefile (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/Makefile Tue Dec 15 17:03:22 2015
@@ -0,0 +1,33 @@
+#----------------------------------------------------------------------
+# Fill in the source files to build
+#----------------------------------------------------------------------
+# Uncomment line below for debugging shell commands
+# SHELL = /bin/sh -x
+
+#----------------------------------------------------------------------
+# Change any build/tool options needed
+#----------------------------------------------------------------------
+ARCH ?= x86_64
+CFLAGS ?=-arch $(ARCH) -gdwarf-2 -O0
+CXX ?= $(shell xcrun -find clang++)
+EXE ?= libheap.dylib
+DSYM ?= $(EXE).dSYM
+
+#----------------------------------------------------------------------
+# Compile the executable from all the objects (default rule) with no
+# dsym file.
+#----------------------------------------------------------------------
+$(EXE) : heap_find.cpp
+	$(CXX) $(CFLAGS) -install_name "@executable_path/libheap.dylib" -dynamiclib -lobjc heap_find.cpp -o "$(EXE)"
+
+#----------------------------------------------------------------------
+# Include all of the makefiles for each source file so we don't have
+# to manually track all of the prerequisites for each source file.
+#----------------------------------------------------------------------
+.PHONY: clean
+all:	$(EXE)
+clean:
+	rm -rf "$(EXE)" "$(DSYM)"
+
+
+

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/heap_find.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/heap_find.cpp?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/heap_find.cpp (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap/heap_find.cpp Tue Dec 15 17:03:22 2015
@@ -0,0 +1,1071 @@
+//===-- heap_find.c ---------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file compiles into a dylib and can be used on darwin to find data that
+// is contained in active malloc blocks. To use this make the project, then
+// load the shared library in a debug session while you are stopped:
+//
+// (lldb) process load /path/to/libheap.dylib
+//
+// Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap" 
+// functions in the expression parser.
+//
+// This will grep everything in all active allocation blocks and print and 
+// malloc blocks that contain the pointer 0x112233000000:
+//
+// (lldb) expression find_pointer_in_heap (0x112233000000)
+//
+// This will grep everything in all active allocation blocks and print and 
+// malloc blocks that contain the C string "hello" (as a substring, no
+// NULL termination included):
+//
+// (lldb) expression find_cstring_in_heap ("hello")
+//
+// The results will be printed to the STDOUT of the inferior program. The 
+// return value of the "find_pointer_in_heap" function is the number of 
+// pointer references that were found. A quick example shows
+//
+// (lldb) expr find_pointer_in_heap(0x0000000104000410)
+// (uint32_t) $5 = 0x00000002
+// 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 (malloc_size = 48)
+// 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 (malloc_size = 4096)
+//
+// From the above output we see that 0x104000410 was found in the malloc block
+// at 0x104000730 and 0x100820000. If we want to see what these blocks are, we
+// can display the memory for this block using the "address" ("A" for short) 
+// format. The address format shows pointers, and if those pointers point to
+// objects that have symbols or know data contents, it will display information
+// about the pointers:
+//
+// (lldb) memory read --format address --count 1 0x104000730 
+// 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString
+// 
+// We can see that the first block is a "MyString" object that contains our
+// pointer value at offset 16.
+//
+// Looking at the next pointers, are a bit more tricky:
+// (lldb) memory read -fA 0x100820000 -c1
+// 0x100820000: 0x4f545541a1a1a1a1
+// (lldb) memory read 0x100820000
+// 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21  ....AUTORELEASE!
+// 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00  x.......`..u....
+// 
+// This is an objective C auto release pool object that contains our pointer.
+// C++ classes will show up if they are virtual as something like:
+// (lldb) memory read --format address --count 1 0x104008000
+// 0x104008000: 0x109008000 vtable for lldb_private::Process
+//
+// This is a clue that the 0x104008000 is a "lldb_private::Process *".
+//===----------------------------------------------------------------------===//
+// C includes
+#include <assert.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <malloc/malloc.h>
+#include <objc/objc-runtime.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// C++ includes
+#include <vector>
+
+//----------------------------------------------------------------------
+// Redefine private types from "/usr/local/include/stack_logging.h"
+//----------------------------------------------------------------------
+typedef struct {
+	uint32_t		type_flags;
+	uint64_t		stack_identifier;
+	uint64_t		argument;
+	mach_vm_address_t	address;
+} mach_stack_logging_record_t;
+
+//----------------------------------------------------------------------
+// Redefine private defines from "/usr/local/include/stack_logging.h"
+//----------------------------------------------------------------------
+#define stack_logging_type_free		0
+#define stack_logging_type_generic	1
+#define stack_logging_type_alloc	2
+#define stack_logging_type_dealloc	4
+// This bit is made up by this code
+#define stack_logging_type_vm_region 8 
+
+//----------------------------------------------------------------------
+// Redefine private function prototypes from 
+// "/usr/local/include/stack_logging.h"
+//----------------------------------------------------------------------
+extern "C" kern_return_t 
+__mach_stack_logging_set_file_path (
+    task_t task, 
+    char* file_path
+);
+
+extern "C" kern_return_t 
+__mach_stack_logging_get_frames (
+    task_t task, 
+    mach_vm_address_t address, 
+    mach_vm_address_t *stack_frames_buffer, 
+    uint32_t max_stack_frames, 
+    uint32_t *count
+);
+
+extern "C" kern_return_t
+__mach_stack_logging_enumerate_records (
+    task_t task,
+    mach_vm_address_t address, 
+    void enumerator(mach_stack_logging_record_t, void *), 
+    void *context
+);
+
+extern "C" kern_return_t
+__mach_stack_logging_frames_for_uniqued_stack (
+    task_t task, 
+    uint64_t stack_identifier, 
+    mach_vm_address_t *stack_frames_buffer, 
+    uint32_t max_stack_frames, 
+    uint32_t *count
+);
+
+extern "C" void *gdb_class_getClass (void *objc_class);
+
+static void
+range_info_callback (task_t task, 
+                     void *baton, 
+                     unsigned type, 
+                     uint64_t ptr_addr, 
+                     uint64_t ptr_size);
+
+//----------------------------------------------------------------------
+// Redefine private global variables prototypes from
+// "/usr/local/include/stack_logging.h"
+//----------------------------------------------------------------------
+
+extern "C" int stack_logging_enable_logging;
+
+//----------------------------------------------------------------------
+// Local defines
+//----------------------------------------------------------------------
+#define MAX_FRAMES 1024
+
+//----------------------------------------------------------------------
+// Local Typedefs and Types
+//----------------------------------------------------------------------
+typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size);
+typedef void zone_callback_t (void *info, const malloc_zone_t *zone);
+typedef int (*comare_function_t)(const void *, const void *);
+struct range_callback_info_t
+{
+    zone_callback_t *zone_callback;
+    range_callback_t *range_callback;
+    void *baton;
+    int check_vm_regions;
+};
+
+enum data_type_t
+{
+    eDataTypeAddress,
+    eDataTypeContainsData,
+    eDataTypeObjC,
+    eDataTypeHeapInfo
+};
+
+struct aligned_data_t
+{
+    const uint8_t *buffer;
+    uint32_t size;
+    uint32_t align;
+};
+
+struct objc_data_t
+{
+    void *match_isa; // Set to NULL for all objective C objects
+    bool match_superclasses;
+};
+
+struct range_contains_data_callback_info_t
+{
+    data_type_t type;
+    const void *lookup_addr;
+    union
+    {
+        uintptr_t addr;
+        aligned_data_t data;
+        objc_data_t objc;
+    };
+    uint32_t match_count;
+    bool done;
+    bool unique;    
+};
+
+struct malloc_match
+{
+    void *addr;
+    intptr_t size;
+    intptr_t offset;
+    uintptr_t type;
+};
+
+struct malloc_stack_entry
+{
+    const void *address;
+    uint64_t argument;
+    uint32_t type_flags;
+    uint32_t num_frames;
+    mach_vm_address_t frames[MAX_FRAMES];
+};
+
+struct malloc_block_contents
+{
+    union {
+        Class isa;
+        void *pointers[2];
+    };
+};
+
+static int 
+compare_void_ptr (const void *a, const void *b)
+{
+    Class a_ptr = *(Class *)a;
+    Class b_ptr = *(Class *)b;
+    if (a_ptr < b_ptr) return -1;
+    if (a_ptr > b_ptr) return +1;
+    return 0;
+}
+
+class MatchResults
+{
+    enum { 
+        k_max_entries = 8 * 1024
+    };
+public:
+    MatchResults () :
+        m_size(0)
+    {
+    }
+    
+    void
+    clear()
+    {
+        m_size = 0;
+        bzero (&m_entries, sizeof(m_entries));
+    }
+    
+    bool
+    empty() const
+    {
+        return m_size == 0;
+    }
+
+    void
+    push_back (const malloc_match& m, bool unique = false)
+    {
+        if (unique)
+        {
+            // Don't add the entry if there is already a match for this address
+            for (uint32_t i=0; i<m_size; ++i)
+            {
+                if (((uint8_t *)m_entries[i].addr + m_entries[i].offset) == ((uint8_t *)m.addr + m.offset))
+                    return; // Duplicate entry
+            }
+        }
+        if (m_size < k_max_entries - 1)
+        {
+            m_entries[m_size] = m;
+            m_size++;
+        }
+    }
+
+    malloc_match *
+    data ()
+    {
+        // If empty, return NULL
+        if (empty())
+            return NULL;
+        // In not empty, terminate and return the result
+        malloc_match terminator_entry = { NULL, 0, 0, 0 };
+        // We always leave room for an empty entry at the end
+        m_entries[m_size] = terminator_entry;
+        return m_entries;
+    }
+
+protected:
+    malloc_match m_entries[k_max_entries];
+    uint32_t m_size;
+};
+
+class MallocStackLoggingEntries
+{
+    enum {  k_max_entries = 128 };
+public:
+    MallocStackLoggingEntries () :
+        m_size(0)
+    {
+    }
+
+    void
+    clear()
+    {
+        m_size = 0;
+    }
+
+    bool
+    empty() const
+    {
+        return m_size == 0;
+    }
+
+
+    malloc_stack_entry *
+    next ()
+    {
+        if (m_size < k_max_entries - 1)
+        {
+            malloc_stack_entry * result = m_entries + m_size;
+            ++m_size;
+            return result;
+        }
+        return NULL; // Out of entries...
+    }
+
+    malloc_stack_entry *
+    data ()
+    {
+        // If empty, return NULL
+        if (empty())
+            return NULL;
+        // In not empty, terminate and return the result
+        m_entries[m_size].address = NULL;
+        m_entries[m_size].argument = 0;
+        m_entries[m_size].type_flags = 0;
+        m_entries[m_size].num_frames = 0;
+        return m_entries;
+    }
+
+protected:  
+    malloc_stack_entry m_entries[k_max_entries];
+    uint32_t m_size;
+};
+
+//----------------------------------------------------------------------
+// A safe way to allocate memory and keep it from interfering with the
+// malloc enumerators.
+//----------------------------------------------------------------------
+void *
+safe_malloc(size_t n_bytes)
+{
+    if (n_bytes > 0)
+    {
+        const int k_page_size = getpagesize();
+        const mach_vm_size_t vm_size = ((n_bytes + k_page_size - 1)/k_page_size) * k_page_size;
+        vm_address_t address = 0;
+        kern_return_t kerr = vm_allocate (mach_task_self(), &address, vm_size, true);
+        if (kerr == KERN_SUCCESS)
+            return (void *)address;
+    }
+    return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// ObjCClasses
+//----------------------------------------------------------------------
+class ObjCClasses
+{
+public:
+    ObjCClasses() :
+        m_objc_class_ptrs (NULL),
+        m_size (0)
+    {
+    }
+
+    bool
+    Update()
+    {
+        // TODO: find out if class list has changed and update if needed
+        if (m_objc_class_ptrs == NULL)
+        {
+            m_size = objc_getClassList(NULL, 0);
+            if (m_size > 0)
+            {
+                // Allocate the class pointers
+                m_objc_class_ptrs = (Class *)safe_malloc (m_size * sizeof(Class));
+                m_size = objc_getClassList(m_objc_class_ptrs, m_size);
+                // Sort Class pointers for quick lookup
+                ::qsort (m_objc_class_ptrs, m_size, sizeof(Class), compare_void_ptr);
+            }
+            else
+                return false;
+        }
+        return true;
+    }
+    
+    uint32_t
+    FindClassIndex (Class isa)
+    {
+        Class *matching_class = (Class *)bsearch (&isa, 
+                                                  m_objc_class_ptrs, 
+                                                  m_size, 
+                                                  sizeof(Class), 
+                                                  compare_void_ptr);
+        if (matching_class)
+        {
+            uint32_t idx = matching_class - m_objc_class_ptrs;
+            return idx;
+        }        
+        return UINT32_MAX;
+    }
+    
+    Class
+    GetClassAtIndex (uint32_t idx) const
+    {
+        if (idx < m_size)
+            return m_objc_class_ptrs[idx];
+        return NULL;
+    }
+    uint32_t
+    GetSize() const
+    {
+        return m_size;
+    }
+private:
+    Class *m_objc_class_ptrs;
+    uint32_t m_size;    
+};
+
+
+
+//----------------------------------------------------------------------
+// Local global variables
+//----------------------------------------------------------------------
+MatchResults g_matches;
+MallocStackLoggingEntries g_malloc_stack_history;
+ObjCClasses g_objc_classes;
+
+//----------------------------------------------------------------------
+// ObjCClassInfo
+//----------------------------------------------------------------------
+
+enum HeapInfoSortType
+{
+    eSortTypeNone,
+    eSortTypeBytes,
+    eSortTypeCount
+};
+
+class ObjCClassInfo
+{
+public:
+    ObjCClassInfo() :
+        m_entries (NULL),
+        m_size (0),
+        m_sort_type (eSortTypeNone)
+    {
+    }
+    
+    void
+    Update (const ObjCClasses &objc_classes)
+    {
+        m_size = objc_classes.GetSize();
+        m_entries = (Entry *)safe_malloc (m_size * sizeof(Entry));
+        m_sort_type = eSortTypeNone;
+        Reset ();
+    }
+    
+    bool
+    AddInstance (uint32_t idx, uint64_t ptr_size)
+    {
+        if (m_size == 0)
+            Update (g_objc_classes);
+        // Update the totals for the classes
+        if (idx < m_size)
+        {
+            m_entries[idx].bytes += ptr_size;
+            ++m_entries[idx].count;
+            return true;
+        }
+        return false;
+    }
+    
+    void
+    Reset ()
+    {
+        m_sort_type = eSortTypeNone;
+        for (uint32_t i=0; i<m_size; ++i)
+        {
+             // In case we sort the entries after gathering the data, we will
+             // want to know the index into the m_objc_class_ptrs[] array.
+            m_entries[i].idx = i;
+            m_entries[i].bytes = 0;
+            m_entries[i].count = 0;
+        }
+    }
+    void
+    SortByTotalBytes (const ObjCClasses &objc_classes, bool print)
+    {
+        if (m_sort_type != eSortTypeBytes && m_size > 0)
+        {
+            ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_bytes);            
+            m_sort_type = eSortTypeBytes;
+        }
+        if (print && m_size > 0)
+        {
+            puts("Objective C objects by total bytes:");
+            puts("Total Bytes Class Name");
+            puts("----------- -----------------------------------------------------------------");
+            for (uint32_t i=0; i<m_size && m_entries[i].bytes > 0; ++i)
+            {
+                printf ("%11llu %s\n", m_entries[i].bytes, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx)));
+            }            
+        }
+    }
+    void
+    SortByTotalCount (const ObjCClasses &objc_classes, bool print)
+    {
+        if (m_sort_type != eSortTypeCount && m_size > 0)
+        {
+            ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_count);            
+            m_sort_type = eSortTypeCount;
+        }
+        if (print && m_size > 0)
+        {
+            puts("Objective C objects by total count:");
+            puts("Count    Class Name");
+            puts("-------- -----------------------------------------------------------------");
+            for (uint32_t i=0; i<m_size && m_entries[i].count > 0; ++i)
+            {
+                printf ("%8u %s\n", m_entries[i].count, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx)));
+            }            
+        }
+    }
+private:
+    struct Entry
+    {
+        uint32_t idx;   // Index into the m_objc_class_ptrs[] array
+        uint32_t count; // Number of object instances that were found
+        uint64_t bytes; // Total number of bytes for each objc class
+    };
+    
+    static int
+    compare_bytes (const Entry *a, const Entry *b)
+    {
+        // Reverse the comparison to most bytes entries end up at top of list
+        if (a->bytes > b->bytes) return -1;
+        if (a->bytes < b->bytes) return +1;
+        return 0;
+    }
+
+    static int
+    compare_count (const Entry *a, const Entry *b)
+    {
+        // Reverse the comparison to most count entries end up at top of list
+        if (a->count > b->count) return -1;
+        if (a->count < b->count) return +1;
+        return 0;
+    }
+
+    Entry *m_entries;
+    uint32_t m_size;    
+    HeapInfoSortType m_sort_type;
+};
+
+ObjCClassInfo g_objc_class_snapshot;
+
+//----------------------------------------------------------------------
+// task_peek
+//
+// Reads memory from this tasks address space. This callback is needed
+// by the code that iterates through all of the malloc blocks to read
+// the memory in this process.
+//----------------------------------------------------------------------
+static kern_return_t
+task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory)
+{
+    *local_memory = (void*) remote_address;
+    return KERN_SUCCESS;
+}
+
+
+static const void
+foreach_zone_in_this_process (range_callback_info_t *info)
+{
+    if (info == NULL || info->zone_callback == NULL)
+        return;
+
+    vm_address_t *zones = NULL;
+    unsigned int num_zones = 0;
+        
+    kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones);
+    if (KERN_SUCCESS == err)
+    {
+        for (unsigned int i=0; i<num_zones; ++i)
+        {
+            info->zone_callback (info, (const malloc_zone_t *)zones[i]);
+        }
+    }
+    
+    if (info->check_vm_regions)
+    {
+#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
+        typedef vm_region_submap_short_info_data_64_t RegionInfo;
+        enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+#else
+        typedef vm_region_submap_info_data_64_t RegionInfo;
+        enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
+#endif
+        task_t task = mach_task_self();
+    	mach_vm_address_t vm_region_base_addr;
+    	mach_vm_size_t vm_region_size;
+    	natural_t vm_region_depth;
+    	RegionInfo vm_region_info;
+
+        ((range_contains_data_callback_info_t *)info->baton)->unique = true;
+
+        for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
+        {
+            mach_msg_type_number_t vm_region_info_size = kRegionInfoSize;
+            const kern_return_t err = mach_vm_region_recurse (task,
+                                                              &vm_region_base_addr,
+                                                              &vm_region_size,
+                                                              &vm_region_depth,
+                                                              (vm_region_recurse_info_t)&vm_region_info,
+                                                              &vm_region_info_size);
+            if (err)
+                break;
+            // Check all read + write regions. This will cover the thread stacks 
+            // and any regions of memory that aren't covered by the heap
+            if (vm_region_info.protection & VM_PROT_WRITE && 
+                vm_region_info.protection & VM_PROT_READ)
+            {
+                //printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n", (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr + vm_region_size);
+                range_info_callback (task, 
+                                     info->baton, 
+                                     stack_logging_type_vm_region, 
+                                     vm_region_base_addr, 
+                                     vm_region_size);
+            }
+        }
+    }
+}
+
+//----------------------------------------------------------------------
+// dump_malloc_block_callback
+//
+// A simple callback that will dump each malloc block and all available
+// info from the enumeration callback perspective.
+//----------------------------------------------------------------------
+static void
+dump_malloc_block_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
+{
+    printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size);
+}
+
+static void 
+ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count) 
+{
+    range_callback_info_t *info = (range_callback_info_t *)baton;
+    while(count--) {
+        info->range_callback (task, info->baton, type, ptrs->address, ptrs->size);
+        ptrs++;
+    }
+}
+
+static void
+enumerate_range_in_zone (void *baton, const malloc_zone_t *zone)
+{
+    range_callback_info_t *info = (range_callback_info_t *)baton;
+
+    if (zone && zone->introspect)
+        zone->introspect->enumerator (mach_task_self(), 
+                                      info, 
+                                      MALLOC_PTR_IN_USE_RANGE_TYPE, 
+                                      (vm_address_t)zone, 
+                                      task_peek, 
+                                      ranges_callback);    
+}
+
+static void
+range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
+{
+    const uint64_t end_addr = ptr_addr + ptr_size;
+    
+    range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton;
+    switch (info->type)
+    {
+    case eDataTypeAddress:
+        // Check if the current malloc block contains an address specified by "info->addr"
+        if (ptr_addr <= info->addr && info->addr < end_addr)
+        {
+            ++info->match_count;
+            malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr, type };
+            g_matches.push_back(match, info->unique);
+        }
+        break;
+    
+    case eDataTypeContainsData:
+        // Check if the current malloc block contains data specified in "info->data"
+        {
+            const uint32_t size = info->data.size;
+            if (size < ptr_size) // Make sure this block can contain this data
+            {
+                uint8_t *ptr_data = NULL;
+                if (task_peek (task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS)
+                {
+                    const void *buffer = info->data.buffer;
+                    assert (ptr_data);
+                    const uint32_t align = info->data.align;
+                    for (uint64_t addr = ptr_addr; 
+                         addr < end_addr && ((end_addr - addr) >= size);
+                         addr += align, ptr_data += align)
+                    {
+                        if (memcmp (buffer, ptr_data, size) == 0)
+                        {
+                            ++info->match_count;
+                            malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr, type };
+                            g_matches.push_back(match, info->unique);
+                        }
+                    }
+                }
+                else
+                {
+                    printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size);
+                }   
+            }
+        }
+        break;
+    
+    case eDataTypeObjC:
+        // Check if the current malloc block contains an objective C object
+        // of any sort where the first pointer in the object is an OBJC class
+        // pointer (an isa)
+        {
+            malloc_block_contents *block_contents = NULL;
+            if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS)
+            {
+                // We assume that g_objc_classes is up to date
+                // that the class list was verified to have some classes in it
+                // before calling this function
+                const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa);
+                if (objc_class_idx != UINT32_MAX)
+                {
+                    bool match = false;
+                    if (info->objc.match_isa == 0)
+                    {
+                        // Match any objective C object
+                        match = true;
+                    }
+                    else 
+                    {
+                        // Only match exact isa values in the current class or
+                        // optionally in the super classes
+                        if (info->objc.match_isa == block_contents->isa)
+                            match = true;
+                        else if (info->objc.match_superclasses)
+                        {
+                            Class super = class_getSuperclass(block_contents->isa);
+                            while (super)
+                            {
+                                match = super == info->objc.match_isa;
+                                if (match)
+                                    break;
+                                super = class_getSuperclass(super);
+                            }
+                        }
+                    }
+                    if (match)
+                    {
+                        //printf (" success\n");
+                        ++info->match_count;
+                        malloc_match match = { (void *)ptr_addr, ptr_size, 0, type };
+                        g_matches.push_back(match, info->unique);
+                    }
+                    else
+                    {
+                        //printf (" error: wrong class: %s\n", dl_info.dli_sname);                        
+                    }
+                }
+                else
+                {
+                    //printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname);
+                    return;
+                }
+            }
+        }
+        break;
+
+    case eDataTypeHeapInfo:
+        // Check if the current malloc block contains an objective C object
+        // of any sort where the first pointer in the object is an OBJC class
+        // pointer (an isa)
+        {
+            malloc_block_contents *block_contents = NULL;
+            if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS)
+            {
+                // We assume that g_objc_classes is up to date
+                // that the class list was verified to have some classes in it
+                // before calling this function
+                const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa);
+                if (objc_class_idx != UINT32_MAX)
+                {
+                    // This is an objective C object
+                    g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size);
+                }
+                else
+                {
+                    // Classify other heap info
+                }
+            }
+        }
+        break;
+
+    }
+}
+
+static void 
+get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record, void *task_ptr)
+{
+    malloc_stack_entry *stack_entry = g_malloc_stack_history.next();
+    if (stack_entry)
+    {
+        stack_entry->address = (void *)stack_record.address;
+        stack_entry->type_flags = stack_record.type_flags;
+        stack_entry->argument = stack_record.argument;
+        stack_entry->num_frames = 0;
+        stack_entry->frames[0] = 0;
+        kern_return_t err = __mach_stack_logging_frames_for_uniqued_stack (*(task_t *)task_ptr, 
+                                                                           stack_record.stack_identifier,
+                                                                           stack_entry->frames,
+                                                                           MAX_FRAMES,
+                                                                           &stack_entry->num_frames);    
+        // Terminate the frames with zero if there is room
+        if (stack_entry->num_frames < MAX_FRAMES)
+            stack_entry->frames[stack_entry->num_frames] = 0; 
+    }
+}
+
+malloc_stack_entry *
+get_stack_history_for_address (const void * addr, int history)
+{
+    if (!stack_logging_enable_logging)
+        return NULL;
+    g_malloc_stack_history.clear();
+    kern_return_t err;
+    task_t task = mach_task_self();
+    if (history)
+    {
+        err = __mach_stack_logging_enumerate_records (task,
+                                                      (mach_vm_address_t)addr, 
+                                                      get_stack_for_address_enumerator,
+                                                      &task);
+    }
+    else
+    {
+        malloc_stack_entry *stack_entry = g_malloc_stack_history.next();
+        if (stack_entry)
+        {
+            stack_entry->address = addr;
+            stack_entry->type_flags = stack_logging_type_alloc;
+            stack_entry->argument = 0;
+            stack_entry->num_frames = 0;
+            stack_entry->frames[0] = 0;
+            err = __mach_stack_logging_get_frames(task, (mach_vm_address_t)addr, stack_entry->frames, MAX_FRAMES, &stack_entry->num_frames);
+            if (err == 0 && stack_entry->num_frames > 0)
+            {
+                // Terminate the frames with zero if there is room
+                if (stack_entry->num_frames < MAX_FRAMES)
+                    stack_entry->frames[stack_entry->num_frames] = 0;
+            }
+            else
+            {
+                g_malloc_stack_history.clear();                
+            }
+        }
+    }
+    // Return data if there is any
+    return g_malloc_stack_history.data();
+}
+
+//----------------------------------------------------------------------
+// find_pointer_in_heap
+//
+// Finds a pointer value inside one or more currently valid malloc
+// blocks.
+//----------------------------------------------------------------------
+malloc_match *
+find_pointer_in_heap (const void * addr, int check_vm_regions)
+{
+    g_matches.clear();
+    // Setup "info" to look for a malloc block that contains data
+    // that is the pointer
+    if (addr)
+    {
+        range_contains_data_callback_info_t data_info;
+        data_info.type = eDataTypeContainsData;      // Check each block for data
+        data_info.data.buffer = (uint8_t *)&addr;    // What data? The pointer value passed in
+        data_info.data.size = sizeof(addr);          // How many bytes? The byte size of a pointer
+        data_info.data.align = sizeof(addr);         // Align to a pointer byte size
+        data_info.match_count = 0;                   // Initialize the match count to zero
+        data_info.done = false;                      // Set done to false so searching doesn't stop
+        data_info.unique = false;                    // Set to true when iterating on the vm_regions
+        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
+        foreach_zone_in_this_process (&info);
+        
+        
+    }
+    return g_matches.data();
+}
+
+//----------------------------------------------------------------------
+// find_pointer_in_memory
+//
+// Finds a pointer value inside one or more currently valid malloc
+// blocks.
+//----------------------------------------------------------------------
+malloc_match *
+find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * addr)
+{
+    g_matches.clear();
+    // Setup "info" to look for a malloc block that contains data
+    // that is the pointer
+    range_contains_data_callback_info_t data_info;
+    data_info.type = eDataTypeContainsData;      // Check each block for data
+    data_info.data.buffer = (uint8_t *)&addr;    // What data? The pointer value passed in
+    data_info.data.size = sizeof(addr);          // How many bytes? The byte size of a pointer
+    data_info.data.align = sizeof(addr);         // Align to a pointer byte size
+    data_info.match_count = 0;                   // Initialize the match count to zero
+    data_info.done = false;                      // Set done to false so searching doesn't stop
+    data_info.unique = false;                    // Set to true when iterating on the vm_regions
+    range_info_callback (mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size);
+    return g_matches.data();
+}
+
+//----------------------------------------------------------------------
+// find_objc_objects_in_memory
+//
+// Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is
+// NULL. If 'c' is non NULL, then also check objects to see if they 
+// inherit from 'c'
+//----------------------------------------------------------------------
+malloc_match *
+find_objc_objects_in_memory (void *isa, int check_vm_regions)
+{
+    g_matches.clear();
+    if (g_objc_classes.Update())
+    {
+        // Setup "info" to look for a malloc block that contains data
+        // that is the pointer
+        range_contains_data_callback_info_t data_info;
+        data_info.type = eDataTypeObjC;      // Check each block for data
+        data_info.objc.match_isa = isa;
+        data_info.objc.match_superclasses = true;
+        data_info.match_count = 0;                   // Initialize the match count to zero
+        data_info.done = false;                      // Set done to false so searching doesn't stop
+        data_info.unique = false;                    // Set to true when iterating on the vm_regions
+        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
+        foreach_zone_in_this_process (&info);
+    }
+    return g_matches.data();
+}
+
+//----------------------------------------------------------------------
+// get_heap_info
+//
+// Gather information for all allocations on the heap and report 
+// statistics.
+//----------------------------------------------------------------------
+
+void
+get_heap_info (int sort_type)
+{
+    if (g_objc_classes.Update())
+    {
+        // Reset all stats
+        g_objc_class_snapshot.Reset ();
+        // Setup "info" to look for a malloc block that contains data
+        // that is the pointer
+        range_contains_data_callback_info_t data_info;
+        data_info.type = eDataTypeHeapInfo; // Check each block for data
+        data_info.match_count = 0;          // Initialize the match count to zero
+        data_info.done = false;             // Set done to false so searching doesn't stop
+        data_info.unique = false;           // Set to true when iterating on the vm_regions
+        const int check_vm_regions = false;
+        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
+        foreach_zone_in_this_process (&info);
+        
+        // Sort and print byte total bytes
+        switch (sort_type)
+        {
+        case eSortTypeNone:
+        default:
+        case eSortTypeBytes:
+            g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true);
+            break;
+            
+        case eSortTypeCount:
+            g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true);
+            break;
+        }
+    }
+    else
+    {
+        printf ("error: no objective C classes\n");
+    }
+}
+
+//----------------------------------------------------------------------
+// find_cstring_in_heap
+//
+// Finds a C string inside one or more currently valid malloc blocks.
+//----------------------------------------------------------------------
+malloc_match *
+find_cstring_in_heap (const char *s, int check_vm_regions)
+{
+    g_matches.clear();
+    if (s == NULL || s[0] == '\0')
+    {
+        printf ("error: invalid argument (empty cstring)\n");
+        return NULL;
+    }
+    // Setup "info" to look for a malloc block that contains data
+    // that is the C string passed in aligned on a 1 byte boundary
+    range_contains_data_callback_info_t data_info;
+    data_info.type = eDataTypeContainsData;  // Check each block for data
+    data_info.data.buffer = (uint8_t *)s;    // What data? The C string passed in
+    data_info.data.size = strlen(s);         // How many bytes? The length of the C string
+    data_info.data.align = 1;                // Data doesn't need to be aligned, so set the alignment to 1
+    data_info.match_count = 0;               // Initialize the match count to zero
+    data_info.done = false;                  // Set done to false so searching doesn't stop
+    data_info.unique = false;                // Set to true when iterating on the vm_regions
+    range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
+    foreach_zone_in_this_process (&info);
+    return g_matches.data();
+}
+
+//----------------------------------------------------------------------
+// find_block_for_address
+//
+// Find the malloc block that whose address range contains "addr".
+//----------------------------------------------------------------------
+malloc_match *
+find_block_for_address (const void *addr, int check_vm_regions)
+{
+    g_matches.clear();
+    // Setup "info" to look for a malloc block that contains data
+    // that is the C string passed in aligned on a 1 byte boundary
+    range_contains_data_callback_info_t data_info;
+    data_info.type = eDataTypeAddress;  // Check each block to see if the block contains the address passed in
+    data_info.addr = (uintptr_t)addr;   // What data? The C string passed in
+    data_info.match_count = 0;          // Initialize the match count to zero
+    data_info.done = false;             // Set done to false so searching doesn't stop
+    data_info.unique = false;           // Set to true when iterating on the vm_regions
+    range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
+    foreach_zone_in_this_process (&info);
+    return g_matches.data();
+}

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/runtime/__init__.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/runtime/__init__.py?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/runtime/__init__.py (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/runtime/__init__.py Tue Dec 15 17:03:22 2015
@@ -0,0 +1,3 @@
+__all__ = []
+for x in __all__:
+    __import__('lldb.runtime.'+x)

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/__init__.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/__init__.py?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/__init__.py (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/__init__.py Tue Dec 15 17:03:22 2015
@@ -0,0 +1,3 @@
+__all__ = ["symbolication"]
+for x in __all__:
+    __import__('lldb.utils.'+x)

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/symbolication.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/symbolication.py?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/symbolication.py (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/symbolication.py Tue Dec 15 17:03:22 2015
@@ -0,0 +1,640 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# Be sure to add the python path that points to the LLDB shared library.
+#
+# To use this in the embedded python interpreter using "lldb":
+#
+#   cd /path/containing/crashlog.py
+#   lldb
+#   (lldb) script import crashlog
+#   "crashlog" command installed, type "crashlog --help" for detailed help
+#   (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash
+#
+# The benefit of running the crashlog command inside lldb in the 
+# embedded python interpreter is when the command completes, there 
+# will be a target with all of the files loaded at the locations
+# described in the crash log. Only the files that have stack frames
+# in the backtrace will be loaded unless the "--load-all" option
+# has been specified. This allows users to explore the program in the
+# state it was in right at crash time. 
+#
+# On MacOSX csh, tcsh:
+#   ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash )
+#
+# On MacOSX sh, bash:
+#   PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash
+#----------------------------------------------------------------------
+
+import lldb
+import commands
+import optparse
+import os
+import plistlib
+import re
+import shlex
+import sys
+import time
+import uuid
+
+class Address:
+    """Class that represents an address that will be symbolicated"""
+    def __init__(self, target, load_addr):
+        self.target = target
+        self.load_addr = load_addr # The load address that this object represents
+        self.so_addr = None # the resolved lldb.SBAddress (if any), named so_addr for section/offset address
+        self.sym_ctx = None # The cached symbol context for this address
+        self.description = None # Any original textual description of this address to be used as a backup in case symbolication fails
+        self.symbolication = None # The cached symbolicated string that describes this address
+        self.inlined = False
+    def __str__(self):
+        s = "%#16.16x" % (self.load_addr)
+        if self.symbolication:
+            s += " %s" % (self.symbolication)
+        elif self.description:
+            s += " %s" % (self.description)
+        elif self.so_addr:
+            s += " %s" % (self.so_addr)
+        return s
+
+    def resolve_addr(self):
+        if self.so_addr == None:
+            self.so_addr = self.target.ResolveLoadAddress (self.load_addr)
+        return self.so_addr
+
+    def is_inlined(self):
+        return self.inlined
+    
+    def get_symbol_context(self):
+        if self.sym_ctx == None:
+            sb_addr = self.resolve_addr()
+            if sb_addr:
+                self.sym_ctx = self.target.ResolveSymbolContextForAddress (sb_addr, lldb.eSymbolContextEverything)
+            else:
+                self.sym_ctx = lldb.SBSymbolContext()
+        return self.sym_ctx
+
+    def get_instructions(self):
+        sym_ctx = self.get_symbol_context()
+        if sym_ctx:
+            function = sym_ctx.GetFunction()
+            if function:
+                return function.GetInstructions(self.target)
+            return sym_ctx.GetSymbol().GetInstructions(self.target)
+        return None
+    
+    def symbolicate(self, verbose = False):
+        if self.symbolication == None:
+            self.symbolication = ''
+            self.inlined = False
+            sym_ctx = self.get_symbol_context()
+            if sym_ctx:
+                module = sym_ctx.GetModule()
+                if module:
+                    # Print full source file path in verbose mode
+                    if verbose:
+                        self.symbolication += str(module.GetFileSpec()) + '`'
+                    else:
+                        self.symbolication += module.GetFileSpec().GetFilename() + '`'
+                    function_start_load_addr = -1
+                    function = sym_ctx.GetFunction()
+                    block = sym_ctx.GetBlock()
+                    line_entry = sym_ctx.GetLineEntry()
+                    symbol = sym_ctx.GetSymbol()
+                    inlined_block = block.GetContainingInlinedBlock();
+                    if function:
+                        self.symbolication += function.GetName()
+
+                        if inlined_block:
+                            self.inlined = True
+                            self.symbolication += ' [inlined] ' + inlined_block.GetInlinedName();
+                            block_range_idx = inlined_block.GetRangeIndexForBlockAddress (self.so_addr)
+                            if block_range_idx < lldb.UINT32_MAX:
+                                block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx)
+                                function_start_load_addr = block_range_start_addr.GetLoadAddress (self.target)
+                        if function_start_load_addr == -1:
+                            function_start_load_addr = function.GetStartAddress().GetLoadAddress (self.target)
+                    elif symbol:
+                        self.symbolication += symbol.GetName()
+                        function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (self.target)
+                    else:
+                        self.symbolication = ''
+                        return False
+
+                    # Dump the offset from the current function or symbol if it is non zero
+                    function_offset = self.load_addr - function_start_load_addr
+                    if function_offset > 0:
+                        self.symbolication += " + %u" % (function_offset)
+                    elif function_offset < 0:
+                        self.symbolication += " %i (invalid negative offset, file a bug) " % function_offset
+
+                    # Print out any line information if any is available
+                    if line_entry.GetFileSpec():
+                        # Print full source file path in verbose mode
+                        if verbose:
+                            self.symbolication += ' at %s' % line_entry.GetFileSpec()
+                        else:
+                            self.symbolication += ' at %s' % line_entry.GetFileSpec().GetFilename()
+                        self.symbolication += ':%u' % line_entry.GetLine ()
+                        column = line_entry.GetColumn()
+                        if column > 0:
+                            self.symbolication += ':%u' % column
+                    return True
+        return False
+
+class Section:
+    """Class that represents an load address range"""
+    sect_info_regex = re.compile('(?P<name>[^=]+)=(?P<range>.*)')
+    addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$')
+    range_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$')
+
+    def __init__(self, start_addr = None, end_addr = None, name = None):
+        self.start_addr = start_addr
+        self.end_addr = end_addr
+        self.name = name
+    
+    @classmethod
+    def InitWithSBTargetAndSBSection(cls, target, section):
+        sect_load_addr = section.GetLoadAddress(target)
+        if sect_load_addr != lldb.LLDB_INVALID_ADDRESS:
+            obj = cls(sect_load_addr, sect_load_addr + section.size, section.name)
+            return obj
+        else:
+            return None
+        
+    def contains(self, addr):
+        return self.start_addr <= addr and addr < self.end_addr;
+    
+    def set_from_string(self, s):
+        match = self.sect_info_regex.match (s)
+        if match:
+            self.name = match.group('name')
+            range_str = match.group('range')
+            addr_match = self.addr_regex.match(range_str)
+            if addr_match:
+                self.start_addr = int(addr_match.group('start'), 16)
+                self.end_addr = None
+                return True
+
+            range_match = self.range_regex.match(range_str)
+            if range_match:
+                self.start_addr = int(range_match.group('start'), 16)
+                self.end_addr = int(range_match.group('end'), 16)
+                op = range_match.group('op')
+                if op == '+':
+                    self.end_addr += self.start_addr
+                return True
+        print 'error: invalid section info string "%s"' % s
+        print 'Valid section info formats are:'
+        print 'Format                Example                    Description'
+        print '--------------------- -----------------------------------------------'
+        print '<name>=<base>        __TEXT=0x123000             Section from base address only'
+        print '<name>=<base>-<end>  __TEXT=0x123000-0x124000    Section from base address and end address'
+        print '<name>=<base>+<size> __TEXT=0x123000+0x1000      Section from base address and size'
+        return False
+        
+    def __str__(self):
+        if self.name:
+            if self.end_addr != None:
+                if self.start_addr != None:
+                    return "%s=[0x%16.16x - 0x%16.16x)" % (self.name, self.start_addr, self.end_addr)
+            else:
+                if self.start_addr != None:
+                    return "%s=0x%16.16x" % (self.name, self.start_addr)
+            return self.name
+        return "<invalid>"
+            
+class Image:
+    """A class that represents an executable image and any associated data"""
+    
+    def __init__(self, path, uuid = None):
+        self.path = path
+        self.resolved_path = None
+        self.resolved = False
+        self.unavailable = False
+        self.uuid = uuid
+        self.section_infos = list()
+        self.identifier = None
+        self.version = None
+        self.arch = None
+        self.module = None
+        self.symfile = None
+        self.slide = None
+        
+    @classmethod
+    def InitWithSBTargetAndSBModule(cls, target, module):
+        '''Initialize this Image object with a module from a target.'''
+        obj = cls(module.file.fullpath, module.uuid)
+        obj.resolved_path = module.platform_file.fullpath
+        obj.resolved = True
+        obj.arch = module.triple
+        for section in module.sections:
+            symb_section = Section.InitWithSBTargetAndSBSection(target, section)
+            if symb_section:
+                obj.section_infos.append (symb_section)
+        obj.arch = module.triple
+        obj.module = module
+        obj.symfile = None
+        obj.slide = None
+        return obj
+        
+    def dump(self, prefix):
+        print "%s%s" % (prefix, self)
+
+    def debug_dump(self):
+        print 'path = "%s"' % (self.path)
+        print 'resolved_path = "%s"' % (self.resolved_path)
+        print 'resolved = %i' % (self.resolved)
+        print 'unavailable = %i' % (self.unavailable)
+        print 'uuid = %s' % (self.uuid)
+        print 'section_infos = %s' % (self.section_infos)
+        print 'identifier = "%s"' % (self.identifier)
+        print 'version = %s' % (self.version)
+        print 'arch = %s' % (self.arch)
+        print 'module = %s' % (self.module)
+        print 'symfile = "%s"' % (self.symfile)
+        print 'slide = %i (0x%x)' % (self.slide, self.slide)
+        
+    def __str__(self):
+        s = ''
+        if self.uuid:
+            s += "%s " % (self.get_uuid())
+        if self.arch:
+            s += "%s " % (self.arch)
+        if self.version:
+            s += "%s " % (self.version)
+        resolved_path = self.get_resolved_path()
+        if resolved_path:
+            s += "%s " % (resolved_path)
+        for section_info in self.section_infos:
+            s += ", %s" % (section_info)
+        if self.slide != None:
+            s += ', slide = 0x%16.16x' % self.slide
+        return s        
+    
+    def add_section(self, section):
+        #print "added '%s' to '%s'" % (section, self.path)
+        self.section_infos.append (section)
+        
+    def get_section_containing_load_addr (self, load_addr):
+        for section_info in self.section_infos:
+            if section_info.contains(load_addr):
+                return section_info
+        return None
+
+    def get_resolved_path(self):
+        if self.resolved_path:
+            return self.resolved_path
+        elif self.path:
+            return self.path
+        return None
+
+    def get_resolved_path_basename(self):
+        path = self.get_resolved_path()
+        if path:
+            return os.path.basename(path)
+        return None
+
+    def symfile_basename(self):
+        if self.symfile:
+            return os.path.basename(self.symfile)
+        return None
+    
+    def has_section_load_info(self):
+        return self.section_infos or self.slide != None
+    
+    def load_module(self, target):
+        if self.unavailable:
+            return None # We already warned that we couldn't find this module, so don't return an error string
+        # Load this module into "target" using the section infos to
+        # set the section load addresses
+        if self.has_section_load_info():
+            if target:
+                if self.module:
+                    if self.section_infos:
+                        num_sections_loaded = 0
+                        for section_info in self.section_infos:
+                            if section_info.name:
+                                section = self.module.FindSection (section_info.name)
+                                if section:
+                                    error = target.SetSectionLoadAddress (section, section_info.start_addr)
+                                    if error.Success():
+                                        num_sections_loaded += 1
+                                    else:
+                                        return 'error: %s' % error.GetCString()
+                                else:
+                                    return 'error: unable to find the section named "%s"' % section_info.name
+                            else:
+                                return 'error: unable to find "%s" section in "%s"' % (range.name, self.get_resolved_path())
+                        if num_sections_loaded == 0:
+                            return 'error: no sections were successfully loaded'
+                    else:
+                        err = target.SetModuleLoadAddress(self.module, self.slide)
+                        if err.Fail():
+                            return err.GetCString()
+                    return None
+                else:
+                    return 'error: invalid module'
+            else:
+                return 'error: invalid target'
+        else:
+            return 'error: no section infos'
+        
+    def add_module(self, target):
+        '''Add the Image described in this object to "target" and load the sections if "load" is True.'''
+        if target:
+            # Try and find using UUID only first so that paths need not match up
+            uuid_str = self.get_normalized_uuid_string()
+            if uuid_str:
+                self.module = target.AddModule (None, None, uuid_str)
+            if not self.module:
+                self.locate_module_and_debug_symbols ()
+                if self.unavailable:
+                    return None
+                resolved_path = self.get_resolved_path()
+                self.module = target.AddModule (resolved_path, self.arch, uuid_str, self.symfile)
+            if not self.module:
+                return 'error: unable to get module for (%s) "%s"' % (self.arch, self.get_resolved_path())
+            if self.has_section_load_info():
+                return self.load_module(target)
+            else:
+                return None # No sections, the module was added to the target, so success
+        else:
+            return 'error: invalid target'
+    
+    def locate_module_and_debug_symbols (self):
+        # By default, just use the paths that were supplied in:
+        # self.path
+        # self.resolved_path
+        # self.module
+        # self.symfile
+        # Subclasses can inherit from this class and override this function
+        self.resolved = True
+        return True
+    
+    def get_uuid(self):
+        if not self.uuid and self.module:
+            self.uuid = uuid.UUID(self.module.GetUUIDString())
+        return self.uuid
+
+    def get_normalized_uuid_string(self):
+        if self.uuid:
+            return str(self.uuid).upper()
+        return None
+
+    def create_target(self):
+        '''Create a target using the information in this Image object.'''
+        if self.unavailable:
+            return None
+
+        if self.locate_module_and_debug_symbols ():
+            resolved_path = self.get_resolved_path();
+            path_spec = lldb.SBFileSpec (resolved_path)
+            #result.PutCString ('plist[%s] = %s' % (uuid, self.plist))
+            error = lldb.SBError()
+            target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error);
+            if target:
+                self.module = target.FindModule(path_spec)
+                if self.has_section_load_info():
+                    err = self.load_module(target)
+                    if err:
+                        print 'ERROR: ', err
+                return target
+            else:
+                print 'error: unable to create a valid target for (%s) "%s"' % (self.arch, self.path)
+        else:
+            print 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path)
+        return None
+    
+class Symbolicator:
+
+    def __init__(self):
+        """A class the represents the information needed to symbolicate addresses in a program"""
+        self.target = None
+        self.images = list() # a list of images to be used when symbolicating
+        self.addr_mask = 0xffffffffffffffff
+    
+    @classmethod
+    def InitWithSBTarget(cls, target):
+        obj = cls()
+        obj.target = target
+        obj.images = list();
+        triple = target.triple
+        if triple:
+            arch = triple.split('-')[0]
+            if "arm" in arch:
+                obj.addr_mask = 0xfffffffffffffffe
+
+        for module in target.modules:
+            image = Image.InitWithSBTargetAndSBModule(target, module)
+            obj.images.append(image)
+        return obj
+        
+    def __str__(self):
+        s = "Symbolicator:\n"
+        if self.target:
+            s += "Target = '%s'\n" % (self.target)
+            s += "Target modules:\n"
+            for m in self.target.modules:
+                s += str(m) + "\n"
+        s += "Images:\n"
+        for image in self.images:
+            s += '    %s\n' % (image)
+        return s
+    
+    def find_images_with_identifier(self, identifier):
+        images = list()
+        for image in self.images:
+            if image.identifier == identifier:
+                images.append(image)
+        if len(images) == 0:
+            regex_text = '^.*\.%s$' % (identifier)
+            regex = re.compile(regex_text)
+            for image in self.images:
+                if regex.match(image.identifier):
+                    images.append(image)
+        return images
+        
+    def find_image_containing_load_addr(self, load_addr):
+        for image in self.images:
+            if image.get_section_containing_load_addr (load_addr):
+                return image
+        return None
+    
+    def create_target(self):
+        if self.target:
+            return self.target
+
+        if self.images:
+            for image in self.images:
+                self.target = image.create_target ()
+                if self.target:
+                    if self.target.GetAddressByteSize() == 4:
+                        triple = self.target.triple
+                        if triple:
+                            arch = triple.split('-')[0]
+                            if "arm" in arch:
+                                self.addr_mask = 0xfffffffffffffffe
+                    return self.target
+        return None
+    
+            
+    def symbolicate(self, load_addr, verbose = False):
+        if not self.target:
+            self.create_target()
+        if self.target:
+            live_process = False
+            process = self.target.process
+            if process:
+                state = process.state
+                if state > lldb.eStateUnloaded and state < lldb.eStateDetached:
+                    live_process = True
+            # If we don't have a live process, we can attempt to find the image
+            # that a load address belongs to and lazily load its module in the
+            # target, but we shouldn't do any of this if we have a live process
+            if not live_process:
+                image = self.find_image_containing_load_addr (load_addr)
+                if image:
+                    image.add_module (self.target)
+            symbolicated_address = Address(self.target, load_addr)
+            if symbolicated_address.symbolicate (verbose):
+                if symbolicated_address.so_addr:
+                    symbolicated_addresses = list()
+                    symbolicated_addresses.append(symbolicated_address)
+                    # See if we were able to reconstruct anything?
+                    while 1:
+                        inlined_parent_so_addr = lldb.SBAddress()
+                        inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope (symbolicated_address.so_addr, inlined_parent_so_addr)
+                        if not inlined_parent_sym_ctx:
+                            break
+                        if not inlined_parent_so_addr:
+                            break
+
+                        symbolicated_address = Address(self.target, inlined_parent_so_addr.GetLoadAddress(self.target))
+                        symbolicated_address.sym_ctx = inlined_parent_sym_ctx
+                        symbolicated_address.so_addr = inlined_parent_so_addr
+                        symbolicated_address.symbolicate (verbose)
+            
+                        # push the new frame onto the new frame stack
+                        symbolicated_addresses.append (symbolicated_address)
+        
+                    if symbolicated_addresses:
+                        return symbolicated_addresses
+        else:
+            print 'error: no target in Symbolicator'
+        return None
+            
+        
+def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_after_pc, non_zeroeth_frame):
+    lines = list()
+    pc_index = -1
+    comment_column = 50
+    for inst_idx, inst in enumerate(instructions):
+        inst_pc = inst.GetAddress().GetLoadAddress(target);
+        if pc == inst_pc:
+            pc_index = inst_idx
+        mnemonic = inst.GetMnemonic (target)
+        operands =  inst.GetOperands (target)
+        comment =  inst.GetComment (target)
+        #data = inst.GetData (target)
+        lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands))
+        if comment:
+            line_len = len(lines[-1])
+            if line_len < comment_column:
+                lines[-1] += ' ' * (comment_column - line_len)
+                lines[-1] += "; %s" % comment
+
+    if pc_index >= 0:
+        # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1
+        if non_zeroeth_frame and pc_index > 0:
+            pc_index = pc_index - 1
+        if insts_before_pc == -1:
+            start_idx = 0
+        else:
+            start_idx = pc_index - insts_before_pc
+        if start_idx < 0:
+            start_idx = 0
+        if insts_before_pc == -1:
+            end_idx = inst_idx
+        else:
+            end_idx = pc_index + insts_after_pc
+        if end_idx > inst_idx:
+            end_idx = inst_idx
+        for i in range(start_idx, end_idx+1):
+            if i == pc_index:
+                print ' -> ', lines[i]
+            else:
+                print '    ', lines[i]
+
+def print_module_section_data (section):
+    print section
+    section_data = section.GetSectionData()
+    if section_data:
+        ostream = lldb.SBStream()
+        section_data.GetDescription (ostream, section.GetFileAddress())
+        print ostream.GetData()
+
+def print_module_section (section, depth):
+    print section
+    if depth > 0:
+        num_sub_sections = section.GetNumSubSections()
+        for sect_idx in range(num_sub_sections):
+            print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1)
+
+def print_module_sections (module, depth):
+    for sect in module.section_iter():
+        print_module_section (sect, depth)
+
+def print_module_symbols (module):
+    for sym in module:
+        print sym
+
+def Symbolicate(command_args):
+    
+    usage = "usage: %prog [options] <addr1> [addr2 ...]"
+    description='''Symbolicate one or more addresses using LLDB's python scripting API..'''
+    parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage)
+    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+    parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".')
+    parser.add_option('-f', '--file', type='string', metavar='file', dest='file', help='Specify a file to use when symbolicating')
+    parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify a architecture to use when symbolicating')
+    parser.add_option('-s', '--slide', type='int', metavar='slide', dest='slide', help='Specify the slide to use on the file specified with the --file option', default=None)
+    parser.add_option('--section', type='string', action='append', dest='section_strings', help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>')
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+    symbolicator = Symbolicator()
+    images = list();
+    if options.file:
+        image = Image(options.file);
+        image.arch = options.arch
+        # Add any sections that were specified with one or more --section options
+        if options.section_strings:
+            for section_str in options.section_strings:
+                section = Section()
+                if section.set_from_string (section_str):
+                    image.add_section (section)
+                else:
+                    sys.exit(1)
+        if options.slide != None:
+            image.slide = options.slide
+        symbolicator.images.append(image)
+    
+    target = symbolicator.create_target()
+    if options.verbose:
+        print symbolicator
+    if target:
+        for addr_str in args:
+            addr = int(addr_str, 0)
+            symbolicated_addrs = symbolicator.symbolicate(addr, options.verbose)
+            for symbolicated_addr in symbolicated_addrs:
+                print symbolicated_addr
+            print
+    else:
+        print 'error: no target for %s' % (symbolicator)
+        
+if __name__ == '__main__':
+    # Create a new debugger instance
+    lldb.debugger = lldb.SBDebugger.Create()
+    Symbolicate (sys.argv[1:])

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/Python/lldb/utils/symbolication.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/darwin-debug
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/darwin-debug?rev=255697&view=auto
==============================================================================
Binary file - no diff available.

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/darwin-debug
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/darwin-debug
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/debugserver
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/debugserver?rev=255697&view=auto
==============================================================================
Binary file - no diff available.

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/debugserver
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/debugserver
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-argdumper
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-argdumper?rev=255697&view=auto
==============================================================================
Binary file - no diff available.

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-argdumper
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-argdumper
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-server
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-server?rev=255697&view=auto
==============================================================================
Binary file - no diff available.

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-server
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/A/Resources/lldb-server
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: lldb/trunk/build/Debug/LLDB.framework/Versions/Current
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/build/Debug/LLDB.framework/Versions/Current?rev=255697&view=auto
==============================================================================
--- lldb/trunk/build/Debug/LLDB.framework/Versions/Current (added)
+++ lldb/trunk/build/Debug/LLDB.framework/Versions/Current Tue Dec 15 17:03:22 2015
@@ -0,0 +1 @@
+link A
\ No newline at end of file

Propchange: lldb/trunk/build/Debug/LLDB.framework/Versions/Current
------------------------------------------------------------------------------
    svn:special = *

Modified: lldb/trunk/include/lldb/API/LLDB.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/LLDB.h?rev=255697&r1=255696&r2=255697&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/LLDB.h (original)
+++ lldb/trunk/include/lldb/API/LLDB.h Tue Dec 15 17:03:22 2015
@@ -16,6 +16,7 @@
 // Project includes
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBAddress.h"
+#include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBBlock.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBBreakpointLocation.h"
@@ -29,31 +30,48 @@
 #include "lldb/API/SBDeclaration.h"
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBEvent.h"
-#include "lldb/API/SBExpressionOptions.h"
 #include "lldb/API/SBExecutionContext.h"
+#include "lldb/API/SBExpressionOptions.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFileSpecList.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBFunction.h"
 #include "lldb/API/SBHostOS.h"
 #include "lldb/API/SBInstruction.h"
 #include "lldb/API/SBInstructionList.h"
+#include "lldb/API/SBLanguageRuntime.h"
+#include "lldb/API/SBLaunchInfo.h"
 #include "lldb/API/SBLineEntry.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBModule.h"
+#include "lldb/API/SBModuleSpec.h"
+#include "lldb/API/SBPlatform.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBQueue.h"
 #include "lldb/API/SBQueueItem.h"
+#include "lldb/API/SBSection.h"
 #include "lldb/API/SBSourceManager.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 #include "lldb/API/SBSymbol.h"
 #include "lldb/API/SBSymbolContext.h"
+#include "lldb/API/SBSymbolContextList.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
+#include "lldb/API/SBThreadCollection.h"
+#include "lldb/API/SBThreadPlan.h"
 #include "lldb/API/SBType.h"
+#include "lldb/API/SBTypeCategory.h"
+#include "lldb/API/SBTypeEnumMember.h"
+#include "lldb/API/SBTypeFilter.h"
+#include "lldb/API/SBTypeFormat.h"
+#include "lldb/API/SBTypeNameSpecifier.h"
+#include "lldb/API/SBTypeSummary.h"
+#include "lldb/API/SBTypeSynthetic.h"
 #include "lldb/API/SBUnixSignals.h"
 #include "lldb/API/SBValue.h"
 #include "lldb/API/SBValueList.h"
 #include "lldb/API/SBVariablesOptions.h"
+#include "lldb/API/SBWatchpoint.h"
 
 #endif  // LLDB_LLDB_h_

Modified: lldb/trunk/include/lldb/API/SBStream.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBStream.h?rev=255697&r1=255696&r2=255697&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBStream.h (original)
+++ lldb/trunk/include/lldb/API/SBStream.h Tue Dec 15 17:03:22 2015
@@ -21,7 +21,9 @@ class LLDB_API SBStream
 public:
 
     SBStream ();
-    
+
+    SBStream (SBStream &&rhs);
+
     ~SBStream ();
 
     bool

Modified: lldb/trunk/source/API/SBStream.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBStream.cpp?rev=255697&r1=255696&r2=255697&view=diff
==============================================================================
--- lldb/trunk/source/API/SBStream.cpp (original)
+++ lldb/trunk/source/API/SBStream.cpp Tue Dec 15 17:03:22 2015
@@ -23,6 +23,13 @@ SBStream::SBStream () :
 {
 }
 
+SBStream::SBStream (SBStream &&rhs) :
+    m_opaque_ap (std::move(rhs.m_opaque_ap)),
+    m_is_file (rhs.m_is_file)
+{
+}
+
+
 SBStream::~SBStream ()
 {
 }




More information about the lldb-commits mailing list