Error ProcessWindows::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { // Even though m_session_data is accessed here, it is before a debugger thread has been // kicked off. So there's no race conditions, and it shouldn't be necessary to acquire // the mutex. Error result; if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) { StreamString stream; stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can only be used for debug launches.", launch_info.GetExecutableFile().GetPath().c_str()); std::string message = stream.GetString(); result.SetErrorString(message.c_str()); WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str()); return result; } bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); m_session_data.reset(new ProcessWindowsData(stop_at_entry)); SetPrivateState(eStateLaunching); DebugDelegateSP delegate(new LocalDebugDelegate(shared_from_this())); m_session_data->m_debugger.reset(new DebuggerThread(delegate)); DebuggerThreadSP debugger = m_session_data->m_debugger; // Kick off the DebugLaunch asynchronously and wait for it to complete. result = debugger->DebugLaunch(launch_info); if (result.Fail()) { WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString()); return result; } HostProcess process; Error error = WaitForDebuggerConnection(debugger, process); if (error.Fail()) { WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s", launch_info.GetExecutableFile().GetPath().c_str(), error.AsCString()); return error; } WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'", launch_info.GetExecutableFile().GetPath().c_str()); // We've hit the initial stop. If eLaunchFlagsStopAtEntry was specified, the private state // should already be set to eStateStopped as a result of hitting the initial breakpoint. If // it was not set, the breakpoint should have already been resumed from and the private state // should already be eStateRunning. launch_info.SetProcessID(process.GetProcessId()); SetID(process.GetProcessId()); return result; }
lldb::thread_result_t DebuggerThread::DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info) { // Grab a shared_ptr reference to this so that we know it won't get deleted until after the // thread routine has exited. std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread preparing to launch '%s' on background thread.", launch_info.GetExecutableFile().GetPath().c_str()); Error error; ProcessLauncherWindows launcher; HostProcess process(launcher.LaunchProcess(launch_info, error)); // If we couldn't create the process, notify waiters immediately. Otherwise enter the debug // loop and wait until we get the create process debug notification. Note that if the process // was created successfully, we can throw away the process handle we got from CreateProcess // because Windows will give us another (potentially more useful?) handle when it sends us the // CREATE_PROCESS_DEBUG_EVENT. if (error.Success()) DebugLoop(); else m_debug_delegate->OnDebuggerError(error, 0); return 0; }
HostProcess ProcessLauncherAndroid::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) { // TODO: Handle other launch parameters specified in launc_info char exe_path[PATH_MAX]; launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); lldb::pid_t pid = ::fork(); if (pid == static_cast<lldb::pid_t>(-1)) { // Fork failed error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno)); return HostProcess(LLDB_INVALID_PROCESS_ID); } else if (pid == 0) { if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) { const char* path = file_action->GetPath(); if (path && ::strlen(path)) if (!DupDescriptor(path, STDIN_FILENO, O_RDONLY)) exit(-1); } if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) { const char* path = file_action->GetPath(); if (path && ::strlen(path)) if (!DupDescriptor(path, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) exit(-1); } if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) { const char* path = file_action->GetPath(); if (path && ::strlen(path)) if (!DupDescriptor(path, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) exit(-1); } // Child process const char **argv = launch_info.GetArguments().GetConstArgumentVector(); Args env = launch_info.GetEnvironmentEntries(); FixupEnvironment(env); const char **envp = env.GetConstArgumentVector(); const char *working_dir = launch_info.GetWorkingDirectory(); if (working_dir != nullptr && working_dir[0]) { if (::chdir(working_dir) != 0) exit(-1); } execve(argv[0], const_cast<char *const *>(argv), const_cast<char *const *>(envp)); exit(-1); } return HostProcess(pid); }
HostProcess ProcessLauncherPosix::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) { lldb::pid_t pid; char exe_path[PATH_MAX]; launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); // TODO(zturner): Move the code from LaunchProcessPosixSpawn to here, and make // MacOSX re-use this // ProcessLauncher when it wants a posix_spawn launch. error = Host::LaunchProcessPosixSpawn(exe_path, launch_info, pid); return HostProcess(pid); }
Error PlatformLinux::LaunchNativeProcess ( ProcessLaunchInfo &launch_info, lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate, NativeProcessProtocolSP &process_sp) { #if !defined(__linux__) return Error("only implemented on Linux hosts"); #else if (!IsHost ()) return Error("PlatformLinux::%s (): cannot launch a debug process when not the host", __FUNCTION__); // Retrieve the exe module. lldb::ModuleSP exe_module_sp; Error error = ResolveExecutable ( launch_info.GetExecutableFile (), launch_info.GetArchitecture (), exe_module_sp, NULL); if (!error.Success ()) return error; if (!exe_module_sp) return Error("exe_module_sp could not be resolved for %s", launch_info.GetExecutableFile ().GetPath ().c_str ()); // Launch it for debugging error = NativeProcessLinux::LaunchProcess ( exe_module_sp.get (), launch_info, native_delegate, process_sp); return error; #endif }
Error DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) { WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str()); Error error; DebugLaunchContext *context = new DebugLaunchContext(this, launch_info); HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", DebuggerThreadLaunchRoutine, context, &error)); if (!error.Success()) { WINERR_IFALL(WINDOWS_LOG_PROCESS, "DebugLaunch couldn't launch debugger thread. %s", error.AsCString()); } return error; }
HostProcess ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) { char exe_path[PATH_MAX]; launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); // A pipe used by the child process to report errors. PipePosix pipe; const bool child_processes_inherit = false; error = pipe.CreateNew(child_processes_inherit); if (error.Fail()) return HostProcess(); ::pid_t pid = ::fork(); if (pid == -1) { // Fork failed error.SetErrorStringWithFormatv("Fork failed with error message: {0}", llvm::sys::StrError()); return HostProcess(LLDB_INVALID_PROCESS_ID); } if (pid == 0) { // child process pipe.CloseReadFileDescriptor(); ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info); } // parent process pipe.CloseWriteFileDescriptor(); char buf[1000]; int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf); if (r == 0) return HostProcess(pid); // No error. We're done. error.SetErrorString(buf); llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0); return HostProcess(); }
Error GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, uint16_t in_port, ProcessLaunchInfo &launch_info, uint16_t &out_port) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunication::%s(hostname=%s, in_port=%" PRIu16 ", out_port=%" PRIu16, __FUNCTION__, hostname ? hostname : "<empty>", in_port, out_port); out_port = in_port; Error error; // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; char debugserver_path[PATH_MAX]; FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); // Always check to see if we have an environment override for the path // to the debugserver to use and use it if we do. const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); if (env_debugserver_path) { debugserver_file_spec.SetFile (env_debugserver_path, false); if (log) log->Printf ("GDBRemoteCommunication::%s() gdb-remote stub exe path set from environment variable: %s", __FUNCTION__, env_debugserver_path); } else debugserver_file_spec = g_debugserver_file_spec; bool debugserver_exists = debugserver_file_spec.Exists(); if (!debugserver_exists) { // The debugserver binary is in the LLDB.framework/Resources // directory. if (HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, debugserver_file_spec)) { debugserver_file_spec.AppendPathComponent (DEBUGSERVER_BASENAME); debugserver_exists = debugserver_file_spec.Exists(); if (debugserver_exists) { if (log) log->Printf ("GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ()); g_debugserver_file_spec = debugserver_file_spec; } else { if (log) log->Printf ("GDBRemoteCommunication::%s() could not find gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ()); g_debugserver_file_spec.Clear(); debugserver_file_spec.Clear(); } } } if (debugserver_exists) { debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); Args &debugserver_args = launch_info.GetArguments(); debugserver_args.Clear(); char arg_cstr[PATH_MAX]; // Start args with "debugserver /file/path -r --" debugserver_args.AppendArgument(debugserver_path); #if !defined(__APPLE__) // First argument to lldb-server must be mode in which to run. debugserver_args.AppendArgument("gdbserver"); #endif // If a host and port is supplied then use it char host_and_port[128]; if (hostname) { snprintf (host_and_port, sizeof(host_and_port), "%s:%u", hostname, in_port); debugserver_args.AppendArgument(host_and_port); } else { host_and_port[0] = '\0'; } // use native registers, not the GDB registers debugserver_args.AppendArgument("--native-regs"); if (launch_info.GetLaunchInSeparateProcessGroup()) { debugserver_args.AppendArgument("--setsid"); } llvm::SmallString<PATH_MAX> named_pipe_path; Pipe port_pipe; bool listen = false; if (host_and_port[0]) { // Create a temporary file to get the stdout/stderr and redirect the // output of the command into this file. We will later read this file // if all goes well and fill the data into "command_output_ptr" if (in_port == 0) { // Binding to port zero, we need to figure out what port it ends up // using using a named pipe... error = port_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path); if (error.Success()) { debugserver_args.AppendArgument("--named-pipe"); debugserver_args.AppendArgument(named_pipe_path.c_str()); } else { if (log) log->Printf("GDBRemoteCommunication::%s() " "named pipe creation failed: %s", __FUNCTION__, error.AsCString()); // let's try an unnamed pipe error = port_pipe.CreateNew(true); if (error.Fail()) { if (log) log->Printf("GDBRemoteCommunication::%s() " "unnamed pipe creation failed: %s", __FUNCTION__, error.AsCString()); return error; } int write_fd = port_pipe.GetWriteFileDescriptor(); debugserver_args.AppendArgument("--pipe"); debugserver_args.AppendArgument(std::to_string(write_fd).c_str()); launch_info.AppendCloseFileAction(port_pipe.GetReadFileDescriptor()); } } else { listen = true; } } else { // No host and port given, so lets listen on our end and make the debugserver // connect to us.. error = StartListenThread ("127.0.0.1", 0); if (error.Fail()) return error; ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection (); // Wait for 10 seconds to resolve the bound port out_port = connection->GetListeningPort(10); if (out_port > 0) { char port_cstr[32]; snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", out_port); // Send the host and port down that debugserver and specify an option // so that it connects back to the port we are listening to in this process debugserver_args.AppendArgument("--reverse-connect"); debugserver_args.AppendArgument(port_cstr); } else { error.SetErrorString ("failed to bind to port 0 on 127.0.0.1"); return error; } } const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); if (env_debugserver_log_file) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); debugserver_args.AppendArgument(arg_cstr); } const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); if (env_debugserver_log_flags) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); debugserver_args.AppendArgument(arg_cstr); } // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an env var doesn't come back. uint32_t env_var_index = 1; bool has_env_var; do { char env_var_name[64]; snprintf (env_var_name, sizeof (env_var_name), "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++); const char *extra_arg = getenv(env_var_name); has_env_var = extra_arg != nullptr; if (has_env_var) { debugserver_args.AppendArgument (extra_arg); if (log) log->Printf ("GDBRemoteCommunication::%s adding env var %s contents to stub command line (%s)", __FUNCTION__, env_var_name, extra_arg); } } while (has_env_var); // Close STDIN, STDOUT and STDERR. launch_info.AppendCloseFileAction (STDIN_FILENO); launch_info.AppendCloseFileAction (STDOUT_FILENO); launch_info.AppendCloseFileAction (STDERR_FILENO); // Redirect STDIN, STDOUT and STDERR to "/dev/null". launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); error = Host::LaunchProcess(launch_info); if (error.Success() && launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { if (named_pipe_path.size() > 0) { error = port_pipe.OpenAsReader(named_pipe_path, false); if (error.Fail()) if (log) log->Printf("GDBRemoteCommunication::%s() " "failed to open named pipe %s for reading: %s", __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); } if (port_pipe.CanWrite()) port_pipe.CloseWriteFileDescriptor(); if (port_pipe.CanRead()) { char port_cstr[256]; port_cstr[0] = '\0'; size_t num_bytes = sizeof(port_cstr); // Read port from pipe with 10 second timeout. error = port_pipe.ReadWithTimeout(port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes); if (error.Success()) { assert(num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); out_port = StringConvert::ToUInt32(port_cstr, 0); if (log) log->Printf("GDBRemoteCommunication::%s() " "debugserver listens %u port", __FUNCTION__, out_port); } else { if (log) log->Printf("GDBRemoteCommunication::%s() " "failed to read a port value from pipe %s: %s", __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); } port_pipe.Close(); } if (named_pipe_path.size() > 0) { const auto err = port_pipe.Delete(named_pipe_path); if (err.Fail()) { if (log) log->Printf ("GDBRemoteCommunication::%s failed to delete pipe %s: %s", __FUNCTION__, named_pipe_path.c_str(), err.AsCString()); } } // Make sure we actually connect with the debugserver... JoinListenThread(); } } else { error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME ); } return error; }
Error Host::LaunchProcess (ProcessLaunchInfo &launch_info) { Error error; char exe_path[PATH_MAX]; PlatformSP host_platform_sp (Platform::GetHostPlatform ()); const ArchSpec &arch_spec = launch_info.GetArchitecture(); FileSpec exe_spec(launch_info.GetExecutableFile()); FileSpec::FileType file_type = exe_spec.GetFileType(); if (file_type != FileSpec::eFileTypeRegular) { lldb::ModuleSP exe_module_sp; error = host_platform_sp->ResolveExecutable (exe_spec, arch_spec, exe_module_sp, NULL); if (error.Fail()) return error; if (exe_module_sp) exe_spec = exe_module_sp->GetFileSpec(); } if (exe_spec.Exists()) { exe_spec.GetPath (exe_path, sizeof(exe_path)); } else { launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); return error; } assert(!launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)); ::pid_t pid = LLDB_INVALID_PROCESS_ID; error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); if (pid != LLDB_INVALID_PROCESS_ID) { // If all went well, then set the process ID into the launch info launch_info.SetProcessID(pid); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); // Make sure we reap any processes we spawn or we will have zombies. if (!launch_info.MonitorProcess()) { const bool monitor_signals = false; StartMonitoringChildProcess (Process::SetProcessExitStatus, NULL, pid, monitor_signals); if (log) log->PutCString ("monitored child process with default Process::SetProcessExitStatus."); } else { if (log) log->PutCString ("monitored child process with user-specified process monitor."); } } else { // Invalid process ID, something didn't go well if (error.Success()) error.SetErrorString ("process launch failed for unknown reasons"); } return error; }