void listener_func() { while (!g_done) { SBEvent event; bool got_event = g_listener.WaitForEvent(1, event); if (got_event) { if (!event.IsValid()) throw Exception("event is not valid in listener thread"); // send process description SBProcess process = SBProcess::GetProcessFromEvent(event); if (!process.IsValid()) throw Exception("process is not valid"); if (SBProcess::GetStateFromEvent(event) != lldb::eStateStopped || SBProcess::GetRestartedFromEvent(event)) continue; // Only interested in "stopped" events. SBStream description; for (int i = 0; i < process.GetNumThreads(); ++i) { // send each thread description SBThread thread = process.GetThreadAtIndex(i); // send each frame function name uint32_t num_frames = thread.GetNumFrames(); for(int j = 0; j < num_frames; ++j) { const char* function_name = thread.GetFrameAtIndex(j).GetSymbol().GetName(); if (function_name) g_frame_functions.push(string(function_name)); } } } } }
void SBDebugger::HandleProcessEvent (const SBProcess &process, const SBEvent &event, FILE *out, FILE *err) { if (!process.IsValid()) return; TargetSP target_sp (process.GetTarget().GetSP()); if (!target_sp) return; const uint32_t event_type = event.GetType(); char stdio_buffer[1024]; size_t len; Mutex::Locker api_locker (target_sp->GetAPIMutex()); if (event_type & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitStateChanged)) { // Drain stdout when we stop just in case we have any bytes while ((len = process.GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0) if (out != NULL) ::fwrite (stdio_buffer, 1, len, out); } if (event_type & (Process::eBroadcastBitSTDERR | Process::eBroadcastBitStateChanged)) { // Drain stderr when we stop just in case we have any bytes while ((len = process.GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0) if (err != NULL) ::fwrite (stdio_buffer, 1, len, err); } if (event_type & Process::eBroadcastBitStateChanged) { StateType event_state = SBProcess::GetStateFromEvent (event); if (event_state == eStateInvalid) return; bool is_stopped = StateIsStoppedState (event_state); if (!is_stopped) process.ReportEventState (event, out); } }
size_t Driver::EditLineInputReaderCallback ( void *baton, SBInputReader *reader, InputReaderAction notification, const char *bytes, size_t bytes_len ) { Driver *driver = (Driver *)baton; switch (notification) { case eInputReaderActivate: break; case eInputReaderReactivate: driver->ReadyForCommand(); break; case eInputReaderDeactivate: break; case eInputReaderAsynchronousOutputWritten: if (driver->m_io_channel_ap.get() != NULL) driver->m_io_channel_ap->RefreshPrompt(); break; case eInputReaderInterrupt: if (driver->m_io_channel_ap.get() != NULL) { SBProcess process = driver->GetDebugger().GetSelectedTarget().GetProcess(); if (!driver->m_io_channel_ap->EditLineHasCharacters() && process.IsValid() && process.GetState() == lldb::eStateRunning) { process.Stop(); } else { driver->m_io_channel_ap->OutWrite ("^C\n", 3, NO_ASYNC); // I wish I could erase the entire input line, but there's no public API for that. driver->m_io_channel_ap->EraseCharsBeforeCursor(); driver->m_io_channel_ap->RefreshPrompt(); } } break; case eInputReaderEndOfFile: if (driver->m_io_channel_ap.get() != NULL) { driver->m_io_channel_ap->OutWrite ("^D\n", 3, NO_ASYNC); driver->m_io_channel_ap->RefreshPrompt (); } #ifdef __unix__ write (driver->m_editline_pty.GetMasterFileDescriptor(), "quit\n", 5); #endif break; case eInputReaderGotToken: #ifdef __unix__ write (driver->m_editline_pty.GetMasterFileDescriptor(), bytes, bytes_len); #endif break; case eInputReaderDone: break; } return bytes_len; }
// This function handles events that were broadcast by the process. void Driver::HandleProcessEvent (const SBEvent &event) { using namespace lldb; const uint32_t event_type = event.GetType(); if (event_type & SBProcess::eBroadcastBitSTDOUT) { // The process has stdout available, get it and write it out to the // appropriate place. GetProcessSTDOUT (); } else if (event_type & SBProcess::eBroadcastBitSTDERR) { // The process has stderr available, get it and write it out to the // appropriate place. GetProcessSTDERR (); } else if (event_type & SBProcess::eBroadcastBitStateChanged) { // Drain all stout and stderr so we don't see any output come after // we print our prompts GetProcessSTDOUT (); GetProcessSTDERR (); // Something changed in the process; get the event and report the process's current status and location to // the user. StateType event_state = SBProcess::GetStateFromEvent (event); if (event_state == eStateInvalid) return; SBProcess process (SBProcess::GetProcessFromEvent (event)); assert (process.IsValid()); switch (event_state) { case eStateInvalid: case eStateUnloaded: case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStepping: case eStateDetached: { char message[1024]; int message_len = ::snprintf (message, sizeof(message), "Process %llu %s\n", process.GetProcessID(), m_debugger.StateAsCString (event_state)); m_io_channel_ap->OutWrite(message, message_len, ASYNC); } break; case eStateRunning: // Don't be chatty when we run... break; case eStateExited: { SBCommandReturnObject result; m_debugger.GetCommandInterpreter().HandleCommand("process status", result, false); m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize(), ASYNC); m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize(), ASYNC); } break; case eStateStopped: case eStateCrashed: case eStateSuspended: // Make sure the program hasn't been auto-restarted: if (SBProcess::GetRestartedFromEvent (event)) { // FIXME: Do we want to report this, or would that just be annoyingly chatty? char message[1024]; int message_len = ::snprintf (message, sizeof(message), "Process %llu stopped and was programmatically restarted.\n", process.GetProcessID()); m_io_channel_ap->OutWrite(message, message_len, ASYNC); } else { if (GetDebugger().GetSelectedTarget() == process.GetTarget()) { SBCommandReturnObject result; UpdateSelectedThread (); m_debugger.GetCommandInterpreter().HandleCommand("process status", result, false); m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize(), ASYNC); m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize(), ASYNC); } else { SBStream out_stream; uint32_t target_idx = GetDebugger().GetIndexOfTarget(process.GetTarget()); if (target_idx != UINT32_MAX) out_stream.Printf ("Target %d: (", target_idx); else out_stream.Printf ("Target <unknown index>: ("); process.GetTarget().GetDescription (out_stream, eDescriptionLevelBrief); out_stream.Printf (") stopped.\n"); m_io_channel_ap->OutWrite (out_stream.GetData(), out_stream.GetSize(), ASYNC); } } break; } } }
int main (int argc, char **argv, char **envp) { int narg; fd_set set; char commandLine[BIG_LINE_MAX]; // data from cdt char consoleLine[LINE_MAX]; // data from eclipse's console long chars; struct timeval timeout; int isVersion=0, isInterpreter=0; const char *testCommand=NULL; int isLog=0; int logmask=LOG_ALL; state.ptyfd = EOF; state.gdbPrompt = "GNU gdb (GDB) 7.7.1\n"; sprintf (state.lldbmi2Prompt, "lldbmi2 version %s\n", LLDBMI2_VERSION); state.cdtbufferB.grow(BIG_LINE_MAX); limits.frames_max = FRAMES_MAX; limits.children_max = CHILDREN_MAX; limits.walk_depth_max = WALK_DEPTH_MAX; limits.change_depth_max = CHANGE_DEPTH_MAX; // get args for (narg=0; narg<argc; narg++) { logarg (argv[narg]); if (strcmp (argv[narg],"--version") == 0) isVersion = 1; else if (strcmp (argv[narg],"--interpreter") == 0 ) { isInterpreter = 1; if (++narg<argc) logarg(argv[narg]); } else if (strcmp (argv[narg],"--test") == 0 ) { limits.istest = true; if (++narg<argc) sscanf (logarg(argv[narg]), "%d", &state.test_sequence); if (state.test_sequence) setTestSequence (state.test_sequence); } else if (strcmp (argv[narg],"--script") == 0 ) { limits.istest = true; if (++narg<argc) strcpy (state.test_script, logarg(argv[narg])); // no spaces allowed in the name if (state.test_script[0]) setTestScript (state.test_script); } else if (strcmp (argv[narg],"--log") == 0 ) isLog = 1; else if (strcmp (argv[narg],"--logmask") == 0 ) { isLog = 1; if (++narg<argc) sscanf (logarg(argv[narg]), "%x", &logmask); } else if (strcmp (argv[narg],"--frames") == 0 ) { if (++narg<argc) sscanf (logarg(argv[narg]), "%d", &limits.frames_max); } else if (strcmp (argv[narg],"--children") == 0 ) { if (++narg<argc) sscanf (logarg(argv[narg]), "%d", &limits.children_max); } else if (strcmp (argv[narg],"--walkdepth") == 0 ) { if (++narg<argc) sscanf (logarg(argv[narg]), "%d", &limits.walk_depth_max); } else if (strcmp (argv[narg],"--changedepth") == 0 ) { if (++narg<argc) sscanf (logarg(argv[narg]), "%d", &limits.change_depth_max); } } // create a log filename from program name and open log file if (isLog) { if (limits.istest) setlogfile (state.logfilename, sizeof(state.logfilename), argv[0], "lldbmi2t.log"); else setlogfile (state.logfilename, sizeof(state.logfilename), argv[0], "lldbmi2.log"); openlog (state.logfilename); setlogmask (logmask); } // log program args addlog("\n"); logprintf (LOG_ARGS, NULL); state.envp[0] = NULL; state.envpentries = 0; state.envspointer = state.envs; const char *wl = "PWD="; // want to get eclipse project_loc if any int wll = strlen(wl); // copy environment for tested program for (int ienv=0; envp[ienv]; ienv++) { addEnvironment (&state, envp[ienv]); if (strncmp(envp[ienv], wl, wll)==0) strcpy (state.project_loc, envp[ienv]+wll); } // return gdb version if --version if (isVersion) { writetocdt (state.gdbPrompt); writetocdt (state.lldbmi2Prompt); return EXIT_SUCCESS; } // check if --interpreter mi2 else if (!isInterpreter) { help (&state); return EXIT_FAILURE; } initializeSB (&state); signal (SIGINT, signalHandler); signal (SIGSTOP, signalHandler); cdtprintf ("(gdb)\n"); // main loop FD_ZERO (&set); while (!state.eof) { if (limits.istest) logprintf (LOG_NONE, "main loop\n"); // get inputs timeout.tv_sec = 0; timeout.tv_usec = 200000; // check command from CDT FD_SET (STDIN_FILENO, &set); if (state.ptyfd != EOF) { // check data from Eclipse's console FD_SET (state.ptyfd, &set); select(state.ptyfd+1, &set, NULL, NULL, &timeout); } else select(STDIN_FILENO+1, &set, NULL, NULL, &timeout); if (FD_ISSET(STDIN_FILENO, &set) && !state.eof && !limits.istest) { logprintf (LOG_NONE, "read in\n"); chars = read (STDIN_FILENO, commandLine, sizeof(commandLine)-1); logprintf (LOG_NONE, "read out %d chars\n", chars); if (chars>0) { commandLine[chars] = '\0'; while (fromCDT (&state,commandLine,sizeof(commandLine)) == MORE_DATA) commandLine[0] = '\0'; } else state.eof = true; } if (state.ptyfd!=EOF && state.isrunning) { // input from user to program if (FD_ISSET(state.ptyfd, &set) && !state.eof && !limits.istest) { logprintf (LOG_NONE, "pty read in\n"); chars = read (state.ptyfd, consoleLine, sizeof(consoleLine)-1); logprintf (LOG_NONE, "pty read out %d chars\n", chars); if (chars>0) { logprintf (LOG_PROG_OUT, "pty read %d chars\n", chars); consoleLine[chars] = '\0'; SBProcess process = state.process; if (process.IsValid()) process.PutSTDIN (consoleLine, chars); } } } // execute test command if test mode if (!state.eof && limits.istest && !state.isrunning) { if ((testCommand=getTestCommand ())!=NULL) { snprintf (commandLine, sizeof(commandLine), "%s\n", testCommand); fromCDT (&state, commandLine, sizeof(commandLine)); } } // execute stacked commands if many command arrived once if (!state.eof && state.cdtbufferB.size()>0) { commandLine[0] = '\0'; while (fromCDT (&state, commandLine, sizeof(commandLine)) == MORE_DATA) ; } } if (state.ptyfd != EOF) close (state.ptyfd); terminateSB (); logprintf (LOG_INFO, "main exit\n"); closelog (); return EXIT_SUCCESS; }