[NFC] Darwin llgs support from Week of Code
authorTodd Fiala <todd.fiala@gmail.com>
Sun, 4 Sep 2016 00:18:56 +0000 (00:18 +0000)
committerTodd Fiala <todd.fiala@gmail.com>
Sun, 4 Sep 2016 00:18:56 +0000 (00:18 +0000)
This code represents the Week of Code work I did on bringing up
lldb-server LLGS support for Darwin.  It does not include the
Xcode project changes needed, as we don't want to throw that switch
until more support is implemented (i.e. this change is inert, no
build systems use it yet.  I've verified on Ubuntu 16.04, macOS
Xcode and macOS cmake builds).

This change does some minimal refactoring of code that is shared
with the Linux LLGS portion, moving it from NativeProcessLinux into
NativeProcessProtocol.  That code is also used by NativeProcessDarwin.

Current state on Darwin:
* Process launching is implemented.  (Attach is not).
  Launching on devices has not yet been tested (FBS/BKS might
  need a bit of work).
* Inferior waitpid monitoring and communication of exit status
  via MainLoop callback is implemented.
* Memory read/write, breakpoints, thread register context, etc.
  are not yet implemented.  This impacts process stop/resume, as
  the initial launch suspended immediately starts the process
  up and running because it doesn't know it is supposed to remain
  stopped.
* I implemented the equivalent of MachThreadList as
  NativeThreadListDarwin, in anticipation that we might want to
  factor out common parts into NativeThreadList{Protocol} and share
  some code here.  After writing it, though, the fallout from merging
  Mach Task/Process into a single concept plus some other minor
  changes makes the whole NativeThreadListDarwin concept nothing more
  than dead weight.  I am likely going to get rid of this class and
  just manage it directly in NativeProcessDarwin, much like I did
  for NativeProcessLinux.
* There is a stub-out call for starting a STDIO thread.  That will
  go away and adopt the MainLoop pselect-based IOObject reading.

I am developing the fully-integrated changes in the following repo,
which contains the necessary Xcode bits and the glue that enables
lldb-debugserver on a macOS system:

  https://github.com/tfiala/lldb/tree/llgs-darwin

This change also breaks out a few of the lldb-server tests into
their own directory, and adds some $qHostInfo tests (not sure why
I didn't write those tests back when I initially implemented that
on the Linux side).

llvm-svn: 280604

35 files changed:
lldb/include/lldb/Host/Debug.h
lldb/include/lldb/Host/common/NativeProcessProtocol.h
lldb/include/lldb/lldb-private-forward.h
lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteProcessInfo.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/TestGdbRemoteExitCode.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/TestGdbRemoteHostInfo.py [new file with mode: 0644]
lldb/source/Host/common/NativeProcessProtocol.cpp
lldb/source/Plugins/Process/Darwin/CFBundle.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/CFBundle.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/CFString.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/CFString.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/CFUtils.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/LaunchFlavor.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/MachException.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/MachException.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h [new file with mode: 0644]
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/source/Target/UnixSignals.cpp
lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist [new file with mode: 0644]
lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist [new file with mode: 0644]
lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist [new file with mode: 0644]
lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs [new file with mode: 0644]

index 7954ee0..9c4dd04 100644 (file)
@@ -201,7 +201,7 @@ namespace lldb_private {
             {
                 uint64_t type;
                 uint32_t data_count;
-                lldb::addr_t data[2];
+                lldb::addr_t data[8];
             } exception;
         } details;
     };
index d306a58..2c89cb4 100644 (file)
@@ -37,12 +37,6 @@ namespace lldb_private
 
     public:
 
-        // lldb_private::Host calls should be used to launch a process for debugging, and
-        // then the process should be attached to. When attaching to a process
-        // lldb_private::Host calls should be used to locate the process to attach to,
-        // and then this function should be called.
-        NativeProcessProtocol (lldb::pid_t pid);
-
         virtual ~NativeProcessProtocol ()
         {
         }
@@ -379,6 +373,12 @@ namespace lldb_private
         int m_terminal_fd;
         uint32_t m_stop_id;
 
+        // lldb_private::Host calls should be used to launch a process for debugging, and
+        // then the process should be attached to. When attaching to a process
+        // lldb_private::Host calls should be used to locate the process to attach to,
+        // and then this function should be called.
+        NativeProcessProtocol (lldb::pid_t pid);
+
         // -----------------------------------------------------------
         // Internal interface for state handling
         // -----------------------------------------------------------
@@ -415,6 +415,12 @@ namespace lldb_private
         NativeThreadProtocolSP
         GetThreadByIDUnlocked (lldb::tid_t tid);
 
+        // -----------------------------------------------------------
+        // Static helper methods for derived classes.
+        // -----------------------------------------------------------
+        static Error
+        ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch);
+
     private:
 
         void
index bcfeb68..043ab5e 100644 (file)
@@ -24,6 +24,7 @@ namespace lldb_private
     class NativeProcessProtocol;
     class NativeRegisterContext;
     class NativeThreadProtocol;
+    class ResumeActionList;
     class UnixSignals;
 
     // ---------------------------------------------------------------
index f26b620..fd89ddf 100644 (file)
@@ -155,6 +155,13 @@ class TestGdbRemoteProcessInfo(gdbremote_testcase.GdbRemoteTestCaseBase):
         self.build()
         self.qProcessInfo_contains_keys(set(['cputype', 'cpusubtype']))
 
+    @skipUnlessDarwin
+    @llgs_test
+    def test_qProcessInfo_contains_cputype_cpusubtype_llgs_darwin(self):
+        self.init_llgs_test()
+        self.build()
+        self.qProcessInfo_contains_keys(set(['cputype', 'cpusubtype']))
+
     @skipUnlessPlatform(["linux"])
     @llgs_test
     def test_qProcessInfo_contains_triple_llgs_linux(self):
@@ -172,6 +179,16 @@ class TestGdbRemoteProcessInfo(gdbremote_testcase.GdbRemoteTestCaseBase):
         # for the remote Host and Process.
         self.qProcessInfo_does_not_contain_keys(set(['triple']))
 
+    @skipUnlessDarwin
+    @llgs_test
+    def test_qProcessInfo_does_not_contain_triple_llgs_darwin(self):
+        self.init_llgs_test()
+        self.build()
+        # We don't expect to see triple on darwin.  If we do, we'll prefer triple
+        # to cputype/cpusubtype and skip some darwin-based ProcessGDBRemote ArchSpec setup
+        # for the remote Host and Process.
+        self.qProcessInfo_does_not_contain_keys(set(['triple']))
+
     @skipUnlessPlatform(["linux"])
     @llgs_test
     def test_qProcessInfo_does_not_contain_cputype_cpusubtype_llgs_linux(self):
index b60d08d..9cc20c1 100644 (file)
@@ -97,101 +97,6 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
         self.init_llgs_test()
         self.list_threads_in_stop_reply_supported()
 
