// ************************************************************************* // gdbcontroller.cpp - description // ------------------- // begin : Sun Aug 8 1999 // copyright : (C) 1999 by John Birch // email : jbb@kdevelop.org // ************************************************************************** // // ************************************************************************** // * * // * This program is free software; you can redistribute it and/or modify * // * it under the terms of the GNU General Public License as published by * // * the Free Software Foundation; either version 2 of the License, or * // * (at your option) any later version. * // * * // ************************************************************************** #include "gdbcontroller.h" #include "breakpoint.h" #include "gdbcommand.h" #include "stty.h" #include "domutil.h" #include "settings.h" #include "mi/miparser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // ************************************************************************** // // Does all the communication between gdb and the kdevelop's debugger code. // Significatant classes being used here are // // GDBParser - parses the "variable" data using the vartree and varitems // VarTree - where the variable data will end up // FrameStack - tracks the program frames and allows the user to switch between // and therefore view the calling funtions and their data // Breakpoint - Where and what to do with breakpoints. // STTY - the tty that the _application_ will run on. // // Significant variables // state_ - be very careful setting this. The controller is totally // dependent on this reflecting the correct state. For instance, // if the app is busy but we don't think so, then we lose control // of the app. The only way to get out of these situations is to // delete (stop) the controller. // currentFrame_ // - Holds the frame number where and locals/variable information will // go to // // Certain commands need to be "wrapped", so that the output gdb produces is // of the form "\032data_id gdb output \032data_id" // Then a very simple parse can extract this gdb output and hand it off // to its' respective parser. // To do this we set the prompt to be \032data_id before the command and then // reset to \032i to indicate the "idle". // // Note that the following does not work because in certain situations // gdb can get an error in performing the command and therefore will not // output the final echo. Hence the data will be thrown away. // (certain "info locals" will generate this error. // // queueCmd(new GDBCommand(TQString().sprintf("define printlocal\n" // "echo \32%c\ninfo locals\necho \32%c\n" // "end", // LOCALS, LOCALS))); // (although replacing echo with "set prompt" appropriately could work Hmmmm.) // // Shared libraries and breakpoints // ================================ // Shared libraries and breakpoints have a problem that has a reasonable solution. // The problem is that gdb will not accept breakpoints in source that is in a // shared library that has _not_ _yet_ been opened but will be opened via a // dlopen. // // The solution is to get gdb to tell us when a shared library has been opened. // This means that when the user sets a breakpoint, we flag this breakpoint as // pending, try to set the breakpoint and if gdb says it succeeded then flag it // as active. If gdb is not successful then we leave the breakpoint as pending. // // This is known as "lazy breakpoints" // // If the user has selected a file that is really outside the program and tried to // set a breakpoint then this breakpoint will always be pending. I can't do // anything about that, because it _might_ be in a shared library. If not they // are either fools or just misguided... // // Now that the breakpoint is pending, we need gdb to tell us when a shared // library has been loaded. We use "set stop-on 1". This breaks on _any_ // library event, and we just try to set the pending breakpoints. Once we're // done, we then "continue" // // Now here's the problem with all this. If the user "step"s over code that // contains a library dlopen then it'll just keep running, because we receive a // break and hence end up doing a continue. In this situation, I do _not_ // do a continue but leave it stopped with the status line reflecting the // stopped state. The frame stack is in the dl routine that caused the stop. // // There isn't any way around this, but I could allievate the problem somewhat // by only doing a "set stop-on 1" when we have pending breakpoints. // // ************************************************************************** namespace GDBDebugger { // This is here so we can check for startup /shutdown problems int debug_controllerExists = false; GDBController::GDBController(TQDomDocument &projectDom) : DbgController(), currentFrame_(0), viewedThread_(-1), holdingZone_(), currentCmd_(0), tty_(0), badCore_(TQString()), state_(s_dbgNotStarted|s_appNotStarted), programHasExited_(false), dom(projectDom), config_breakOnLoadingLibrary_(true), config_forceBPSet_(true), config_displayStaticMembers_(false), config_asmDemangle_(true), config_dbgTerminal_(false), config_gdbPath_(), config_outputRadix_(10), state_reload_needed(false), stateReloadInProgress_(false) { configure(); cmdList_.setAutoDelete(true); Q_ASSERT(! debug_controllerExists); debug_controllerExists = true; } // ************************************************************************** // Deleting the controller involves shutting down gdb nicely. // When were attached to a process, we must first detach so that the process // can continue running as it was before being attached. gdb is quite slow to // detach from a process, so we must process events within here to get a "clean" // shutdown. GDBController::~GDBController() { debug_controllerExists = false; } // ************************************************************************** void GDBController::configure() { // A a configure.gdb script will prevent these from uncontrolled growth... config_configGdbScript_ = DomUtil::readEntry(dom, "/kdevdebugger/general/configGdbScript").latin1(); config_runShellScript_ = DomUtil::readEntry(dom, "/kdevdebugger/general/runShellScript").latin1(); config_runGdbScript_ = DomUtil::readEntry(dom, "/kdevdebugger/general/runGdbScript").latin1(); // add macros for reading TQStrings? or in configGdbScript? config_forceBPSet_ = DomUtil::readBoolEntry(dom, "/kdevdebugger/general/allowforcedbpset", true); config_dbgTerminal_ = DomUtil::readBoolEntry(dom, "/kdevdebugger/general/separatetty", false); config_gdbPath_ = DomUtil::readEntry(dom, "/kdevdebugger/general/gdbpath"); bool old_displayStatic = config_displayStaticMembers_; config_displayStaticMembers_ = DomUtil::readBoolEntry(dom, "/kdevdebugger/display/staticmembers",false); bool old_asmDemangle = config_asmDemangle_; config_asmDemangle_ = DomUtil::readBoolEntry(dom, "/kdevdebugger/display/demanglenames",true); bool old_breakOnLoadingLibrary_ = config_breakOnLoadingLibrary_; config_breakOnLoadingLibrary_ = DomUtil::readBoolEntry(dom, "/kdevdebugger/general/breakonloadinglibs",true); // FIXME: should move this into debugger part or variable widget. int old_outputRadix = config_outputRadix_; #if 0 config_outputRadix_ = DomUtil::readIntEntry(dom, "/kdevdebugger/display/outputradix", 10); varTree_->setRadix(config_outputRadix_); #endif if (( old_displayStatic != config_displayStaticMembers_ || old_asmDemangle != config_asmDemangle_ || old_breakOnLoadingLibrary_ != config_breakOnLoadingLibrary_ || old_outputRadix != config_outputRadix_) && dbgProcess_) { bool restart = false; if (stateIsOn(s_dbgBusy)) { pauseApp(); restart = true; } if (old_displayStatic != config_displayStaticMembers_) { if (config_displayStaticMembers_) queueCmd(new GDBCommand("set print static-members on")); else queueCmd(new GDBCommand("set print static-members off")); } if (old_asmDemangle != config_asmDemangle_) { if (config_asmDemangle_) queueCmd(new GDBCommand("set print asm-demangle on")); else queueCmd(new GDBCommand("set print asm-demangle off")); } // Disabled for MI port. if (old_outputRadix != config_outputRadix_) { queueCmd(new GDBCommand(TQCString().sprintf("set output-radix %d", config_outputRadix_))); // FIXME: should do this in variable widget anyway. // After changing output radix, need to refresh variables view. raiseEvent(program_state_changed); } if (!config_configGdbScript_.isEmpty()) queueCmd(new GDBCommand("source " + config_configGdbScript_)); if (restart) queueCmd(new GDBCommand("-exec-continue")); } } // ************************************************************************** void GDBController::addCommand(GDBCommand* cmd) { queueCmd(cmd); } void GDBController::addCommand(const TQString& str) { queueCmd(new GDBCommand(str)); } void GDBController::addCommandToFront(GDBCommand* cmd) { queueCmd(cmd, queue_at_front); } void GDBController::addCommandBeforeRun(GDBCommand* cmd) { queueCmd(cmd, queue_before_run); } int GDBController::currentThread() const { return viewedThread_; } int GDBController::currentFrame() const { return currentFrame_; } // Fairly obvious that we'll add whatever command you give me to a queue // If you tell me to, I'll put it at the head of the queue so it'll run ASAP // Not quite so obvious though is that if we are going to run again. then any // information requests become redundent and must be removed. // We also try and run whatever command happens to be at the head of // the queue. void GDBController::queueCmd(GDBCommand *cmd, enum queue_where queue_where) { if (stateIsOn(s_dbgNotStarted)) { KMessageBox::information( 0, i18n("Gdb command sent when debugger is not running
" "The command was:
%1").arg(cmd->initialString()), i18n("Internal error"), "gdb_error"); return; } if (stateReloadInProgress_) stateReloadingCommands_.insert(cmd); if (queue_where == queue_at_front) cmdList_.insert(0, cmd); else if (queue_where == queue_at_end) cmdList_.append (cmd); else if (queue_where == queue_before_run) { unsigned i; for (i = 0; i < cmdList_.count(); ++i) if (cmdList_.at(i)->isRun()) break; cmdList_.insert(i, cmd); } kdDebug(9012) << "QUEUE: " << cmd->initialString() << (stateReloadInProgress_ ? " (state reloading)\n" : "\n"); setStateOn(s_dbgBusy); emit dbgStatus("", state_); raiseEvent(debugger_busy); executeCmd(); } // ************************************************************************** // If the appliction can accept a command and we've got one waiting // then send it. // Commands can be just request for data (or change gdbs state in someway) // or they can be "run" commands. If a command is sent to gdb our internal // state will get updated. void GDBController::executeCmd() { if (stateIsOn(s_dbgNotStarted|s_waitForWrite|s_shuttingDown) || !dbgProcess_) { return; } if (!currentCmd_) { if (cmdList_.isEmpty()) return; currentCmd_ = cmdList_.take(0); } else { return; } TQString commandText = currentCmd_->cmdToSend(); bool bad_command = false; TQString message; unsigned length = commandText.length(); // No i18n for message since it's mainly for debugging. if (length == 0) { // The command might decide it's no longer necessary to send // it. if (SentinelCommand* sc = dynamic_cast(currentCmd_)) { kdDebug(9012) << "SEND: sentinel command, not sending\n"; sc->invokeHandler(); } else { kdDebug(9012) << "SEND: command " << currentCmd_->initialString() << " changed its mind, not sending\n"; } destroyCurrentCommand(); executeCmd(); commandDone(); return; } else { if (commandText[length-1] != '\n') { bad_command = true; message = "Debugger command does not end with newline"; } } if (bad_command) { KMessageBox::information(0, i18n("Invalid debugger command
") + message, i18n("Invalid debugger command"), "gdb_error"); return; } kdDebug(9012) << "SEND: " << commandText; dbgProcess_->writeStdin(commandText.local8Bit(), commandText.length()); setStateOn(s_waitForWrite); TQString prettyCmd = currentCmd_->cmdToSend(); prettyCmd.replace( TQRegExp("set prompt \032.\n"), "" ); prettyCmd = "(gdb) " + prettyCmd; if (currentCmd_->isUserCommand()) emit gdbUserCommandStdout( prettyCmd.latin1() ); else emit gdbInternalCommandStdout( prettyCmd.latin1() ); emit dbgStatus ("", state_); } // ************************************************************************** void GDBController::destroyCmds() { if (currentCmd_) { destroyCurrentCommand(); } while (!cmdList_.isEmpty()) delete cmdList_.take(0); } // Pausing an app removes any pending run commands so that the app doesn't // start again. If we want to be silent then we remove any pending info // commands as well. void GDBController::pauseApp() { setStateOn(s_explicitBreakInto); /* FIXME: need to decide if we really need this, and the consistenly mark info commands as such. int i = cmdList_.count(); while (i) { i--; DbgCommand *cmd = cmdList_.at(i); if (cmd->isAnInfoCmd()) delete cmdList_.take(i); } */ if (dbgProcess_) dbgProcess_->kill(SIGINT); } void GDBController::actOnProgramPauseMI(const GDBMI::ResultRecord& r) { // Is this stop on shared library load? Gdb smartly does not // print any 'reason' field in this case. bool shared_library_load = false; if (currentCmd_) { const TQValueVector& lines = currentCmd_->allStreamOutput(); for(unsigned int i = 0; i < lines.count(); ++i) { if (lines[i].startsWith("Stopped due to shared library event")) { shared_library_load = true; break; } } } if (shared_library_load) { raiseEvent(shared_library_loaded); queueCmd(new GDBCommand("-exec-continue")); return; } if (!r.hasField("reason")) { // FIXME: throw an exception, and add the gdb reply in the // caller. Show message box in the caller, not here. // FIXME: remove this 'bla-bla-bla'. KMessageBox::detailedSorry( 0, i18n("Invalid gdb reply" "

