diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -159,7 +159,12 @@ // 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 @@ 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 @@ 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 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -677,143 +677,133 @@ // 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() : "", - stdout_file_spec ? stdout_file_spec.GetCString() : "", - stderr_file_spec ? stderr_file_spec.GetCString() : ""); - } + 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() : "", - stdout_file_spec ? stdout_file_spec.GetCString() : "", - stderr_file_spec ? stderr_file_spec.GetCString() : ""); - - 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() : "", + stdout_file_spec ? stdout_file_spec.GetCString() : "", + stderr_file_spec ? stderr_file_spec.GetCString() : ""); + } - // 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() : "", + stdout_file_spec ? stdout_file_spec.GetCString() : "", + stderr_file_spec ? stderr_file_spec.GetCString() : ""); + + 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 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2493,118 +2493,125 @@ 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 --- /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 """ + + i386:x86-64 + + + + """, 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")