//===-- GDBServer.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include "GDBServerLog.h" #include "GDBRemoteSession.h" using namespace lldb; //---------------------------------------------------------------------- // Run loop modes which determine which run loop function will be called //---------------------------------------------------------------------- typedef enum { eDCGSRunLoopModeInvalid = 0, eDCGSRunLoopModeGetStartModeFromRemoteProtocol, eDCGSRunLoopModeInferiorAttaching, eDCGSRunLoopModeInferiorLaunching, eDCGSRunLoopModeInferiorExecuting, eDCGSRunLoopModeInferiorKillOrDetach, eDCGSRunLoopModeExit } GSRunLoopMode; typedef enum { eLaunchFlavorDefault = 0, eLaunchFlavorPosixSpawn, #if defined (__arm__) eLaunchFlavorSpringBoard, #endif eLaunchFlavorForkExec, } GSLaunchFlavor; typedef lldb::shared_ptr GDBRemoteSP; typedef struct HandleBroadcastEventInfo { TargetSP target_sp; GDBRemoteSP remote_sp; GSRunLoopMode mode; Target * GetTarget () { return target_sp.get(); } Process * GetProcess() { if (target_sp.get()) return target_sp->GetProcess().get(); return NULL; } GDBRemoteSession * GetRemote () { return remote_sp.get(); } }; //---------------------------------------------------------------------- // Global Variables //---------------------------------------------------------------------- static int g_lockdown_opt = 0; static int g_applist_opt = 0; static GSLaunchFlavor g_launch_flavor = eLaunchFlavorDefault; int g_isatty = 0; //---------------------------------------------------------------------- // Run Loop function prototypes //---------------------------------------------------------------------- void GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info); void GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info); //---------------------------------------------------------------------- // Get our program path and arguments from the remote connection. // We will need to start up the remote connection without a PID, get the // arguments, wait for the new process to finish launching and hit its // entry point, and then return the run loop mode that should come next. //---------------------------------------------------------------------- void GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info) { std::string packet; Target *target = info->GetTarget(); GDBRemoteSession *remote = info->GetRemote(); if (target != NULL && remote != NULL) { // Spin waiting to get the A packet. while (1) { gdb_err_t err = gdb_err; GDBRemoteSession::PacketEnum type; err = remote->HandleReceivedPacket (&type); // check if we tried to attach to a process if (type == GDBRemoteSession::vattach || type == GDBRemoteSession::vattachwait) { if (err == gdb_success) { info->mode = eDCGSRunLoopModeInferiorExecuting; return; } else { Log::STDERR ("error: attach failed."); info->mode = eDCGSRunLoopModeExit; return; } } if (err == gdb_success) { // If we got our arguments we are ready to launch using the arguments // and any environment variables we received. if (type == GDBRemoteSession::set_argv) { info->mode = eDCGSRunLoopModeInferiorLaunching; return; } } else if (err == gdb_not_connected) { Log::STDERR ("error: connection lost."); info->mode = eDCGSRunLoopModeExit; return; } else { // a catch all for any other gdb remote packets that failed GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.",__FUNCTION__); continue; } GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); } } info->mode = eDCGSRunLoopModeExit; } //---------------------------------------------------------------------- // This run loop mode will wait for the process to launch and hit its // entry point. It will currently ignore all events except for the // process state changed event, where it watches for the process stopped // or crash process state. //---------------------------------------------------------------------- GSRunLoopMode GSRunLoopLaunchInferior (HandleBroadcastEventInfo *info) { // The Process stuff takes a c array, the GSContext has a vector... // So make up a c array. Target *target = info->GetTarget(); GDBRemoteSession *remote = info->GetRemote(); Process* process = info->GetProcess(); if (process == NULL) return eDCGSRunLoopModeExit; GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Launching '%s'...", __FUNCTION__, target->GetExecutableModule()->GetFileSpec().GetFilename().AsCString()); // Our launch type hasn't been set to anything concrete, so we need to // figure our how we are going to launch automatically. GSLaunchFlavor launch_flavor = g_launch_flavor; if (launch_flavor == eLaunchFlavorDefault) { // Our default launch method is posix spawn launch_flavor = eLaunchFlavorPosixSpawn; #if defined (__arm__) // Check if we have an app bundle, if so launch using SpringBoard. if (strstr(inferior_argv[0], ".app")) { launch_flavor = eLaunchFlavorSpringBoard; } #endif } //ctx.SetLaunchFlavor(launch_flavor); const char *stdio_file = NULL; lldb::pid_t pid = process->Launch (remote->GetARGV(), remote->GetENVP(), stdio_file, stdio_file, stdio_file); if (pid == LLDB_INVALID_PROCESS_ID) { Log::STDERR ("error: process launch failed: %s", process->GetError().AsCString()); } else { if (remote->IsConnected()) { // It we are connected already, the next thing gdb will do is ask // whether the launch succeeded, and if not, whether there is an // error code. So we need to fetch one packet from gdb before we wait // on the stop from the target. gdb_err_t err = gdb_err; GDBRemoteSession::PacketEnum type; err = remote->HandleReceivedPacket (&type); if (err != gdb_success) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.", __FUNCTION__); return eDCGSRunLoopModeExit; } if (type != GDBRemoteSession::query_launch_success) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); } } } Listener listener("GSRunLoopLaunchInferior"); listener.StartListeningForEvents (process, Process::eBroadcastBitStateChanged); while (process->GetID() != LLDB_INVALID_PROCESS_ID) { uint32_t event_mask = 0; while (listener.WaitForEvent(NULL, &event_mask)) { if (event_mask & Process::eBroadcastBitStateChanged) { Event event; StateType event_state; while ((event_state = process->GetNextEvent (&event))) if (StateIsStoppedState(event_state)) { GDBServerLog::LogIf (GS_LOG_EVENTS, "%s process %4.4x stopped with state %s", __FUNCTION__, pid, StateAsCString(event_state)); switch (event_state) { default: case eStateInvalid: case eStateUnloaded: case eStateAttaching: case eStateLaunching: case eStateSuspended: break; // Ignore case eStateRunning: case eStateStepping: // Still waiting to stop at entry point... break; case eStateStopped: case eStateCrashed: return eDCGSRunLoopModeInferiorExecuting; case eStateDetached: case eStateExited: pid = LLDB_INVALID_PROCESS_ID; return eDCGSRunLoopModeExit; } } if (event_state = eStateInvalid) break; } } } return eDCGSRunLoopModeExit; } //---------------------------------------------------------------------- // This run loop mode will wait for the process to launch and hit its // entry point. It will currently ignore all events except for the // process state changed event, where it watches for the process stopped // or crash process state. //---------------------------------------------------------------------- GSRunLoopMode GSRunLoopLaunchAttaching (HandleBroadcastEventInfo *info, lldb::pid_t& pid) { Process* process = info->GetProcess(); GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, pid); pid = process->Attach(pid); if (pid == LLDB_INVALID_PROCESS_ID) return eDCGSRunLoopModeExit; return eDCGSRunLoopModeInferiorExecuting; } //---------------------------------------------------------------------- // Watch for signals: // SIGINT: so we can halt our inferior. (disabled for now) // SIGPIPE: in case our child process dies //---------------------------------------------------------------------- lldb::pid_t g_pid; int g_sigpipe_received = 0; void signal_handler(int signo) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (%s)", __FUNCTION__, Host::GetSignalAsCString(signo)); switch (signo) { // case SIGINT: // DNBProcessKill (g_pid, signo); // break; case SIGPIPE: g_sigpipe_received = 1; break; } } // Return the new run loop mode based off of the current process state void HandleProcessStateChange (HandleBroadcastEventInfo *info, bool initialize) { Process *process = info->GetProcess(); if (process == NULL) { info->mode = eDCGSRunLoopModeExit; return; } if (process->GetID() == LLDB_INVALID_PROCESS_ID) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); info->mode = eDCGSRunLoopModeExit; return; } StateType pid_state = process->GetState (); GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (info, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, StateAsCString(pid_state)); switch (pid_state) { case eStateInvalid: case eStateUnloaded: // Something bad happened info->mode = eDCGSRunLoopModeExit; return; case eStateAttaching: case eStateLaunching: info->mode = eDCGSRunLoopModeInferiorExecuting; return; case eStateSuspended: case eStateCrashed: case eStateStopped: if (initialize == false) { // Compare the last stop count to our current notion of a stop count // to make sure we don't notify more than once for a given stop. static uint32_t g_prev_stop_id = 0; uint32_t stop_id = process->GetStopID(); bool pid_stop_count_changed = g_prev_stop_id != stop_id; if (pid_stop_count_changed) { info->GetRemote()->FlushSTDIO(); if (stop_id == 1) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); } else { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); info->GetRemote()->NotifyThatProcessStopped (); } } else { GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); } } info->mode = eDCGSRunLoopModeInferiorExecuting; return; case eStateStepping: case eStateRunning: info->mode = eDCGSRunLoopModeInferiorExecuting; return; case eStateExited: info->GetRemote()->HandlePacket_last_signal (NULL); info->mode = eDCGSRunLoopModeExit; return; } // Catch all... info->mode = eDCGSRunLoopModeExit; } bool CommunicationHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) { HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; Process *process = info->GetProcess(); if (process == NULL) { info->mode = eDCGSRunLoopModeExit; return true; } if (event_mask & Communication::eBroadcastBitPacketAvailable) { if (process->IsRunning()) { if (info->GetRemote()->HandleAsyncPacket() == gdb_not_connected) info->mode = eDCGSRunLoopModeExit; } else { if (info->GetRemote()->HandleReceivedPacket() == gdb_not_connected) info->mode = eDCGSRunLoopModeExit; } } if (event_mask & Communication::eBroadcastBitReadThreadDidExit) { info->mode = eDCGSRunLoopModeExit; } if (event_mask & Communication::eBroadcastBitDisconnected) { info->mode = eDCGSRunLoopModeExit; } return true; } bool ProcessHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) { HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; Process *process = info->GetProcess(); if (process == NULL) { info->mode = eDCGSRunLoopModeExit; return true; } if (event_mask & Process::eBroadcastBitStateChanged) { // Consume all available process events with no timeout Event event; StateType process_state; while ((process_state = process->GetNextEvent (&event)) != eStateInvalid) { if (StateIsStoppedState(process_state)) info->GetRemote()->FlushSTDIO(); HandleProcessStateChange (info, false); if (info->mode != eDCGSRunLoopModeInferiorExecuting) break; } } else if (event_mask & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR)) { info->GetRemote()->FlushSTDIO(); } return true; } // This function handles the case where our inferior program is stopped and // we are waiting for gdb remote protocol packets. When a packet occurs that // makes the inferior run, we need to leave this function with a new state // as the return code. void GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info) { GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); // Init our mode and set 'is_running' based on the current process state HandleProcessStateChange (info, true); uint32_t desired_mask, acquired_mask; Listener listener("GSRunLoopInferiorExecuting"); desired_mask = Communication::eBroadcastBitPacketAvailable | Communication::eBroadcastBitReadThreadDidExit | Communication::eBroadcastBitDisconnected; acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), desired_mask, CommunicationHandleBroadcastEvent, info); assert (acquired_mask == desired_mask); desired_mask = GDBRemotePacket::eBroadcastBitPacketAvailable; acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), desired_mask, CommunicationHandleBroadcastEvent, info); assert (acquired_mask == desired_mask); desired_mask = Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR ; acquired_mask = listener.StartListeningForEvents (info->GetProcess (), desired_mask, ProcessHandleBroadcastEvent, info); assert (acquired_mask == desired_mask); Process *process = info->GetProcess(); while (process->IsAlive()) { if (!info->GetRemote()->IsConnected()) { info->mode = eDCGSRunLoopModeInferiorKillOrDetach; break; } // We want to make sure we consume all process state changes and have // whomever is notifying us to wait for us to reset the event bit before // continuing. //ctx.Events().SetResetAckMask (GSContext::event_proc_state_changed); uint32_t event_mask = 0; Broadcaster *broadcaster = listener.WaitForEvent(NULL, &event_mask); if (broadcaster) { listener.HandleBroadcastEvent(broadcaster, event_mask); } } } //---------------------------------------------------------------------- // Convenience function to set up the remote listening port // Returns 1 for success 0 for failure. //---------------------------------------------------------------------- static bool StartListening (HandleBroadcastEventInfo *info, int listen_port) { if (!info->GetRemote()->IsConnected()) { Log::STDOUT ("Listening to port %i...\n", listen_port); char connect_url[256]; snprintf(connect_url, sizeof(connect_url), "listen://%i", listen_port); Communication &comm = info->remote_sp->GetPacketComm(); comm.SetConnection (new ConnectionFileDescriptor); if (comm.Connect (connect_url)) { if (comm.StartReadThread()) return true; Log::STDERR ("Failed to start the communication read thread.\n", connect_url); comm.Disconnect(); } else { Log::STDERR ("Failed to connection to %s.\n", connect_url); } return false; } return true; } //---------------------------------------------------------------------- // ASL Logging callback that can be registered with DNBLogSetLogDCScriptInterpreter::Type //---------------------------------------------------------------------- //void //ASLLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) //{ // if (format == NULL) // return; // static aslmsg g_aslmsg = NULL; // if (g_aslmsg == NULL) // { // g_aslmsg = ::asl_new (ASL_TYPE_MSG); // char asl_key_sender[PATH_MAX]; // snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.dc-gdbserver-%g", dc_gdbserverVersionNumber); // ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); // } // // int asl_level; // if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; // else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; // else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; // else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; // else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; // // ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); //} //---------------------------------------------------------------------- // FILE based Logging callback that can be registered with // DNBLogSetLogDCScriptInterpreter::Type //---------------------------------------------------------------------- void FileLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) { if (baton == NULL || format == NULL) return; ::vfprintf ((FILE *)baton, format, args); ::fprintf ((FILE *)baton, "\n"); } //---------------------------------------------------------------------- // option descriptors for getopt_long() //---------------------------------------------------------------------- static struct option g_long_options[] = { { "arch", required_argument, NULL, 'c' }, { "attach", required_argument, NULL, 'a' }, { "debug", no_argument, NULL, 'g' }, { "verbose", no_argument, NULL, 'v' }, { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" { "log-file", required_argument, NULL, 'l' }, { "log-flags", required_argument, NULL, 'f' }, { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose namet starts with ARG { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name { NULL, 0, NULL, 0 } }; extern const double dc_gdbserverVersionNumber; int main (int argc, char *argv[]) { Initialize(); Host::ThreadCreated ("[main]"); g_isatty = ::isatty (STDIN_FILENO); // signal (SIGINT, signal_handler); signal (SIGPIPE, signal_handler); Log *log = GDBServerLog::GetLogIfAllCategoriesSet(GS_LOG_ALL); const char *this_exe_name = argv[0]; int i; int attach_pid = LLDB_INVALID_PROCESS_ID; for (i=0; iCreateProcess ()); info.remote_sp.reset (new GDBRemoteSession (process_sp)); info.remote_sp->SetLog (log); StreamString sstr; sstr.Printf("ConnectionFileDescriptor(%s)", argv[0]); if (info.remote_sp.get() == NULL) { Log::STDERR ("error: failed to create a GDBRemoteSession class\n"); return -1; } // If we know we're waiting to attach, we don't need any of this other info. if (start_mode != eDCGSRunLoopModeInferiorAttaching) { if (argc == 0 || g_lockdown_opt) { if (g_lockdown_opt != 0) { // Work around for SIGPIPE crashes due to posix_spawn issue. We have to close // STDOUT and STDERR, else the first time we try and do any, we get SIGPIPE and // die as posix_spawn is doing bad things with our file descriptors at the moment. int null = open("/dev/null", O_RDWR); dup2(null, STDOUT_FILENO); dup2(null, STDERR_FILENO); } else if (g_applist_opt != 0) { // // List all applications we are able to see // std::string applist_plist; // int err = ListApplications(applist_plist, false, false); // if (err == 0) // { // fputs (applist_plist.c_str(), stdout); // } // else // { // Log::STDERR ("error: ListApplications returned error %i\n", err); // } // // Exit with appropriate error if we were asked to list the applications // // with no other args were given (and we weren't trying to do this over // // lockdown) // return err; return 0; } //DNBLogDebug("Get args from remote protocol..."); start_mode = eDCGSRunLoopModeGetStartModeFromRemoteProtocol; } else { start_mode = eDCGSRunLoopModeInferiorLaunching; // Fill in the argv array in the context from the rest of our args. // Skip the name of this executable and the port number info.remote_sp->SetArguments (argc, argv); } } if (start_mode == eDCGSRunLoopModeExit) return -1; info.mode = start_mode; while (info.mode != eDCGSRunLoopModeExit) { switch (info.mode) { case eDCGSRunLoopModeGetStartModeFromRemoteProtocol: #if defined (__arm__) if (g_lockdown_opt) { if (!info.remote_sp->GetCommunication()->IsConnected()) { if (info.remote_sp->GetCommunication()->ConnectToService () != gdb_success) { Log::STDERR ("Failed to get connection from a remote gdb process.\n"); info.mode = eDCGSRunLoopModeExit; } else if (g_applist_opt != 0) { // List all applications we are able to see std::string applist_plist; if (ListApplications(applist_plist, false, false) == 0) { //DNBLogDebug("Task list: %s", applist_plist.c_str()); info.remote_sp->GetCommunication()->Write(applist_plist.c_str(), applist_plist.size()); // Issue a read that will never yield any data until the other side // closes the socket so this process doesn't just exit and cause the // socket to close prematurely on the other end and cause data loss. std::string buf; info.remote_sp->GetCommunication()->Read(buf); } info.remote_sp->GetCommunication()->Disconnect(false); info.mode = eDCGSRunLoopModeExit; break; } else { // Start watching for remote packets info.remote_sp->StartReadRemoteDataThread(); } } } else #endif { if (StartListening (&info, listen_port)) Log::STDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); else info.mode = eDCGSRunLoopModeExit; } if (info.mode != eDCGSRunLoopModeExit) GSRunLoopGetStartModeFromRemote (&info); break; case eDCGSRunLoopModeInferiorAttaching: if (!waitfor_pid_name.empty()) { // Set our end wait time if we are using a waitfor-duration // option that may have been specified TimeValue attach_timeout_abstime; if (waitfor_duration != 0) { attach_timeout_abstime = TimeValue::Now(); attach_timeout_abstime.OffsetWithSeconds (waitfor_duration); } GSLaunchFlavor launch_flavor = g_launch_flavor; if (launch_flavor == eLaunchFlavorDefault) { // Our default launch method is posix spawn launch_flavor = eLaunchFlavorPosixSpawn; #if defined (__arm__) // Check if we have an app bundle, if so launch using SpringBoard. if (waitfor_pid_name.find (".app") != std::string::npos) { launch_flavor = eLaunchFlavorSpringBoard; } #endif } //ctx.SetLaunchFlavor(launch_flavor); lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); if (pid == LLDB_INVALID_PROCESS_ID) { info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); info.mode = eDCGSRunLoopModeExit; } else { info.mode = eDCGSRunLoopModeInferiorExecuting; } } else if (attach_pid != LLDB_INVALID_PROCESS_ID) { Log::STDOUT ("Attaching to process %i...\n", attach_pid); info.mode = GSRunLoopLaunchAttaching (&info, attach_pid); if (info.mode != eDCGSRunLoopModeInferiorExecuting) { const char *error_str = info.GetRemote()->GetLaunchError().AsCString(); Log::STDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); info.mode = eDCGSRunLoopModeExit; } } else if (!attach_pid_name.empty ()) { lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); if (pid == LLDB_INVALID_PROCESS_ID) { info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); info.mode = eDCGSRunLoopModeExit; } else { info.mode = eDCGSRunLoopModeInferiorExecuting; } } else { Log::STDERR ("error: asked to attach with empty name and invalid PID."); info.mode = eDCGSRunLoopModeExit; } if (info.mode != eDCGSRunLoopModeExit) { if (StartListening (&info, listen_port)) Log::STDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); else info.mode = eDCGSRunLoopModeExit; } break; case eDCGSRunLoopModeInferiorLaunching: info.mode = GSRunLoopLaunchInferior (&info); if (info.mode == eDCGSRunLoopModeInferiorExecuting) { if (StartListening (&info, listen_port)) Log::STDOUT ("Got a connection, waiting for debugger instructions for task \"%s\".\n", argv[0]); else info.mode = eDCGSRunLoopModeExit; } else { Log::STDERR ("error: failed to launch process %s: %s\n", argv[0], info.GetRemote()->GetLaunchError().AsCString()); } break; case eDCGSRunLoopModeInferiorExecuting: GSRunLoopInferiorExecuting (&info); break; case eDCGSRunLoopModeInferiorKillOrDetach: { Process *process = info.GetProcess(); if (process && process->IsAlive()) { process->Kill(SIGCONT); process->Kill(SIGKILL); } } info.mode = eDCGSRunLoopModeExit; break; default: info.mode = eDCGSRunLoopModeExit; case eDCGSRunLoopModeExit: break; } } return 0; }