The 'stopped' packet does not include the 'reason' field'."), i18n("The gdb reply is: bla-bla-bla"), i18n("Invalid gdb reply")); return; } TQString reason = r["reason"].literal(); if (reason == "exited-normally" || reason == "exited") { programNoApp("Exited normally", false); programHasExited_ = true; state_reload_needed = false; return; } if (reason == "exited-signalled") { programNoApp(i18n("Exited on signal %1") .arg(r["signal-name"].literal()), false); // FIXME: figure out why this variable is needed. programHasExited_ = true; state_reload_needed = false; return; } if (reason == "watchpoint-scope") { TQString number = r["wpnum"].literal(); // FIXME: shuld remove this watchpoint // But first, we should consider if removing all // watchpoinst on program exit is the right thing to // do. queueCmd(new GDBCommand("-exec-continue")); state_reload_needed = false; return; } if (reason == "signal-received") { TQString name = r["signal-name"].literal(); TQString user_name = r["signal-meaning"].literal(); // SIGINT is a "break into running program". // We do this when the user set/mod/clears a breakpoint but the // application is running. // And the user does this to stop the program also. bool suppress_reporting = false; if (name == "SIGINT" && stateIsOn(s_explicitBreakInto)) { suppress_reporting = true; // TODO: check that we do something reasonable on // implicit break into program (for setting breakpoints, // or whatever). setStateOff(s_explicitBreakInto); emit dbgStatus("Application interrupted", state_); // Will show the source line in the code // handling non-special stop kinds, below. } if (!suppress_reporting) { // Whenever we have a signal raised then tell the user, but don't // end the program as we want to allow the user to look at why the // program has a signal that's caused the prog to stop. // Continuing from SIG FPE/SEGV will cause a "Cannot ..." and // that'll end the program. KMessageBox::information(0, i18n("Program received signal %1 (%2)") .arg(name).arg(user_name), i18n("Received signal")); } } if (reason == "breakpoint-hit") { int id = r["bkptno"].literal().toInt(); emit breakpointHit(id); } } void GDBController::reloadProgramState() { const GDBMI::ResultRecord& r = *last_stop_result; // In gdb 6.3, the *stopped reply does not include full // name of the source file. Need to send extra command. // Don't send it unless there was 'line' field in last *stopped response. // The command has a bug that makes it always returns some file/line, // even if we're not in one. // // FIXME: For gdb 6.4, should not send extra commands. // That's for later, so that I verify that this three-command // approach works fine. if (r.hasField("frame") && r["frame"].hasField("line")) queueCmd(new GDBCommand( "-file-list-exec-source-file", this, &GDBController::handleMiFileListExecSourceFile)); else { maybeAnnounceWatchpointHit(); } emit dbgStatus ("", state_); // We're always at frame zero when the program stops // and we must reset the active flag if (r.hasField("thread-id")) viewedThread_ = r["thread-id"].literal().toInt(); else viewedThread_ = -1; currentFrame_ = 0; raiseEvent(program_state_changed); state_reload_needed = false; } // ************************************************************************** // There is no app anymore. This can be caused by program exiting // an invalid program specified or ... // gdb is still running though, but only the run command (may) make sense // all other commands are disabled. void GDBController::programNoApp(const TQString &msg, bool msgBox) { setState(s_appNotStarted|s_programExited|(state_&s_shuttingDown)); destroyCmds(); // We're always at frame zero when the program stops // and we must reset the active flag viewedThread_ = -1; currentFrame_ = 0; // The application has existed, but it's possible that // some of application output is still in the pipe. We use // different pipes to communicate with gdb and to get application // output, so "exited" message from gdb might have arrived before // last application output. Get this last bit. // Note: this method can be called when we open an invalid // core file. In that case, tty_ won't be set. if (tty_) tty_->readRemaining(); // Tty is no longer usable, delete it. Without this, TQSocketNotifier // will continiously bomd STTY with signals, so we need to either disable // TQSocketNotifier, or delete STTY. The latter is simpler, since we can't // reuse it for future debug sessions anyway. delete tty_; tty_ = 0; raiseEvent(program_exited); if (msgBox) KMessageBox::information(0, i18n("gdb message:\n")+msg,"Warning", "gdb_error"); emit dbgStatus (msg, state_); /* Also show message in gdb window, so that users who prefer to look at gdb window know what's up. */ emit gdbUserCommandStdout(msg.ascii()); } void GDBController::parseCliLine(const TQString& line) { if (line.startsWith("The program no longer exists") || line.startsWith("Program exited") || line.startsWith("Program terminated")) { programNoApp(line, false); return; } #if 0 if (strncmp(buf, "No symbol", 9) == 0 || // watch point failed strncmp(buf, "Single", 6) == 0 || // Single stepping strncmp(buf, "No source file named", 20) == 0 || // breakpoint not set strncmp(buf, "[Switching to Thread", 20) == 0 || // strncmp(buf, "[Thread debugging using", 23) == 0 || strncmp(buf, "Current language:", 17) == 0 || strncmp(buf, "Error while mapping shared library sections:", 44) == 0 || strncmp(buf, "Error while reading shared library symbols:", 43) == 0 || *buf == ':' ) { // We don't change state, because this falls out when a run command // starts rather than when a run command stops. // Or.... it falls out with other messages that _are_ handled. return; } #endif #if 0 /// @todo - Only do this at start up if ( strstr(buf, "not in executable format:") || strstr(buf, "No such file or directory.") || // does this fall out? strstr(buf, i18n("No such file or directory.").local8Bit())|| // from system via gdb strstr(buf, "is not a core dump:") || strncmp(buf, "ptrace: No such process.", 24)==0 || strncmp(buf, "ptrace: Operation not permitted.", 32)==0 || strncmp(buf, "No executable file specified.", 29)==0) { programNoApp(TQString(buf), true); kdDebug(9012) << "Bad file <" << buf << ">" << endl; return; } #endif } void GDBController::handleMiFileListExecSourceFile(const GDBMI::ResultRecord& r) { if (r.reason != "done") { return; // FIXME: throw an exception here. Move reporting // to the caller, who knows the gdb output. #if 0 KMessageBox::information( 0, i18n("Invalid gdb reply\n" "Command was: %1\n" "Response is: %2\n" "Invalid response kind: \"%3\"") .arg(currentCmd_->rawDbgCommand()) .arg(buf) .arg(r.reason), i18n("Invalid gdb reply"), "gdb_error"); #endif } TQString fullname = ""; if (r.hasField("fullname")) fullname = r["fullname"].literal(); showStepInSource(fullname, r["line"].literal().toInt(), (*last_stop_result)["frame"]["addr"].literal()); /* Watchpoint hit is announced only after we've highlighted the current line. */ maybeAnnounceWatchpointHit(); last_stop_result.reset(); } void GDBController::maybeAnnounceWatchpointHit() { /* For some cases, for example catchpoints, gdb does not report any reason at all. */ if ((*last_stop_result).hasField("reason")) { TQString last_stop_reason = (*last_stop_result)["reason"].literal(); if (last_stop_reason == "watchpoint-trigger") { emit watchpointHit((*last_stop_result)["wpt"]["number"] .literal().toInt(), (*last_stop_result)["value"]["old"].literal(), (*last_stop_result)["value"]["new"].literal()); } else if (last_stop_reason == "read-watchpoint-trigger") { emit dbgStatus ("Read watchpoint triggered", state_); } } } void GDBController::handleMiFrameSwitch(const GDBMI::ResultRecord& r) { raiseEvent(thread_or_frame_changed); const GDBMI::Value& frame = r["frame"]; TQString file; if (frame.hasField("fullname")) file = frame["fullname"].literal(); else if (frame.hasField("file")) file = frame["file"].literal(); int line = -1; if (frame.hasField("line")) line = frame["line"].literal().toInt(); showStepInSource(file, line, frame["addr"].literal()); } // ************************************************************************** // SLOTS // ***** // For most of these slots data can only be sent to gdb when it // isn't busy and it is running. // ************************************************************************** bool GDBController::start(const TQString& shell, const DomUtil::PairList& run_envvars, const TQString& run_directory, const TQString &application, const TQString& run_arguments) { kdDebug(9012) << "Starting debugger controller\n"; badCore_ = TQString(); Q_ASSERT (!dbgProcess_ && !tty_); dbgProcess_ = new KProcess; connect( dbgProcess_, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)), this, TQT_SLOT(slotDbgStdout(KProcess *, char *, int)) ); connect( dbgProcess_, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)), this, TQT_SLOT(slotDbgStderr(KProcess *, char *, int)) ); connect( dbgProcess_, TQT_SIGNAL(wroteStdin(KProcess *)), this, TQT_SLOT(slotDbgWroteStdin(KProcess *)) ); connect( dbgProcess_, TQT_SIGNAL(processExited(KProcess*)), this, TQT_SLOT(slotDbgProcessExited(KProcess*)) ); application_ = application; TQString gdb = "gdb"; // Prepend path to gdb, if needed. Using TQDir, // path can either end with slash, or not. if (!config_gdbPath_.isEmpty()) { gdb = config_gdbPath_; } if (!shell.isEmpty()) { *dbgProcess_ << "/bin/sh" << "-c" << shell + " " + gdb + + " " + application + " --interpreter=mi2 -quiet"; emit gdbUserCommandStdout( TQString( "/bin/sh -c " + shell + " " + gdb + " " + application + " --interpreter=mi2 -quiet\n" ).latin1()); } else { *dbgProcess_ << gdb << application << "-interpreter=mi2" << "-quiet"; emit gdbUserCommandStdout( TQString( gdb + " " + application + " --interpreter=mi2 -quiet\n" ).latin1()); } if (!dbgProcess_->start( KProcess::NotifyOnExit, KProcess::Communication(KProcess::All))) { KMessageBox::information( 0, i18n("Could not start debugger." "

