Home | History | Annotate | Download | only in source
      1 //===-- libdebugserver.cpp --------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include <sys/socket.h>
     11 #include <sys/types.h>
     12 #include <errno.h>
     13 #include <getopt.h>
     14 #include <netinet/in.h>
     15 #include <sys/select.h>
     16 #include <sys/sysctl.h>
     17 
     18 #include "DNB.h"
     19 #include "DNBLog.h"
     20 #include "DNBTimer.h"
     21 #include "PseudoTerminal.h"
     22 #include "RNBContext.h"
     23 #include "RNBServices.h"
     24 #include "RNBSocket.h"
     25 #include "RNBRemote.h"
     26 #include "SysSignal.h"
     27 
     28 //----------------------------------------------------------------------
     29 // Run loop modes which determine which run loop function will be called
     30 //----------------------------------------------------------------------
     31 typedef enum
     32 {
     33     eRNBRunLoopModeInvalid = 0,
     34     eRNBRunLoopModeGetStartModeFromRemoteProtocol,
     35     eRNBRunLoopModeInferiorExecuting,
     36     eRNBRunLoopModeExit
     37 } RNBRunLoopMode;
     38 
     39 
     40 //----------------------------------------------------------------------
     41 // Global Variables
     42 //----------------------------------------------------------------------
     43 RNBRemoteSP g_remoteSP;
     44 int g_disable_aslr = 0;
     45 int g_isatty = 0;
     46 
     47 #define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
     48 #define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
     49 
     50 
     51 //----------------------------------------------------------------------
     52 // Get our program path and arguments from the remote connection.
     53 // We will need to start up the remote connection without a PID, get the
     54 // arguments, wait for the new process to finish launching and hit its
     55 // entry point,  and then return the run loop mode that should come next.
     56 //----------------------------------------------------------------------
     57 RNBRunLoopMode
     58 RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP)
     59 {
     60     std::string packet;
     61 
     62     if (remoteSP.get() != NULL)
     63     {
     64         RNBRemote* remote = remoteSP.get();
     65         RNBContext& ctx = remote->Context();
     66         uint32_t event_mask = RNBContext::event_read_packet_available;
     67 
     68         // Spin waiting to get the A packet.
     69         while (1)
     70         {
     71             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
     72             nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
     73             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
     74 
     75             if (set_events & RNBContext::event_read_packet_available)
     76             {
     77                 rnb_err_t err = rnb_err;
     78                 RNBRemote::PacketEnum type;
     79 
     80                 err = remote->HandleReceivedPacket (&type);
     81 
     82                 // check if we tried to attach to a process
     83                 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
     84                 {
     85                     if (err == rnb_success)
     86                         return eRNBRunLoopModeInferiorExecuting;
     87                     else
     88                     {
     89                         RNBLogSTDERR ("error: attach failed.");
     90                         return eRNBRunLoopModeExit;
     91                     }
     92                 }
     93 
     94 
     95                 if (err == rnb_success)
     96                 {
     97                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__);
     98 					continue;
     99 				}
    100 				else if (err == rnb_not_connected)
    101                 {
    102                     RNBLogSTDERR ("error: connection lost.");
    103                     return eRNBRunLoopModeExit;
    104                 }
    105                 else
    106                 {
    107                     // a catch all for any other gdb remote packets that failed
    108                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
    109                     continue;
    110                 }
    111 
    112                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
    113             }
    114             else
    115             {
    116                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
    117                 return eRNBRunLoopModeExit;
    118             }
    119         }
    120     }
    121     return eRNBRunLoopModeExit;
    122 }
    123 
    124 
    125 //----------------------------------------------------------------------
    126 // Watch for signals:
    127 // SIGINT: so we can halt our inferior. (disabled for now)
    128 // SIGPIPE: in case our child process dies
    129 //----------------------------------------------------------------------
    130 nub_process_t g_pid;
    131 int g_sigpipe_received = 0;
    132 void
    133 signal_handler(int signo)
    134 {
    135     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
    136 
    137     switch (signo)
    138     {
    139 			//  case SIGINT:
    140 			//      DNBProcessKill (g_pid, signo);
    141 			//      break;
    142 
    143 		case SIGPIPE:
    144 			g_sigpipe_received = 1;
    145 			break;
    146     }
    147 }
    148 
    149 // Return the new run loop mode based off of the current process state
    150 RNBRunLoopMode
    151 HandleProcessStateChange (RNBRemoteSP &remote, bool initialize)
    152 {
    153     RNBContext& ctx = remote->Context();
    154     nub_process_t pid = ctx.ProcessID();
    155 
    156     if (pid == INVALID_NUB_PROCESS)
    157     {
    158         DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
    159         return eRNBRunLoopModeExit;
    160     }
    161     nub_state_t pid_state = DNBProcessGetState (pid);
    162 
    163     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
    164 
    165     switch (pid_state)
    166     {
    167 		case eStateInvalid:
    168 		case eStateUnloaded:
    169 			// Something bad happened
    170 			return eRNBRunLoopModeExit;
    171 			break;
    172 
    173 		case eStateAttaching:
    174 		case eStateLaunching:
    175 			return eRNBRunLoopModeInferiorExecuting;
    176 
    177 		case eStateSuspended:
    178 		case eStateCrashed:
    179 		case eStateStopped:
    180 			if (initialize == false)
    181 			{
    182 				// Compare the last stop count to our current notion of a stop count
    183 				// to make sure we don't notify more than once for a given stop.
    184 				nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
    185 				bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
    186 				if (pid_stop_count_changed)
    187 				{
    188 					remote->FlushSTDIO();
    189 
    190 					if (ctx.GetProcessStopCount() == 1)
    191 					{
    192 						DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
    193 					}
    194 					else
    195 					{
    196 
    197 						DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
    198 						remote->NotifyThatProcessStopped ();
    199 					}
    200 				}
    201 				else
    202 				{
    203 					DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
    204 				}
    205 			}
    206 			return eRNBRunLoopModeInferiorExecuting;
    207 
    208 		case eStateStepping:
    209 		case eStateRunning:
    210 			return eRNBRunLoopModeInferiorExecuting;
    211 
    212 		case eStateExited:
    213 			remote->HandlePacket_last_signal(NULL);
    214 			return eRNBRunLoopModeExit;
    215 		case eStateDetached:
    216             return eRNBRunLoopModeExit;
    217 
    218     }
    219 
    220     // Catch all...
    221     return eRNBRunLoopModeExit;
    222 }
    223 // This function handles the case where our inferior program is stopped and
    224 // we are waiting for gdb remote protocol packets. When a packet occurs that
    225 // makes the inferior run, we need to leave this function with a new state
    226 // as the return code.
    227 RNBRunLoopMode
    228 RNBRunLoopInferiorExecuting (RNBRemoteSP &remote)
    229 {
    230     DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
    231     RNBContext& ctx = remote->Context();
    232 
    233     // Init our mode and set 'is_running' based on the current process state
    234     RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
    235 
    236     while (ctx.ProcessID() != INVALID_NUB_PROCESS)
    237     {
    238 
    239         std::string set_events_str;
    240         uint32_t event_mask = ctx.NormalEventBits();
    241 
    242         if (!ctx.ProcessStateRunning())
    243         {
    244             // Clear the stdio bits if we are not running so we don't send any async packets
    245             event_mask &= ~RNBContext::event_proc_stdio_available;
    246         }
    247 
    248         // We want to make sure we consume all process state changes and have
    249         // whomever is notifying us to wait for us to reset the event bit before
    250         // continuing.
    251         //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
    252 
    253         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
    254         nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
    255         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
    256 
    257         if (set_events)
    258         {
    259             if ((set_events & RNBContext::event_proc_thread_exiting) ||
    260                 (set_events & RNBContext::event_proc_stdio_available))
    261             {
    262                 remote->FlushSTDIO();
    263             }
    264 
    265             if (set_events & RNBContext::event_read_packet_available)
    266             {
    267                 // handleReceivedPacket will take care of resetting the
    268                 // event_read_packet_available events when there are no more...
    269                 set_events ^= RNBContext::event_read_packet_available;
    270 
    271                 if (ctx.ProcessStateRunning())
    272                 {
    273                     if (remote->HandleAsyncPacket() == rnb_not_connected)
    274                     {
    275                         // TODO: connect again? Exit?
    276                     }
    277                 }
    278                 else
    279                 {
    280                     if (remote->HandleReceivedPacket() == rnb_not_connected)
    281                     {
    282                         // TODO: connect again? Exit?
    283                     }
    284                 }
    285             }
    286 
    287             if (set_events & RNBContext::event_proc_state_changed)
    288             {
    289                 mode = HandleProcessStateChange (remote, false);
    290                 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
    291                 set_events ^= RNBContext::event_proc_state_changed;
    292             }
    293 
    294             if (set_events & RNBContext::event_proc_thread_exiting)
    295             {
    296                 mode = eRNBRunLoopModeExit;
    297             }
    298 
    299             if (set_events & RNBContext::event_read_thread_exiting)
    300             {
    301                 // Out remote packet receiving thread exited, exit for now.
    302                 if (ctx.HasValidProcessID())
    303                 {
    304                     // TODO: We should add code that will leave the current process
    305                     // in its current state and listen for another connection...
    306                     if (ctx.ProcessStateRunning())
    307                     {
    308                         DNBProcessKill (ctx.ProcessID(), SIGINT);
    309                     }
    310                 }
    311                 mode = eRNBRunLoopModeExit;
    312             }
    313         }
    314 
    315         // Reset all event bits that weren't reset for now...
    316         if (set_events != 0)
    317 			ctx.Events().ResetEvents(set_events);
    318 
    319         if (mode != eRNBRunLoopModeInferiorExecuting)
    320 			break;
    321     }
    322 
    323     return mode;
    324 }
    325 
    326 void
    327 ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
    328 {
    329 #if 0
    330 	vprintf(format, args);
    331 #endif
    332 }
    333 
    334 extern "C" int
    335 debug_server_main(int fd)
    336 {
    337 #if 1
    338 	g_isatty = 0;
    339 #else
    340 	g_isatty = ::isatty (STDIN_FILENO);
    341 
    342 	DNBLogSetDebug(1);
    343 	DNBLogSetVerbose(1);
    344 	DNBLogSetLogMask(-1);
    345 	DNBLogSetLogCallback(ASLLogCallback, NULL);
    346 #endif
    347 
    348     signal (SIGPIPE, signal_handler);
    349 
    350     g_remoteSP.reset (new RNBRemote);
    351 
    352     RNBRemote *remote = g_remoteSP.get();
    353     if (remote == NULL)
    354     {
    355         RNBLogSTDERR ("error: failed to create a remote connection class\n");
    356         return -1;
    357     }
    358 
    359 
    360     RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
    361 
    362     while (mode != eRNBRunLoopModeExit)
    363     {
    364         switch (mode)
    365         {
    366 			case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
    367 				if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
    368 					RNBLogSTDOUT("Starting remote data thread.\n");
    369 					g_remoteSP->StartReadRemoteDataThread();
    370 
    371 					RNBLogSTDOUT("Waiting for start mode from remote.\n");
    372 					mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
    373 				}
    374 				else
    375 				{
    376 					mode = eRNBRunLoopModeExit;
    377 				}
    378 				break;
    379 
    380 			case eRNBRunLoopModeInferiorExecuting:
    381 				mode = RNBRunLoopInferiorExecuting(g_remoteSP);
    382 				break;
    383 
    384 			default:
    385 				mode = eRNBRunLoopModeExit;
    386 				break;
    387 
    388 			case eRNBRunLoopModeExit:
    389 				break;
    390         }
    391     }
    392 
    393     g_remoteSP->StopReadRemoteDataThread ();
    394     g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
    395 
    396     return 0;
    397 }
    398