[Lldb-commits] [lldb] 92eaad2 - Revert "Revert "Make it possible for lldb to launch a remote binary with no local file.""

Jim Ingham via lldb-commits lldb-commits at lists.llvm.org
Wed Nov 17 18:00:32 PST 2021


Author: Jim Ingham
Date: 2021-11-17T17:59:47-08:00
New Revision: 92eaad2dd7adb5ee92f397cef85ab11f2612294e

URL: https://github.com/llvm/llvm-project/commit/92eaad2dd7adb5ee92f397cef85ab11f2612294e
DIFF: https://github.com/llvm/llvm-project/commit/92eaad2dd7adb5ee92f397cef85ab11f2612294e.diff

LOG: Revert "Revert "Make it possible for lldb to launch a remote binary with no local file.""

This reverts commit dd5505a8f2c75a903ec944b6e46aed2042610673.

I picked the wrong class for the test, should have been GDBRemoteTestBase.

Added: 
    lldb/test/API/functionalities/gdb_remote_client/TestNoLocalFile.py

Modified: 
    lldb/source/Commands/CommandObjectProcess.cpp
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/source/Target/Process.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 8ce14f2a96d5e..5fd1718e84840 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -159,7 +159,12 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
     // If our listener is nullptr, users aren't allows to launch
     ModuleSP exe_module_sp = target->GetExecutableModule();
 