Could not run '%1'. " "Make sure that the path name is specified correctly." ).arg(dbgProcess_->args()[0].data()), i18n("Could not start debugger"), "gdb_error"); return false; } setStateOff(s_dbgNotStarted); emit dbgStatus ("", state_); saw_gdb_prompt_ = false; // Initialise gdb. At this stage gdb is sitting wondering what to do, // and to whom. Organise a few things, then set up the tty for the application, // and the application itself // The following two are not necessary in MI, and the first one // just breaks MI completely. // queueCmd(new GDBCommand("set edit off", NOTRUNCMD, NOTINFOCMD, 0)); // queueCmd(new GDBCommand("set confirm off", NOTRUNCMD, NOTINFOCMD)); if (config_displayStaticMembers_) queueCmd(new GDBCommand("set print static-members on")); else queueCmd(new GDBCommand("set print static-members off")); // This makes gdb pump a variable out on one line. queueCmd(new GDBCommand("set width 0")); queueCmd(new GDBCommand("set height 0")); queueCmd(new GDBCommand("handle SIG32 pass nostop noprint")); queueCmd(new GDBCommand("handle SIG41 pass nostop noprint")); queueCmd(new GDBCommand("handle SIG42 pass nostop noprint")); queueCmd(new GDBCommand("handle SIG43 pass nostop noprint")); // Print some nicer names in disassembly output. Although for an assembler // person this may actually be wrong and the mangled name could be better. if (config_asmDemangle_) queueCmd(new GDBCommand("set print asm-demangle on")); else queueCmd(new GDBCommand("set print asm-demangle off")); // make sure output radix is always set to users view. queueCmd(new GDBCommand(TQCString().sprintf("set output-radix %d", config_outputRadix_))); // Change the "Working directory" to the correct one TQCString tmp( "cd " + TQFile::encodeName( run_directory )); queueCmd(new GDBCommand(tmp)); // Set the run arguments if (!run_arguments.isEmpty()) queueCmd( new GDBCommand(TQCString("set args ") + run_arguments.local8Bit())); // Get the run environment variables pairs into the environstr string // in the form of: "ENV_VARIABLE=ENV_VALUE" and send to gdb using the // "set enviroment" command // Note that we quote the variable value due to the possibility of // embedded spaces TQString environstr; DomUtil::PairList::ConstIterator it; for (it = run_envvars.begin(); it != run_envvars.end(); ++it) { environstr = "set environment "; environstr += (*it).first; environstr += "="; environstr += (*it).second; queueCmd(new GDBCommand(environstr.latin1())); } queueCmd(new GDBCommand( "-list-features", this, &GDBController::handleListFeatures, true /* handles error */)); queueCmd(new SentinelCommand(this, &GDBController::startDone)); // Now gdb has been started and the application has been loaded, // BUT the app hasn't been started yet! A run command is about to be issued // by whoever is controlling us. Or we might be asked to load a core, or // attach to a running process. return true; } void GDBController::startDone() { // Needed so that breakpoint widget has a chance to insert breakpoints. // FIXME: a bit hacky, as we're really not ready for new commands. setStateOn(s_dbgBusy); raiseEvent(debugger_ready); raiseEvent(connected_to_program); } void GDBController::handleListFeatures(const GDBMI::ResultRecord& r) { mi_pending_breakpoints_ = false; if (r.reason == "done") { const GDBMI::Value& features = r["features"]; for (unsigned i = 0; i < features.size(); ++i) if (features[i].literal() == "pending-breakpoints") { mi_pending_breakpoints_ = true; } } if (!mi_pending_breakpoints_) { // This version of GDB does not support pending breakpoints in MI, // so use a workaround. // The below command makes GDB notify us about shared library // events, and on each stop we'll try to set breakpoint again. addCommandToFront(new GDBCommand("set stop-on-solib-events 1")); } } // ************************************************************************** void GDBController::slotStopDebugger() { kdDebug(9012) << "GDBController::slotStopDebugger() called" << endl; if (stateIsOn(s_shuttingDown) || !dbgProcess_) return; setStateOn(s_shuttingDown); kdDebug(9012) << "GDBController::slotStopDebugger() executing" << endl; TQTime start; TQTime now; // Get gdb's attention if it's busy. We need gdb to be at the // command line so we can stop it. if (stateIsOn(s_dbgBusy)) { kdDebug(9012) << "gdb busy on shutdown - stopping gdb (SIGINT)" << endl; dbgProcess_->kill(SIGINT); start = TQTime::currentTime(); while (-1) { kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 20 ); now = TQTime::currentTime(); if (!stateIsOn(s_dbgBusy) || start.msecsTo( now ) > 2000) break; } } // If the app is attached then we release it here. This doesn't stop // the app running. if (stateIsOn(s_attached)) { const char *detach="detach\n"; if (!dbgProcess_->writeStdin(detach, strlen(detach))) kdDebug(9012) << "failed to write 'detach' to gdb" << endl; emit gdbUserCommandStdout("(gdb) detach\n"); start = TQTime::currentTime(); while (-1) { kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 20 ); now = TQTime::currentTime(); if (!stateIsOn(s_attached) || start.msecsTo( now ) > 2000) break; } } // Now try to stop gdb running. const char *quit="quit\n"; if (!dbgProcess_->writeStdin(quit, strlen(quit))) kdDebug(9012) << "failed to write 'quit' to gdb" << endl; emit gdbUserCommandStdout("(gdb) quit"); start = TQTime::currentTime(); while (-1) { kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 20 ); now = TQTime::currentTime(); if (stateIsOn(s_programExited) || start.msecsTo( now ) > 2000) break; } // We cannot wait forever. if (!stateIsOn(s_programExited)) { kdDebug(9012) << "gdb not shutdown - killing" << endl; dbgProcess_->kill(SIGKILL); } destroyCmds(); delete dbgProcess_; dbgProcess_ = 0; delete tty_; tty_ = 0; // The gdb output buffer might contain start marker of some // previously issued command that crashed gdb (so there's no end marker) // If we don't clear this, then after restart, we'll be trying to search // for the end marker of the command issued in previous gdb session, // and never succeed. gdbOutput_ = ""; setState(s_dbgNotStarted | s_appNotStarted); emit dbgStatus (i18n("Debugger stopped"), state_); raiseEvent(debugger_exited); } // ************************************************************************** void GDBController::slotCoreFile(const TQString &coreFile) { setStateOff(s_programExited|s_appNotStarted); setStateOn(s_core); queueCmd(new GDBCommand(TQCString("core ") + coreFile.latin1())); raiseEvent(connected_to_program); raiseEvent(program_state_changed); } // ************************************************************************** void GDBController::slotAttachTo(int pid) { setStateOff(s_appNotStarted|s_programExited); setStateOn(s_attached); // Currently, we always start debugger with a name of binary, // we might be connecting to a different binary completely, // so cancel all symbol tables gdb has. // We can't omit application name from gdb invocation // because for libtool binaries, we have no way to guess // real binary name. queueCmd(new GDBCommand(TQString("file"))); // The MI interface does not implements -target-attach yet, // and we don't recognize whatever gibberish 'attach' pours out, so... queueCmd(new GDBCommand( TQCString().sprintf("attach %d", pid))); raiseEvent(connected_to_program); // ...emit a separate MI command to step one instruction more. We'll // notice the '*stopped' response from it and proceed as usual. queueCmd(new GDBCommand("-exec-step-instruction")); } // ************************************************************************** void GDBController::slotRun() { if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) return; if (stateIsOn(s_appNotStarted)) { delete tty_; tty_ = new STTY(config_dbgTerminal_, Settings::terminalEmulatorName( *kapp->config() )); if (!config_dbgTerminal_) { connect( tty_, TQT_SIGNAL(OutOutput(const char*)), TQT_SIGNAL(ttyStdout(const char*)) ); connect( tty_, TQT_SIGNAL(ErrOutput(const char*)), TQT_SIGNAL(ttyStderr(const char*)) ); } TQString tty(tty_->getSlave()); if (tty.isEmpty()) { KMessageBox::information(0, i18n("GDB cannot use the tty* or pty* devices.\n" "Check the settings on /dev/tty* and /dev/pty*\n" "As root you may need to \"chmod ug+rw\" tty* and pty* devices " "and/or add the user to the tty group using " "\"usermod -G tty username\"."), "Warning", "gdb_error"); delete tty_; tty_ = 0; return; } queueCmd(new GDBCommand(TQCString("tty ")+tty.latin1())); if (!config_runShellScript_.isEmpty()) { // Special for remote debug... TQCString tty(tty_->getSlave().latin1()); TQCString options = TQCString(">") + tty + TQCString(" 2>&1 <") + tty; KProcess *proc = new KProcess; *proc << "sh" << "-c"; *proc << config_runShellScript_ + " " + application_.latin1() + options; proc->start(KProcess::DontCare); } if (!config_runGdbScript_.isEmpty()) {// gdb script at run is requested // Race notice: wait for the remote gdbserver/executable // - but that might be an issue for this script to handle... // Future: the shell script should be able to pass info (like pid) // to the gdb script... queueCmd(new GDBCommand("source " + config_runGdbScript_)); // Note: script could contain "run" or "continue" } else { TQFileInfo app(application_); if (!app.exists()) { KMessageBox::error( 0, i18n("Application does not exist" "

The application you are trying to debug,
" " %1\n" "
does not exist. Check that you have specified " "the right application in the debugger configuration." ).arg(app.fileName()), i18n("Application does not exist")); // FIXME: after this, KDevelop will still show that debugger // is running, because DebuggerPart::slotStopDebugger won't be // called, and core()->running(this, false) won't be called too. slotStopDebugger(); return; } if (!app.isExecutable()) { KMessageBox::error( 0, i18n("Could not run application '%1'." "

The application does not have the executable bit set. " "Try rebuilding the project, or change permissions " "manually." ).arg(app.fileName()), i18n("Could not run application")); slotStopDebugger(); } else { GDBCommand *cmd = new GDBCommand("-exec-run"); cmd->setRun(true); queueCmd(cmd); } } } else { removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-continue")); } setStateOff(s_appNotStarted|s_programExited); } void GDBController::slotKill() { if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) return; if (stateIsOn(s_dbgBusy)) { pauseApp(); } queueCmd(new GDBCommand("kill")); setStateOn(s_appNotStarted); } // ************************************************************************** void GDBController::slotRunUntil(const TQString &fileName, int lineNum) { if (stateIsOn(s_dbgBusy|s_dbgNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); if (fileName.isEmpty()) queueCmd(new GDBCommand( TQCString().sprintf("-exec-until %d", lineNum))); else queueCmd(new GDBCommand( TQCString(). sprintf("-exec-until %s:%d", fileName.latin1(), lineNum))); } // ************************************************************************** void GDBController::slotJumpTo(const TQString &fileName, int lineNum) { if (stateIsOn(s_dbgBusy|s_dbgNotStarted|s_shuttingDown)) return; if (!fileName.isEmpty()) { queueCmd(new GDBCommand(TQCString().sprintf("tbreak %s:%d", fileName.latin1(), lineNum))); queueCmd(new GDBCommand(TQCString().sprintf("jump %s:%d", fileName.latin1(), lineNum))); } } // ************************************************************************** void GDBController::slotStepInto() { if (stateIsOn(s_dbgBusy|s_appNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-step")); } // ************************************************************************** void GDBController::slotStepIntoIns() { if (stateIsOn(s_dbgBusy|s_appNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-step-instruction")); } // ************************************************************************** void GDBController::slotStepOver() { if (stateIsOn(s_dbgBusy|s_appNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-next")); } // ************************************************************************** void GDBController::slotStepOverIns() { if (stateIsOn(s_dbgBusy|s_appNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-next-instruction")); } // ************************************************************************** void GDBController::slotStepOutOff() { if (stateIsOn(s_dbgBusy|s_appNotStarted|s_shuttingDown)) return; removeStateReloadingCommands(); queueCmd(new GDBCommand("-exec-finish")); } // ************************************************************************** // Only interrupt a running program. void GDBController::slotBreakInto() { pauseApp(); } // ************************************************************************** void GDBController::selectFrame(int frameNo, int threadNo) { // FIXME: this either should be removed completely, or // trigger an error message. if (stateIsOn(s_dbgBusy|s_dbgNotStarted|s_shuttingDown)) return; if (threadNo != -1) { if (viewedThread_ != threadNo) queueCmd(new GDBCommand( TQString("-thread-select %1").arg(threadNo).ascii())); } queueCmd(new GDBCommand( TQString("-stack-select-frame %1").arg(frameNo).ascii())); // Will emit the 'thread_or_frame_changed' event. queueCmd(new GDBCommand("-stack-info-frame", this, &GDBController::handleMiFrameSwitch)); // FIXME: the above commands might not be the first in queue, and // previous commands might using values of 'viewedThread_' or // 'currentFrame_'. Ideally, should change the values only after // response from gdb. viewedThread_ = threadNo; currentFrame_ = frameNo; } // ************************************************************************** void GDBController::defaultErrorHandler(const GDBMI::ResultRecord& result) { TQString msg = result["msg"].literal(); if (msg.contains("No such process")) { setState(s_appNotStarted|s_programExited); emit dbgStatus (i18n("Process exited"), state_); raiseEvent(program_exited); return; } KMessageBox::information( 0, i18n("Debugger error" "

