lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess( ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Error &error) { lldb::ProcessSP process_sp; if (IsRemote()) { if (IsConnected()) { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; if (!LaunchGDBServer(debugserver_pid, connect_url)) { error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", GetHostname()); } else { if (target == NULL) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget( debugger, NULL, NULL, false, NULL, new_target_sp); target = new_target_sp.get(); } else error.Clear(); if (target && error.Success()) { debugger.GetTargetList().SetSelectedTarget(target); // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! process_sp = target->CreateProcess( launch_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) { error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); // Retry the connect remote one time... if (error.Fail()) error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); if (error.Success()) error = process_sp->Launch(launch_info); else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { printf("error: connect remote failed (%s)\n", error.AsCString()); KillSpawnedProcess(debugserver_pid); } } } } } else { error.SetErrorString("not connected to remote gdb server"); } } return process_sp; }
ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error) { // Windows has special considerations that must be followed when launching or // attaching to a process. The // key requirement is that when launching or attaching to a process, you must // do it from the same the thread // that will go into a permanent loop which will then receive debug events // from the process. In particular, // this means we can't use any of LLDB's generic mechanisms to do it for us, // because it doesn't have the // special knowledge required for setting up the background thread or passing // the right flags. // // Another problem is that that LLDB's standard model for debugging a process // is to first launch it, have // it stop at the entry point, and then attach to it. In Windows this doesn't // quite work, you have to // specify as an argument to CreateProcess() that you're going to debug the // process. So we override DebugProcess // here to handle this. Launch operations go directly to the process plugin, // and attach operations almost go // directly to the process plugin (but we hijack the events first). In // essence, we encapsulate all the logic // of Launching and Attaching in the process plugin, and // PlatformWindows::DebugProcess is just a pass-through // to get to the process plugin. if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { // This is a process attach. Don't need to launch anything. ProcessAttachInfo attach_info(launch_info); return Attach(attach_info, debugger, target, error); } else { ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger), launch_info.GetProcessPluginName(), nullptr); // We need to launch and attach to the process. launch_info.GetFlags().Set(eLaunchFlagDebug); if (process_sp) error = process_sp->Launch(launch_info); return process_sp; } }
// For local debugging, Linux will override the debug logic to use llgs-launch rather than // lldb-launch, llgs-attach. This differs from current lldb-launch, debugserver-attach // approach on MacOSX. lldb::ProcessSP PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one Error &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); if (log) log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target)); // If we're a remote host, use standard behavior from parent class. if (!IsHost ()) return PlatformPOSIX::DebugProcess (launch_info, debugger, target, error); // // For local debugging, we'll insist on having ProcessGDBRemote create the process. // ProcessSP process_sp; // Ensure we're using llgs for local debugging. if (!UseLlgsForLocalDebugging ()) { assert (false && "we're trying to debug a local process but platform.plugin.linux.use-llgs-for-local is false, should never get here"); error.SetErrorString ("attempted to start gdb-remote-based debugging for local process but platform.plugin.linux.use-llgs-for-local is false"); return process_sp; } // Make sure we stop at the entry point launch_info.GetFlags ().Set (eLaunchFlagDebug); // We always launch the process we are going to debug in a separate process // group, since then we can handle ^C interrupts ourselves w/o having to worry // about the target getting them as well. launch_info.SetLaunchInSeparateProcessGroup(true); // Ensure we have a target. if (target == nullptr) { if (log) log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__); TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget (debugger, nullptr, nullptr, false, nullptr, new_target_sp); if (error.Fail ()) { if (log) log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ()); return process_sp; } target = new_target_sp.get(); if (!target) { error.SetErrorString ("CreateTarget() returned nullptr"); if (log) log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); return process_sp; } } else { if (log) log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__); } // Mark target as currently selected target. debugger.GetTargetList().SetSelectedTarget(target); // Now create the gdb-remote process. if (log) log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__); process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); if (!process_sp) { error.SetErrorString ("CreateProcess() failed for gdb-remote process"); if (log) log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); return process_sp; } else { if (log) log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__); } // Set the unix signals properly. process_sp->SetUnixSignals (Host::GetUnixSignals ()); // Adjust launch for a hijacker. ListenerSP listener_sp; if (!launch_info.GetHijackListener ()) { if (log) log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__); listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack")); launch_info.SetHijackListener (listener_sp); process_sp->HijackProcessEvents (listener_sp.get ()); } // Log file actions. if (log) { log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__); StreamString stream; size_t i = 0; const FileAction *file_action; while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr) { file_action->Dump (stream); log->PutCString (stream.GetString().c_str ()); stream.Clear(); } } // Do the launch. error = process_sp->Launch(launch_info); if (error.Success ()) { // Handle the hijacking of process events. if (listener_sp) { const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); process_sp->RestoreProcessEvents(); if (state == eStateStopped) { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n", __FUNCTION__, process_sp->GetID (), StateAsCString (state)); } else { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n", __FUNCTION__, process_sp->GetID (), StateAsCString (state)); } } // Hook up process PTY if we have one (which we should for local debugging with llgs). int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ()); } else { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ()); } } else { if (log) log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ()); // FIXME figure out appropriate cleanup here. Do we delete the target? Do we delete the process? Does our caller do that? } return process_sp; }