-    if (exe_module_sp == nullptr) {
+    // If the target already has an executable module, then use that.  If it
+    // doesn't then someone must be trying to launch using a path that will
+    // make sense to the remote stub, but doesn't exist on the local host.
+    // In that case use the ExecutableFile that was set in the target's
+    // ProcessLaunchInfo.
+    if (exe_module_sp == nullptr && !target->GetProcessLaunchInfo().GetExecutableFile()) {
       result.AppendError("no file in target, create a debug target using the "
                          "'target create' command");
       return false;
@@ -219,11 +224,17 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
     if (!target_settings_argv0.empty()) {
       m_options.launch_info.GetArguments().AppendArgument(
           target_settings_argv0);
-      m_options.launch_info.SetExecutableFile(
-          exe_module_sp->GetPlatformFileSpec(), false);
+      if (exe_module_sp)
+        m_options.launch_info.SetExecutableFile(
+            exe_module_sp->GetPlatformFileSpec(), false);
+      else
+        m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), false);
     } else {
-      m_options.launch_info.SetExecutableFile(
-          exe_module_sp->GetPlatformFileSpec(), true);
+      if (exe_module_sp)
+        m_options.launch_info.SetExecutableFile(
+            exe_module_sp->GetPlatformFileSpec(), true);
+      else
+        m_options.launch_info.SetExecutableFile(target->GetProcessLaunchInfo().GetExecutableFile(), true);
     }
 
     if (launch_args.GetArgumentCount() == 0) {
@@ -250,11 +261,20 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
         llvm::StringRef data = stream.GetString();
         if (!data.empty())
           result.AppendMessage(data);
-        const char *archname =
-            exe_module_sp->GetArchitecture().GetArchitectureName();
-        result.AppendMessageWithFormat(
-            "Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
-            exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
+        // If we didn't have a local executable, then we wouldn't have had an
+        // executable module before launch.
+        if (!exe_module_sp)
+          exe_module_sp = target->GetExecutableModule();
+        if (!exe_module_sp) {
+          result.AppendWarning("Could not get executable module after launch.");
+        } else {
+
+          const char *archname =
+              exe_module_sp->GetArchitecture().GetArchitectureName();
+          result.AppendMessageWithFormat(
+              "Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
+              exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
+        }
         result.SetStatus(eReturnStatusSuccessFinishResult);
         result.SetDidChangeProcessState(true);
       } else {

diff  --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a666aeb8bb3f6..2233bf6758190 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -677,143 +677,133 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
   //  LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
   //  ::LogSetLogFile ("/dev/stdout");
 
-  ObjectFile *object_file = exe_module->GetObjectFile();
-  if (object_file) {
-    error = EstablishConnectionIfNeeded(launch_info);
-    if (error.Success()) {
-      PseudoTerminal pty;
-      const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
-
-      PlatformSP platform_sp(GetTarget().GetPlatform());
-      if (disable_stdio) {
-        // set to /dev/null unless redirected to a file above
-        if (!stdin_file_spec)
-          stdin_file_spec.SetFile(FileSystem::DEV_NULL,
-                                  FileSpec::Style::native);
-        if (!stdout_file_spec)
-          stdout_file_spec.SetFile(FileSystem::DEV_NULL,
-                                   FileSpec::Style::native);
-        if (!stderr_file_spec)
-          stderr_file_spec.SetFile(FileSystem::DEV_NULL,
-                                   FileSpec::Style::native);
-      } else if (platform_sp && platform_sp->IsHost()) {
-        // If the debugserver is local and we aren't disabling STDIO, lets use
-        // a pseudo terminal to instead of relying on the 'O' packets for stdio
-        // since 'O' packets can really slow down debugging if the inferior
-        // does a lot of output.
-        if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
-            !errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) {
-          FileSpec secondary_name(pty.GetSecondaryName());
-
-          if (!stdin_file_spec)
-            stdin_file_spec = secondary_name;
-
-          if (!stdout_file_spec)
-            stdout_file_spec = secondary_name;
-
-          if (!stderr_file_spec)
-            stderr_file_spec = secondary_name;
-        }
-        LLDB_LOGF(
-            log,
-            "ProcessGDBRemote::%s adjusted STDIO paths for local platform "
-            "(IsHost() is true) using secondary: stdin=%s, stdout=%s, "
-            "stderr=%s",
-            __FUNCTION__,
-            stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
-            stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
-            stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
-      }
+  error = EstablishConnectionIfNeeded(launch_info);
+  if (error.Success()) {
+    PseudoTerminal pty;
+    const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
 
-      LLDB_LOGF(log,
-                "ProcessGDBRemote::%s final STDIO paths after all "
-                "adjustments: stdin=%s, stdout=%s, stderr=%s",
-                __FUNCTION__,
-                stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
-                stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
-                stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
-
-      if (stdin_file_spec)
-        m_gdb_comm.SetSTDIN(stdin_file_spec);
-      if (stdout_file_spec)
-        m_gdb_comm.SetSTDOUT(stdout_file_spec);
-      if (stderr_file_spec)
-        m_gdb_comm.SetSTDERR(stderr_file_spec);
-
-      m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
-      m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
+    PlatformSP platform_sp(GetTarget().GetPlatform());
+    if (disable_stdio) {
+      // set to /dev/null unless redirected to a file above
+      if (!stdin_file_spec)
+        stdin_file_spec.SetFile(FileSystem::DEV_NULL,
+                                FileSpec::Style::native);
+      if (!stdout_file_spec)
+        stdout_file_spec.SetFile(FileSystem::DEV_NULL,
+                                 FileSpec::Style::native);
+      if (!stderr_file_spec)
+        stderr_file_spec.SetFile(FileSystem::DEV_NULL,
+                                 FileSpec::Style::native);
+    } else if (platform_sp && platform_sp->IsHost()) {
+      // If the debugserver is local and we aren't disabling STDIO, lets use
+      // a pseudo terminal to instead of relying on the 'O' packets for stdio
+      // since 'O' packets can really slow down debugging if the inferior
+      // does a lot of output.
+      if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
+          !errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) {
+        FileSpec secondary_name(pty.GetSecondaryName());
 
-      m_gdb_comm.SendLaunchArchPacket(
-          GetTarget().GetArchitecture().GetArchitectureName());
+        if (!stdin_file_spec)
+          stdin_file_spec = secondary_name;
 
-      const char *launch_event_data = launch_info.GetLaunchEventData();
-      if (launch_event_data != nullptr && *launch_event_data != '\0')
-        m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
+        if (!stdout_file_spec)
+          stdout_file_spec = secondary_name;
 
-      if (working_dir) {
-        m_gdb_comm.SetWorkingDir(working_dir);
+        if (!stderr_file_spec)
+          stderr_file_spec = secondary_name;
       }
+      LLDB_LOGF(
+          log,
+          "ProcessGDBRemote::%s adjusted STDIO paths for local platform "
+          "(IsHost() is true) using secondary: stdin=%s, stdout=%s, "
+          "stderr=%s",
+          __FUNCTION__,
+          stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+          stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+          stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+    }
 
-      // Send the environment and the program + arguments after we connect
-      m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
+    LLDB_LOGF(log,
+              "ProcessGDBRemote::%s final STDIO paths after all "
+              "adjustments: stdin=%s, stdout=%s, stderr=%s",
+              __FUNCTION__,
+              stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+              stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+              stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+
+    if (stdin_file_spec)
+      m_gdb_comm.SetSTDIN(stdin_file_spec);
+    if (stdout_file_spec)
+      m_gdb_comm.SetSTDOUT(stdout_file_spec);
+    if (stderr_file_spec)
+      m_gdb_comm.SetSTDERR(stderr_file_spec);
+
+    m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
+    m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
+
+    m_gdb_comm.SendLaunchArchPacket(
+        GetTarget().GetArchitecture().GetArchitectureName());
+
+    const char *launch_event_data = launch_info.GetLaunchEventData();
+    if (launch_event_data != nullptr && *launch_event_data != '\0')
+      m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
+
+    if (working_dir) {
+      m_gdb_comm.SetWorkingDir(working_dir);
+    }
 
-      {
-        // Scope for the scoped timeout object
-        GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
-                                                      std::chrono::seconds(10));
+    // Send the environment and the program + arguments after we connect
+    m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
 
-        int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
-        if (arg_packet_err == 0) {
-          std::string error_str;
-          if (m_gdb_comm.GetLaunchSuccess(error_str)) {
-            SetID(m_gdb_comm.GetCurrentProcessID());
-          } else {
-            error.SetErrorString(error_str.c_str());
-          }
+    {
+      // Scope for the scoped timeout object
+      GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
+                                                    std::chrono::seconds(10));
+
+      int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
+      if (arg_packet_err == 0) {
+        std::string error_str;
+        if (m_gdb_comm.GetLaunchSuccess(error_str)) {
+          SetID(m_gdb_comm.GetCurrentProcessID());
         } else {
-          error.SetErrorStringWithFormat("'A' packet returned an error: %i",
-                                         arg_packet_err);
+          error.SetErrorString(error_str.c_str());
         }
+      } else {
+        error.SetErrorStringWithFormat("'A' packet returned an error: %i",
+                                       arg_packet_err);
       }
+    }
 
-      if (GetID() == LLDB_INVALID_PROCESS_ID) {
-        LLDB_LOGF(log, "failed to connect to debugserver: %s",
-                  error.AsCString());
-        KillDebugserverProcess();
-        return error;
-      }
+    if (GetID() == LLDB_INVALID_PROCESS_ID) {
+      LLDB_LOGF(log, "failed to connect to debugserver: %s",
+                error.AsCString());
+      KillDebugserverProcess();
+      return error;
+    }
 
-      StringExtractorGDBRemote response;
-      if (m_gdb_comm.GetStopReply(response)) {
-        SetLastStopPacket(response);
+    StringExtractorGDBRemote response;
+    if (m_gdb_comm.GetStopReply(response)) {
+      SetLastStopPacket(response);
 
-        const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
+      const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
 
-        if (process_arch.IsValid()) {
-          GetTarget().MergeArchitecture(process_arch);
-        } else {
-          const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
-          if (host_arch.IsValid())
-            GetTarget().MergeArchitecture(host_arch);
-        }
+      if (process_arch.IsValid()) {
+        GetTarget().MergeArchitecture(process_arch);
+      } else {
+        const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
+        if (host_arch.IsValid())
+          GetTarget().MergeArchitecture(host_arch);
+      }
 
-        SetPrivateState(SetThreadStopInfo(response));
+      SetPrivateState(SetThreadStopInfo(response));
 
-        if (!disable_stdio) {
-          if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd)
-            SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor());
-        }
+      if (!disable_stdio) {
+        if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd)
+          SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor());
       }
-    } else {
-      LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString());
     }
   } else {
-    // Set our user ID to an invalid process ID.
-    SetID(LLDB_INVALID_PROCESS_ID);
-    error.SetErrorStringWithFormat(
-        "failed to get object file from '%s' for arch %s",
-        exe_module->GetFileSpec().GetFilename().AsCString(),
-        exe_module->GetArchitecture().GetArchitectureName());
+    LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString());
   }
   return error;
 }

diff  --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 519db0482e4ba..3fbd6434e8683 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2493,118 +2493,125 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) {
   m_process_input_reader.reset();
 
   Module *exe_module = GetTarget().GetExecutableModulePointer();
-  if (!exe_module) {
-    error.SetErrorString("executable module does not exist");
-    return error;
-  }
 
-  char local_exec_file_path[PATH_MAX];
-  char platform_exec_file_path[PATH_MAX];
-  exe_module->GetFileSpec().GetPath(local_exec_file_path,
-                                    sizeof(local_exec_file_path));
-  exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path,
-                                            sizeof(platform_exec_file_path));
-  if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
+  // The "remote executable path" is hooked up to the local Executable
+  // module.  But we should be able to debug a remote process even if the
+  // executable module only exists on the remote.  However, there needs to
+  // be a way to express this path, without actually having a module.
+  // The way to do that is to set the ExecutableFile in the LaunchInfo.
+  // Figure that out here:
+  
+  FileSpec exe_spec_to_use;
+  if (!exe_module) {
+    if (!launch_info.GetExecutableFile()) {
+      error.SetErrorString("executable module does not exist");
+      return error;
+    }
+    exe_spec_to_use = launch_info.GetExecutableFile();
+  } else
+    exe_spec_to_use = exe_module->GetFileSpec();
+  
+  if (exe_module && FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
     // Install anything that might need to be installed prior to launching.
     // For host systems, this will do nothing, but if we are connected to a
     // remote platform it will install any needed binaries
     error = GetTarget().Install(&launch_info);
     if (error.Fail())
       return error;
+  }
+  // Listen and queue events that are broadcasted during the process launch.
+  ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack"));
+  HijackProcessEvents(listener_sp);
+  auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); });
 