Debugger reported the following error:" "

") + result["msg"].literal(), i18n("Debugger error"), "gdb_error"); // Error most likely means that some change made in GUI // was not communicated to the gdb, so GUI is now not // in sync with gdb. Resync it. // // Another approach is to make each widget reload it content // on errors from commands that it sent, but that's too complex. // Errors are supposed to happen rarely, so full reload on error // is not a big deal. Well, maybe except for memory view, but // it's no auto-reloaded anyway. // // Also, don't reload state on errors appeared during state // reloading! if (stateReloadingCommands_.count(currentCmd_) == 0) raiseEvent(program_state_changed); } void GDBController::processMICommandResponse(const GDBMI::ResultRecord& result) { kdDebug(9012) << "MI stop reason " << result.reason << "\n"; if (result.reason == "stopped") { actOnProgramPauseMI(result); } else if (result.reason == "done") { // At least in one case, for 'detach', debuger write // command directly, and 'currentCmd_' will be unset. // Checking for currentCmd_ is safer in any case. if (currentCmd_) { // Assume that if this command is part of state reloading, // then any further commands issued in command handler // are part of state reloading as well. if (stateReloadingCommands_.count(currentCmd_)) { stateReloadInProgress_ = true; } currentCmd_->invokeHandler(result); stateReloadInProgress_ = false; } } else if (result.reason == "error") { // Some commands want to handle errors themself. if (currentCmd_ && currentCmd_->handlesError() && currentCmd_->invokeHandler(result)) { // Done, nothing more needed } else { defaultErrorHandler(result); } } } // Data from gdb gets processed here. void GDBController::slotDbgStdout(KProcess *, char *buf, int buflen) { static bool parsing = false; TQCString msg(buf, buflen+1); // Copy the data out of the KProcess buffer before it gets overwritten // Append to the back of the holding zone. holdingZone_ += TQCString(buf, buflen+1); // Already parsing? then get out quick. // VP, 2006-01-30. I'm not sure how this could happen, since // parsing of gdb reply should not ever execute TQt message loop. Except, // maybe, when we pop up a message box. But even in that case, // it's likely we won't return to slotDbgStdout again. if (parsing) { kdDebug(9012) << "Already parsing" << endl; return; } bool ready_for_next_command = false; int i; bool got_any_command = false; // For each gdb reply. In MI mode, each reply is one string. while((i = holdingZone_.find('\n')) != -1) { got_any_command = true; TQCString reply(holdingZone_.left(i)); holdingZone_ = holdingZone_.mid(i+1); kdDebug(9012) << "REPLY: " << reply << "\n"; FileSymbol file; file.contents = reply; std::auto_ptr r(mi_parser_.parse(&file)); if (r.get() == 0) { // FIXME: Issue an error! kdDebug(9012) << "Invalid MI message: " << reply << "\n"; ready_for_next_command = true; continue; } try { switch(r->kind) { case GDBMI::Record::Result: { GDBMI::ResultRecord& result = static_cast(*r); if (result.reason != "running") { kdDebug(9012) << "Command execution time " << commandExecutionTime.elapsed() << " ms.\n"; } /* The currentCmd_ may be NULL here, because when detaching from debugger, we directly write "detach" to gdb and busy-wait for a reply. Uisng the commands mechanism won't work there, because command result are communicated asynchronously. This is will be fixed in KDevelop4. */ if (currentCmd_ && currentCmd_->isUserCommand()) emit gdbUserCommandStdout(reply); else emit gdbInternalCommandStdout(reply + "\n"); if (result.reason == "stopped") { // Transfers ownership. // Needed so that in // handleMiFileListExecSourceFile(GDBMI::ResultRecord& r); // we can use the last stop reason. last_stop_result.reset(static_cast(r.get())); r.release(); state_reload_needed = true; } else if (result.reason == "running") { setStateOn(s_appRunning); raiseEvent(program_running); } // All MI commands have just one response, except for // run-like command, which result in // // ^running // // followed by // // stopped. ready_for_next_command = (result.reason != "running"); if (ready_for_next_command) { // Need to do this before procesing response, // so that when processing response we don't // think that application is running. setStateOff(s_appRunning); } processMICommandResponse(result); if (ready_for_next_command) { destroyCurrentCommand(); } break; } case GDBMI::Record::Stream: { GDBMI::StreamRecord& s = dynamic_cast(*r); /* The way current code works is that we start gdb, and immediately send commands to it without waiting for a prompt. As result, when we return back to the event loop and read the first line from GDB, currentCmd_ is already set. But really, we want to output everything that gdb prints prior to prompt -- it might be output from user's .gdbinit that user cares about. */ if (!saw_gdb_prompt_ || !currentCmd_ || currentCmd_->isUserCommand()) emit gdbUserCommandStdout(s.message.ascii()); else emit gdbInternalCommandStdout(s.message.ascii()); if (currentCmd_) currentCmd_->newOutput(s.message); parseCliLine(s.message); static TQRegExp print_output("^\\$(\\d+) = "); if (print_output.search(s.message) != -1) { kdDebug(9012) << "Found 'print' output: " << s.message << "\n"; print_command_result = s.message.ascii(); } /* This is output from the program. Route it to the Application window. */ if (s.reason == '@') emit ttyStderr(s.message.ascii()); break; } case GDBMI::Record::Prompt: saw_gdb_prompt_ = true; break; } } catch(const std::exception& e) { KMessageBox::detailedSorry( 0, i18n("Internal debugger error", "

