[Lldb-commits] [lldb] b715b79 - 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
Tue Nov 16 16:08:50 PST 2021


Author: Jim Ingham
Date: 2021-11-16T16:06:07-08:00
New Revision: b715b79d54d5ca2d4e8c91089b8f6a9389d9dc48

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

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

We don't actually need a local copy of the main executable to debug
a remote process.  So instead of treating "no local module" as an error,
see if the LaunchInfo has an executable it wants lldb to use, and if so
use it.  Then report whatever error the remote server returns.

Differential Revision: https://reviews.llvm.org/D113521

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 8ce14f2a96d5..5fd1718e8484 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 a666aeb8bb3f..2233bf675819 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 519db0482e4b..3fbd6434e868 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 000000000000..fd9dc3ef592b
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestNoLocalFile.py
@@ -0,0 +1,88 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+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. """
+
+    
+    @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:
+                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