-    // Listen and queue events that are broadcasted during the process launch.
-    ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack"));
-    HijackProcessEvents(listener_sp);
-    auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); });
+  if (PrivateStateThreadIsValid())
+    PausePrivateStateThread();
 
-    if (PrivateStateThreadIsValid())
-      PausePrivateStateThread();
+  error = WillLaunch(exe_module);
+  if (error.Success()) {
+    const bool restarted = false;
+    SetPublicState(eStateLaunching, restarted);
+    m_should_detach = false;
 
-    error = WillLaunch(exe_module);
-    if (error.Success()) {
-      const bool restarted = false;
-      SetPublicState(eStateLaunching, restarted);
-      m_should_detach = false;
+    if (m_public_run_lock.TrySetRunning()) {
+      // Now launch using these arguments.
+      error = DoLaunch(exe_module, launch_info);
+    } else {
+      // This shouldn't happen
+      error.SetErrorString("failed to acquire process run lock");
+    }
 
-      if (m_public_run_lock.TrySetRunning()) {
-        // Now launch using these arguments.
-        error = DoLaunch(exe_module, launch_info);
-      } else {
-        // This shouldn't happen
-        error.SetErrorString("failed to acquire process run lock");
+    if (error.Fail()) {
+      if (GetID() != LLDB_INVALID_PROCESS_ID) {
+        SetID(LLDB_INVALID_PROCESS_ID);
+        const char *error_string = error.AsCString();
+        if (error_string == nullptr)
+          error_string = "launch failed";
+        SetExitStatus(-1, error_string);
       }
+    } else {
+      EventSP event_sp;
 
-      if (error.Fail()) {
-        if (GetID() != LLDB_INVALID_PROCESS_ID) {
-          SetID(LLDB_INVALID_PROCESS_ID);
-          const char *error_string = error.AsCString();
-          if (error_string == nullptr)
-            error_string = "launch failed";
-          SetExitStatus(-1, error_string);
-        }
-      } else {
-        EventSP event_sp;
-
-        // Now wait for the process to launch and return control to us, and then
-        // call DidLaunch:
-        StateType state = WaitForProcessStopPrivate(event_sp, seconds(10));
-
-        if (state == eStateInvalid || !event_sp) {
-          // We were able to launch the process, but we failed to catch the
-          // initial stop.
-          error.SetErrorString("failed to catch stop after launch");
-          SetExitStatus(0, "failed to catch stop after launch");
-          Destroy(false);
-        } else if (state == eStateStopped || state == eStateCrashed) {
-          DidLaunch();
-
-          DynamicLoader *dyld = GetDynamicLoader();
-          if (dyld)
-            dyld->DidLaunch();
-
-          GetJITLoaders().DidLaunch();
-
-          SystemRuntime *system_runtime = GetSystemRuntime();
-          if (system_runtime)
-            system_runtime->DidLaunch();
-
-          if (!m_os_up)
-            LoadOperatingSystemPlugin(false);
-
-          // We successfully launched the process and stopped, now it the
-          // right time to set up signal filters before resuming.
-          UpdateAutomaticSignalFiltering();
-
-          // Note, the stop event was consumed above, but not handled. This
-          // was done to give DidLaunch a chance to run. The target is either
-          // stopped or crashed. Directly set the state.  This is done to
-          // prevent a stop message with a bunch of spurious output on thread
-          // status, as well as not pop a ProcessIOHandler.
-          // We are done with the launch hijack listener, and this stop should
-          // go to the public state listener:
-          RestoreProcessEvents();
-          SetPublicState(state, false);
-
-          if (PrivateStateThreadIsValid())
-            ResumePrivateStateThread();
-          else
-            StartPrivateStateThread();
+      // Now wait for the process to launch and return control to us, and then
+      // call DidLaunch:
+      StateType state = WaitForProcessStopPrivate(event_sp, seconds(10));
+
+      if (state == eStateInvalid || !event_sp) {
+        // We were able to launch the process, but we failed to catch the
+        // initial stop.
+        error.SetErrorString("failed to catch stop after launch");
+        SetExitStatus(0, "failed to catch stop after launch");
+        Destroy(false);
+      } else if (state == eStateStopped || state == eStateCrashed) {
+        DidLaunch();
+
+        DynamicLoader *dyld = GetDynamicLoader();
+        if (dyld)
+          dyld->DidLaunch();
+
+        GetJITLoaders().DidLaunch();
+
+        SystemRuntime *system_runtime = GetSystemRuntime();
+        if (system_runtime)
+          system_runtime->DidLaunch();
+
+        if (!m_os_up)
+          LoadOperatingSystemPlugin(false);
+
+        // We successfully launched the process and stopped, now it the
+        // right time to set up signal filters before resuming.
+        UpdateAutomaticSignalFiltering();
+
+        // Note, the stop event was consumed above, but not handled. This
+        // was done to give DidLaunch a chance to run. The target is either
+        // stopped or crashed. Directly set the state.  This is done to
+        // prevent a stop message with a bunch of spurious output on thread
+        // status, as well as not pop a ProcessIOHandler.
+        // We are done with the launch hijack listener, and this stop should
+        // go to the public state listener:
+        RestoreProcessEvents();
+        SetPublicState(state, false);
+
+        if (PrivateStateThreadIsValid())
+          ResumePrivateStateThread();
+        else
+          StartPrivateStateThread();
 
-          // Target was stopped at entry as was intended. Need to notify the
-          // listeners about it.
-          if (state == eStateStopped &&
-              launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
-            HandlePrivateEvent(event_sp);
-        } else if (state == eStateExited) {
-          // We exited while trying to launch somehow.  Don't call DidLaunch
-          // as that's not likely to work, and return an invalid pid.
+        // Target was stopped at entry as was intended. Need to notify the
+        // listeners about it.
+        if (state == eStateStopped &&
+            launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
           HandlePrivateEvent(event_sp);
-        }
+      } else if (state == eStateExited) {
+        // We exited while trying to launch somehow.  Don't call DidLaunch
+        // as that's not likely to work, and return an invalid pid.
+        HandlePrivateEvent(event_sp);
       }
     }
   } else {
+    std::string local_exec_file_path = exe_spec_to_use.GetPath();
     error.SetErrorStringWithFormat("file doesn't exist: '%s'",
-                                   local_exec_file_path);
+                                   local_exec_file_path.c_str());
   }
 
   return error;

diff  --git a/lldb/test/API/functionalities/gdb_remote_client/TestNoLocalFile.py b/lldb/test/API/functionalities/gdb_remote_client/TestNoLocalFile.py
new file mode 100644
index 0000000000000..8cc10133783f0
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestNoLocalFile.py
@@ -0,0 +1,91 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+class TestNoLocalFile(GDBRemoteTestBase):
+    """ Test the case where there is NO local copy of the file
+        being debugged.  We shouldn't immediately error out, but
+        rather lldb should ask debugserver if it knows about the file. """
+
+    mydir = TestBase.compute_mydir(__file__)
+    
+    @skipIfXmlSupportMissing
+    def test(self):
+        self.absent_file = '/nosuch_dir/nosuch_subdir/nosuch_executable'
+        self.a_packet_file = None
+        class MyResponder(MockGDBServerResponder):
+            def __init__(self, testcase):
+                MockGDBServerResponder.__init__(self)
+                self.after_launch = False
+                self.testcase = testcase
+                self.current_thread = 0
+                
+            def A(self, packet):
+                # This is the main test, we want to see that lldb DID send the
+                # A packet to get debugserver to load the file.
+                # Skip the length and second length:
+                print("Got A packet: {0}".format(packet))
+                a_arr = packet.split(",")
+                self.testcase.a_packet_file = bytearray.fromhex(a_arr[2]).decode()
+                return "OK"
+
+            def qXferRead(self, obj, annex, offset, length):
+                if annex == "target.xml":
+                    return """<?xml version="1.0"?>
+                        <target version="1.0">
+                          <architecture>i386:x86-64</architecture>
+                          <feature name="org.gnu.gdb.i386.core">
+                            <reg name="rip" bitsize="64" regnum="0" type="code_ptr" group="general"/>
+                          </feature>
+                        </target>""", False
+                else:
+                    return None, False
+
+            def qC(self):
+                if not self.after_launch:
+                    return "QC0"
+                return "0"
+
+            def qfThreadInfo(self):
+                if not self.after_launch:
+                    return "OK"
+                return "m0"
+
+            def qsThreadInfo(self):
+                if not self.after_launch:
+                    return "OK"
+                return "l"
+
+            def qLaunchSuccess(self):
+                return "OK"
+
+            def qProcessInfo(self):
+                return "$pid:10b70;parent-pid:10b20;real-uid:1f6;real-gid:14;effective-uid:1f6;effective-gid:14;cputype:1000007;cpusubtype:8;ptrsize:8;ostype:macosx;vendor:apple;endian:little;"
+
+            
+        error = lldb.SBError()
+        self.server.responder = MyResponder(self)
+        target = self.dbg.CreateTarget(None, "x86_64-apple-macosx", "remote-macosx", False, error)
+        self.assertSuccess(error, "Made a valid target")
+        launch_info = target.GetLaunchInfo()
+        launch_info.SetExecutableFile(lldb.SBFileSpec(self.absent_file), True)
+        flags = launch_info.GetLaunchFlags()
+        flags |= lldb.eLaunchFlagStopAtEntry
+        launch_info.SetLaunchFlags(flags)
+
+        process = self.connect(target)
+        self.assertTrue(process.IsValid(), "Process is valid")
+
+        # We need to fetch the connected event:
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected])
+
+        self.server.responder.after_launch = True
+
+        process = target.Launch(launch_info, error)
+
+        self.assertSuccess(error, "Successfully launched.")
+        self.assertEqual(process.GetState(), lldb.eStateStopped, "Should be stopped at entry")
+        self.assertIsNotNone(self.a_packet_file, "A packet was sent")
+        self.assertEqual(self.absent_file, self.a_packet_file, "The A packet file was correct")


        


More information about the lldb-commits mailing list