The debugger component encountered an internal error while " "processing a reply from gdb. Please submit a bug report."), i18n("The exception is: %1\n" "The MI response is: %2").arg(e.what()).arg(reply.data()), i18n("Internal debugger error")); destroyCurrentCommand(); ready_for_next_command = true; } } // check the queue for any commands to send if (ready_for_next_command) { executeCmd(); } if (got_any_command) kdDebug(9012) << "COMMANDS: " << cmdList_.count() << " in queue, " << int(bool(currentCmd_)) << " executing\n"; commandDone(); } void GDBController::commandDone() { bool no_more_commands = (cmdList_.isEmpty() && !currentCmd_); if (no_more_commands && state_reload_needed) { kdDebug(9012) << "Finishing program stop\n"; // Set to false right now, so that if 'actOnProgramPauseMI_part2' // sends some commands, we won't call it again when handling replies // from that commands. state_reload_needed = false; reloadProgramState(); } if (no_more_commands) { kdDebug(9012) << "No more commands\n"; setStateOff(s_dbgBusy); emit dbgStatus("", state_); raiseEvent(debugger_ready); } } void GDBController::destroyCurrentCommand() { stateReloadingCommands_.erase(currentCmd_); delete currentCmd_; currentCmd_ = 0; } void GDBController::removeStateReloadingCommands() { int i = cmdList_.count(); while (i) { i--; GDBCommand* cmd = cmdList_.at(i); if (stateReloadingCommands_.count(cmd)) { kdDebug(9012) << "UNTQUEUE: " << cmd->initialString() << "\n"; delete cmdList_.take(i); } } if (stateReloadingCommands_.count(currentCmd_)) { // This effectively prevents handler for this command // to be ever invoked. destroyCurrentCommand(); } } void GDBController::raiseEvent(event_t e) { if (e == program_exited || e == debugger_exited) { stateReloadInProgress_ = false; } if (e == program_state_changed) { stateReloadInProgress_ = true; kdDebug(9012) << "State reload in progress\n"; } emit event(e); if (e == program_state_changed) { stateReloadInProgress_ = false; } } void GDBController::slotDbgStderr(KProcess *proc, char *buf, int buflen) { // At the moment, just drop a message out and redirect kdDebug(9012) << "STDERR: " << TQString::fromLatin1(buf, buflen+1) << endl; slotDbgStdout(proc, buf, buflen); } // ************************************************************************** void GDBController::slotDbgWroteStdin(KProcess *) { commandExecutionTime.start(); setStateOff(s_waitForWrite); // FIXME: need to remove s_waitForWrite flag completely. executeCmd(); } // ************************************************************************** void GDBController::slotDbgProcessExited(KProcess* process) { Q_ASSERT(process == dbgProcess_); bool abnormal = !process->normalExit(); delete dbgProcess_; dbgProcess_ = 0; delete tty_; tty_ = 0; if (abnormal) emit debuggerAbnormalExit(); raiseEvent(debugger_exited); destroyCmds(); setState(s_dbgNotStarted|s_appNotStarted|s_programExited); emit dbgStatus (i18n("Process exited"), state_); emit gdbUserCommandStdout("(gdb) Process exited\n"); } // ************************************************************************** void GDBController::slotUserGDBCmd(const TQString& cmd) { queueCmd(new UserCommand(cmd.latin1())); // User command can theoreticall modify absolutely everything, // so need to force a reload. // We can do it right now, and don't wait for user command to finish // since commands used to reload all view will be executed after // user command anyway. //if (!stateIsOn(s_appNotStarted) && !stateIsOn(s_programExited)) // raiseEvent(program_state_changed); } void GDBController::explainDebuggerStatus() { TQString information("%1 commands in queue\n" "%2 commands being processed by gdb\n" "Debugger state: %3\n"); information = information.arg(cmdList_.count()).arg(currentCmd_ ? 1 : 0) .arg(state_); if (currentCmd_) { TQString extra("Current command class: '%1'\n" "Current command text: '%2'\n" "Current command origianl text: '%3'\n"); extra = extra.arg( typeid(*currentCmd_).name()).arg(currentCmd_->cmdToSend()). arg(currentCmd_->initialString()); information += extra; } KMessageBox::information(0, information, "Debugger status"); } bool GDBController::stateIsOn(int state) { return state_ & state; } void GDBController::setStateOn(int stateOn) { debugStateChange(state_, state_ | stateOn); state_ |= stateOn; } void GDBController::setStateOff(int stateOff) { debugStateChange(state_, state_ & ~stateOff); state_ &= ~stateOff; } void GDBController::setState(int newState) { debugStateChange(state_, newState); state_ = newState; } void GDBController::debugStateChange(int oldState, int newState) { int delta = oldState ^ newState; if (delta) { TQString out = "STATE: "; for(unsigned i = 1; i < s_lastDbgState; i <<= 1) { if (delta & i) { if (i & newState) out += "+"; else out += "-"; bool found = false; #define STATE_CHECK(name)\ if (i == name) { out += #name; found = true; } STATE_CHECK(s_dbgNotStarted); STATE_CHECK(s_appNotStarted); STATE_CHECK(s_waitForWrite); STATE_CHECK(s_programExited); STATE_CHECK(s_viewBT); STATE_CHECK(s_viewBP); STATE_CHECK(s_attached); STATE_CHECK(s_core); STATE_CHECK(s_waitTimer); STATE_CHECK(s_shuttingDown); STATE_CHECK(s_explicitBreakInto); STATE_CHECK(s_dbgBusy); STATE_CHECK(s_appRunning); #undef STATE_CHECK if (!found) out += TQString::number(i); out += " "; } } kdDebug(9012) << out << "\n"; } } int GDBController::qtVersion( ) const { return DomUtil::readIntEntry( dom, "/kdevcppsupport/qt/version", 3 ); } void GDBController::demandAttention() const { if ( TQWidget * w = kapp->mainWidget() ) { KWin::demandAttention( w->winId(), true ); } } bool GDBController::miPendingBreakpoints() const { return mi_pending_breakpoints_; } } // ************************************************************************** // ************************************************************************** // ************************************************************************** #include "gdbcontroller.moc"