-    def install_and_create_launch_args(self):
-        exe_path = os.path.abspath('a.out')
-        if not lldb.remote_platform:
-            return [exe_path]
-        remote_path = lldbutil.append_to_process_working_directory(os.path.basename(exe_path))
-        remote_file_spec = lldb.SBFileSpec(remote_path, False)
-        err = lldb.remote_platform.Install(lldb.SBFileSpec(exe_path, True), remote_file_spec)
-        if err.Fail():
-            raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (exe_path, remote_path, err))
-        return [remote_path]
-
-    def start_inferior(self):
-        launch_args = self.install_and_create_launch_args()
-
-        server = self.connect_to_debug_monitor()
-        self.assertIsNotNone(server)
-
-        self.add_no_ack_remote_stream()
-        self.test_sequence.add_log_lines(
-            ["read packet: %s" % lldbgdbserverutils.build_gdbremote_A_packet(launch_args),
-             "send packet: $OK#9a"],
-            True)
-        self.expect_gdbremote_sequence()
-
-    @debugserver_test
-    def test_start_inferior_debugserver(self):
-        self.init_debugserver_test()
-        self.build()
-        self.start_inferior()
-
-    @llgs_test
-    def test_start_inferior_llgs(self):
-        self.init_llgs_test()
-        self.build()
-        self.start_inferior()
-
-    def inferior_exit_0(self):
-        launch_args = self.install_and_create_launch_args()
-
-        server = self.connect_to_debug_monitor()
-        self.assertIsNotNone(server)
-
-        self.add_no_ack_remote_stream()
-        self.add_verified_launch_packets(launch_args)
-        self.test_sequence.add_log_lines(
-            ["read packet: $vCont;c#a8",
-             "send packet: $W00#00"],
-            True)
-
-        self.expect_gdbremote_sequence()
-
-    @debugserver_test
-    def test_inferior_exit_0_debugserver(self):
-        self.init_debugserver_test()
-        self.build()
-        self.inferior_exit_0()
-
-    @llgs_test
-    def test_inferior_exit_0_llgs(self):
-        self.init_llgs_test()
-        self.build()
-        self.inferior_exit_0()
-
-    def inferior_exit_42(self):
-        launch_args = self.install_and_create_launch_args()
-
-        server = self.connect_to_debug_monitor()
-        self.assertIsNotNone(server)
-
-        RETVAL = 42
-
-        # build launch args
-        launch_args += ["retval:%d" % RETVAL]
-
-        self.add_no_ack_remote_stream()
-        self.add_verified_launch_packets(launch_args)
-        self.test_sequence.add_log_lines(
-            ["read packet: $vCont;c#a8",
-             "send packet: $W{0:02x}#00".format(RETVAL)],
-            True)
-
-        self.expect_gdbremote_sequence()
-
-    @debugserver_test
-    def test_inferior_exit_42_debugserver(self):
-        self.init_debugserver_test()
-        self.build()
-        self.inferior_exit_42()
-
-    @llgs_test
-    def test_inferior_exit_42_llgs(self):
-        self.init_llgs_test()
-        self.build()
-        self.inferior_exit_42()
-
     def c_packet_works(self):
         launch_args = self.install_and_create_launch_args()
 
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/Makefile b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/Makefile
new file mode 100644 (file)
index 0000000..1370b53
--- /dev/null
@@ -0,0 +1,10 @@
+LEVEL = ../../../make
+
+VPATH = ..
+
+override CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
+ENABLE_THREADS := YES
+CXX_SOURCES := main.cpp
+MAKE_DSYM :=NO
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/TestGdbRemoteExitCode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/TestGdbRemoteExitCode.py
new file mode 100644 (file)
index 0000000..e77f2b7
--- /dev/null
@@ -0,0 +1,124 @@
+from __future__ import print_function
+
+# lldb test suite imports
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import TestBase
+
+# gdb-remote-specific imports
+import lldbgdbserverutils
+from gdbremote_testcase import GdbRemoteTestCaseBase
+
+
+class TestGdbRemoteExitCode(GdbRemoteTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    FAILED_LAUNCH_CODE = "E08"
+
+    def get_launch_fail_reason(self):
+        self.reset_test_sequence()
+        self.test_sequence.add_log_lines(
+            ["read packet: $qLaunchSuccess#00"],
+            True)
+        self.test_sequence.add_log_lines(
+            [{"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$",
+              "capture": {1: "launch_result"}}],
+            True)
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        return context.get("launch_result")[1:]
+
+    def start_inferior(self):
+        launch_args = self.install_and_create_launch_args()
+
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+
+        self.add_no_ack_remote_stream()
+        self.test_sequence.add_log_lines(
+            ["read packet: %s" % lldbgdbserverutils.build_gdbremote_A_packet(
+                launch_args)],
+            True)
+        self.test_sequence.add_log_lines(
+            [{"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$",
+              "capture": {1: "A_result"}}],
+            True)
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        launch_result = context.get("A_result")
+        self.assertIsNotNone(launch_result)
+        if launch_result == self.FAILED_LAUNCH_CODE:
+            fail_reason = self.get_launch_fail_reason()
+            self.fail("failed to launch inferior: " + fail_reason)
+
+    @debugserver_test
+    def test_start_inferior_debugserver(self):
+        self.init_debugserver_test()
+        self.build()
+        self.start_inferior()
+
+    @llgs_test
+    def test_start_inferior_llgs(self):
+        self.init_llgs_test()
+        self.build()
+        self.start_inferior()
+
+    def inferior_exit_0(self):
+        launch_args = self.install_and_create_launch_args()
+
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+
+        self.add_no_ack_remote_stream()
+        self.add_verified_launch_packets(launch_args)
+        self.test_sequence.add_log_lines(
+            ["read packet: $vCont;c#a8",
+             "send packet: $W00#00"],
+            True)
+
+        self.expect_gdbremote_sequence()
+
+    @debugserver_test
+    def test_inferior_exit_0_debugserver(self):
+        self.init_debugserver_test()
+        self.build()
+        self.inferior_exit_0()
+
+    @llgs_test
+    def test_inferior_exit_0_llgs(self):
+        self.init_llgs_test()
+        self.build()
+        self.inferior_exit_0()
+
+    def inferior_exit_42(self):
+        launch_args = self.install_and_create_launch_args()
+
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+
+        RETVAL = 42
+
+        # build launch args
+        launch_args += ["retval:%d" % RETVAL]
+
+        self.add_no_ack_remote_stream()
+        self.add_verified_launch_packets(launch_args)
+        self.test_sequence.add_log_lines(
+            ["read packet: $vCont;c#a8",
+             "send packet: $W{0:02x}#00".format(RETVAL)],
+            True)
+
+        self.expect_gdbremote_sequence()
+
+    @debugserver_test
+    def test_inferior_exit_42_debugserver(self):
+        self.init_debugserver_test()
+        self.build()
+        self.inferior_exit_42()
+
+    @llgs_test
+    def test_inferior_exit_42_llgs(self):
+        self.init_llgs_test()
+        self.build()
+        self.inferior_exit_42()
index 4db630d..12c1033 100644 (file)
@@ -133,7 +133,7 @@ class GdbRemoteTestCaseBase(TestBase):
             self.debug_monitor_extra_args.append("--log-file=" + log_file)
             self.debug_monitor_extra_args.append("--log-channels={}".format(":".join(lldbtest_config.channels)))
         else:
-            self.debug_monitor_extra_args = ["--log-file=" + self.log_file, "--log-flags=0x800000"]
+            self.debug_monitor_extra_args = ["--log-file=" + log_file, "--log-flags=0x800000"]
 
     def get_next_port(self):
         return 12000 + random.randint(0,3999)
@@ -1370,3 +1370,16 @@ class GdbRemoteTestCaseBase(TestBase):
     def maybe_strict_output_regex(self, regex):
         return '.*'+regex+'.*' if lldbplatformutil.hasChattyStderr(self) else '^'+regex+'$'
 
+    def install_and_create_launch_args(self):
+        exe_path = os.path.abspath('a.out')
+        if not lldb.remote_platform:
+            return [exe_path]
+        remote_path = lldbutil.append_to_process_working_directory(
+            os.path.basename(exe_path))
+        remote_file_spec = lldb.SBFileSpec(remote_path, False)
+        err = lldb.remote_platform.Install(lldb.SBFileSpec(exe_path, True),
+                                           remote_file_spec)
+        if err.Fail():
+            raise Exception("remote_platform.Install('%s', '%s') failed: %s" %
+                            (exe_path, remote_path, err))
+        return [remote_path]
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/Makefile b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/Makefile
new file mode 100644 (file)
index 0000000..1370b53
--- /dev/null
@@ -0,0 +1,10 @@
+LEVEL = ../../../make
+
+VPATH = ..
+
+override CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
+ENABLE_THREADS := YES
+CXX_SOURCES := main.cpp
+MAKE_DSYM :=NO
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/TestGdbRemoteHostInfo.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/host-info/TestGdbRemoteHostInfo.py
new file mode 100644 (file)
index 0000000..6502f03
--- /dev/null
@@ -0,0 +1,125 @@
+from __future__ import print_function
+
+# lldb test suite imports
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import TestBase
+
+# gdb-remote-specific imports
+import lldbgdbserverutils
+from gdbremote_testcase import GdbRemoteTestCaseBase
+
+
+class TestGdbRemoteHostInfo(GdbRemoteTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    KNOWN_HOST_INFO_KEYS = set([
+        "cputype",
+        "cpusubtype",
+        "distribution_id",
+        "endian",
+        "hostname",
+        "ostype",
+        "os_build",
+        "os_kernel",
+        "os_version",
+        "ptrsize",
+        "triple",
+        "vendor",
+        "watchpoint_exceptions_received"
+    ])
+
+    DARWIN_REQUIRED_HOST_INFO_KEYS = set([
+        "cputype",
+        "cpusubtype",
+        "endian",
+        "ostype",
+        "ptrsize",
+        "vendor",
+        "watchpoint_exceptions_received"
+    ])
+
+    def add_host_info_collection_packets(self):
+        self.test_sequence.add_log_lines(
+            ["read packet: $qHostInfo#9b",
+             {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$",
+              "capture": {1: "host_info_raw"}}],
+            True)
+
+    def parse_host_info_response(self, context):
+        # Ensure we have a host info response.
+        self.assertIsNotNone(context)
+        host_info_raw = context.get("host_info_raw")
+        self.assertIsNotNone(host_info_raw)
+
+        # Pull out key:value; pairs.
+        host_info_dict = {match.group(1): match.group(2)
+                          for match in re.finditer(r"([^:]+):([^;]+);",
+                                                   host_info_raw)}
+
+        import pprint
+        print("\nqHostInfo response:")
+        pprint.pprint(host_info_dict)
+
+        # Validate keys are known.
+        for (key, val) in list(host_info_dict.items()):
+            self.assertTrue(key in self.KNOWN_HOST_INFO_KEYS,
+                            "unknown qHostInfo key: " + key)
+            self.assertIsNotNone(val)
+
+        # Return the key:val pairs.
+        return host_info_dict
+
+    def get_qHostInfo_response(self):
+        # Launch the debug monitor stub, attaching to the inferior.
+        server = self.connect_to_debug_monitor()
+        self.assertIsNotNone(server)
+        self.add_no_ack_remote_stream()
+
+        # Request qHostInfo and get response
+        self.add_host_info_collection_packets()
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        # Parse qHostInfo response.
+        host_info = self.parse_host_info_response(context)
+        self.assertIsNotNone(host_info)
+        self.assertGreater(len(host_info), 0, "qHostInfo should have returned "
+                           "at least one key:val pair.")
+        return host_info
+
+    def validate_darwin_minimum_host_info_keys(self, host_info_dict):
+        self.assertIsNotNone(host_info_dict)
+        missing_keys = [key for key in self.DARWIN_REQUIRED_HOST_INFO_KEYS
+                        if key not in host_info_dict]
+        self.assertEquals(0, len(missing_keys),
+                          "qHostInfo is missing the following required "
+                          "keys: " + str(missing_keys))
+
+    @debugserver_test
+    def test_qHostInfo_returns_at_least_one_key_val_pair_debugserver(self):
+        self.init_debugserver_test()
+        self.build()
+        self.get_qHostInfo_response()
+
+    @llgs_test
+    def test_qHostInfo_returns_at_least_one_key_val_pair_llgs(self):
+        self.init_llgs_test()
+        self.build()
+        self.get_qHostInfo_response()
+
+    @skipUnlessDarwin
+    @debugserver_test
+    def test_qHostInfo_contains_darwin_required_keys_debugserver(self):
+        self.init_debugserver_test()
+        self.build()
+        host_info_dict = self.get_qHostInfo_response()
+        self.validate_darwin_minimum_host_info_keys(host_info_dict)
+
+    @skipUnlessDarwin
+    @llgs_test
+    def test_qHostInfo_contains_darwin_required_keys_llgs(self):
+        self.init_llgs_test()
+        self.build()
+        host_info_dict = self.get_qHostInfo_response()
+        self.validate_darwin_minimum_host_info_keys(host_info_dict)
index dfac0cb..bdc58fb 100644 (file)
 #include "lldb/lldb-enumerations.h"
 #include "lldb/Core/ArchSpec.h"
 #include "lldb/Core/Log.h"
+#include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/State.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/common/NativeRegisterContext.h"
-
 #include "lldb/Host/common/NativeThreadProtocol.h"
 #include "lldb/Host/common/SoftwareBreakpoint.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/LLDBAssert.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -436,6 +439,29 @@ NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */)
     // Default implementation does nothing.
 }
 
+Error
+NativeProcessProtocol::ResolveProcessArchitecture(lldb::pid_t pid,
+                                                  ArchSpec &arch)
+{
+    // Grab process info for the running process.
+    ProcessInstanceInfo process_info;
+    if (!Host::GetProcessInfo(pid, process_info))
+        return Error("failed to get process info");
+
+    // Resolve the executable module.
+    ModuleSpecList module_specs;
+    if (!ObjectFile::GetModuleSpecifications(process_info.GetExecutableFile(),
+                                             0, 0, module_specs))
+        return Error("failed to get module specifications");
+    lldbassert(module_specs.GetSize() == 1);
+
+    arch = module_specs.GetModuleSpecRefAtIndex(0).GetArchitecture();
+    if (arch.IsValid())
+        return Error();
+    else
+        return Error("failed to retrieve a valid architecture from the exe module");
+}
+
 #ifndef __linux__
 // These need to be implemented to support lldb-gdb-server on a given platform. Stubs are
 // provided to make the rest of the code link on non-supported platforms.
diff --git a/lldb/source/Plugins/Process/Darwin/CFBundle.cpp b/lldb/source/Plugins/Process/Darwin/CFBundle.cpp
new file mode 100644 (file)
index 0000000..fdcb7cc
--- /dev/null
@@ -0,0 +1,97 @@
+//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+//----------------------------------------------------------------------
+// CFBundle constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const char *path) :
+    CFReleaser<CFBundleRef>(),
+    m_bundle_url()
+{
+    if (path && path[0])
+        SetPath(path);
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle::CFBundle(const CFBundle& rhs) :
+    CFReleaser<CFBundleRef>(rhs),
+    m_bundle_url(rhs.m_bundle_url)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFBundle copy constructor
+//----------------------------------------------------------------------
+CFBundle&
+CFBundle::operator=(const CFBundle& rhs)
+{
+    *this = rhs;
+    return *this;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFBundle::~CFBundle()
+{
+}
+
+//----------------------------------------------------------------------
+// Set the path for a bundle by supplying a
+//----------------------------------------------------------------------
+bool
+CFBundle::SetPath (const char *path)
+{
+    CFAllocatorRef alloc = kCFAllocatorDefault;
+    // Release our old bundle and ULR
+    reset();    // This class is a CFReleaser<CFBundleRef>
+    m_bundle_url.reset();
+    // Make a CFStringRef from the supplied path
+    CFString cf_path;
+    cf_path.SetFileSystemRepresentation(path);
+    if (cf_path.get())
+    {
+        // Make our Bundle URL
+        m_bundle_url.reset (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+        if (m_bundle_url.get())
+        {
+            reset (::CFBundleCreate (alloc, m_bundle_url.get()));
+        }
+    }
+    return get() != NULL;
+}
+
+CFStringRef
+CFBundle::GetIdentifier () const
+{
+    CFBundleRef bundle = get();
+    if (bundle != NULL)
+        return ::CFBundleGetIdentifier (bundle);
+    return NULL;
+}
+
+
+CFURLRef
+CFBundle::CopyExecutableURL () const
+{
+    CFBundleRef bundle = get();
+    if (bundle != NULL)
+        return CFBundleCopyExecutableURL(bundle);
+    return NULL;
+}
diff --git a/lldb/source/Plugins/Process/Darwin/CFBundle.h b/lldb/source/Plugins/Process/Darwin/CFBundle.h
new file mode 100644 (file)
index 0000000..e08290a
--- /dev/null
@@ -0,0 +1,43 @@
+//===-- CFBundle.h ----------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFBundle_h__
+#define __CFBundle_h__
+
+#include "CFUtils.h"
+
+class CFBundle : public CFReleaser<CFBundleRef>
+{
+public:
+    //------------------------------------------------------------------
+    // Constructors and Destructors
+    //------------------------------------------------------------------
+    CFBundle(const char *path = NULL);
+    CFBundle(const CFBundle& rhs);
+    CFBundle& operator=(const CFBundle& rhs);
+    virtual
+    ~CFBundle();
+    bool
+    SetPath (const char *path);
+
+    CFStringRef
+    GetIdentifier () const;
+
+    CFURLRef
+    CopyExecutableURL () const;
+    
+protected:
+        CFReleaser<CFURLRef> m_bundle_url;
+};
+
+#endif // #ifndef __CFBundle_h__
diff --git a/lldb/source/Plugins/Process/Darwin/CFString.cpp b/lldb/source/Plugins/Process/Darwin/CFString.cpp
new file mode 100644 (file)
index 0000000..819024c
--- /dev/null
@@ -0,0 +1,201 @@
+//===-- CFString.cpp --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFString.h"
+#include <string>
+#include <glob.h>
+
+//----------------------------------------------------------------------
+// CFString constructor
+//----------------------------------------------------------------------
+CFString::CFString(CFStringRef s) :
+    CFReleaser<CFStringRef> (s)
+{
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString::CFString(const CFString& rhs) :
+    CFReleaser<CFStringRef> (rhs)
+{
+
+}
+
+//----------------------------------------------------------------------
+// CFString copy constructor
+//----------------------------------------------------------------------
+CFString&
+CFString::operator=(const CFString& rhs)
+{
+    if (this != &rhs)
+        *this = rhs;
+    return *this;
+}
+
+CFString::CFString (const char *cstr, CFStringEncoding cstr_encoding) :
+    CFReleaser<CFStringRef> ()
+{
+    if (cstr && cstr[0])
+    {
+        reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+    }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CFString::~CFString()
+{
+}
+
+const char *
+CFString::GetFileSystemRepresentation(std::string& s)
+{
+    return CFString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentation (const char *path)
+{
+    CFStringRef new_value = NULL;
+    if (path && path[0])
+        new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path);
+    reset(new_value);
+    return get();
+}
+
+
+CFStringRef
+CFString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type)
+{
+    CFStringRef new_value = NULL;
+    if (cf_type != NULL)
+    {
+        CFTypeID cf_type_id = ::CFGetTypeID(cf_type);
+
+        if (cf_type_id == ::CFStringGetTypeID())
+        {
+            // Retain since we are using the existing object
+            new_value = (CFStringRef)::CFRetain(cf_type);
+        }
+        else if (cf_type_id == ::CFURLGetTypeID())
+        {
+            new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle);
+        }
+    }
+    reset(new_value);
+    return get();
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentationAndExpandTilde (const char *path)
+{
+    std::string expanded_path;
+    if (CFString::GlobPath(path, expanded_path))
+        SetFileSystemRepresentation(expanded_path.c_str());
+    else
+        reset();
+    return get();
+}
+
+const char *
+CFString::UTF8(std::string& str)
+{
+    return CFString::UTF8(get(), str);
+}
+
+// Static function that puts a copy of the UTF8 contents of CF_STR into STR
+// and returns the C string pointer that is contained in STR when successful, else
+// NULL is returned. This allows the std::string parameter to own the extracted string,
+// and also allows that string to be returned as a C string pointer that can be used.
+
+const char *
+CFString::UTF8 (CFStringRef cf_str, std::string& str)
+{
+    if (cf_str)
+    {
+        const CFStringEncoding encoding = kCFStringEncodingUTF8;
+        CFIndex max_utf8_str_len = CFStringGetLength (cf_str);
+        max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding);
+        if (max_utf8_str_len > 0)
+        {
+            str.resize(max_utf8_str_len);
+            if (!str.empty())
+            {
+                if (CFStringGetCString (cf_str, &str[0], str.size(), encoding))
+                {
+                    str.resize(strlen(str.c_str()));
+                    return str.c_str();
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+// Static function that puts a copy of the file system representation of CF_STR
+// into STR and returns the C string pointer that is contained in STR when
+// successful, else NULL is returned. This allows the std::string parameter
+// to own the extracted string, and also allows that string to be returned as
+// a C string pointer that can be used.
+
+const char *
+CFString::FileSystemRepresentation (CFStringRef cf_str, std::string& str)
+{
+    if (cf_str)
+    {
+        CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str);
+        if (max_length > 0)
+        {
+            str.resize(max_length);
+            if (!str.empty())
+            {
+                if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size()))
+                {
+                    str.erase(::strlen(str.c_str()));
+                    return str.c_str();
+                }
+            }
+        }
+    }
+    str.erase();
+    return NULL;
+}
+
+
+CFIndex
+CFString::GetLength() const
+{
+    CFStringRef str = get();
+    if (str)
+        return CFStringGetLength (str);
+    return 0;
+}
+
+
+const char*
+CFString::GlobPath(const char* path, std::string &expanded_path)
+{
+    glob_t globbuf;
+    if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0)
+    {
+        expanded_path = globbuf.gl_pathv[0];
+        ::globfree (&globbuf);
+    }
+    else
+        expanded_path.clear();
+
+    return expanded_path.c_str();
+}
+
diff --git a/lldb/source/Plugins/Process/Darwin/CFString.h b/lldb/source/Plugins/Process/Darwin/CFString.h
new file mode 100644 (file)
index 0000000..73945a2
--- /dev/null
@@ -0,0 +1,43 @@
+//===-- CFString.h ----------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFString_h__
+#define __CFString_h__
+
+#include "CFUtils.h"
+#include <iosfwd>
+
+class CFString : public CFReleaser<CFStringRef>
+{
+public:
+    //------------------------------------------------------------------
+    // Constructors and Destructors
+    //------------------------------------------------------------------
+                        CFString (CFStringRef cf_str = NULL);
+                        CFString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8);
+                        CFString (const CFString& rhs);
+                        CFString& operator= (const CFString& rhs);
+                        virtual ~CFString ();
+
+        const char *    GetFileSystemRepresentation (std::string& str);
+        CFStringRef     SetFileSystemRepresentation (const char *path);
+        CFStringRef     SetFileSystemRepresentationFromCFType (CFTypeRef cf_type);
+        CFStringRef     SetFileSystemRepresentationAndExpandTilde (const char *path);
+        const char *    UTF8 (std::string& str);
+        CFIndex         GetLength() const;
+        static const char *UTF8 (CFStringRef cf_str, std::string& str);
+        static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str);
+        static const char* GlobPath(const char* path, std::string &expanded_path);
+};
+
+#endif // #ifndef __CFString_h__
diff --git a/lldb/source/Plugins/Process/Darwin/CFUtils.h b/lldb/source/Plugins/Process/Darwin/CFUtils.h
new file mode 100644 (file)
index 0000000..afa984f
--- /dev/null
@@ -0,0 +1,81 @@
+//===-- CFUtils.h -----------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 3/5/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFUtils_h__
+#define __CFUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+//----------------------------------------------------------------------
+// Templatized CF helper class that can own any CF pointer and will
+// call CFRelease() on any valid pointer it owns unless that pointer is
+// explicitly released using the release() member function.
+//----------------------------------------------------------------------
+template <class T>
+class CFReleaser
+{
+public:
+            // Type names for the avlue
+            typedef T element_type;
+
+            // Constructors and destructors
+            CFReleaser(T ptr = NULL) : _ptr(ptr) { }
+            CFReleaser(const CFReleaser& copy) : _ptr(copy.get())
+            {
+                if (get())
+                    ::CFRetain(get());
+            }
+            virtual ~CFReleaser() { reset(); }
+
+            // Assignments
+            CFReleaser& operator= (const CFReleaser<T>& copy)
+            {
+                if (copy != *this)
+                {
+                    // Replace our owned pointer with the new one
+                    reset(copy.get());
+                    // Retain the current pointer that we own
+                    if (get())
+                        ::CFRetain(get());
+                }
+            }
+            // Get the address of the contained type
+            T *     ptr_address()   { return &_ptr; }
+
+            // Access the pointer itself
+    const   T       get() const     { return _ptr;  }
+            T       get()           { return _ptr;  }
+
+            // Set a new value for the pointer and CFRelease our old
+            // value if we had a valid one.
+            void    reset(T ptr = NULL)
+                    {
+                        if (ptr != _ptr)
+                        {
+                            if (_ptr != NULL)
+                                ::CFRelease(_ptr);
+                            _ptr = ptr;
+                        }
+                    }
+
+            // Release ownership without calling CFRelease
+            T    release() { T tmp = _ptr; _ptr = NULL; return tmp; }
+private:
+    element_type _ptr;
+};
+
+#endif    // #ifdef __cplusplus
+#endif    // #ifndef __CFUtils_h__
+
diff --git a/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp b/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp
new file mode 100644 (file)
index 0000000..91abe4f
--- /dev/null
@@ -0,0 +1,737 @@
+//
+//  DarwinProcessLauncher.cpp
+//  lldb
+//
+//  Created by Todd Fiala on 8/30/16.
+//
+//
+
+#include "DarwinProcessLauncher.h"
+
+// C includes
+#include <spawn.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR       0x0100
+#endif
+
+// LLDB includes
+#include "lldb/lldb-enumerations.h"
+
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+namespace
+{
+    static LaunchFlavor g_launch_flavor = LaunchFlavor::Default;
+}
+
+namespace lldb_private
+{
+namespace darwin_process_launcher
+{
+
+static uint32_t
+GetCPUTypeForLocalProcess(::pid_t pid)
+{
+    int mib[CTL_MAXNAME]={0,};
+    size_t len = CTL_MAXNAME;
+    if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+        return 0;
+
+    mib[len] = pid;
+    len++;
+
+    cpu_type_t cpu;
+    size_t cpu_len = sizeof(cpu);
+    if (::sysctl (mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+        cpu = 0;
+    return cpu;
+}
+
+static bool
+ResolveExecutablePath(const char *path, char *resolved_path,
+                      size_t resolved_path_size)
+{
+    if (path == NULL || path[0] == '\0')
+        return false;
+
+    char max_path[PATH_MAX];
+    std::string result;
+    CFString::GlobPath(path, result);
+
+    if (result.empty())
+        result = path;
+
+    struct stat path_stat;
+    if (::stat(path, &path_stat) == 0)
+    {
+        if ((path_stat.st_mode & S_IFMT) == S_IFDIR)
+        {
+            CFBundle bundle(path);
+            CFReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
+            if (url.get())
+            {
+                if (::CFURLGetFileSystemRepresentation(url.get(), true,
+                                                       (UInt8*)resolved_path,
+                                                       resolved_path_size))
+                    return true;
+            }
+        }
+    }
+
+    if (realpath(path, max_path))
+    {
+        // Found the path relatively...
+        ::strncpy(resolved_path, max_path, resolved_path_size);
+        return strlen(resolved_path) + 1 < resolved_path_size;
+    }
+    else
+    {
+        // Not a relative path, check the PATH environment variable if the
+        const char *PATH = getenv("PATH");
+        if (PATH)
+        {
+            const char *curr_path_start = PATH;
+            const char *curr_path_end;
+            while (curr_path_start && *curr_path_start)
+            {
+                curr_path_end = strchr(curr_path_start, ':');
+                if (curr_path_end == NULL)
+                {
+                    result.assign(curr_path_start);
+                    curr_path_start = NULL;
+                }
+                else if (curr_path_end > curr_path_start)
+                {
+                    size_t len = curr_path_end - curr_path_start;
+                    result.assign(curr_path_start, len);
+                    curr_path_start += len + 1;
+                }
+                else
+                    break;
+
+                result += '/';
+                result += path;
+                struct stat s;
+                if (stat(result.c_str(), &s) == 0)
+                {
+                    ::strncpy(resolved_path, result.c_str(),
+                              resolved_path_size);
+                    return result.size() + 1 < resolved_path_size;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+// TODO check if we have a general purpose fork and exec.  We may be
+// able to get rid of this entirely.
+static Error
+ForkChildForPTraceDebugging(const char *path,
+                            char const *argv[],
+                            char const *envp[],
+                            ::pid_t *pid,
+                            int *pty_fd)
+{
+    Error error;
+    if (!path || !argv || !envp || !pid || !pty_fd)
+    {
+        error.SetErrorString("invalid arguments");
+        return error;
+    }
+
+    // Use a fork that ties the child process's stdin/out/err to a pseudo
+    // terminal so we can read it in our MachProcess::STDIOThread
+    // as unbuffered io.
+    lldb_utility::PseudoTerminal pty;
+    char fork_error[256];
+    memset(fork_error, 0, sizeof(fork_error));
+    *pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error)));
+    if (*pid < 0)
+    {
+        //--------------------------------------------------------------
+        // Error during fork.
+        //--------------------------------------------------------------
+        *pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID);
+        error.SetErrorStringWithFormat("%s(): fork failed: %s",
+                                       __FUNCTION__, fork_error);
+        return error;
+    }
+    else if (pid == 0)
+    {
+        //--------------------------------------------------------------
+        // Child process
+        //--------------------------------------------------------------
+
+        // Debug this process.
+        ::ptrace(PT_TRACE_ME, 0, 0, 0);
+
+        // Get BSD signals as mach exceptions.
+        ::ptrace(PT_SIGEXC, 0, 0, 0);
+
+        // If our parent is setgid, lets make sure we don't inherit those
+        // extra powers due to nepotism.
+        if (::setgid(getgid()) == 0)
+        {
+            // Let the child have its own process group. We need to execute
+            // this call in both the child and parent to avoid a race
+            // condition between the two processes.
+
+            // Set the child process group to match its pid.
+            ::setpgid(0, 0);
+
+            // Sleep a bit to before the exec call.
+            ::sleep(1);
+
+            // Turn this process into the given executable.
+            ::execv(path, (char * const *)argv);
+        }
+        // Exit with error code. Child process should have taken
+        // over in above exec call and if the exec fails it will
+        // exit the child process below.
+        ::exit(127);
+    }
+    else
+    {
+        //--------------------------------------------------------------
+        // Parent process
+        //--------------------------------------------------------------
+        // Let the child have its own process group. We need to execute
+        // this call in both the child and parent to avoid a race condition
+        // between the two processes.
+
+        // Set the child process group to match its pid
+        ::setpgid(*pid, *pid);
+        if (pty_fd)
+        {
+            // Release our master pty file descriptor so the pty class doesn't
+            // close it and so we can continue to use it in our STDIO thread
+            *pty_fd = pty.ReleaseMasterFileDescriptor();
+        }
+    }
+    return error;
+}
+
+static Error
+CreatePosixSpawnFileAction(const FileAction &action,
+                           posix_spawn_file_actions_t *file_actions)
+{
+    Error error;
+
+    // Log it.
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+    if (log)
+    {
+        StreamString stream;
+        stream.PutCString("converting file action for posix_spawn(): ");
+        action.Dump(stream);
+        stream.Flush();
+        log->PutCString(stream.GetString().c_str());
+    }
+
+    // Validate args.
+    if (!file_actions)
+    {
+        error.SetErrorString("mandatory file_actions arg is null");
+        return error;
+    }
+
+    // Build the posix file action.
+    switch (action.GetAction())
+    {
+        case FileAction::eFileActionOpen:
+        {
+            const int error_code =
+                ::posix_spawn_file_actions_addopen(file_actions,
+                                                   action.GetFD(),
+                                                   action.GetPath(),
+                                                   action.GetActionArgument(),
+                                                   0);
+            if (error_code != 0)
+            {
+                error.SetError(error_code, eErrorTypePOSIX);
+                return error;
+            }
+            break;
+        }
+
+        case FileAction::eFileActionClose:
+        {
+            const int error_code =
+            ::posix_spawn_file_actions_addclose(file_actions,
+                                                action.GetFD());
+            if (error_code != 0)
+            {
+                error.SetError(error_code, eErrorTypePOSIX);
+                return error;
+            }
+            break;
+        }
+
+        case FileAction::eFileActionDuplicate:
+        {
+            const int error_code =
+            ::posix_spawn_file_actions_adddup2(file_actions,
+                                               action.GetFD(),
+                                               action.GetActionArgument());
+            if (error_code != 0)
+            {
+                error.SetError(error_code, eErrorTypePOSIX);
+                return error;
+            }
+            break;
+        }
+
+        case FileAction::eFileActionNone:
+        default:
+            if (log)
+                log->Printf("%s(): unsupported file action %u",
+                            __FUNCTION__, action.GetAction());
+            break;
+    }
+
+    return error;
+}
+
+static Error
+PosixSpawnChildForPTraceDebugging(const char *path,
+                                  ProcessLaunchInfo &launch_info,
+                                  ::pid_t *pid,
+                                  cpu_type_t *actual_cpu_type)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    if (!pid)
+    {
+        error.SetErrorStringWithFormat("%s(): pid arg cannot be null",
+                                       __FUNCTION__);
+        return error;
+    }
+
+    posix_spawnattr_t attr;
+    short flags;
+    if (log)
+    {
+        StreamString stream;
+        stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path);
+        launch_info.Dump(stream, nullptr);
+        stream.Flush();
+        log->PutCString(stream.GetString().c_str());
+    }
+
+    int error_code;
+    if ((error_code = ::posix_spawnattr_init(&attr)) != 0)
+    {
+        if (log)
+            log->Printf("::posix_spawnattr_init(&attr) failed");
+        error.SetError(error_code, eErrorTypePOSIX);
+        return error;
+    }
+
+    // Ensure we clean up the spawnattr structure however we exit this
+    // function.
+    std::unique_ptr<posix_spawnattr_t, int(*)(posix_spawnattr_t*)>
+        spawnattr_up(&attr, ::posix_spawnattr_destroy);
+
+
+    flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
+        POSIX_SPAWN_SETSIGMASK;
+    if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
+        flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+    sigset_t no_signals;
+    sigset_t all_signals;
+    sigemptyset (&no_signals);
+    sigfillset (&all_signals);
+    ::posix_spawnattr_setsigmask(&attr, &no_signals);
+    ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+    if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0)
+    {
+        if (log)
+            log->Printf("::posix_spawnattr_setflags(&attr, "
+                        "POSIX_SPAWN_START_SUSPENDED%s) failed: %s",
+                        flags & _POSIX_SPAWN_DISABLE_ASLR ?
+                        " | _POSIX_SPAWN_DISABLE_ASLR" : "",
+                        strerror(error_code));
+        error.SetError(error_code, eErrorTypePOSIX);
+        return error;
+    }
+
+#if !defined(__arm__)
+
+    // We don't need to do this for ARM, and we really shouldn't now that we
+    // have multiple CPU subtypes and no posix_spawnattr call that allows us
+    // to set which CPU subtype to launch...
+    cpu_type_t desired_cpu_type =
+        launch_info.GetArchitecture().GetMachOCPUType();
+    if (desired_cpu_type != LLDB_INVALID_CPUTYPE)
+    {
+        size_t ocount = 0;
+        error_code = ::posix_spawnattr_setbinpref_np(&attr, 1,
+                                                     &desired_cpu_type,
+                                                     &ocount);
+        if (error_code != 0)
+        {
+            if (log)
+                log->Printf("::posix_spawnattr_setbinpref_np(&attr, 1, "
+                            "cpu_type = 0x%8.8x, count => %llu): %s",
+                            desired_cpu_type, (uint64_t)ocount,
+                            strerror(error_code));
+            error.SetError(error_code, eErrorTypePOSIX);
+            return error;
+        }
+        if (ocount != 1)
+        {
+            error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np "
+                                           "did not set the expected number "
+                                           "of cpu_type entries: expected 1 "
+                                           "but was %zu", ocount);
+            return error;
+        }
+    }
+#endif
+
+    posix_spawn_file_actions_t file_actions;
+    if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0)
+    {
+        if (log)
+            log->Printf("::posix_spawn_file_actions_init(&file_actions) "
+                        "failed: %s",
+                        strerror(error_code));
+        error.SetError(error_code, eErrorTypePOSIX);
+        return error;
+    }
+
+    // Ensure we clean up file actions however we exit this.  When the
+    // file_actions_up below goes out of scope, we'll get our file action
+    // cleanup.
+    std::unique_ptr<posix_spawn_file_actions_t,
+                    int(*)(posix_spawn_file_actions_t*)>
+        file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy);
+
+    // We assume the caller has setup the file actions appropriately.  We
+    // are not in the business of figuring out what we really need here.
+    // lldb-server will have already called FinalizeFileActions() as well
+    // to button these up properly.
+    const size_t num_actions = launch_info.GetNumFileActions();
+    for (size_t action_index = 0; action_index < num_actions; ++action_index)
+    {
+        const FileAction *const action =
+            launch_info.GetFileActionAtIndex(action_index);
+        if (!action)
+            continue;
+
+        error = CreatePosixSpawnFileAction(*action, &file_actions);
+        if (!error.Success())
+        {
+            if (log)
+                log->Printf("%s(): error converting FileAction to posix_spawn "
+                            "file action: %s", __FUNCTION__, error.AsCString());
+            return error;
+        }
+    }
+
+    // TODO: Verify if we can set the working directory back immediately
+    // after the posix_spawnp call without creating a race condition???
+    const char *const working_directory =
+        launch_info.GetWorkingDirectory().GetCString();
+    if (working_directory && working_directory[0])
+        ::chdir(working_directory);
+
+    auto argv = launch_info.GetArguments().GetArgumentVector();
+    auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector();
+    error_code = ::posix_spawnp(pid, path, &file_actions, &attr,
+                                (char * const*)argv, (char * const*)envp);
+    if (error_code != 0)
+    {
+        if (log)
+            log->Printf("::posix_spawnp(pid => %p, path = '%s', file_actions "
+                        "= %p, attr = %p, argv = %p, envp = %p) failed: %s",
+                        pid, path, &file_actions, &attr, argv, envp,
+                        strerror(error_code));
+        error.SetError(error_code, eErrorTypePOSIX);
+        return error;
+    }
+
+    // Validate we got a pid.
+    if (pid == LLDB_INVALID_PROCESS_ID)
+    {
+        error.SetErrorString("posix_spawn() did not indicate a failure but it "
+                             "failed to return a pid, aborting.");
+        return error;
+    }
+
+    if (actual_cpu_type)
+    {
+        *actual_cpu_type = GetCPUTypeForLocalProcess(*pid);
+        if (log)
+            log->Printf("%s(): cpu type for launched process pid=%i: "
+                        "cpu_type=0x%8.8x", __FUNCTION__, *pid,
+                        *actual_cpu_type);
+    }
+
+    return error;
+}
+
+Error
+LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+               LaunchFlavor *launch_flavor)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    if (!launch_flavor)
+    {
+        error.SetErrorString("mandatory launch_flavor field was null");
+        return error;
+    }
+
+    if (log)
+    {
+        StreamString stream;
+        stream.Printf("NativeProcessDarwin::%s(): launching with the "
+                      "following launch info:", __FUNCTION__);
+        launch_info.Dump(stream, nullptr);
+        stream.Flush();
+        log->PutCString(stream.GetString().c_str());
+    }
+
+    // Retrieve the binary name given to us.
+    char given_path[PATH_MAX];
+    given_path[0] = '\0';
+    launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path));
+
+    // Determine the manner in which we'll launch.
+    *launch_flavor = g_launch_flavor;
+    if (*launch_flavor == LaunchFlavor::Default)
+    {
+        // Our default launch method is posix spawn
+        *launch_flavor = LaunchFlavor::PosixSpawn;
+#if defined WITH_FBS
+        // Check if we have an app bundle, if so launch using BackBoard Services.
+        if (strstr(given_path, ".app"))
+        {
+            *launch_flavor = eLaunchFlavorFBS;
+        }
+#elif defined WITH_BKS
+        // Check if we have an app bundle, if so launch using BackBoard Services.
+        if (strstr(given_path, ".app"))
+        {
+            *launch_flavor = eLaunchFlavorBKS;
+        }
+#elif defined WITH_SPRINGBOARD
+        // Check if we have an app bundle, if so launch using SpringBoard.
+        if (strstr(given_path, ".app"))
+        {
+            *launch_flavor = eLaunchFlavorSpringBoard;
+        }
+#endif
+    }
+
+    // Attempt to resolve the binary name to an absolute path.
+    char resolved_path[PATH_MAX];
+    resolved_path[0] = '\0';
+
+    if (log)
+        log->Printf("%s(): attempting to resolve given binary path: \"%s\"",
+                    __FUNCTION__, given_path);
+
+    // If we fail to resolve the path to our executable, then just use what we
+    // were given and hope for the best
+    if (!ResolveExecutablePath(given_path, resolved_path,
+                               sizeof(resolved_path)) )
+    {
+        if (log)
+            log->Printf("%s(): failed to resolve binary path, using "
+                        "what was given verbatim and hoping for the best",
+                        __FUNCTION__);
+        ::strncpy(resolved_path, given_path,
+                  sizeof(resolved_path));
+    }
+    else
+    {
+        if (log)
+            log->Printf("%s(): resolved given binary path to: \"%s\"",
+                        __FUNCTION__, resolved_path);
+    }
+
+    char launch_err_str[PATH_MAX];
+    launch_err_str[0] = '\0';
+    
+    // TODO figure out how to handle QSetProcessEvent
+    // const char *process_event = ctx.GetProcessEvent();
+    
+    // Ensure the binary is there.
+    struct stat path_stat;
+    if (::stat(resolved_path, &path_stat) == -1)
+    {
+        error.SetErrorToErrno();
+        return error;
+    }
+    
+    // Fork a child process for debugging
+    // state_callback(eStateLaunching);
+
+    const auto argv = launch_info.GetArguments().GetConstArgumentVector();
+    const auto envp =
+        launch_info.GetEnvironmentEntries().GetConstArgumentVector();
+
+    switch (*launch_flavor)
+    {
+        case LaunchFlavor::ForkExec:
+        {
+            ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+            error = ForkChildForPTraceDebugging(resolved_path, argv, envp,
+                                                &pid, pty_master_fd);
+            if (error.Success())
+            {
+                launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+            }
+            else
+            {
+                // Reset any variables that might have been set during a failed
+                // launch attempt.
+                if (pty_master_fd)
+                    *pty_master_fd = -1;
+                
+                // We're done.
+                return error;
+            }
+        }
+            break;
+
+#ifdef WITH_FBS
+        case LaunchFlavor::FBS:
+        {
+            const char *app_ext = strstr(path, ".app");
+            if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
+            {
+                std::string app_bundle_path(path, app_ext + strlen(".app"));
+                m_flags |= eMachProcessFlagsUsingFBS;
+                if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+                    return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+                else
+                    break; // We tried a FBS launch, but didn't succeed lets get out
+            }
+        }
+            break;
+#endif
+            
+#ifdef WITH_BKS
+        case LaunchFlavor::BKS:
+        {
+            const char *app_ext = strstr(path, ".app");
+            if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
+            {
+                std::string app_bundle_path(path, app_ext + strlen(".app"));
+                m_flags |= eMachProcessFlagsUsingBKS;
+                if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+                    return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+                else
+                    break; // We tried a BKS launch, but didn't succeed lets get out
+            }
+        }
+            break;
+#endif
+            
+#ifdef WITH_SPRINGBOARD
+        case LaunchFlavor::SpringBoard:
+        {
+            //  .../whatever.app/whatever ?
+            //  Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here
+            const char *app_ext = strstr (path, ".app/");
+            if (app_ext == NULL)
+            {
+                // .../whatever.app ?
+                int len = strlen (path);
+                if (len > 5)
+                {
+                    if (strcmp (path + len - 4, ".app") == 0)
+                    {
+                        app_ext = path + len - 4;
+                    }
+                }
+            }
+            if (app_ext)
+            {
+                std::string app_bundle_path(path, app_ext + strlen(".app"));
+                if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0)
+                    return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+                else
+                    break; // We tried a springboard launch, but didn't succeed lets get out
+            }
+        }
+            break;
+#endif
+            
+        case LaunchFlavor::PosixSpawn:
+        {
+            ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+            // Retrieve paths for stdin/stdout/stderr.
+            cpu_type_t actual_cpu_type = 0;
+            error = PosixSpawnChildForPTraceDebugging(resolved_path,
+                                                      launch_info,
+                                                      &pid,
+                                                      &actual_cpu_type);
+            if (error.Success())
+            {
+                launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+                if (pty_master_fd)
+                    *pty_master_fd = launch_info.GetPTY().
+                        ReleaseMasterFileDescriptor();
+            }
+            else
+            {
+                // Reset any variables that might have been set during a failed
+                // launch attempt.
+                if (pty_master_fd)
+                    *pty_master_fd = -1;
+
+                // We're done.
+                return error;
+            }
+            break;
+        }
+            
+        default:
+            // Invalid launch flavor.
+            error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown "
+                                           "launch flavor %d", __FUNCTION__,
+                                           (int)*launch_flavor);
+            return error;
+    }
+    
+    if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID)
+    {
+        // If we don't have a valid process ID and no one has set the error,
+        // then return a generic error.
+        if (error.Success())
+            error.SetErrorStringWithFormat("%s(): failed to launch, no reason "
+                                           "specified", __FUNCTION__);
+    }
+
+    // We're done with the launch side of the operation.
+    return error;
+}
+
+}} // namespaces
+
diff --git a/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h b/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h
new file mode 100644 (file)
index 0000000..d06056a
--- /dev/null
@@ -0,0 +1,51 @@
+//===-- DarwinProcessLauncher.h ---------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinProcessLauncher_h
+#define DarwinProcessLauncher_h
+
+// C headers
+#include <mach/machine.h>
+#include <sys/types.h>
+
+// C++ headers
+#include <functional>
+
+// LLDB headers
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+
+#include "LaunchFlavor.h"
+
+namespace lldb_private
+{
+namespace darwin_process_launcher
+{
+// =============================================================================
+/// Launches a process for debugging.
+///
+/// @param[inout] launch_info
+///     Specifies details about the process to launch (e.g. path, architecture,
+///     etc.).  On output, includes the launched ProcessID (pid).
+///
+/// @param[out] pty_master_fd
+///     Returns the master side of the pseudo-terminal used to communicate
+///     with stdin/stdout from the launched process.  May be nullptr.
+///
+/// @param[out] launch_flavor
+///     Contains the launch flavor used when launching the process.
+// =============================================================================
+Error
+LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+               lldb_private::process_darwin::LaunchFlavor *launch_flavor);
+
+} // darwin_process_launcher
+} // lldb_private
+
+#endif /* DarwinProcessLauncher_h */
diff --git a/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h b/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h
new file mode 100644 (file)
index 0000000..02182f7
--- /dev/null
@@ -0,0 +1,34 @@
+//===-- LaunchFlavor.h ---------------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LaunchFlavor_h
+#define LaunchFlavor_h
+
+namespace lldb_private {
+namespace process_darwin {
+
+enum class LaunchFlavor
+{
+    Default = 0,
+    PosixSpawn = 1,
+    ForkExec = 2,
+#ifdef WITH_SPRINGBOARD
+    SpringBoard = 3,
+#endif
+#ifdef WITH_BKS
+    BKS = 4,
+#endif
+#ifdef WITH_FBS
+    FBS = 5
+#endif
+};
+
+}} // namespaces
+
+#endif /* LaunchFlavor_h */
diff --git a/lldb/source/Plugins/Process/Darwin/MachException.cpp b/lldb/source/Plugins/Process/Darwin/MachException.cpp
new file mode 100644 (file)
index 0000000..d969319
--- /dev/null
@@ -0,0 +1,684 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachException.h"
+
+// C includes
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+// C++ includes
+#include <mutex>
+
+// LLDB includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+// Routine mach_exception_raise
+extern "C"
+kern_return_t catch_mach_exception_raise
+(
+    mach_port_t exception_port,
+    mach_port_t thread,
+    mach_port_t task,
+    exception_type_t exception,
+    mach_exception_data_t code,
+    mach_msg_type_number_t codeCnt
+);
+
+extern "C"
+kern_return_t catch_mach_exception_raise_state
+(
+    mach_port_t exception_port,
+    exception_type_t exception,
+    const mach_exception_data_t code,
+    mach_msg_type_number_t codeCnt,
+    int *flavor,
+    const thread_state_t old_state,
+    mach_msg_type_number_t old_stateCnt,
+    thread_state_t new_state,
+    mach_msg_type_number_t *new_stateCnt
+);
+
+// Routine mach_exception_raise_state_identity
+extern "C"
+kern_return_t catch_mach_exception_raise_state_identity
+(
+    mach_port_t exception_port,
+    mach_port_t thread,
+    mach_port_t task,
+    exception_type_t exception,
+    mach_exception_data_t code,
+    mach_msg_type_number_t codeCnt,
+    int *flavor,
+    thread_state_t old_state,
+    mach_msg_type_number_t old_stateCnt,
+    thread_state_t new_state,
+    mach_msg_type_number_t *new_stateCnt
+);
+
+extern "C" boolean_t mach_exc_server(
+        mach_msg_header_t *InHeadP,
+        mach_msg_header_t *OutHeadP);
+
+// Any access to the g_message variable should be done by locking the
+// g_message_mutex first, using the g_message variable, then unlocking
+// the g_message_mutex. See MachException::Message::CatchExceptionRaise()
+// for sample code.
+
+static MachException::Data *g_message = NULL;
+
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state
+(
+    mach_port_t                 exc_port,
+    exception_type_t            exc_type,
+    const mach_exception_data_t exc_data,
+    mach_msg_type_number_t      exc_data_count,
+    int *                       flavor,
+    const thread_state_t        old_state,
+    mach_msg_type_number_t      old_stateCnt,
+    thread_state_t              new_state,
+    mach_msg_type_number_t *    new_stateCnt
+)
+{
+    // TODO change to LIBLLDB_LOG_EXCEPTION
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+    if (log)
+    {
+        log->Printf("::%s(exc_port = 0x%4.4x, exc_type = %d (%s), "
+                    "exc_data = 0x%llx, exc_data_count = %d)",
+                        __FUNCTION__, exc_port, exc_type,
+                    MachException::Name(exc_type), (uint64_t)exc_data,
+                    exc_data_count);
+    }
+    return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state_identity
+(
+    mach_port_t             exc_port,
+    mach_port_t             thread_port,
+    mach_port_t             task_port,
+    exception_type_t        exc_type,
+    mach_exception_data_t   exc_data,
+    mach_msg_type_number_t  exc_data_count,
+    int *                   flavor,
+    thread_state_t          old_state,
+    mach_msg_type_number_t  old_stateCnt,
+    thread_state_t          new_state,
+    mach_msg_type_number_t *new_stateCnt
+)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+    if (log)
+    {
+        log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
+                    "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] = "
+                    "{ 0x%llx, 0x%llx })", __FUNCTION__, exc_port, thread_port,
+                    task_port, exc_type, MachException::Name(exc_type),
+                    exc_data_count,
+                    (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+                    (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+    }
+    mach_port_deallocate (mach_task_self(), task_port);
+    mach_port_deallocate (mach_task_self(), thread_port);
+
+    return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise
+(
+    mach_port_t             exc_port,
+    mach_port_t             thread_port,
+    mach_port_t             task_port,
+    exception_type_t        exc_type,
+    mach_exception_data_t   exc_data,
+    mach_msg_type_number_t  exc_data_count)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+    if (log)
+    {
+        log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
+                    "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] "
+                    "= { 0x%llx, 0x%llx })", __FUNCTION__, exc_port,
+                    thread_port, task_port, exc_type,
+                    MachException::Name(exc_type), exc_data_count,
+                    (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+                    (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+    }
+
+    if (task_port == g_message->task_port)
+    {
+        g_message->task_port = task_port;
+        g_message->thread_port = thread_port;
+        g_message->exc_type = exc_type;
+        g_message->exc_data.resize(exc_data_count);
+        ::memcpy(&g_message->exc_data[0], exc_data,
+                 g_message->exc_data.size() *
+                 sizeof (mach_exception_data_type_t));
+        return KERN_SUCCESS;
+    }
+    return KERN_FAILURE;
+}
+
+#if 0
+void
+MachException::Message::Dump(Stream &stream) const
+{
+    stream.Printf("exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
+                  "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x "
+                  "id = 0x%8.8x }\n",
+        exc_msg.hdr.msgh_bits,
+        exc_msg.hdr.msgh_size,
+        exc_msg.hdr.msgh_remote_port,
+        exc_msg.hdr.msgh_local_port,
+        exc_msg.hdr.msgh_reserved,
+        exc_msg.hdr.msgh_id);
+
+    stream.Printf("reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port "
+                  "= 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x "
+                  "id = 0x%8.8x }",
+                  reply_msg.hdr.msgh_bits,
+                  reply_msg.hdr.msgh_size,
+                  reply_msg.hdr.msgh_remote_port,
+                  reply_msg.hdr.msgh_local_port,
+                  reply_msg.hdr.msgh_reserved,
+                  reply_msg.hdr.msgh_id);
+    stream.Flush();
+}
+#endif
+
+bool
+MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info,
+                                 const UnixSignals &signals,
+                                 Stream &stream) const
+{
+    if (!stop_info)
+        return false;
+
+    // Zero out the structure.
+    memset(stop_info, 0, sizeof(struct ThreadStopInfo));
+
+    if (exc_type == 0)
+    {
+        stop_info->reason = eStopReasonInvalid;
+        return true;
+    }
+
+    // We always stop with a mach exception.
+    stop_info->reason = eStopReasonException;
+    // Save the EXC_XXXX exception type.
+    stop_info->details.exception.type = exc_type;
+
+    // Fill in a text description
+    const char * exc_name = MachException::Name(exc_type);
+    if (exc_name)
+        stream.Printf("%s", exc_name);
+    else
+        stream.Printf("%i", exc_type);
+
+    stop_info->details.exception.data_count = exc_data.size();
+
+    int soft_signal = SoftSignal();
+    if (soft_signal)
+    {
+        const char *sig_str = signals.GetSignalAsCString(soft_signal);
+        stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal,
+                      sig_str ? sig_str : "unknown signal");
+    }
+    else
+    {
+        // No special disassembly for exception data, just print it.
+        size_t idx;
+        stream.Printf(" data[%llu] = {",
+                      (uint64_t)stop_info->details.exception.data_count);
+
+        for (idx = 0; idx < stop_info->details.exception.data_count; ++idx)
+        {
+            stream.Printf("0x%llx%c", (uint64_t)exc_data[idx],
+                          ((idx + 1 == stop_info->details.exception.data_count)
+                           ? '}' : ','));
+        }
+    }
+
+    // Copy the exception data
+    for (size_t i = 0; i < stop_info->details.exception.data_count; i++)
+        stop_info->details.exception.data[i] = exc_data[i];
+
+    return true;
+}
+
+Error
+MachException::Message::Receive(mach_port_t port, mach_msg_option_t options,
+                                mach_msg_timeout_t timeout,
+                                mach_port_t notify_port)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    mach_msg_timeout_t mach_msg_timeout =
+        options & MACH_RCV_TIMEOUT ? timeout : 0;
+    if (log && ((options & MACH_RCV_TIMEOUT) == 0))
+    {
+        // Dump this log message if we have no timeout in case it never returns
+        log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
+                    "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+                    "option = %#x, send_size = 0, rcv_size = %llu, "
+                    "rcv_name = %#x, timeout = %u, notify = %#x)",
+                        exc_msg.hdr.msgh_bits,
+                        exc_msg.hdr.msgh_size,
+                        exc_msg.hdr.msgh_remote_port,
+                        exc_msg.hdr.msgh_local_port,
+                        exc_msg.hdr.msgh_reserved,
+                        exc_msg.hdr.msgh_id,
+                        options,
+                        (uint64_t)sizeof (exc_msg.data),
+                        port,
+                        mach_msg_timeout,
+                        notify_port);
+    }
+
+    mach_msg_return_t mach_err =
+        ::mach_msg (&exc_msg.hdr,
+                    options,                  // options
+                    0,                        // Send size
+                    sizeof (exc_msg.data),    // Receive size
+                    port,                     // exception port to watch for
+                                              // exception on
+                    mach_msg_timeout,         // timeout in msec (obeyed only
+                                              // if MACH_RCV_TIMEOUT is ORed
+                                              // into the options parameter)
+                    notify_port);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+
+    // Dump any errors we get
+    if (error.Fail() && log)
+    {
+        log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
+                    "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+                    "option = %#x, send_size = %u, rcv_size = %lu, rcv_name "
+                    "= %#x, timeout = %u, notify = %#x) failed: %s",
+                    exc_msg.hdr.msgh_bits,
+                    exc_msg.hdr.msgh_size,
+                    exc_msg.hdr.msgh_remote_port,
+                    exc_msg.hdr.msgh_local_port,
+                    exc_msg.hdr.msgh_reserved,
+                    exc_msg.hdr.msgh_id,
+                    options,
+                    0,
+                    sizeof(exc_msg.data),
+                    port,
+                    mach_msg_timeout,
+                    notify_port,
+                    error.AsCString());
+    }
+    return error;
+}
+
+void
+MachException::Message::Dump(Stream &stream) const
+{
+    stream.Printf("  exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
+                  "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
+                  "0x%8.8x }\n",
+                  exc_msg.hdr.msgh_bits,
+                  exc_msg.hdr.msgh_size,
+                  exc_msg.hdr.msgh_remote_port,
+                  exc_msg.hdr.msgh_local_port,
+                  exc_msg.hdr.msgh_reserved,
+                  exc_msg.hdr.msgh_id);
+
+    stream.Printf("  reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
+                  "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
+                  "0x%8.8x }",
+                  reply_msg.hdr.msgh_bits,
+                  reply_msg.hdr.msgh_size,
+                  reply_msg.hdr.msgh_remote_port,
+                  reply_msg.hdr.msgh_local_port,
+                  reply_msg.hdr.msgh_reserved,
+                  reply_msg.hdr.msgh_id);
+}
+
+bool
+MachException::Message::CatchExceptionRaise(task_t task)
+{
+    bool success = false;
+    // locker will keep a mutex locked until it goes out of scope
+//    PThreadMutex::Locker locker(&g_message_mutex);
+    //    DNBLogThreaded("calling  mach_exc_server");
+    state.task_port = task;
+    g_message = &state;
+    // The exc_server function is the MIG generated server handling function
+    // to handle messages from the kernel relating to the occurrence of an
+    // exception in a thread. Such messages are delivered to the exception port
+    // set via thread_set_exception_ports or task_set_exception_ports. When an
+    // exception occurs in a thread, the thread sends an exception message to
+    // its exception port, blocking in the kernel waiting for the receipt of a
+    // reply. The exc_server function performs all necessary argument handling
+    // for this kernel message and calls catch_exception_raise,
+    // catch_exception_raise_state or catch_exception_raise_state_identity,
+    // which should handle the exception. If the called routine returns
+    // KERN_SUCCESS, a reply message will be sent, allowing the thread to
+    // continue from the point of the exception; otherwise, no reply message
+    // is sent and the called routine must have dealt with the exception
+    // thread directly.
+    if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr))
+    {
+        success = true;
+    }
+    else
+    {
+        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                          LIBLLDB_LOG_VERBOSE));
+        if (log)
+            log->Printf("MachException::Message::%s(): mach_exc_server "
+                        "returned zero...", __FUNCTION__);
+    }
+    g_message = NULL;
+    return success;
+}
+
+Error
+MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task,
+                              int signal)
+{
+    // Reply to the exception...
+    Error error;
+
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    // If we had a soft signal, we need to update the thread first so it can
+    // continue without signaling
+    int soft_signal = state.SoftSignal();
+    if (soft_signal)
+    {
+        int state_pid = -1;
+        if (inferior_task == state.task_port)
+        {
+            // This is our task, so we can update the signal to send to it
+            state_pid = inferior_pid;
+            soft_signal = signal;
+        }
+        else
+        {
+            auto mach_err = ::pid_for_task(state.task_port, &state_pid);
+            if (mach_err)
+            {
+                error.SetError(mach_err, eErrorTypeMachKernel);
+                if (log)
+                    log->Printf("MachException::Message::%s(): pid_for_task() "
+                                "failed: %s", __FUNCTION__, error.AsCString());
+                return error;
+            }
+        }
+
+        lldbassert(state_pid != -1);
+        if (state_pid != -1)
+        {
+            errno = 0;
+            caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port;
+            if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal)
+                != 0)
+                error.SetError(errno, eErrorTypePOSIX);
+
+            if (!error.Success())
+            {
+                if (log)
+                    log->Printf("::ptrace(request = PT_THUPDATE, pid = "
+                                "0x%4.4x, tid = 0x%4.4x, signal = %i)",
+                                state_pid, state.thread_port, soft_signal);
+                return error;
+            }
+        }
+    }
+
+    if (log)
+        log->Printf("::mach_msg ( msg->{bits = %#x, size = %u, remote_port "
+                    "= %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+                    "option = %#x, send_size = %u, rcv_size = %u, rcv_name "
+                    "= %#x, timeout = %u, notify = %#x)",
+                    reply_msg.hdr.msgh_bits,
+                    reply_msg.hdr.msgh_size,
+                    reply_msg.hdr.msgh_remote_port,
+                    reply_msg.hdr.msgh_local_port,
+                    reply_msg.hdr.msgh_reserved,
+                    reply_msg.hdr.msgh_id,
+                    MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+                    reply_msg.hdr.msgh_size,
+                    0,
+                    MACH_PORT_NULL,
+                    MACH_MSG_TIMEOUT_NONE,
+                    MACH_PORT_NULL);
+
+    auto mach_err = ::mach_msg(&reply_msg.hdr,
+                               MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+                               reply_msg.hdr.msgh_size,
+                               0,
+                               MACH_PORT_NULL,
+                               MACH_MSG_TIMEOUT_NONE,
+                               MACH_PORT_NULL);
+    if (mach_err)
+        error.SetError(mach_err, eErrorTypeMachKernel);
+
+    // Log our error if we have one.
+    if (error.Fail() && log)
+    {
+        if (error.GetError() == MACH_SEND_INTERRUPTED)
+        {
+            log->PutCString("::mach_msg() - send interrupted");
+            // TODO: keep retrying to reply???
+        }
+        else if (state.task_port == inferior_task)
+        {
+            log->Printf("mach_msg(): returned an error when replying "
+                        "to a mach exception: error = %u (%s)",
+                        error.GetError(), error.AsCString());
+        }
+        else
+        {
+            log->Printf("::mach_msg() - failed (child of task): %u (%s)",
+                        error.GetError(), error.AsCString());
+        }
+    }
+
+    return error;
+}
+
+#define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS      | \
+                           EXC_MASK_BAD_INSTRUCTION | \
+                           EXC_MASK_ARITHMETIC      | \
+                           EXC_MASK_EMULATION       | \
+                           EXC_MASK_SOFTWARE        | \
+                           EXC_MASK_BREAKPOINT      | \
+                           EXC_MASK_SYSCALL         | \
+                           EXC_MASK_MACH_SYSCALL    | \
+                           EXC_MASK_RPC_ALERT       | \
+                           EXC_MASK_MACHINE)
+
+// Don't listen for EXC_RESOURCE, it should really get handled by the system handler.
+
+#ifndef EXC_RESOURCE
+#define EXC_RESOURCE 11
+#endif
+
+#ifndef EXC_MASK_RESOURCE
+#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
+#endif
+
+#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
+
+Error
+MachException::PortInfo::Save(task_t task)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    if (log)
+        log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)",
+                    __FUNCTION__, task);
+
+    // Be careful to be able to have debugserver built on a newer OS than what
+    // it is currently running on by being able to start with all exceptions
+    // and back off to just what is supported on the current system
+    mask = LLDB_EXC_MASK;
+
+    count = (sizeof(ports) / sizeof(ports[0]));
+    auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+                                               behaviors, flavors);
+    if (mach_err)
+        error.SetError(mach_err, eErrorTypeMachKernel);
+
+    if (log)
+    {
+        if (error.Success())
+        {
+            log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+                        "0x%x, maskCnt => %u, ports, behaviors, flavors)",
+                        task, mask, count);
+        }
+        else
+        {
+            log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, "
+                        "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)",
+                        task, mask, count, error.GetError(), error.AsCString());
+        }
+    }
+
+    if ((error.GetError() == KERN_INVALID_ARGUMENT) &&
+        (mask != PREV_EXC_MASK_ALL))
+    {
+        mask = PREV_EXC_MASK_ALL;
+        count = (sizeof(ports) / sizeof(ports[0]));
+        mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+                                              behaviors, flavors);
+        error.SetError(mach_err, eErrorTypeMachKernel);
+        if (log)
+        {
+            if (error.Success())
+            {
+                log->Printf("::task_get_exception_ports(task = 0x%4.4x, "
+                            "mask = 0x%x, maskCnt => %u, ports, behaviors, "
+                            "flavors)", task, mask, count);
+            }
+            else
+            {
+                log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+                            "0x%x, maskCnt => %u, ports, behaviors, flavors) "
+                            "error: %u (%s)", task, mask, count,
+                            error.GetError(), error.AsCString());
+            }
+        }
+    }
+    if (error.Fail())
+    {
+        mask = 0;
+        count = 0;
+    }
+    return error;
+}
+
+Error
+MachException::PortInfo::Restore(task_t task)
+{
+    Error error;
+
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    if (log)
+        log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task);
+
+    uint32_t i = 0;
+    if (count > 0)
+    {
+        for (i = 0; i < count; i++)
+        {
+            auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i],
+                                                       behaviors[i],
+                                                       flavors[i]);
+            if (mach_err)
+                error.SetError(mach_err, eErrorTypeMachKernel);
+            if (log)
+            {
+                if (error.Success())
+                {
+                    log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
+                                "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+                                "behavior = 0x%8.8x, new_flavor = 0x%8.8x)",
+                                task, masks[i], ports[i], behaviors[i],
+                                flavors[i]);
+                }
+                else
+                {
+                    log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
+                                "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+                                "behavior = 0x%8.8x, new_flavor = 0x%8.8x): "
+                                "error %u (%s)", task, masks[i], ports[i],
+                                behaviors[i], flavors[i], error.GetError(),
+                                error.AsCString());
+                }
+            }
+
+            // Bail if we encounter any errors
+            if (error.Fail())
+                break;
+        }
+    }
+
+    count = 0;
+    return error;
+}
+
+const char *
+MachException::Name(exception_type_t exc_type)
+{
+    switch (exc_type)
+    {
+    case EXC_BAD_ACCESS:        return "EXC_BAD_ACCESS";
+    case EXC_BAD_INSTRUCTION:   return "EXC_BAD_INSTRUCTION";
+    case EXC_ARITHMETIC:        return "EXC_ARITHMETIC";
+    case EXC_EMULATION:         return "EXC_EMULATION";
+    case EXC_SOFTWARE:          return "EXC_SOFTWARE";
+    case EXC_BREAKPOINT:        return "EXC_BREAKPOINT";
+    case EXC_SYSCALL:           return "EXC_SYSCALL";
+    case EXC_MACH_SYSCALL:      return "EXC_MACH_SYSCALL";
+    case EXC_RPC_ALERT:         return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+    case EXC_CRASH:             return "EXC_CRASH";
+#endif
+    default:
+        break;
+    }
+    return NULL;
+}
diff --git a/lldb/source/Plugins/Process/Darwin/MachException.h b/lldb/source/Plugins/Process/Darwin/MachException.h
new file mode 100644 (file)
index 0000000..a96090b
--- /dev/null
@@ -0,0 +1,163 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __MachException_h__
+#define __MachException_h__
+
+#include <mach/mach.h>
+#include <vector>
+
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+#include "lldb/Host/Debug.h"
+
+namespace lldb_private
+{
+namespace process_darwin
+{
+
+typedef union MachMessageTag
+{
+    mach_msg_header_t hdr;
+    char data[1024];
+} MachMessage;
+
+
+class MachException
+{
+public:
+
+    struct PortInfo
+    {
+        exception_mask_t        mask; // the exception mask for this device which may be a subset of EXC_MASK_ALL...
+        exception_mask_t        masks[EXC_TYPES_COUNT];
+        mach_port_t             ports[EXC_TYPES_COUNT];
+        exception_behavior_t    behaviors[EXC_TYPES_COUNT];
+        thread_state_flavor_t   flavors[EXC_TYPES_COUNT];
+        mach_msg_type_number_t  count;
+
+        Error
+        Save(task_t task);
+
+        Error
+        Restore(task_t task);
+    };
+
+    struct Data
+    {
+        task_t task_port;
+        thread_t thread_port;
+        exception_type_t exc_type;
+        std::vector<mach_exception_data_type_t> exc_data;
+        Data() :
+            task_port(TASK_NULL),
+            thread_port(THREAD_NULL),
+            exc_type(0),
+            exc_data()
+            {
+            }
+
+        void
+        Clear()
+        {
+            task_port = TASK_NULL;
+            thread_port = THREAD_NULL;
+            exc_type = 0;
+            exc_data.clear();
+        }
+
+        bool
+        IsValid() const
+        {
+            return  task_port != TASK_NULL &&
+                    thread_port != THREAD_NULL &&
+                    exc_type != 0;
+        }
+        
+        // Return the SoftSignal for this MachException data, or zero if there is none
+        int
+        SoftSignal() const
+        {
+            if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL)
+                return static_cast<int>(exc_data[1]);
+            return 0;
+        }
+
+        bool
+        IsBreakpoint() const
+        {
+            return (exc_type == EXC_BREAKPOINT || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1));
+        }
+
+        bool
+        GetStopInfo(ThreadStopInfo *stop_info, const UnixSignals &signals,
+                         Stream &stream) const;
+    };
+
+    struct Message
+    {
+        MachMessage exc_msg;
+        MachMessage reply_msg;
+        Data state;
+
+        Message() :
+            state()
+        {
+            memset(&exc_msg,   0, sizeof(exc_msg));
+            memset(&reply_msg, 0, sizeof(reply_msg));
+        }
+
+        bool
+        CatchExceptionRaise(task_t task);
+
+        Error
+        Reply(::pid_t inferior_pid, task_t inferior_task, int signal);
+
+        Error
+        Receive(mach_port_t receive_port,
+                mach_msg_option_t options,
+                mach_msg_timeout_t timeout,
+                mach_port_t notify_port = MACH_PORT_NULL);
+
+        void
+        Dump(Stream &stream) const;
+
+        typedef std::vector<Message>        collection;
+        typedef collection::iterator        iterator;
+        typedef collection::const_iterator    const_iterator;
+    };
+
+    enum
+    {
+        e_actionForward,    // Forward signal to inferior process
+        e_actionStop,        // Stop when this signal is received
+    };
+    struct Action
+    {
+        task_t task_port;            // Set to TASK_NULL for any TASK
+        thread_t thread_port;        // Set to THREAD_NULL for any thread
+        exception_type_t exc_mask;    // Mach exception mask to watch for
+        std::vector<mach_exception_data_type_t> exc_data_mask;    // Mask to apply to exception data, or empty to ignore exc_data value for exception
+        std::vector<mach_exception_data_type_t> exc_data_value;    // Value to compare to exception data after masking, or empty to ignore exc_data value for exception
+        uint8_t flags;                // Action flags describing what to do with the exception
+    };
+
+    static const char*
+    Name(exception_type_t exc_type);
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp b/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp
new file mode 100644 (file)
index 0000000..d0eaac3
--- /dev/null
@@ -0,0 +1,1825 @@
+//===-- NativeProcessDarwin.cpp ---------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessDarwin.h"
+
+// C includes
+#include <mach/mach_init.h>
+#include <mach/mach_traps.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+// C++ includes
+// LLDB includes
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+#include "DarwinProcessLauncher.h"
+
+#include "MachException.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+// -----------------------------------------------------------------------------
+// Hidden Impl
+// -----------------------------------------------------------------------------
+
+namespace
+{
+    struct hack_task_dyld_info {
+        mach_vm_address_t   all_image_info_addr;
+        mach_vm_size_t      all_image_info_size;
+    };
+}
+
+// -----------------------------------------------------------------------------
+// Public Static Methods
+// -----------------------------------------------------------------------------
+
+Error
+NativeProcessProtocol::Launch(ProcessLaunchInfo &launch_info,
+                              NativeProcessProtocol::NativeDelegate
+                              &native_delegate,
+                              MainLoop &mainloop,
+                              NativeProcessProtocolSP &native_process_sp)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    Error error;
+
+    // Verify the working directory is valid if one was specified.
+    FileSpec working_dir(launch_info.GetWorkingDirectory());
+    if (working_dir &&
+        (!working_dir.ResolvePath() ||
+         working_dir.GetFileType() != FileSpec::eFileTypeDirectory))
+    {
+        error.SetErrorStringWithFormat("No such file or directory: %s",
+                                       working_dir.GetCString());
+        return error;
+    }
+
+    // Launch the inferior.
+    int pty_master_fd = -1;
+    LaunchFlavor launch_flavor = LaunchFlavor::Default;
+
+    error = LaunchInferior(launch_info, &pty_master_fd, &launch_flavor);
+
+    // Handle launch failure.
+    if (!error.Success())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() failed to launch process: "
+                        "%s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+
+    // Handle failure to return a pid.
+    if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID)
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() launch succeeded but no "
+                        "pid was returned!  Aborting.", __FUNCTION__);
+        return error;
+    }
+
+    // Create the Darwin native process impl.
+    std::shared_ptr<NativeProcessDarwin>
+        np_darwin_sp(new NativeProcessDarwin(launch_info.GetProcessID(),
+                                             pty_master_fd));
+    if (!np_darwin_sp->RegisterNativeDelegate(native_delegate))
+    {
+        native_process_sp.reset ();
+        error.SetErrorStringWithFormat ("failed to register the native delegate");
+        return error;
+    }
+
+    // Finalize the processing needed to debug the launched process with
+    // a NativeProcessDarwin instance.
+    error = np_darwin_sp->FinalizeLaunch(launch_flavor, mainloop);
+    if (!error.Success())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() aborting, failed to finalize"
+                        " the launching of the process: %s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+
+    // Return the process and process id to the caller through the launch args.
+    native_process_sp = np_darwin_sp;
+    return error;
+}
+
+Error
+NativeProcessProtocol::Attach(lldb::pid_t pid,
+                              NativeProcessProtocol::NativeDelegate
+                              &native_delegate,
+                              MainLoop &mainloop,
+                              NativeProcessProtocolSP &native_process_sp)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+    if (log)
+        log->Printf ("NativeProcessDarwin::%s(pid = %" PRIi64 ")", __FUNCTION__,
+                     pid);
+
+    // Retrieve the architecture for the running process.
+    ArchSpec process_arch;
+    Error error = ResolveProcessArchitecture(pid, process_arch);
+    if (!error.Success())
+        return error;
+
+    // TODO get attach to return this value.
+    const int pty_master_fd = -1;
+    std::shared_ptr<NativeProcessDarwin> native_process_darwin_sp(
+                                   new NativeProcessDarwin(pid, pty_master_fd));
+
+    if (!native_process_darwin_sp->RegisterNativeDelegate(native_delegate))
+    {
+        error.SetErrorStringWithFormat("failed to register the native "
+                                       "delegate");
+        return error;
+    }
+
+    native_process_darwin_sp->AttachToInferior(mainloop, pid, error);
+    if (!error.Success())
+        return error;
+
+    native_process_sp = native_process_darwin_sp;
+    return error;
+}
+
+// -----------------------------------------------------------------------------
+// ctor/dtor
+// -----------------------------------------------------------------------------
+
+NativeProcessDarwin::NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd) :
+    NativeProcessProtocol(pid),
+    m_task(TASK_NULL),
+    m_did_exec(false),
+    m_cpu_type(0),
+    m_exception_port(MACH_PORT_NULL),
+    m_exc_port_info(),
+    m_exception_thread(nullptr),
+    m_exception_messages_mutex(),
+    m_sent_interrupt_signo(0),
+    m_auto_resume_signo(0),
+    m_thread_list(),
+    m_thread_actions(),
+    m_waitpid_pipe(),
+    m_waitpid_thread(nullptr),
+    m_waitpid_reader_handle()
+{
+    // TODO add this to the NativeProcessProtocol constructor.
+    m_terminal_fd = pty_master_fd;
+}
+
+NativeProcessDarwin::~NativeProcessDarwin()
+{
+}
+
+// -----------------------------------------------------------------------------
+// Instance methods
+// -----------------------------------------------------------------------------
+
+Error
+NativeProcessDarwin::FinalizeLaunch(LaunchFlavor launch_flavor,
+                                    MainLoop &main_loop)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+#if 0
+    m_path = path;
+    size_t i;
+    char const *arg;
+    for (i=0; (arg = argv[i]) != NULL; i++)
+        m_args.push_back(arg);
+#endif
+
+    error = StartExceptionThread();
+    if (!error.Success())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failure starting the "
+                        "mach exception port monitor thread: %s",
+                        __FUNCTION__, error.AsCString());
+
+        // Terminate the inferior process.  There's nothing meaningful we can
+        // do if we can't receive signals and exceptions.  Since we launched
+        // the process, it's fair game for us to kill it.
+        ::ptrace(PT_KILL, m_pid, 0, 0);
+        SetState(eStateExited);
+
+        return error;
+    }
+
+    StartSTDIOThread();
+
+    if (launch_flavor == LaunchFlavor::PosixSpawn)
+    {
+        SetState(eStateAttaching);
+        errno = 0;
+        int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+        if (err == 0)
+        {
+            // m_flags |= eMachProcessFlagsAttached;
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(): successfully spawned "
+                            "process with pid %" PRIu64, __FUNCTION__, m_pid);
+        }
+        else
+        {
+            error.SetErrorToErrno();
+            SetState(eStateExited);
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(): error: failed to "
+                            "attach to spawned pid %" PRIu64 " (error=%d (%s))",
+                            __FUNCTION__, m_pid, (int)error.GetError(),
+                            error.AsCString());
+            return error;
+        }
+    }
+
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): new pid is %" PRIu64 "...",
+                    __FUNCTION__, m_pid);
+
+    // Spawn a thread to reap our child inferior process...
+    error = StartWaitpidThread(main_loop);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to start waitpid() "
+                        "thread: %s", __FUNCTION__, error.AsCString());
+        kill(SIGKILL, static_cast<::pid_t>(m_pid));
+        return error;
+    }
+
+    if (TaskPortForProcessID(error) == TASK_NULL)
+    {
+        // We failed to get the task for our process ID which is bad.
+        // Kill our process; otherwise, it will be stopped at the entry
+        // point and get reparented to someone else and never go away.
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): could not get task port "
+                        "for process, sending SIGKILL and exiting: %s",
+                        __FUNCTION__, error.AsCString());
+        kill(SIGKILL, static_cast<::pid_t>(m_pid));
+        return error;
+    }
+
+    // Indicate that we're stopped, as we always launch suspended.
+    SetState(eStateStopped);
+
+    // Success.
+    return error;
+}
+
+Error
+NativeProcessDarwin::SaveExceptionPortInfo()
+{
+    return m_exc_port_info.Save(m_task);
+}
+
+bool
+NativeProcessDarwin::ProcessUsingSpringBoard() const
+{
+    // TODO implement flags
+    // return (m_flags & eMachProcessFlagsUsingSBS) != 0;
+    return false;
+}
+
+bool
+NativeProcessDarwin::ProcessUsingBackBoard() const
+{
+    // TODO implement flags
+    // return (m_flags & eMachProcessFlagsUsingBKS) != 0;
+    return false;
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void
+NativeProcessDarwin::ExceptionMessageReceived(const MachException::Message&
+                                              message)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+    if (m_exception_messages.empty())
+    {
+        // Suspend the task the moment we receive our first exception message.
+        SuspendTask();
+    }
+
+    // Use a locker to automatically unlock our mutex in case of exceptions
+    // Add the exception to our internal exception stack
+    m_exception_messages.push_back(message);
+
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): new queued message count: %lu",
+                    __FUNCTION__, m_exception_messages.size());
+}
+
+void*
+NativeProcessDarwin::ExceptionThread(void *arg)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+    if (!arg)
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): cannot run mach exception "
+                        "thread, mandatory process arg was null", __FUNCTION__);
+        return nullptr;
+    }
+
+    return reinterpret_cast<NativeProcessDarwin*>(arg)->DoExceptionThread();
+}
+
+void*
+NativeProcessDarwin::DoExceptionThread()
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(arg=%p) starting thread...",
+                    __FUNCTION__, this);
+
+    pthread_setname_np("exception monitoring thread");
+
+    // Ensure we don't get CPU starved.
+    MaybeRaiseThreadPriority();
+
+    // We keep a count of the number of consecutive exceptions received so
+    // we know to grab all exceptions without a timeout. We do this to get a
+    // bunch of related exceptions on our exception port so we can process
+    // then together. When we have multiple threads, we can get an exception
+    // per thread and they will come in consecutively. The main loop in this
+    // thread can stop periodically if needed to service things related to this
+    // process.
+    //
+    // [did we lose some words here?]
+    //
+    // flag set in the options, so we will wait forever for an exception on
+    //0 our exception port. After we get one exception, we then will use the
+    // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+    // exceptions for our process. After we have received the last pending
+    // exception, we will get a timeout which enables us to then notify
+    // our main thread that we have an exception bundle available. We then wait
+    // for the main thread to tell this exception thread to start trying to get
+    // exceptions messages again and we start again with a mach_msg read with
+    // infinite timeout.
+    //
+    // We choose to park a thread on this, rather than polling, because the
+    // polling is expensive.  On devices, we need to minimize overhead caused
+    // by the process monitor.
+    uint32_t num_exceptions_received = 0;
+    Error error;
+    task_t task = m_task;
+    mach_msg_timeout_t periodic_timeout = 0;
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+    mach_msg_timeout_t watchdog_elapsed = 0;
+    mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+    ::pid_t pid = (::pid_t)process->GetID();
+    CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+    if (process->ProcessUsingSpringBoard())
+    {
+        // Request a renewal for every 60 seconds if we attached using
+        // SpringBoard.
+        watchdog.reset(::SBSWatchdogAssertionCreateForPID(nullptr, pid, 60));
+        if (log)
+            log->Printf("::SBSWatchdogAssertionCreateForPID(NULL, %4.4x, 60) "
+                        "=> %p", pid, watchdog.get());
+
+        if (watchdog.get())
+        {
+            ::SBSWatchdogAssertionRenew (watchdog.get());
+
+            CFTimeInterval watchdogRenewalInterval =
+                ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
+            if (log)
+                log->Printf("::SBSWatchdogAssertionGetRenewalInterval(%p) => "
+                            "%g seconds", watchdog.get(),
+                            watchdogRenewalInterval);
+            if (watchdogRenewalInterval > 0.0)
+            {
+                watchdog_timeout =
+                    (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+                if (watchdog_timeout > 3000)
+                {
+                    // Give us a second to renew our timeout.
+                    watchdog_timeout -= 1000;
+                }
+                else if (watchdog_timeout > 1000)
+                {
+                    // Give us a quarter of a second to renew our timeout.
+                    watchdog_timeout -= 250;
+                }
+            }
+        }
+        if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+            periodic_timeout = watchdog_timeout;
+    }
+#endif  // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+#ifdef WITH_BKS
+    CFReleaser<BKSWatchdogAssertionRef> watchdog;
+    if (process->ProcessUsingBackBoard())
+    {
+        ::pid_t pid = process->GetID();
+        CFAllocatorRef alloc = kCFAllocatorDefault;
+        watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid));
+    }
+#endif // #ifdef WITH_BKS
+
+    // Do we want to use a weak pointer to the NativeProcessDarwin here, in
+    // which case we can guarantee we don't whack the process monitor if we
+    // race between this thread and the main one on shutdown?
+    while (IsExceptionPortValid())
+    {
+        ::pthread_testcancel();
+
+        MachException::Message exception_message;
+
+        if (num_exceptions_received > 0)
+        {
+            // We don't want a timeout here, just receive as many exceptions as
+            // we can since we already have one.  We want to get all currently
+            // available exceptions for this task at once.
+            error = exception_message.Receive(GetExceptionPort(),
+                                              MACH_RCV_MSG | MACH_RCV_INTERRUPT
+                                              | MACH_RCV_TIMEOUT, 0);
+        }
+        else if (periodic_timeout > 0)
+        {
+            // We need to stop periodically in this loop, so try and get a mach
+            // message with a valid timeout (ms).
+            error = exception_message.Receive(GetExceptionPort(),
+                                              MACH_RCV_MSG | MACH_RCV_INTERRUPT
+                                              | MACH_RCV_TIMEOUT,
+                                              periodic_timeout);
+        }
+        else
+        {
+            // We don't need to parse all current exceptions or stop
+            // periodically, just wait for an exception forever.
+            error = exception_message.Receive(GetExceptionPort(),
+                                              MACH_RCV_MSG | MACH_RCV_INTERRUPT,
+                                              0);
+        }
+
+        if (error.Success())
+        {
+            // We successfully received an exception.
+            if (exception_message.CatchExceptionRaise(task))
+            {
+                ++num_exceptions_received;
+                ExceptionMessageReceived(exception_message);
+            }
+        }
+        else
+        {
+            if (error.GetError() == MACH_RCV_INTERRUPTED)
+            {
+                // We were interrupted.
+
+                // If we have no task port we should exit this thread, as it implies
+                // the inferior went down.
+                if (!IsExceptionPortValid())
+                {
+                    if (log)
+                        log->Printf("NativeProcessDarwin::%s(): the inferior "
+                                    "exception port is no longer valid, "
+                                    "canceling exception thread...", __FUNCTION__);
+                    // Should we be setting a process state here?
+                    break;
+                }
+
+                // Make sure the inferior task is still valid.
+                if (IsTaskValid())
+                {
+                    // Task is still ok.
+                    if (log)
+                        log->Printf("NativeProcessDarwin::%s(): interrupted, but "
+                                    "the inferior task iss till valid, "
+                                    "continuing...", __FUNCTION__);
+                    continue;
+                }
+                else
+                {
+                    // The inferior task is no longer valid.  Time to exit as
+                    // the process has gone away.
+                    if (log)
+                        log->Printf("NativeProcessDarwin::%s(): the inferior task "
+                                    "has exited, and so will we...", __FUNCTION__);
+                    // Does this race at all with our waitpid()?
+                    SetState(eStateExited);
+                    break;
+                }
+            }
+            else if (error.GetError() == MACH_RCV_TIMED_OUT)
+            {
+                // We timed out when waiting for exceptions.
+
+                if (num_exceptions_received > 0)
+                {
+                    // We were receiving all current exceptions with a timeout of
+                    // zero.  It is time to go back to our normal looping mode.
+                    num_exceptions_received = 0;
+
+                    // Notify our main thread we have a complete exception message
+                    // bundle available.  Get the possibly updated task port back
+                    // from the process in case we exec'ed and our task port
+                    // changed.
+                    task = ExceptionMessageBundleComplete();
+
+                    // In case we use a timeout value when getting exceptions,
+                    // make sure our task is still valid.
+                    if (IsTaskValid(task))
+                    {
+                        // Task is still ok.
+                        if (log)
+                            log->Printf("NativeProcessDarwin::%s(): got a timeout, "
+                                        "continuing...", __FUNCTION__);
+                        continue;
+                    }
+                    else
+                    {
+                        // The inferior task is no longer valid.  Time to exit as
+                        // the process has gone away.
+                        if (log)
+                            log->Printf("NativeProcessDarwin::%s(): the inferior "
+                                        "task has exited, and so will we...",
+                                        __FUNCTION__);
+                        // Does this race at all with our waitpid()?
+                        SetState(eStateExited);
+                        break;
+                    }
+                }
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+                if (watchdog.get())
+                {
+                    watchdog_elapsed += periodic_timeout;
+                    if (watchdog_elapsed >= watchdog_timeout)
+                    {
+                        if (log)
+                            log->Printf("SBSWatchdogAssertionRenew(%p)",
+                                        watchdog.get());
+                        ::SBSWatchdogAssertionRenew (watchdog.get());
+                        watchdog_elapsed = 0;
+                    }
+                }
+#endif
+            }
+            else
+            {
+                if (log)
+                    log->Printf("NativeProcessDarwin::%s(): continuing after "
+                                "receiving an unexpected error: %u (%s)",
+                                __FUNCTION__, error.GetError(), error.AsCString());
+                // TODO: notify of error?
+            }
+        }
+    }
+
+#if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+    if (watchdog.get())
+    {
+        // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
+        // all are up and running on systems that support it. The SBS framework has a #define
+        // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
+        // so it should still build either way.
+        DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
+        ::SBSWatchdogAssertionRelease (watchdog.get());
+    }
+#endif  // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+    
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(%p): thread exiting...",
+                    __FUNCTION__, this);
+    return nullptr;
+}
+
+Error
+NativeProcessDarwin::StartExceptionThread()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+    if (log)
+        log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+    // Make sure we've looked up the inferior port.
+    TaskPortForProcessID(error);
+
+    // Ensure the inferior task is valid.
+    if (!IsTaskValid())
+    {
+        error.SetErrorStringWithFormat("cannot start exception thread: "
+                                       "task 0x%4.4x is not valid", m_task);
+        return error;
+    }
+
+    // Get the mach port for the process monitor.
+    mach_port_t task_self = mach_task_self();
+
+    // Allocate an exception port that we will use to track our child process
+    auto mach_err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
+                                         &m_exception_port);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): mach_port_allocate("
+                        "task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, "
+                        "&m_exception_port) failed: %u (%s)", __FUNCTION__,
+                        task_self, error.GetError(), error.AsCString());
+        return error;
+    }
+
+    // Add the ability to send messages on the new exception port
+    mach_err = ::mach_port_insert_right(task_self, m_exception_port,
+                                   m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): mach_port_insert_right("
+                        "task_self=0x%4.4x, m_exception_port=0x%4.4x, "
+                        "m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND) "
+                        "failed: %u (%s)", __FUNCTION__,
+                        task_self, m_exception_port, m_exception_port,
+                        error.GetError(), error.AsCString());
+        return error;
+    }
+
+    // Save the original state of the exception ports for our child process.
+    error = SaveExceptionPortInfo();
+    if (error.Fail() || (m_exc_port_info.mask == 0))
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): SaveExceptionPortInfo() "
+                        "failed, cannot install exception handler: %s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+
+    // Set the ability to get all exceptions on this port.
+    mach_err = ::task_set_exception_ports(m_task, m_exc_port_info.mask,
+                                          m_exception_port,
+                                          EXCEPTION_DEFAULT |
+                                          MACH_EXCEPTION_CODES,
+                                          THREAD_STATE_NONE);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("::task_set_exception_ports (task = 0x%4.4x, "
+                        "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+                        "behavior = 0x%8.8x, new_flavor = 0x%8.8x) failed: "
+                        "%u (%s)", m_task, m_exc_port_info.mask,
+                        m_exception_port, (EXCEPTION_DEFAULT |
+                                           MACH_EXCEPTION_CODES),
+                        THREAD_STATE_NONE, error.GetError(),
+                        error.AsCString());
+        return error;
+    }
+
+    // Create the exception thread.
+    auto pthread_err = ::pthread_create(&m_exception_thread, nullptr,
+                                        ExceptionThread, this);
+    error.SetError(pthread_err, eErrorTypePOSIX);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to create Mach "
+                        "exception-handling thread: %u (%s)", __FUNCTION__,
+                        error.GetError(), error.AsCString());
+    }
+
+    return error;
+}
+
+lldb::addr_t
+NativeProcessDarwin::GetDYLDAllImageInfosAddress(Error &error) const
+{
+    error.Clear();
+
+    struct hack_task_dyld_info dyld_info;
+    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+    // Make sure that COUNT isn't bigger than our hacked up struct
+    // hack_task_dyld_info.  If it is, then make COUNT smaller to match.
+    if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
+    {
+        count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
+    }
+
+    TaskPortForProcessID(error);
+    if (error.Fail())
+        return LLDB_INVALID_ADDRESS;
+
+    auto mach_err = ::task_info(m_task, TASK_DYLD_INFO, (task_info_t)&dyld_info,
+                                &count);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+    if (error.Success())
+    {
+        // We now have the address of the all image infos structure.
+        return dyld_info.all_image_info_addr;
+    }
+
+    // We don't have it.
+    return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t
+NativeProcessDarwin::GetCPUTypeForLocalProcess(::pid_t pid)
+{
+    int mib[CTL_MAXNAME] = {0, };
+    size_t len = CTL_MAXNAME;
+
+    if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+        return 0;
+
+    mib[len] = pid;
+    len++;
+
+    cpu_type_t cpu;
+    size_t cpu_len = sizeof(cpu);
+    if (::sysctl (mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+        cpu = 0;
+    return cpu;
+}
+
+uint32_t
+NativeProcessDarwin::GetCPUType() const
+{
+    if (m_cpu_type == 0 && m_pid != 0)
+        m_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+    return m_cpu_type;
+}
+
+task_t
+NativeProcessDarwin::ExceptionMessageBundleComplete()
+{
+    // We have a complete bundle of exceptions for our child process.
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): processing %lu exception "
+                    "messages.", __FUNCTION__, m_exception_messages.size());
+
+    if (m_exception_messages.empty())
+    {
+        // Not particularly useful...
+        return m_task;
+    }
+
+    bool auto_resume = false;
+    m_did_exec = false;
+
+    // First check for any SIGTRAP and make sure we didn't exec
+    const task_t task = m_task;
+    size_t i;
+    if (m_pid != 0)
+    {
+        bool received_interrupt = false;
+        uint32_t num_task_exceptions = 0;
+        for (i = 0; i < m_exception_messages.size(); ++i)
+        {
+            if (m_exception_messages[i].state.task_port != task)
+            {
+                // This is an exception that is not for our inferior, ignore.
+                continue;
+            }
+
+            // This is an exception for the inferior.
+            ++num_task_exceptions;
+            const int signo = m_exception_messages[i].state.SoftSignal();
+            if (signo == SIGTRAP)
+            {
+                // SIGTRAP could mean that we exec'ed. We need to check the
+                // dyld all_image_infos.infoArray to see if it is NULL and if
+                // so, say that we exec'ed.
+                const addr_t aii_addr =
+                    GetDYLDAllImageInfosAddress(error);
+                if (aii_addr == LLDB_INVALID_ADDRESS)
+                    break;
+
+                const addr_t info_array_count_addr = aii_addr + 4;
+                uint32_t info_array_count = 0;
+                size_t bytes_read = 0;
+                Error read_error;
+                read_error = ReadMemory(info_array_count_addr,   // source addr
+                                        &info_array_count,       // dest addr
+                                        4,                       // byte count
+                                        bytes_read);             // #bytes read
+                if (read_error.Success() && (bytes_read == 4))
+                {
+                    if (info_array_count == 0)
+                    {
+                        // We got the all infos address, and there are zero
+                        // entries.  We think we exec'd.
+                        m_did_exec = true;
+
+                        // Force the task port to update itself in case the
+                        // task port changed after exec
+                        const task_t old_task = m_task;
+                        const bool force_update = true;
+                        const task_t new_task =
+                            TaskPortForProcessID(error, force_update);
+                        if (old_task != new_task)
+                        {
+                            if (log)
+                                log->Printf("exec: inferior task port changed "
+                                            "from 0x%4.4x to 0x%4.4x", old_task,
+                                            new_task);
+                        }
+                    }
+                }
+                else
+                {
+                    if (log)
+                        log->Printf("NativeProcessDarwin::%s() warning: "
+                                    "failed to read all_image_infos."
+                                    "infoArrayCount from 0x%8.8llx",
+                                    __FUNCTION__, info_array_count_addr);
+                }
+            }
+            else if ((m_sent_interrupt_signo != 0) &&
+                     (signo == m_sent_interrupt_signo))
+            {
+                // We just received the interrupt that we sent to ourselves.
+                received_interrupt = true;
+            }
+        }
+
+        if (m_did_exec)
+        {
+            cpu_type_t process_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+            if (m_cpu_type != process_cpu_type)
+            {
+                if (log)
+                    log->Printf("NativeProcessDarwin::%s(): arch changed from "
+                                "0x%8.8x to 0x%8.8x", __FUNCTION__, m_cpu_type,
+                                process_cpu_type);
+                m_cpu_type = process_cpu_type;
+                // TODO figure out if we need to do something here.
+                // DNBArchProtocol::SetArchitecture (process_cpu_type);
+            }
+            m_thread_list.Clear();
+
+            // TODO hook up breakpoints.
+            // m_breakpoints.DisableAll();
+        }
+
+        if (m_sent_interrupt_signo != 0)
+        {
+            if (received_interrupt)
+            {
+                if (log)
+                    log->Printf("NativeProcessDarwin::%s(): process "
+                                "successfully interrupted with signal %i",
+                                __FUNCTION__, m_sent_interrupt_signo);
+
+                // Mark that we received the interrupt signal
+                m_sent_interrupt_signo = 0;
+                // Now check if we had a case where:
+                // 1 - We called NativeProcessDarwin::Interrupt() but we stopped
+                //     for another reason.
+                // 2 - We called NativeProcessDarwin::Resume() (but still
+                //     haven't gotten the interrupt signal).
+                // 3 - We are now incorrectly stopped because we are handling
+                //     the interrupt signal we missed.
+                // 4 - We might need to resume if we stopped only with the
+                //     interrupt signal that we never handled.
+                if (m_auto_resume_signo != 0)
+                {
+                    // Only auto_resume if we stopped with _only_ the interrupt
+                    // signal.
+                    if (num_task_exceptions == 1)
+                    {
+                        auto_resume = true;
+                        if (log)
+                            log->Printf("NativeProcessDarwin::%s(): auto "
+                                        "resuming due to unhandled interrupt "
+                                        "signal %i", __FUNCTION__,
+                                        m_auto_resume_signo);
+                    }
+                    m_auto_resume_signo = 0;
+                }
+            }
+            else
+            {
+                if (log)
+                    log->Printf("NativeProcessDarwin::%s(): didn't get signal "
+                                "%i after MachProcess::Interrupt()",
+                                __FUNCTION__, m_sent_interrupt_signo);
+            }
+        }
+    }
+
+    // Let all threads recover from stopping and do any clean up based
+    // on the previous thread state (if any).
+    m_thread_list.ProcessDidStop(*this);
+
+    // Let each thread know of any exceptions
+    for (i = 0; i < m_exception_messages.size(); ++i)
+    {
+        // Let the thread list forward all exceptions on down to each thread.
+        if (m_exception_messages[i].state.task_port == task)
+        {
+            // This exception is for our inferior.
+            m_thread_list.NotifyException(m_exception_messages[i].state);
+        }
+
+        if (log)
+        {
+            StreamString stream;
+            m_exception_messages[i].Dump(stream);
+            stream.Flush();
+            log->PutCString(stream.GetString().c_str());
+        }
+    }
+
+    if (log)
+    {
+        StreamString stream;
+        m_thread_list.Dump(stream);
+        stream.Flush();
+        log->PutCString(stream.GetString().c_str());
+    }
+
+    bool step_more = false;
+    if (m_thread_list.ShouldStop(step_more) && (auto_resume == false))
+    {
+        // TODO - need to hook up event system here. !!!!
+#if 0
+        // Wait for the eEventProcessRunningStateChanged event to be reset
+        // before changing state to stopped to avoid race condition with
+        // very fast start/stops.
+        struct timespec timeout;
+
+        //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000);   // Wait for 250 ms
+        DNBTimer::OffsetTimeOfDay(&timeout, 1, 0);  // Wait for 250 ms
+        m_events.WaitForEventsToReset(eEventProcessRunningStateChanged,
+                                      &timeout);
+#endif
+        SetState(eStateStopped);
+    }
+    else
+    {
+        // Resume without checking our current state.
+        PrivateResume();
+    }
+
+    return m_task;
+}
+
+void
+NativeProcessDarwin::StartSTDIOThread()
+{
+    // TODO implement
+}
+
+Error
+NativeProcessDarwin::StartWaitpidThread(MainLoop &main_loop)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    // Strategy: create a thread that sits on waitpid(), waiting for the
+    // inferior process to die, reaping it in the process.  Arrange for
+    // the thread to have a pipe file descriptor that it can send a byte
+    // over when the waitpid completes.  Have the main loop have a read
+    // object for the other side of the pipe, and have the callback for
+    // the read do the process termination message sending.
+
+    // Create a single-direction communication channel.
+    const bool child_inherits = false;
+    error = m_waitpid_pipe.CreateNew(child_inherits);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+                        "communication pipe: %s", __FUNCTION__,
+                        error.AsCString());
+        return error;
+    }
+
+    // Hook up the waitpid reader callback.
+
+    // TODO make PipePOSIX derive from IOObject.  This is goofy here.
+    const bool transfer_ownership = false;
+    auto io_sp = IOObjectSP(new File(m_waitpid_pipe.GetReadFileDescriptor(),
+                                     transfer_ownership));
+    m_waitpid_reader_handle =
+        main_loop.RegisterReadObject(
+                          io_sp,
+                          [this](MainLoopBase &){ HandleWaitpidResult(); },
+                          error);
+
+    // Create the thread.
+    auto pthread_err = ::pthread_create(&m_waitpid_thread, nullptr,
+                                        WaitpidThread, this);
+    error.SetError(pthread_err, eErrorTypePOSIX);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+                        "handling thread: %u (%s)", __FUNCTION__,
+                        error.GetError(), error.AsCString());
+        return error;
+    }
+
+    return error;
+}
+
+void*
+NativeProcessDarwin::WaitpidThread(void *arg)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+    if (!arg)
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): cannot run waitpid "
+                        "thread, mandatory process arg was null", __FUNCTION__);
+        return nullptr;
+    }
+
+    return reinterpret_cast<NativeProcessDarwin*>(arg)->DoWaitpidThread();
+}
+
+void
+NativeProcessDarwin::MaybeRaiseThreadPriority()
+{
+#if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+    struct sched_param thread_param;
+    int thread_sched_policy;
+    if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+                              &thread_param) == 0)
+    {
+        thread_param.sched_priority = 47;
+        pthread_setschedparam(pthread_self(), thread_sched_policy,
+                              &thread_param);
+    }
+#endif
+}
+
+void*
+NativeProcessDarwin::DoWaitpidThread()
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    if (m_pid == LLDB_INVALID_PROCESS_ID)
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): inferior process ID is "
+                        "not set, cannot waitpid on it", __FUNCTION__);
+        return nullptr;
+    }
+
+    // Name the thread.
+    pthread_setname_np("waitpid thread");
+
+    // Ensure we don't get CPU starved.
+    MaybeRaiseThreadPriority();
+
+    Error error;
+    int status = -1;
+
+    while (1)
+    {
+        // Do a waitpid.
+        ::pid_t child_pid = ::waitpid(m_pid, &status, 0);
+        if (child_pid < 0)
+            error.SetErrorToErrno();
+        if (error.Fail())
+        {
+            if (error.GetError() == EINTR)
+            {
+                // This is okay, we can keep going.
+                if (log)
+                    log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %"
+                                PRIu64 ", &status, 0) interrupted, continuing",
+                                __FUNCTION__, m_pid);
+                continue;
+            }
+
+            // This error is not okay, abort.
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+                            ", &status, 0) aborting due to error: %u (%s)",
+                            __FUNCTION__, m_pid, error.GetError(),
+                            error.AsCString());
+            break;
+        }
+
+        // Log the successful result.
+        if (log)
+                log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+                            ", &status, 0) => %i, status = %i", __FUNCTION__,
+                            m_pid, child_pid, status);
+
+        // Handle the result.
+        if (WIFSTOPPED(status))
+        {
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+                            ") received a stop, continuing waitpid() loop",
+                            __FUNCTION__, m_pid);
+            continue;
+        }
+        else // if (WIFEXITED(status) || WIFSIGNALED(status))
+        {
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(pid = %" PRIu64 "): "
+                            "waitpid thread is setting exit status for pid = "
+                            "%i to %i", __FUNCTION__, m_pid,
+                            child_pid, status);
+
+            error = SendInferiorExitStatusToMainLoop(child_pid, status);
+            return nullptr;
+        }
+    }
+
+    // We should never exit as long as our child process is alive.  If we
+    // get here, something completely unexpected went wrong and we should exit.
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): internal error: waitpid thread "
+                    "exited out of its main loop in an unexpected way. pid = %"
+                    PRIu64 ". Sending exit status of -1.", __FUNCTION__, m_pid);
+
+    error = SendInferiorExitStatusToMainLoop((::pid_t)m_pid, -1);
+    return nullptr;
+}
+
+Error
+NativeProcessDarwin::SendInferiorExitStatusToMainLoop(::pid_t pid, int status)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    size_t bytes_written = 0;
+
+    // Send the pid.
+    error = m_waitpid_pipe.Write(&pid, sizeof(pid), bytes_written);
+    if (error.Fail() || (bytes_written < sizeof(pid)))
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() - failed to write "
+                        "waitpid exiting pid to the pipe.  Client will not "
+                        "hear about inferior exit status!",
+                        __FUNCTION__);
+        return error;
+    }
+
+    // Send the status.
+    bytes_written = 0;
+    error = m_waitpid_pipe.Write(&status, sizeof(status), bytes_written);
+    if (error.Fail() || (bytes_written < sizeof(status)))
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() - failed to write "
+                        "waitpid exit result to the pipe.  Client will not "
+                        "hear about inferior exit status!",
+                        __FUNCTION__);
+    }
+    return error;
+}
+
+Error
+NativeProcessDarwin::HandleWaitpidResult()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    // Read the pid.
+    const bool notify_status = true;
+
+    ::pid_t pid = -1;
+    size_t bytes_read = 0;
+    error = m_waitpid_pipe.Read(&pid, sizeof(pid), bytes_read);
+    if (error.Fail() || (bytes_read < sizeof(pid)))
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() - failed to read "
+                        "waitpid exiting pid from the pipe.  Will notify "
+                        "as if parent process died with exit status -1.",
+                        __FUNCTION__);
+        SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result",
+                      notify_status);
+        return error;
+    }
+
+    // Read the status.
+    int status = -1;
+    error = m_waitpid_pipe.Read(&status, sizeof(status), bytes_read);
+    if (error.Fail() || (bytes_read < sizeof(status)))
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() - failed to read "
+                        "waitpid exit status from the pipe.  Will notify "
+                        "as if parent process died with exit status -1.",
+                        __FUNCTION__);
+        SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result",
+                      notify_status);
+        return error;
+    }
+
+    // Notify the monitor that our state has changed.
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): main loop received waitpid "
+                    "exit status info: pid=%i (%s), status=%i",
+                    __FUNCTION__, pid,
+                    (pid == m_pid) ? "the inferior" : "not the inferior",
+                    status);
+
+    ExitType exit_type = eExitTypeInvalid;
+    int exit_status = -1;
+
+    if (WIFEXITED(status))
+    {
+        exit_type = eExitTypeExit;
+        exit_status = WEXITSTATUS(status);
+    }
+    else if (WIFSIGNALED(status))
+    {
+        exit_type = eExitTypeSignal;
+        exit_status = WTERMSIG(status);
+    }
+
+    SetExitStatus(exit_type, exit_status, nullptr, notify_status);
+    return error;
+}
+
+task_t
+NativeProcessDarwin::TaskPortForProcessID(Error &error, bool force) const
+{
+    if ((m_task == TASK_NULL) || force)
+    {
+        Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+        if (m_pid == LLDB_INVALID_PROCESS_ID)
+        {
+            if (log)
+                log->Printf("NativeProcessDarwin::%s(): cannot get task due "
+                            "to invalid pid", __FUNCTION__);
+            return TASK_NULL;
+        }
+
+        const uint32_t num_retries = 10;
+        const uint32_t usec_interval = 10000;
+
+        mach_port_t task_self = mach_task_self();
+        task_t task = TASK_NULL;
+
+        for (uint32_t i = 0; i < num_retries; i++)
+        {
+            kern_return_t err = ::task_for_pid(task_self, m_pid, &task);
+            if (err == 0)
+            {
+                // Succeeded.  Save and return it.
+                error.Clear();
+                m_task = task;
+                log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+                            "stub_port = 0x%4.4x, pid = %llu, &task) "
+                            "succeeded: inferior task port = 0x%4.4x",
+                            __FUNCTION__, task_self, m_pid, m_task);
+                return m_task;
+            }
+            else
+            {
+                // Failed to get the task for the inferior process.
+                error.SetError(err, eErrorTypeMachKernel);
+                if (log)
+                {
+                    log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+                                "stub_port = 0x%4.4x, pid = %llu, &task) "
+                                "failed, err = 0x%8.8x (%s)",
+                                __FUNCTION__, task_self,
+                                m_pid,
+                                err,
+                                error.AsCString());
+                }
+            }
+
+            // Sleep a bit and try again
+            ::usleep (usec_interval);
+        }
+
+        // We failed to get the task for the inferior process.
+        // Ensure that it is cleared out.
+        m_task = TASK_NULL;
+    }
+    return m_task;
+}
+
+void
+NativeProcessDarwin::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
+                                      Error &error)
+{
+    error.SetErrorString("TODO: implement");
+}
+
+Error
+NativeProcessDarwin::PrivateResume()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+    m_auto_resume_signo = m_sent_interrupt_signo;
+
+    if (log)
+    {
+        if (m_auto_resume_signo)
+            log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming (with "
+                        "unhandled interrupt signal %i)...", __FUNCTION__,
+                        m_task, m_auto_resume_signo);
+        else
+            log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming...",
+                        __FUNCTION__, m_task);
+    }
+
+    error = ReplyToAllExceptions();
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): aborting, failed to "
+                        "reply to exceptions: %s", __FUNCTION__,
+                        error.AsCString());
+        return error;
+    }
+    //    bool stepOverBreakInstruction = step;
+
+    // Let the thread prepare to resume and see if any threads want us to
+    // step over a breakpoint instruction (ProcessWillResume will modify
+    // the value of stepOverBreakInstruction).
+    m_thread_list.ProcessWillResume(*this, m_thread_actions);
+
+    // Set our state accordingly
+    if (m_thread_actions.NumActionsWithState(eStateStepping))
+        SetState(eStateStepping);
+    else
+        SetState(eStateRunning);
+
+    // Now resume our task.
+    error = ResumeTask();
+    return error;
+}
+
+Error
+NativeProcessDarwin::ReplyToAllExceptions()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                      LIBLLDB_LOG_VERBOSE));
+
+    TaskPortForProcessID(error);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): no task port, aborting",
+                        __FUNCTION__);
+        return error;
+    }
+
+    std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+    if (m_exception_messages.empty())
+    {
+        // We're done.
+        return error;
+    }
+
+    size_t index = 0;
+    for (auto &message : m_exception_messages)
+    {
+        if (log)
+        {
+            log->Printf("NativeProcessDarwin::%s(): replying to exception "
+                        "%zu...", __FUNCTION__, index++);
+        }
+
+        int thread_reply_signal = 0;
+
+        const tid_t tid =
+            m_thread_list.GetThreadIDByMachPortNumber(message.state
+                                                      .thread_port);
+        const ResumeAction *action = nullptr;
+        if (tid != LLDB_INVALID_THREAD_ID)
+            action = m_thread_actions.GetActionForThread (tid, false);
+
+        if (action)
+        {
+            thread_reply_signal = action->signal;
+            if (thread_reply_signal)
+                m_thread_actions.SetSignalHandledForThread(tid);
+        }
+
+        error = message.Reply(m_pid, m_task, thread_reply_signal);
+        if (error.Fail() && log)
+        {
+            // We log any error here, but we don't stop the exception
+            // response handling.
+            log->Printf("NativeProcessDarwin::%s(): failed to reply to "
+                        "exception: %s", __FUNCTION__, error.AsCString());
+            error.Clear();
+        }
+    }
+
+    // Erase all exception message as we should have used and replied
+    // to them all already.
+    m_exception_messages.clear();
+    return error;
+}
+
+Error
+NativeProcessDarwin::ResumeTask()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    TaskPortForProcessID(error);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to get task port "
+                        "for process when attempting to resume: %s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+    if (m_task == TASK_NULL)
+    {
+        error.SetErrorString("task port retrieval succeeded but task port is "
+                             "null when attempting to resume the task");
+        return error;
+    }
+
+    if (log)
+        log->Printf("NativeProcessDarwin::%s(): requesting resume of task "
+                    "0x%4.4x", __FUNCTION__, m_task);
+
+    // Get the BasicInfo struct to verify that we're suspended before we try
+    // to resume the task.
+    struct task_basic_info task_info;
+    error = GetTaskBasicInfo(m_task, &task_info);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): failed to get task "
+                        "BasicInfo when attempting to resume: %s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+
+    // task_resume isn't counted like task_suspend calls are, so if the
+    // task is not suspended, don't try and resume it since it is already
+    // running
+    if (task_info.suspend_count > 0)
+    {
+        auto mach_err = ::task_resume(m_task);
+        error.SetError(mach_err, eErrorTypeMachKernel);
+        if (log)
+        {
+            if (error.Success())
+                log->Printf("::task_resume(target_task = 0x%4.4x): success",
+                            m_task);
+            else
+                log->Printf("::task_resume(target_task = 0x%4.4x) error: %s",
+                            m_task, error.AsCString());
+        }
+    }
+    else
+    {
+        if (log)
+            log->Printf("::task_resume(target_task = 0x%4.4x): ignored, "
+                        "already running", m_task);
+    }
+
+    return error;
+}
+
+bool
+NativeProcessDarwin::IsTaskValid() const
+{
+    if (m_task == TASK_NULL)
+        return false;
+
+    struct task_basic_info task_info;
+    return GetTaskBasicInfo(m_task, &task_info).Success();
+}
+
+bool
+NativeProcessDarwin::IsTaskValid(task_t task) const
+{
+    if (task == TASK_NULL)
+        return false;
+
+    struct task_basic_info task_info;
+    return GetTaskBasicInfo(task, &task_info).Success();
+}
+
+mach_port_t
+NativeProcessDarwin::GetExceptionPort() const
+{
+    return m_exception_port;
+}
+
+bool
+NativeProcessDarwin::IsExceptionPortValid () const
+{
+    return MACH_PORT_VALID(m_exception_port);
+}
+
+Error
+NativeProcessDarwin::GetTaskBasicInfo(task_t task,
+                                      struct task_basic_info *info) const
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    // Validate args.
+    if (info == NULL)
+    {
+        error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): mandatory "
+                                       "info arg is null", __FUNCTION__);
+        return error;
+    }
+
+    // Grab the task if we don't already have it.
+    if (task == TASK_NULL)
+    {
+        error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): given task "
+                                       "is invalid", __FUNCTION__);
+    }
+
+    mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+    auto err = ::task_info(m_task, TASK_BASIC_INFO, (task_info_t)info, &count);
+    error.SetError(err, eErrorTypeMachKernel);
+    if (error.Fail())
+    {
+        if (log)
+            log->Printf("::task_info(target_task = 0x%4.4x, "
+                        "flavor = TASK_BASIC_INFO, task_info_out => %p, "
+                        "task_info_outCnt => %u) failed: %u (%s)", m_task, info,
+                        count, error.GetError(), error.AsCString());
+        return error;
+    }
+
+    Log *verbose_log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS |
+                                              LIBLLDB_LOG_VERBOSE));
+    if (verbose_log)
+    {
+        float user = (float)info->user_time.seconds +
+            (float)info->user_time.microseconds / 1000000.0f;
+        float system = (float)info->user_time.seconds +
+            (float)info->user_time.microseconds / 1000000.0f;
+        verbose_log->Printf("task_basic_info = { suspend_count = %i, "
+                            "virtual_size = 0x%8.8llx, resident_size = "
+                            "0x%8.8llx, user_time = %f, system_time = %f }",
+                            info->suspend_count,
+                            (uint64_t)info->virtual_size,
+                            (uint64_t)info->resident_size,
+                            user, system);
+    }
+    return error;
+}
+
+Error
+NativeProcessDarwin::SuspendTask()
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    if (m_task == TASK_NULL)
+    {
+        error.SetErrorString("task port is null, cannot suspend task");
+        if (log)
+            log->Printf("NativeProcessDarwin::%s() failed: %s",
+                        __FUNCTION__, error.AsCString());
+        return error;
+    }
+
+    auto mach_err = ::task_suspend(m_task);
+    error.SetError(mach_err, eErrorTypeMachKernel);
+    if (error.Fail() && log)
+        log->Printf("::task_suspend(target_task = 0x%4.4x)", m_task);
+
+    return error;
+}
+
+Error
+NativeProcessDarwin::Resume(const ResumeActionList &resume_actions)
+{
+    Error error;
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+    if (log)
+        log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+    if (CanResume())
+    {
+        m_thread_actions = resume_actions;
+        error = PrivateResume();
+        return error;
+    }
+
+    auto state = GetState();
+    if (state == eStateRunning)
+    {
+        if (log)
+            log->Printf("NativeProcessDarwin::%s(): task 0x%x is already "
+                        "running, ignoring...", __FUNCTION__,
+                        TaskPortForProcessID(error));
+        return error;
+    }
+
+    // We can't resume from this state.
+    error.SetErrorStringWithFormat("task 0x%x has state %s, can't resume",
+                                   TaskPortForProcessID(error),
+                                   StateAsCString(state));
+    return error;
+}
+
+Error
+NativeProcessDarwin::Halt()
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::Detach()
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::Signal(int signo)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::Interrupt()
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::Kill()
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::GetMemoryRegionInfo(lldb::addr_t load_addr,
+                                         MemoryRegionInfo &range_info)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                                size_t &bytes_read)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf,
+                                           size_t size, size_t &bytes_read)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::WriteMemory(lldb::addr_t addr, const void *buf,
+                                 size_t size, size_t &bytes_written)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::AllocateMemory(size_t size, uint32_t permissions,
+                                    lldb::addr_t &addr)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::DeallocateMemory(lldb::addr_t addr)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+lldb::addr_t
+NativeProcessDarwin::GetSharedLibraryInfoAddress()
+{
+    return LLDB_INVALID_ADDRESS;
+}
+
+size_t
+NativeProcessDarwin::UpdateThreads()
+{
+    return 0;
+}
+
+bool
+NativeProcessDarwin::GetArchitecture(ArchSpec &arch) const
+{
+    return false;
+}
+
+Error
+NativeProcessDarwin::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                                   bool hardware)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+void
+NativeProcessDarwin::DoStopIDBumped(uint32_t newBumpId)
+{
+}
+
+Error
+NativeProcessDarwin::GetLoadedModuleFileSpec(const char* module_path,
+                                             FileSpec& file_spec)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+Error
+NativeProcessDarwin::GetFileLoadAddress(const llvm::StringRef& file_name,
+                                        lldb::addr_t& load_addr)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
+
+// -----------------------------------------------------------------
+// NativeProcessProtocol protected interface
+// -----------------------------------------------------------------
+Error
+NativeProcessDarwin::GetSoftwareBreakpointTrapOpcode(size_t
+                                                     trap_opcode_size_hint,
+                                                     size_t &actual_opcode_size,
+                                                     const uint8_t
+                                                     *&trap_opcode_bytes)
+{
+    Error error;
+    error.SetErrorString("TODO: implement");
+    return error;
+}
diff --git a/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h b/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h
new file mode 100644 (file)
index 0000000..1307745
--- /dev/null
@@ -0,0 +1,479 @@
+//===-- NativeProcessDarwin.h --------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef NativeProcessDarwin_h
+#define NativeProcessDarwin_h
+
+// NOTE: this code should only be compiled on Apple Darwin systems.  It is
+// not cross-platform code and is not intended to build on any other platform.
+// Therefore, platform-specific headers and code are okay here.
+
+// C includes
+#include <mach/mach_types.h>
+
+// C++ includes
+#include <mutex>
+#include <unordered_set>
+
+// Other libraries and framework includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/lldb-types.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+
+#include "NativeThreadListDarwin.h"
+#include "LaunchFlavor.h"
+#include "MachException.h"
+#include "NativeThreadDarwin.h"
+
+namespace lldb_private {
+    class Error;
+    class Scalar;
+
+    namespace process_darwin {
+
+        /// @class NativeProcessDarwin
+        /// @brief Manages communication with the inferior (debugee) process.
+        ///
+        /// Upon construction, this class prepares and launches an inferior
+        /// process for debugging.
+        ///
+        /// Changes in the inferior process state are broadcasted.
+        class NativeProcessDarwin: public NativeProcessProtocol
+        {
+            friend Error
+            NativeProcessProtocol::Launch(ProcessLaunchInfo &launch_info,
+                                          NativeDelegate &native_delegate,
+                                          MainLoop &mainloop,
+                                          NativeProcessProtocolSP &process_sp);
+
+            friend Error
+            NativeProcessProtocol::Attach(lldb::pid_t pid,
+                                          NativeProcessProtocol::NativeDelegate
+                                          &native_delegate,
+                                          MainLoop &mainloop,
+                                          NativeProcessProtocolSP &process_sp);
+
+        public:
+
+            ~NativeProcessDarwin() override;
+
+            // -----------------------------------------------------------------
+            // NativeProcessProtocol Interface
+            // -----------------------------------------------------------------
+            Error
+            Resume(const ResumeActionList &resume_actions) override;
+
+            Error
+            Halt() override;
+
+            Error
+            Detach() override;
+
+            Error
+            Signal(int signo) override;
+
+            Error
+            Interrupt() override;
+
+            Error
+            Kill() override;
+
+            Error
+            GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo
+                                &range_info) override;
+
+            Error
+            ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+                       size_t &bytes_read) override;
+
+            Error
+            ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
+                                  size_t &bytes_read) override;
+
+            Error
+            WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+                        size_t &bytes_written) override;
+
+            Error
+            AllocateMemory(size_t size, uint32_t permissions,
+                           lldb::addr_t &addr) override;
+
+            Error
+            DeallocateMemory(lldb::addr_t addr) override;
+
+            lldb::addr_t
+            GetSharedLibraryInfoAddress() override;
+
+            size_t
+            UpdateThreads() override;
+
+            bool
+            GetArchitecture(ArchSpec &arch) const override;
+
+            Error
+            SetBreakpoint(lldb::addr_t addr, uint32_t size,
+                          bool hardware) override;
+
+            void
+            DoStopIDBumped(uint32_t newBumpId) override;
+
+            Error
+            GetLoadedModuleFileSpec(const char* module_path,
+                                    FileSpec& file_spec) override;
+
+            Error
+            GetFileLoadAddress(const llvm::StringRef& file_name,
+                               lldb::addr_t& load_addr) override;
+
+            NativeThreadDarwinSP
+            GetThreadByID(lldb::tid_t id);
+
+            task_t
+            GetTask() const
+            {
+                return m_task;
+            }
+
+            // -----------------------------------------------------------------
+            // Interface used by NativeRegisterContext-derived classes.
+            // -----------------------------------------------------------------
+            static Error
+            PtraceWrapper(int req,
+                          lldb::pid_t pid,
+                          void *addr = nullptr,
+                          void *data = nullptr,
+                          size_t data_size = 0,
+                          long *result = nullptr);
+
+            bool
+            SupportHardwareSingleStepping() const;
+
+        protected:
+            // -----------------------------------------------------------------
+            // NativeProcessProtocol protected interface
+            // -----------------------------------------------------------------
+            Error
+            GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint,
+                                            size_t &actual_opcode_size,
+                                            const uint8_t *&trap_opcode_bytes)
+            override;
+
+        private:
+
+            // -----------------------------------------------------------------
+            /// Mach task-related Member Variables
+            // -----------------------------------------------------------------
+
+            // The task port for the inferior process.
+            mutable task_t  m_task;
+
+            // True if the inferior process did an exec since we started
+            // monitoring it.
+            bool  m_did_exec;
+
+            // The CPU type of this process.
+            mutable cpu_type_t  m_cpu_type;
+
+            // -----------------------------------------------------------------
+            /// Exception/Signal Handling Member Variables
+            // -----------------------------------------------------------------
+
+            // Exception port on which we will receive child exceptions
+            mach_port_t  m_exception_port;
+
+            // Saved state of the child exception port prior to us installing
+            // our own intercepting port.
+            MachException::PortInfo  m_exc_port_info;
+
+            // The thread that runs the Mach exception read and reply handler.
+            pthread_t  m_exception_thread;
+
+            // TODO see if we can remove this if we get the exception collection
+            // and distribution to happen in a single-threaded fashion.
+            std::recursive_mutex  m_exception_messages_mutex;
+
+            // A collection of exception messages caught when listening to the
+            // exception port.
+            MachException::Message::collection  m_exception_messages;
+
+            // When we call MachProcess::Interrupt(), we want to send this
+            // signal (if non-zero).
+            int  m_sent_interrupt_signo;
+
+            // If we resume the process and still haven't received our
+            // interrupt signal (if this is non-zero).
+            int  m_auto_resume_signo;
+
+            // -----------------------------------------------------------------
+            /// Thread-related Member Variables
+            // -----------------------------------------------------------------
+            NativeThreadListDarwin  m_thread_list;
+            ResumeActionList        m_thread_actions;
+
+            // -----------------------------------------------------------------
+            /// Process Lifetime Member Variable
+            // -----------------------------------------------------------------
+
+            // The pipe over which the waitpid thread and the main loop will
+            // communicate.
+            Pipe  m_waitpid_pipe;
+
+            // The thread that runs the waitpid handler.
+            pthread_t  m_waitpid_thread;
+
+            // waitpid reader callback handle.
+            MainLoop::ReadHandleUP m_waitpid_reader_handle;
+
+#if 0
+            ArchSpec m_arch;
+
+            LazyBool m_supports_mem_region;
+            std::vector<MemoryRegionInfo> m_mem_region_cache;
+
+            lldb::tid_t m_pending_notification_tid;
+
+            // List of thread ids stepping with a breakpoint with the address of
+            // the relevan breakpoint
+            std::map<lldb::tid_t, lldb::addr_t>
+            m_threads_stepping_with_breakpoint;
+#endif
+
+            // -----------------------------------------------------------------
+            // Private Instance Methods
+            // -----------------------------------------------------------------
+            NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd);
+
+            // -----------------------------------------------------------------
+            /// Finalize the launch.
+            ///
+            /// This method associates the NativeProcessDarwin instance with
+            /// the host process that was just launched.  It peforms actions
+            /// like attaching a listener to the inferior exception port,
+            /// ptracing the process, and the like.
+            ///
+            /// @param[in] launch_flavor
+            ///     The launch flavor that was used to launch the process.
+            ///
+            /// @param[in] main_loop
+            ///     The main loop that will run the process monitor.  Work
+            ///     that needs to be done (e.g. reading files) gets registered
+            ///     here along with callbacks to process the work.
+            ///
+            /// @return
+            ///     Any error that occurred during the aforementioned
+            ///     operations.  Failure here will force termination of the
+            ///     launched process and debugging session.
+            // -----------------------------------------------------------------
+            Error
+            FinalizeLaunch(LaunchFlavor launch_flavor, MainLoop &main_loop);
+
+            Error
+            SaveExceptionPortInfo();
+
+            void
+            ExceptionMessageReceived(const MachException::Message &message);
+
+            void
+            MaybeRaiseThreadPriority();
+
+            Error
+            StartExceptionThread();
+
+            Error
+            SendInferiorExitStatusToMainLoop(::pid_t pid, int status);
+
+            Error
+            HandleWaitpidResult();
+
+            bool
+            ProcessUsingSpringBoard() const;
+
+            bool
+            ProcessUsingBackBoard() const;
+
+            static void*
+            ExceptionThread(void *arg);
+
+            void*
+            DoExceptionThread();
+
+            lldb::addr_t
+            GetDYLDAllImageInfosAddress(Error &error) const;
+
+            static uint32_t
+            GetCPUTypeForLocalProcess(::pid_t pid);
+
+            uint32_t
+            GetCPUType() const;
+
+            task_t
+            ExceptionMessageBundleComplete();
+
+            void
+            StartSTDIOThread();
+
+            Error
+            StartWaitpidThread(MainLoop &main_loop);
+
+            static void*
+            WaitpidThread(void *arg);
+
+            void*
+            DoWaitpidThread();
+
+            task_t
+            TaskPortForProcessID(Error &error, bool force = false) const;
+
+            /// Attaches to an existing process.  Forms the
+            /// implementation of Process::DoAttach.
+            void
+            AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Error &error);
+
+            ::pid_t
+            Attach(lldb::pid_t pid, Error &error);
+
+            Error
+            PrivateResume();
+
+            Error
+            ReplyToAllExceptions();
+
+            Error
+            ResumeTask();
+
+            bool
+            IsTaskValid() const;
+
+            bool
+            IsTaskValid(task_t task) const;
+
+            mach_port_t
+            GetExceptionPort() const;
+
+            bool
+            IsExceptionPortValid () const;
+
+            Error
+            GetTaskBasicInfo(task_t task, struct task_basic_info *info) const;
+
+            Error
+            SuspendTask();
+
+            static Error
+            SetDefaultPtraceOpts(const lldb::pid_t);
+
+            static void *
+            MonitorThread(void *baton);
+
+            void
+            MonitorCallback(lldb::pid_t pid, bool exited, int signal,
+                            int status);
+
+            void
+            WaitForNewThread(::pid_t tid);
+
+            void
+            MonitorSIGTRAP(const siginfo_t &info, NativeThreadDarwin &thread);
+
+            void
+            MonitorTrace(NativeThreadDarwin &thread);
+
+            void
+            MonitorBreakpoint(NativeThreadDarwin &thread);
+
+            void
+            MonitorWatchpoint(NativeThreadDarwin &thread, uint32_t wp_index);
+
+            void
+            MonitorSignal(const siginfo_t &info, NativeThreadDarwin &thread,
+                          bool exited);
+
+            Error
+            SetupSoftwareSingleStepping(NativeThreadDarwin &thread);
+
+#if 0
+            static ::ProcessMessage::CrashReason
+            GetCrashReasonForSIGSEGV(const siginfo_t *info);
+
+            static ::ProcessMessage::CrashReason
+            GetCrashReasonForSIGILL(const siginfo_t *info);
+
+            static ::ProcessMessage::CrashReason
+            GetCrashReasonForSIGFPE(const siginfo_t *info);
+
+            static ::ProcessMessage::CrashReason
+            GetCrashReasonForSIGBUS(const siginfo_t *info);
+#endif
+
+            bool
+            HasThreadNoLock(lldb::tid_t thread_id);
+
+            bool
+            StopTrackingThread(lldb::tid_t thread_id);
+
+            NativeThreadDarwinSP
+            AddThread(lldb::tid_t thread_id);
+
+            Error
+            GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size);
+
+            Error
+            FixupBreakpointPCAsNeeded(NativeThreadDarwin &thread);
+
+            /// Writes a siginfo_t structure corresponding to the given thread
+            /// ID to the memory region pointed to by @p siginfo.
+            Error
+            GetSignalInfo(lldb::tid_t tid, void *siginfo);
+            
+            /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+            /// corresponding to the given thread ID to the memory pointed to
+            /// by @p message.
+            Error
+            GetEventMessage(lldb::tid_t tid, unsigned long *message);
+            
+            void
+            NotifyThreadDeath(lldb::tid_t tid);
+            
+            Error
+            Detach(lldb::tid_t tid);
+            
+            
+            // This method is requests a stop on all threads which are still
+            // running. It sets up a deferred delegate notification, which will
+            // fire once threads report as stopped. The triggerring_tid will be
+            // set as the current thread (main stop reason).
+            void
+            StopRunningThreads(lldb::tid_t triggering_tid);
+            
+            // Notify the delegate if all threads have stopped.
+            void SignalIfAllThreadsStopped();
+            
+            // Resume the given thread, optionally passing it the given signal.
+            // The type of resume operation (continue, single-step) depends on
+            // the state parameter.
+            Error
+            ResumeThread(NativeThreadDarwin &thread, lldb::StateType state,
+                         int signo);
+            
+            void
+            ThreadWasCreated(NativeThreadDarwin &thread);
+            
+            void
+            SigchldHandler();
+        };
+        
+    } // namespace process_darwin
+} // namespace lldb_private
+
+#endif /* NativeProcessDarwin_h */
diff --git a/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp b/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp
new file mode 100644 (file)
index 0000000..08d1a8d
--- /dev/null
@@ -0,0 +1,324 @@
+//===-- NativeThreadDarwin.cpp -------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadDarwin.h"
+
+// C includes
+#include <libproc.h>
+
+// LLDB includes
+#include "lldb/Core/Stream.h"
+
+#include "NativeProcessDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+uint64_t
+NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID(
+                                                        ::thread_t mach_port_id)
+{
+    thread_identifier_info_data_t tident;
+    mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+
+    auto mach_err = ::thread_info(mach_port_id, THREAD_IDENTIFIER_INFO,
+                                  (thread_info_t) &tident, &tident_count);
+    if (mach_err != KERN_SUCCESS)
+    {
+        // When we fail to get thread info for the supposed port, assume it is
+        // really a globally unique thread id already, or return the best thing
+        // we can, which is the thread port.
+        return mach_port_id;
+    }
+    return tident.thread_id;
+}
+
+NativeThreadDarwin::NativeThreadDarwin(NativeProcessDarwin *process,
+                                       bool is_64_bit,
+                                       lldb::tid_t unique_thread_id,
+                                       ::thread_t mach_thread_port) :
+    NativeThreadProtocol(process, unique_thread_id),
+    m_mach_thread_port(mach_thread_port),
+    m_basic_info(),
+    m_proc_threadinfo()
+{
+}
+
+bool
+NativeThreadDarwin::GetIdentifierInfo()
+{
+    // Don't try to get the thread info once and cache it for the life of the thread.  It changes over time, for instance
+    // if the thread name changes, then the thread_handle also changes...  So you have to refetch it every time.
+    mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+    kern_return_t kret = ::thread_info(m_mach_thread_port,
+                                       THREAD_IDENTIFIER_INFO,
+                                       (thread_info_t) &m_ident_info, &count);
+    return kret == KERN_SUCCESS;
+
+    return false;
+}
+
+std::string
+NativeThreadDarwin::GetName()
+{
+    std::string name;
+
+    if (GetIdentifierInfo())
+    {
+        auto process_sp = GetProcess();
+        if (!process_sp)
+        {
+            name = "<unavailable>";
+            return name;
+        }
+
+        int len = ::proc_pidinfo(process_sp->GetID(), PROC_PIDTHREADINFO,
+                                 m_ident_info.thread_handle, &m_proc_threadinfo,
+                                 sizeof(m_proc_threadinfo));
+
+        if (len && m_proc_threadinfo.pth_name[0])
+            name = m_proc_threadinfo.pth_name;
+    }
+    return name;
+}
+
+lldb::StateType
+NativeThreadDarwin::GetState()
+{
+    // TODO implement
+    return eStateInvalid;
+}
+
+bool
+NativeThreadDarwin::GetStopReason(ThreadStopInfo &stop_info,
+                                  std::string& description)
+{
+    // TODO implement
+    return false;
+}
+
+NativeRegisterContextSP
+NativeThreadDarwin::GetRegisterContext()
+{
+    // TODO implement
+    return NativeRegisterContextSP();
+}
+
+Error
+NativeThreadDarwin::SetWatchpoint(lldb::addr_t addr, size_t size,
+                                  uint32_t watch_flags, bool hardware)
+{
+    Error error;
+    error.SetErrorString("not yet implemented");
+    return error;
+}
+
+Error
+NativeThreadDarwin::RemoveWatchpoint(lldb::addr_t addr)
+{
+    Error error;
+    error.SetErrorString("not yet implemented");
+    return error;
+}
+
+void
+NativeThreadDarwin::Dump(Stream &stream) const
+{
+    // This is what we really want once we have the thread class wired up.
+#if 0
+    DNBLogThreaded("[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64 ", sp: 0x%16.16" PRIx64 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
+                   index,
+                   m_seq_id,
+                   m_unique_id,
+                   GetPC(INVALID_NUB_ADDRESS),
+                   GetSP(INVALID_NUB_ADDRESS),
+                   m_basic_info.user_time.seconds,      m_basic_info.user_time.microseconds,
+                   m_basic_info.system_time.seconds,    m_basic_info.system_time.microseconds,
+                   m_basic_info.cpu_usage,
+                   m_basic_info.policy,
+                   m_basic_info.run_state,
+                   thread_run_state,
+                   m_basic_info.flags,
+                   m_basic_info.suspend_count, m_suspend_count,
+                   m_basic_info.sleep_time);
+
+#else
+    // Here's all we have right now.
+    stream.Printf("tid: 0x%8.8" PRIx64 ", thread port: 0x%4.4x",
+                  GetID(), m_mach_thread_port);
+#endif
+}
+
+bool
+NativeThreadDarwin::NotifyException(MachException::Data &exc)
+{
+    // TODO implement this.
+#if 0
+    // Allow the arch specific protocol to process (MachException::Data &)exc
+    // first before possible reassignment of m_stop_exception with exc.
+    // See also MachThread::GetStopException().
+    bool handled = m_arch_ap->NotifyException(exc);
+
+    if (m_stop_exception.IsValid())
+    {
+        // We may have more than one exception for a thread, but we need to
+        // only remember the one that we will say is the reason we stopped.
+        // We may have been single stepping and also gotten a signal exception,
+        // so just remember the most pertinent one.
+        if (m_stop_exception.IsBreakpoint())
+            m_stop_exception = exc;
+    }
+    else
+    {
+        m_stop_exception = exc;
+    }
+
+    return handled;
+#else
+    // Pretend we handled it.
+    return true;
+#endif
+}
+
+bool
+NativeThreadDarwin::ShouldStop(bool &step_more) const
+{
+    // TODO: implement this
+#if 0
+    // See if this thread is at a breakpoint?
+    DNBBreakpoint *bp = CurrentBreakpoint();
+
+    if (bp)
+    {
+        // This thread is sitting at a breakpoint, ask the breakpoint
+        // if we should be stopping here.
+        return true;
+    }
+    else
+    {
+        if (m_arch_ap->StepNotComplete())
+        {
+            step_more = true;
+            return false;
+        }
+        // The thread state is used to let us know what the thread was
+        // trying to do. MachThread::ThreadWillResume() will set the
+        // thread state to various values depending if the thread was
+        // the current thread and if it was to be single stepped, or
+        // resumed.
+        if (GetState() == eStateRunning)
+        {
+            // If our state is running, then we should continue as we are in
+            // the process of stepping over a breakpoint.
+            return false;
+        }
+        else
+        {
+            // Stop if we have any kind of valid exception for this
+            // thread.
+            if (GetStopException().IsValid())
+                return true;
+        }
+    }
+    return false;
+#else
+    return false;
+#endif
+}
+
+void
+NativeThreadDarwin::ThreadDidStop()
+{
+    // TODO implement this.
+#if 0
+    // This thread has existed prior to resuming under debug nub control,
+    // and has just been stopped. Do any cleanup that needs to be done
+    // after running.
+
+    // The thread state and breakpoint will still have the same values
+    // as they had prior to resuming the thread, so it makes it easy to check
+    // if we were trying to step a thread, or we tried to resume while being
+    // at a breakpoint.
+
+    // When this method gets called, the process state is still in the
+    // state it was in while running so we can act accordingly.
+    m_arch_ap->ThreadDidStop();
+
+
+    // We may have suspended this thread so the primary thread could step
+    // without worrying about race conditions, so lets restore our suspend
+    // count.
+    RestoreSuspendCountAfterStop();
+
+    // Update the basic information for a thread
+    MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
+
+    if (m_basic_info.suspend_count > 0)
+        SetState(eStateSuspended);
+    else
+        SetState(eStateStopped);
+#endif
+}
+
+bool
+NativeThreadDarwin::MachPortNumberIsValid(::thread_t thread)
+{
+    return thread != (::thread_t)(0);
+}
+
+const struct thread_basic_info *
+NativeThreadDarwin::GetBasicInfo() const
+{
+    if (GetBasicInfo(m_mach_thread_port, &m_basic_info))
+        return &m_basic_info;
+    return NULL;
+}
+
+bool
+NativeThreadDarwin::GetBasicInfo(::thread_t thread,
+                                 struct thread_basic_info *basicInfoPtr)
+{
+    if (MachPortNumberIsValid(thread))
+    {
+        unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+        kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count);
+        if (err == KERN_SUCCESS)
+            return true;
+    }
+    ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info));
+    return false;
+}
+
+bool
+NativeThreadDarwin::IsUserReady() const
+{
+    if (m_basic_info.run_state == 0)
+        GetBasicInfo();
+
+    switch (m_basic_info.run_state)
+    {
+        default:
+        case TH_STATE_UNINTERRUPTIBLE:
+            break;
+
+        case TH_STATE_RUNNING:
+        case TH_STATE_STOPPED:
+        case TH_STATE_WAITING:
+        case TH_STATE_HALTED:
+            return true;
+    }
+    return false;
+}
+
+NativeProcessDarwinSP
+NativeThreadDarwin::GetNativeProcessDarwinSP()
+{
+    return std::static_pointer_cast<NativeProcessDarwin>(GetProcess());
+}
diff --git a/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h b/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h
new file mode 100644 (file)
index 0000000..efdf79a
--- /dev/null
@@ -0,0 +1,216 @@
+//===-- NativeThreadDarwin.h ---------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef NativeThreadDarwin_H
+#define NativeThreadDarwin_H
+
+// C includes
+#include <mach/mach_types.h>
+#include <sched.h>
+#include <sys/proc_info.h>
+
+// C++ includes
+#include <map>
+#include <memory>
+#include <string>
+
+// LLDB includes
+#include "lldb/lldb-private-forward.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "MachException.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeProcessDarwin;
+using NativeProcessDarwinSP = std::shared_ptr<NativeProcessDarwin>;
+
+class NativeThreadListDarwin;
+
+class NativeThreadDarwin : public NativeThreadProtocol
+{
+    friend class NativeProcessDarwin;
+    friend class NativeThreadListDarwin;
+
+public:
+
+    static uint64_t
+    GetGloballyUniqueThreadIDForMachPortID(::thread_t mach_port_id);
+
+    NativeThreadDarwin(NativeProcessDarwin *process, bool is_64_bit,
+                       lldb::tid_t unique_thread_id = 0,
+                       ::thread_t mach_thread_port = 0);
+
+    // -----------------------------------------------------------------
+    // NativeThreadProtocol Interface
+    // -----------------------------------------------------------------
+    std::string
+    GetName() override;
+
+    lldb::StateType
+    GetState () override;
+
+    bool
+    GetStopReason(ThreadStopInfo &stop_info,
+                  std::string& description) override;
+
+    NativeRegisterContextSP
+    GetRegisterContext() override;
+
+    Error
+    SetWatchpoint(lldb::addr_t addr, size_t size,
+                  uint32_t watch_flags, bool hardware) override;
+
+    Error
+    RemoveWatchpoint(lldb::addr_t addr) override;
+
+    // -----------------------------------------------------------------
+    // New methods that are fine for others to call.
+    // -----------------------------------------------------------------
+    void
+    Dump(Stream &stream) const;
+
+private:
+    // -----------------------------------------------------------------
+    // Interface for friend classes
+    // -----------------------------------------------------------------
+
+    /// Resumes the thread.  If @p signo is anything but
+    /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+    Error
+    Resume(uint32_t signo);
+
+    /// Single steps the thread.  If @p signo is anything but
+    /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+    Error
+    SingleStep(uint32_t signo);
+
+    bool
+    NotifyException(MachException::Data &exc);
+
+    bool
+    ShouldStop(bool &step_more) const;
+
+    void
+    ThreadDidStop();
+
+    void
+    SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+
+    /// Return true if the thread is stopped.
+    /// If stopped by a signal, indicate the signo in the signo
+    /// argument.  Otherwise, return LLDB_INVALID_SIGNAL_NUMBER.
+    bool
+    IsStopped (int *signo);
+
+    const struct thread_basic_info *
+    GetBasicInfo() const;
+
+    static bool
+    GetBasicInfo(::thread_t thread, struct thread_basic_info *basicInfoPtr);
+
+    bool
+    IsUserReady() const;
+
+    void
+    SetStoppedByExec ();
+
+    void
+    SetStoppedByBreakpoint ();
+
+    void
+    SetStoppedByWatchpoint (uint32_t wp_index);
+
+    bool
+    IsStoppedAtBreakpoint ();
+
+    bool
+    IsStoppedAtWatchpoint ();
+
+    void
+    SetStoppedByTrace ();
+
+    void
+    SetStoppedWithNoReason ();
+
+    void
+    SetExited ();
+
+    Error
+    RequestStop ();
+
+    // -------------------------------------------------------------------------
+    /// Return the mach thread port number for this thread.
+    ///
+    /// @return
+    ///     The mach port number for this thread.  Returns NULL_THREAD
+    ///     when the thread is invalid.
+    // -------------------------------------------------------------------------
+    thread_t
+    GetMachPortNumber() const
+    {
+        return m_mach_thread_port;
+    }
+
+    static bool
+    MachPortNumberIsValid(::thread_t thread);
+
+    // ---------------------------------------------------------------------
+    // Private interface
+    // ---------------------------------------------------------------------
+    bool
+    GetIdentifierInfo();
+
+    void
+    MaybeLogStateChange (lldb::StateType new_state);
+
+    NativeProcessDarwinSP
+    GetNativeProcessDarwinSP();
+
+    void
+    SetStopped();
+
+    inline void
+    MaybePrepareSingleStepWorkaround();
+
+    inline void
+    MaybeCleanupSingleStepWorkaround();
+
+    // -----------------------------------------------------------------
+    // Member Variables
+    // -----------------------------------------------------------------
+
+    // The mach thread port for the thread.
+    ::thread_t  m_mach_thread_port;
+
+    // The most recently-retrieved thread basic info.
+    mutable ::thread_basic_info m_basic_info;
+
+    struct proc_threadinfo  m_proc_threadinfo;
+
+    thread_identifier_info_data_t   m_ident_info;
+
+#if 0
+    lldb::StateType m_state;
+    ThreadStopInfo m_stop_info;
+    NativeRegisterContextSP m_reg_context_sp;
+    std::string m_stop_description;
+    using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+    WatchpointIndexMap m_watchpoint_index_map;
+    // cpu_set_t m_original_cpu_set; // For single-step workaround.
+#endif
+};
+
+typedef std::shared_ptr<NativeThreadDarwin> NativeThreadDarwinSP;
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef NativeThreadDarwin_H
diff --git a/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp b/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp
new file mode 100644 (file)
index 0000000..034c0c9
--- /dev/null
@@ -0,0 +1,745 @@
+//===-- NativeThreadListDarwin.cpp ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadListDarwin.h"
+
+// C includes
+#include <inttypes.h>
+#include <mach/vm_map.h>
+#include <sys/sysctl.h>
+
+// LLDB includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "NativeProcessDarwin.h"
+#include "NativeThreadDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+NativeThreadListDarwin::NativeThreadListDarwin() :
+    m_threads(),
+    m_threads_mutex(),
+    m_is_64_bit(false)
+{
+}
+
+NativeThreadListDarwin::~NativeThreadListDarwin()
+{
+}
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+nub_state_t
+NativeThreadListDarwin::GetState(nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetState();
+    return eStateInvalid;
+}
+
+const char *
+NativeThreadListDarwin::GetName (nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetName();
+    return NULL;
+}
+#endif
+
+// TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+ThreadInfo::QoS
+NativeThreadListDarwin::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index);
+    return ThreadInfo::QoS();
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetPThreadT (nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetPThreadT();
+    return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetDispatchQueueT (nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetDispatchQueueT();
+    return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+    return INVALID_NUB_ADDRESS;
+}
+#endif
+
+// TODO implement these
+#if 0
+nub_thread_t
+NativeThreadListDarwin::SetCurrentThread(nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+    {
+        m_current_thread = thread_sp;
+        return tid;
+    }
+    return INVALID_NUB_THREAD;
+}
+
+
+bool
+NativeThreadListDarwin::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetStopException().GetStopInfo(stop_info);
+    return false;
+}
+
+bool
+NativeThreadListDarwin::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info)
+{
+    thread_t mach_port_number = GetMachPortNumberByThreadID (tid);
+
+    mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+    return ::thread_info (mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS;
+}
+
+void
+NativeThreadListDarwin::DumpThreadStoppedReason (nub_thread_t tid) const
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        thread_sp->GetStopException().DumpStopReason();
+}
+
+const char *
+NativeThreadListDarwin::GetThreadInfo (nub_thread_t tid) const
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetBasicInfoAsString();
+    return NULL;
+}
+
+#endif
+
+NativeThreadDarwinSP
+NativeThreadListDarwin::GetThreadByID(lldb::tid_t tid) const
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp && (thread_sp->GetID() == tid))
+            return thread_sp;
+    }
+    return NativeThreadDarwinSP();
+}
+
+NativeThreadDarwinSP
+NativeThreadListDarwin::GetThreadByMachPortNumber(::thread_t mach_port_number)
+    const
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+            return thread_sp;
+    }
+    return NativeThreadDarwinSP();
+}
+
+lldb::tid_t
+NativeThreadListDarwin::GetThreadIDByMachPortNumber(::thread_t mach_port_number)
+    const
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+            return thread_sp->GetID();
+    }
+    return LLDB_INVALID_THREAD_ID;
+}
+
+// TODO implement
+#if 0
+thread_t
+NativeThreadListDarwin::GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const
+{
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    MachThreadSP thread_sp;
+    const size_t num_threads = m_threads.size();
+    for (size_t idx = 0; idx < num_threads; ++idx)
+    {
+        if (m_threads[idx]->ThreadID() == globally_unique_id)
+        {
+            return m_threads[idx]->MachPortNumber();
+        }
+    }
+    return 0;
+}
+
+bool
+NativeThreadListDarwin::GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value ) const
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetRegisterValue(set, reg, reg_value);
+
+    return false;
+}
+
+bool
+NativeThreadListDarwin::SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value ) const
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->SetRegisterValue(set, reg, reg_value);
+
+    return false;
+}
+
+nub_size_t
+NativeThreadListDarwin::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->GetRegisterContext (buf, buf_len);
+    return 0;
+}
+
+nub_size_t
+NativeThreadListDarwin::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->SetRegisterContext (buf, buf_len);
+    return 0;
+}
+
+uint32_t
+NativeThreadListDarwin::SaveRegisterState (nub_thread_t tid)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->SaveRegisterState ();
+    return 0;
+}
+
+bool
+NativeThreadListDarwin::RestoreRegisterState (nub_thread_t tid, uint32_t save_id)
+{
+    MachThreadSP thread_sp (GetThreadByID (tid));
+    if (thread_sp)
+        return thread_sp->RestoreRegisterState (save_id);
+    return 0;
+}
+#endif
+
+size_t
+NativeThreadListDarwin::GetNumberOfThreads() const
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    return static_cast<size_t>(m_threads.size());
+}
+
+// TODO implement
+#if 0
+nub_thread_t
+NativeThreadListDarwin::ThreadIDAtIndex (nub_size_t idx) const
+{
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    if (idx < m_threads.size())
+        return m_threads[idx]->ThreadID();
+    return INVALID_NUB_THREAD;
+}
+
+nub_thread_t
+NativeThreadListDarwin::CurrentThreadID ( )
+{
+    MachThreadSP thread_sp;
+    CurrentThread(thread_sp);
+    if (thread_sp.get())
+        return thread_sp->ThreadID();
+    return INVALID_NUB_THREAD;
+}
+
+#endif
+
+bool
+NativeThreadListDarwin::NotifyException(MachException::Data &exc)
+{
+    auto thread_sp = GetThreadByMachPortNumber(exc.thread_port);
+    if (thread_sp)
+    {
+        thread_sp->NotifyException(exc);
+        return true;
+    }
+    return false;
+}
+
+void
+NativeThreadListDarwin::Clear()
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    m_threads.clear();
+}
+
+uint32_t
+NativeThreadListDarwin::UpdateThreadList(NativeProcessDarwin &process,
+                                         bool update,
+                                         collection *new_threads)
+{
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    if (log)
+        log->Printf("NativeThreadListDarwin::%s() (pid = %" PRIu64 ", update = "
+                    "%u) process stop count = %u", __FUNCTION__,
+                    process.GetID(), update, process.GetStopID());
+
+    if (process.GetStopID() == 0)
+    {
+        // On our first stop, we'll record details like 32/64 bitness and
+        // select the proper architecture implementation.
+        //
+        int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
+            (int)process.GetID() };
+
+        struct kinfo_proc processInfo;
+        size_t bufsize = sizeof(processInfo);
+        if ((sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo,
+                    &bufsize, NULL, 0) == 0) && (bufsize > 0))
+        {
+            if (processInfo.kp_proc.p_flag & P_LP64)
+                m_is_64_bit = true;
+        }
+
+// TODO implement architecture selection and abstraction.
+#if 0
+#if defined (__i386__) || defined (__x86_64__)
+        if (m_is_64_bit)
+            DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
+        else
+            DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
+#elif defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
+        if (m_is_64_bit)
+            DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+        else
+            DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+#endif
+#endif
+    }
+    
+    if (m_threads.empty() || update)
+    {
+        thread_array_t thread_list = nullptr;
+        mach_msg_type_number_t thread_list_count = 0;
+        task_t task = process.GetTask();
+
+        Error error;
+        auto mach_err = ::task_threads(task, &thread_list, &thread_list_count);
+        error.SetError(mach_err, eErrorTypeMachKernel);
+        if (error.Fail())
+        {
+            if (log)
+                log->Printf("::task_threads(task = 0x%4.4x, thread_list => %p, "
+                            "thread_list_count => %u) failed: %u (%s)", task,
+                            thread_list, thread_list_count, error.GetError(),
+                            error.AsCString());
+            return 0;
+        }
+
+        if (thread_list_count > 0)
+        {
+            collection currThreads;
+            size_t idx;
+            // Iterator through the current thread list and see which threads
+            // we already have in our list (keep them), which ones we don't
+            // (add them), and which ones are not around anymore (remove them).
+            for (idx = 0; idx < thread_list_count; ++idx)
+            {
+                // Get the Mach thread port.
+                const ::thread_t mach_port_num = thread_list[idx];
+
+                // Get the unique thread id for the mach port number.
+                uint64_t unique_thread_id =
+                    NativeThreadDarwin::
+                    GetGloballyUniqueThreadIDForMachPortID(mach_port_num);
+
+                // Retrieve the thread if it exists.
+                auto thread_sp = GetThreadByID(unique_thread_id);
+                if (thread_sp)
+                {
+                    // We are already tracking it. Keep the existing native
+                    // thread instance.
+                    currThreads.push_back(thread_sp);
+                }
+                else
+                {
+                    // We don't have a native thread instance for this thread.
+                    // Create it now.
+                    thread_sp.reset(new NativeThreadDarwin(&process,
+                                                           m_is_64_bit,
+                                                           unique_thread_id,
+                                                           mach_port_num));
+
+                    // Add the new thread regardless of its is user ready state.
+                    // Make sure the thread is ready to be displayed and shown
+                    // to users before we add this thread to our list...
+                    if (thread_sp->IsUserReady())
+                    {
+                        if (new_threads)
+                            new_threads->push_back(thread_sp);
+                    
+                        currThreads.push_back(thread_sp);
+                    }
+                }
+            }
+
+            m_threads.swap(currThreads);
+            m_current_thread.reset();
+
+            // Free the vm memory given to us by ::task_threads()
+            vm_size_t thread_list_size = (vm_size_t) (thread_list_count *
+                                                      sizeof (::thread_t));
+            ::vm_deallocate(::mach_task_self(),
+                            (vm_address_t)thread_list,
+                            thread_list_size);
+        }
+    }
+    return static_cast<uint32_t>(m_threads.size());
+}
+
+// TODO implement
+#if 0
+
+void
+NativeThreadListDarwin::CurrentThread (MachThreadSP& thread_sp)
+{
+    // locker will keep a mutex locked until it goes out of scope
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    if (m_current_thread.get() == NULL)
+    {
+        // Figure out which thread is going to be our current thread.
+        // This is currently done by finding the first thread in the list
+        // that has a valid exception.
+        const size_t num_threads = m_threads.size();
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+        {
+            if (m_threads[idx]->GetStopException().IsValid())
+            {
+                m_current_thread = m_threads[idx];
+                break;
+            }
+        }
+    }
+    thread_sp = m_current_thread;
+}
+
+#endif
+
+void
+NativeThreadListDarwin::Dump(Stream &stream) const
+{
+    bool first = true;
+
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp)
+        {
+            // Handle newlines between thread entries.
+            if (first)
+                first = false;
+            else
+                stream.PutChar('\n');
+            thread_sp->Dump(stream);
+        }
+    }
+}
+
+void
+NativeThreadListDarwin::ProcessWillResume(NativeProcessDarwin &process,
+                                    const ResumeActionList &thread_actions)
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+    // Update our thread list, because sometimes libdispatch or the kernel
+    // will spawn threads while a task is suspended.
+    NativeThreadListDarwin::collection new_threads;
+
+    // TODO implement this.
+#if 0
+    // First figure out if we were planning on running only one thread, and if
+    // so, force that thread to resume.
+    bool run_one_thread;
+    thread_t solo_thread = THREAD_NULL;
+    if ((thread_actions.GetSize() > 0) &&
+        (thread_actions.NumActionsWithState(eStateStepping) +
+         thread_actions.NumActionsWithState (eStateRunning) == 1))
+    {
+        run_one_thread = true;
+        const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst();
+        size_t num_actions = thread_actions.GetSize();
+        for (size_t i = 0; i < num_actions; i++, action_ptr++)
+        {
+            if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning)
+            {
+                solo_thread = action_ptr->tid;
+                break;
+            }
+        }
+    }
+    else
+        run_one_thread = false;
+#endif
+
+    UpdateThreadList(process, true, &new_threads);
+
+#if 0
+    DNBThreadResumeAction resume_new_threads = { -1U, eStateRunning, 0, INVALID_NUB_ADDRESS };
+    // If we are planning to run only one thread, any new threads should be suspended.
+    if (run_one_thread)
+        resume_new_threads.state = eStateSuspended;
+
+    const size_t num_new_threads = new_threads.size();
+    const size_t num_threads = m_threads.size();
+    for (uint32_t idx = 0; idx < num_threads; ++idx)
+    {
+        MachThread *thread = m_threads[idx].get();
+        bool handled = false;
+        for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx)
+        {
+            if (thread == new_threads[new_idx].get())
+            {
+                thread->ThreadWillResume(&resume_new_threads);
+                handled = true;
+                break;
+            }
+        }
+
+        if (!handled)
+        {
+            const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
+            // There must always be a thread action for every thread.
+            assert (thread_action);
+            bool others_stopped = false;
+            if (solo_thread == thread->ThreadID())
+                others_stopped = true;
+            thread->ThreadWillResume (thread_action, others_stopped);
+        }
+    }
+    
+    if (new_threads.size())
+    {
+        for (uint32_t idx = 0; idx < num_new_threads; ++idx)
+        {
+            DNBLogThreadedIf (LOG_THREAD, "NativeThreadListDarwin::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%8.8" PRIx64 ", thread-is-user-ready=%i)",
+                              process->ProcessID(), 
+                              process->StopCount(), 
+                              new_threads[idx]->ThreadID(),
+                              new_threads[idx]->IsUserReady());
+        }
+    }
+#endif
+}
+
+uint32_t
+NativeThreadListDarwin::ProcessDidStop(NativeProcessDarwin &process)
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+    // Update our thread list.
+    UpdateThreadList(process, true);
+
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp)
+            thread_sp->ThreadDidStop();
+    }
+    return (uint32_t)m_threads.size();
+}
+
+//----------------------------------------------------------------------
+// Check each thread in our thread list to see if we should notify our
+// client of the current halt in execution.
+//
+// Breakpoints can have callback functions associated with them than
+// can return true to stop, or false to continue executing the inferior.
+//
+// RETURNS
+//    true if we should stop and notify our clients
+//    false if we should resume our child process and skip notification
+//----------------------------------------------------------------------
+bool
+NativeThreadListDarwin::ShouldStop(bool &step_more)
+{
+    std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+    for (auto thread_sp : m_threads)
+    {
+        if (thread_sp && thread_sp->ShouldStop(step_more))
+            return true;
+    }
+    return false;
+}
+
+// Implement.
+#if 0
+
+void
+NativeThreadListDarwin::NotifyBreakpointChanged (const DNBBreakpoint *bp)
+{
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    const size_t num_threads = m_threads.size();
+    for (uint32_t idx = 0; idx < num_threads; ++idx)
+    {
+        m_threads[idx]->NotifyBreakpointChanged(bp);
+    }
+}
+
+
+uint32_t
+NativeThreadListDarwin::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+    if (bp != NULL)
+    {
+        const size_t num_threads = m_threads.size();
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+            m_threads[idx]->EnableHardwareBreakpoint(bp);
+    }
+    return INVALID_NUB_HW_INDEX;
+}
+
+bool
+NativeThreadListDarwin::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+    if (bp != NULL)
+    {
+        const size_t num_threads = m_threads.size();
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+            m_threads[idx]->DisableHardwareBreakpoint(bp);
+    }
+    return false;
+}
+
+// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> MachProcess::EnableWatchpoint()
+// -> NativeThreadListDarwin::EnableHardwareWatchpoint().
+uint32_t
+NativeThreadListDarwin::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+    uint32_t hw_index = INVALID_NUB_HW_INDEX;
+    if (wp != NULL)
+    {
+        PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+        const size_t num_threads = m_threads.size();
+        // On Mac OS X we have to prime the control registers for new threads.  We do this
+        // using the control register data for the first thread, for lack of a better way of choosing.
+        bool also_set_on_task = true;
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+        {                
+            if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(wp, also_set_on_task)) == INVALID_NUB_HW_INDEX)
+            {
+                // We know that idx failed for some reason.  Let's rollback the transaction for [0, idx).
+                for (uint32_t i = 0; i < idx; ++i)
+                    m_threads[i]->RollbackTransForHWP();
+                return INVALID_NUB_HW_INDEX;
+            }
+            also_set_on_task = false;
+        }
+        // Notify each thread to commit the pending transaction.
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+            m_threads[idx]->FinishTransForHWP();
+
+    }
+    return hw_index;
+}
+
+bool
+NativeThreadListDarwin::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+    if (wp != NULL)
+    {
+        PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+        const size_t num_threads = m_threads.size();
+        
+        // On Mac OS X we have to prime the control registers for new threads.  We do this
+        // using the control register data for the first thread, for lack of a better way of choosing.
+        bool also_set_on_task = true;
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+        {
+            if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task))
+            {
+                // We know that idx failed for some reason.  Let's rollback the transaction for [0, idx).
+                for (uint32_t i = 0; i < idx; ++i)
+                    m_threads[i]->RollbackTransForHWP();
+                return false;
+            }
+            also_set_on_task = false;
+        }
+        // Notify each thread to commit the pending transaction.
+        for (uint32_t idx = 0; idx < num_threads; ++idx)
+            m_threads[idx]->FinishTransForHWP();
+
+        return true;
+    }
+    return false;
+}
+
+uint32_t
+NativeThreadListDarwin::NumSupportedHardwareWatchpoints () const
+{
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    const size_t num_threads = m_threads.size();
+    // Use an arbitrary thread to retrieve the number of supported hardware watchpoints.
+    if (num_threads)
+        return m_threads[0]->NumSupportedHardwareWatchpoints();
+    return 0;
+}
+
+uint32_t
+NativeThreadListDarwin::GetThreadIndexForThreadStoppedWithSignal (const int signo) const
+{
+    PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+    uint32_t should_stop = false;
+    const size_t num_threads = m_threads.size();
+    for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx)
+    {
+        if (m_threads[idx]->GetStopException().SoftSignal () == signo)
+            return idx;
+    }
+    return UINT32_MAX;
+}
+
+#endif
diff --git a/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h b/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h
new file mode 100644 (file)
index 0000000..5880130
--- /dev/null
@@ -0,0 +1,162 @@
+//===-- NativeThreadListDarwin.h --------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __NativeThreadListDarwin_h__
+#define __NativeThreadListDarwin_h__
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+#include "MachException.h"
+
+// #include "ThreadInfo.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeBreakpointDarwin;
+class NativeProcessDarwin;
+
+class NativeThreadDarwin;
+using NativeThreadDarwinSP = std::shared_ptr<NativeThreadDarwin>;
+
+class NativeThreadListDarwin
+{
+public:
+    NativeThreadListDarwin();
+    ~NativeThreadListDarwin();
+
+    void
+    Clear();
+
+    void
+    Dump(Stream &stream) const;
+
+    // These methods will be accessed directly from NativeThreadDarwin
+#if 0
+    bool            GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const;
+    bool            SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value) const;
+    nub_size_t      GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len);
+    nub_size_t      SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len);
+    uint32_t        SaveRegisterState (nub_thread_t tid);
+    bool            RestoreRegisterState (nub_thread_t tid, uint32_t save_id);
+#endif
+
+    const char *
+    GetThreadInfo(lldb::tid_t tid) const;
+
+    void
+    ProcessWillResume(NativeProcessDarwin &process,
+                      const ResumeActionList &thread_actions);
+
+    uint32_t
+    ProcessDidStop(NativeProcessDarwin &process);
+
+    bool
+    NotifyException(MachException::Data& exc);
+
+    bool
+    ShouldStop(bool &step_more);
+
+    // These methods will be accessed directly from NativeThreadDarwin
+#if 0
+    const char *    GetName (nub_thread_t tid);
+    nub_state_t     GetState (nub_thread_t tid);
+    nub_thread_t    SetCurrentThread (nub_thread_t tid);
+#endif
+
+    // TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+    ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index);
+    nub_addr_t      GetPThreadT (nub_thread_t tid);
+    nub_addr_t      GetDispatchQueueT (nub_thread_t tid);
+    nub_addr_t      GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+#endif
+
+    // These methods will be accessed directly from NativeThreadDarwin
+#if 0
+    bool            GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const;
+    void            DumpThreadStoppedReason (nub_thread_t tid) const;
+    bool            GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info);
+#endif
+
+    size_t
+    GetNumberOfThreads() const;
+
+    lldb::tid_t
+    ThreadIDAtIndex(size_t idx) const;
+
+    lldb::tid_t
+    GetCurrentThreadID();
+
+    NativeThreadDarwinSP
+    GetCurrentThread();
+
+    void
+    NotifyBreakpointChanged(const NativeBreakpointDarwin *bp);
+
+    uint32_t
+    EnableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+    bool
+    DisableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+    uint32_t
+    EnableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+    bool
+    DisableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+    uint32_t
+    GetNumberOfSupportedHardwareWatchpoints() const;
+
+    size_t
+    GetThreadIndexForThreadStoppedWithSignal(const int signo) const;
+
+    NativeThreadDarwinSP
+    GetThreadByID(lldb::tid_t tid) const;
+
+    NativeThreadDarwinSP
+    GetThreadByMachPortNumber(::thread_t mach_port_number) const;
+
+    lldb::tid_t
+    GetThreadIDByMachPortNumber(::thread_t mach_port_number) const;
+
+    thread_t
+    GetMachPortNumberByThreadID(lldb::tid_t globally_unique_id) const;
+
+protected:
+    typedef std::vector<NativeThreadDarwinSP>  collection;
+    typedef collection::iterator               iterator;
+    typedef collection::const_iterator         const_iterator;
+
+    // Consider having this return an lldb_private::Error.
+    uint32_t
+    UpdateThreadList (NativeProcessDarwin &process, bool update,
+                      collection *num_threads = nullptr);
+
+    collection                    m_threads;
+    mutable std::recursive_mutex  m_threads_mutex;
+    NativeThreadDarwinSP          m_current_thread;
+    bool                          m_is_64_bit;
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef __NativeThreadListDarwin_h__
+
index 49d57c0..4af5b6d 100644 (file)
@@ -108,27 +108,6 @@ static bool ProcessVmReadvSupported()
 
 namespace
 {
-Error
-ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch)
-{
-    // Grab process info for the running process.
-    ProcessInstanceInfo process_info;
-    if (!Host::GetProcessInfo(pid, process_info))
-        return Error("failed to get process info");
-
-    // Resolve the executable module.
-    ModuleSpecList module_specs;
-    if (!ObjectFile::GetModuleSpecifications(process_info.GetExecutableFile(), 0, 0, module_specs))
-        return Error("failed to get module specifications");
-    assert(module_specs.GetSize() == 1);
-
-    arch = module_specs.GetModuleSpecRefAtIndex(0).GetArchitecture();
-    if (arch.IsValid())
-        return Error();
-    else
-        return Error("failed to retrieve a valid architecture from the exe module");
-}
-
 void
 MaybeLogLaunchInfo(const ProcessLaunchInfo &info)
 {
index 3361ffa..5ae5627 100644 (file)
 #include <errno.h>
 
 // C Includes
+
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 // C++ Includes
 #include <cstring>
 #include <chrono>
@@ -169,8 +174,11 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo (StringExtractorGDBRemote &
         response.PutCString(";");
     }
 
-    // Only send out MachO info when lldb-platform/llgs is running on a MachO host.
 #if defined(__APPLE__)
+    // For parity with debugserver, we'll include the vendor key.
+    response.PutCString("vendor:apple;");
+
+    // Send out MachO info.
     uint32_t cpu = host_arch.GetMachOCPUType();
     uint32_t sub = host_arch.GetMachOCPUSubType();
     if (cpu != LLDB_INVALID_CPUTYPE)
@@ -179,9 +187,26 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo (StringExtractorGDBRemote &
         response.Printf ("cpusubtype:%u;", sub);
 
     if (cpu == ArchSpec::kCore_arm_any)
-        response.Printf("watchpoint_exceptions_received:before;");   // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
+    {
+        // Indicate the OS type.
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+        response.PutCString("ostype:tvos;");
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+        response.PutCString("ostype:watchos;");
+#else
+        response.PutCString("ostype:ios;");
+#endif
+
+        // On arm, we use "synchronous" watchpoints which means the exception is
+        // delivered before the instruction executes.
+        response.PutCString("watchpoint_exceptions_received:before;");
+    }
     else
+    {
+        response.PutCString("ostype:macosx;");
         response.Printf("watchpoint_exceptions_received:after;");
+    }
+
 #else
     if (host_arch.GetMachine() == llvm::Triple::aarch64 ||
         host_arch.GetMachine() == llvm::Triple::aarch64_be ||
index 81c54ed..04720ac 100644 (file)
@@ -216,7 +216,8 @@ GDBRemoteCommunicationServerLLGS::LaunchProcess ()
     Error error;
     {
         std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex);
-        assert (!m_debugged_process_sp && "lldb-gdbserver creating debugged process but one already exists");
+        assert (!m_debugged_process_sp && "lldb-server creating debugged "
+                "process but one already exists");
         error = NativeProcessProtocol::Launch(
             m_process_launch_info,
             *this,
index cf244ba..9230906 100644 (file)
@@ -89,6 +89,8 @@ UnixSignals::Reset ()
     // This builds one standard set of Unix Signals.  If yours aren't quite in this
     // order, you can either subclass this class, and use Add & Remove to change them
     // or you can subclass and build them afresh in your constructor;
+    //
+    // Note: the signals below are the Darwin signals.  Do not change these!
     m_signals.clear();
     //        SIGNO  NAME          SUPPRESS STOP   NOTIFY DESCRIPTION
     //        ====== ============  ======== ====== ====== ===================================================
diff --git a/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist b/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist
new file mode 100644 (file)
index 0000000..58a34ca
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.lldb-server</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>lldb-server</string>
+       <key>CFBundleVersion</key>
+       <string>2</string>
+       <key>SecTaskAccess</key>
+       <array>
+               <string>allowed</string>
+               <string>debug</string>
+       </array>
+</dict>
+</plist>
diff --git a/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist b/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist
new file mode 100644 (file)
index 0000000..4134ee9
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>com.apple.springboard.debugapplications</key>
+    <true/>
+    <key>com.apple.backboardd.launchapplications</key>
+    <true/>
+    <key>com.apple.backboardd.debugapplications</key>
+    <true/>
+    <key>com.apple.frontboard.launchapplications</key>
+    <true/>
+    <key>com.apple.frontboard.debugapplications</key>
+    <true/>
+    <key>run-unsigned-code</key>
+    <true/>
+    <key>seatbelt-profiles</key>
+    <array>
+        <string>debugserver</string>
+    </array>
+    <key>com.apple.diagnosticd.diagnostic</key>
+    <true/>
+    <key>com.apple.security.network.server</key>
+    <true/>
+    <key>com.apple.security.network.client</key>
+    <true/>
+</dict>
+</plist>
diff --git a/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist b/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist
new file mode 100644 (file)
index 0000000..eddbaa0
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>com.apple.diagnosticd.diagnostic</key>
+    <true/>
+</dict>
+</plist>
diff --git a/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs b/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs
new file mode 100644 (file)
index 0000000..cd5be17
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+ * nub.defs
+ */
+#import <mach/mach_exc.defs>