/*************************************************************************** gdbcontroller.h - 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. * * * ***************************************************************************/ #ifndef _GDBCONTROLLER_H_ #define _GDBCONTROLLER_H_ #include "dbgcontroller.h" #include "mi/gdbmi.h" #include "mi/miparser.h" #include #include #include #include #include #include #include #include #include class TDEProcess; namespace GDBDebugger { class Breakpoint; class DbgCommand; class GDBCommand; class VarItem; class STTY; /** * A front end implementation to the gdb command line debugger * @author jbb */ class GDBController : public DbgController { Q_OBJECT public: GDBController(TQDomDocument &projectDom); ~GDBController(); enum event_t { program_state_changed = 1, program_exited, debugger_exited, thread_or_frame_changed, debugger_busy, debugger_ready, shared_library_loaded, // Raised when debugger believe that program start running. // Can be used to hide current line indicator. // Don't count on this being raise in all cases where // program is running. program_running, connected_to_program }; /** Adds a command to the end of queue of commands to be executed by gdb. The command will be actually sent to gdb only when replies from all previous commands are received and full processed. The literal command sent to gdb is obtained by calling cmd->cmdToSend. The call is made immediately before sending the command, so it's possible to use results of prior commands when computing the exact command to send. */ void addCommand(GDBCommand* cmd); /** Same as above, but internally constructs new GDBCommand instance from the string. */ void addCommand(const TQString& cmd); /** Adds command to the front of the commands queue. It will be executed next. This is usefull to implement 'atomic' command sequences. For example, if one wants to switch to each thread in turn, asking gdb where that thread stand, this should never be interrupted by other command, since other commands might not expect that thread magically changes. In this specific case, first -thread-list-ids commands is issued. The handler for reply will add a number of command to front, and it will guarantee that no other command will get in between. Note that if you call: addCommandToFront(cmd1); addCommandToFront(cmd2); The execution order will be 'cmd2', then 'cmd1'. FIXME: is not used for now, maybe remove. */ void addCommandToFront(GDBCommand* cmd); /* If current command queue has any command for which isRun is true, inserts 'cmd' before the first such command. Otherwise, works the same as addCommand. */ void addCommandBeforeRun(GDBCommand* cmd); /** Selects the specified thread/frame. Immediately emits thread_or_frame_changed event. */ void selectFrame(int frameNo, int threadNo); /** Returns the numerical identfier of the current thread, or -1 if the program is not threaded (i.e. there's just one thread. */ int currentThread() const; int currentFrame() const; bool start(const TQString& shell, const DomUtil::PairList& run_envvars, const TQString& run_directory, const TQString &application, const TQString& run_arguments); int qtVersion() const; /** * Call this when something very interesting happens that the user * might be unaware of. It will make KDevelop's taskbar entry flash * if the application doesn't already have focus. * Typical use case: The debugger has stopped on a breakpoint. */ void demandAttention() const; void pauseApp(); bool miPendingBreakpoints() const; protected: enum queue_where { queue_at_end, queue_at_front, queue_before_run }; void queueCmd(GDBCommand *cmd, enum queue_where queue_where = queue_at_end); private: void parseLocals (char type, char *buf); /** Parses the CLI output line, and catches interesting messages like "Program exited". This is intended to allow using console commands in the gdb window despite the fact that GDB does not produce right MI notification for CLI commands. I.e. if you run "continue" there will be no MI message if the application has exited. */ void parseCliLine (const TQString&); /** Handles a result response from a MI command -- that is all MI responses except for Stream and Prompt responses. Uses currentCmd to decide what to do with response and calls appropriate method. */ void processMICommandResponse(const GDBMI::ResultRecord& r); void handleMiFileListExecSourceFile(const GDBMI::ResultRecord& r); /** Handles reply from -stack-info-frame command issues after switching the stack frame. */ void handleMiFrameSwitch(const GDBMI::ResultRecord& r); void executeCmd (); void destroyCmds(); void removeInfoRequests(); void actOnProgramPauseMI(const GDBMI::ResultRecord& mi_record); /** Called when there are no pending commands and 'state_reload_needed' is true. Issues commands to completely reload all program state shown to the user. */ void reloadProgramState(); void programNoApp(const TQString &msg, bool msgBox); void setStateOn(int stateOn); void setStateOff(int stateOff); void setState(int newState); void debugStateChange(int oldState, int newState); void commandDone(); void destroyCurrentCommand(); /** Removes all 'stateReloading' commands from the queue. */ void removeStateReloadingCommands(); /** Raises the specified event. Should be used instead of emitting 'event' directly, since this method can perform additional book-keeping for events. */ void raiseEvent(event_t e); void maybeAnnounceWatchpointHit(); void handleListFeatures(const GDBMI::ResultRecord& result); void startDone(); /** Default handler for errors. Tries to guess is the error message is telling that target is gone, if so, informs the user. Otherwise, shows a dialog box and reloads view state. */ void defaultErrorHandler(const GDBMI::ResultRecord& result); public: bool stateIsOn(int state); public slots: void configure(); //void slotStart(const TQString& shell, const TQString &application); void slotCoreFile(const TQString &coreFile); void slotAttachTo(int pid); void slotStopDebugger(); void slotRun(); void slotKill(); void slotRunUntil(const TQString &filename, int lineNum); void slotJumpTo(const TQString &filename, int lineNum); void slotStepInto(); void slotStepOver(); void slotStepIntoIns(); void slotStepOverIns(); void slotStepOutOff(); void slotBreakInto(); void slotUserGDBCmd(const TQString&); // Pops up a dialog box with some hopefully // detailed information about which state debugger // is in, which commands were sent and so on. void explainDebuggerStatus(); protected slots: void slotDbgStdout(TDEProcess *proc, char *buf, int buflen); void slotDbgStderr(TDEProcess *proc, char *buf, int buflen); void slotDbgWroteStdin(TDEProcess *proc); void slotDbgProcessExited(TDEProcess *proc); signals: /** This signal is emitted whenever the given event in a program happens. See DESIGN.txt for expected handled of each event. NOTE: this signal should never be emitted directly. Instead, use raiseEvent. */ void event(GDBController::event_t e); void debuggerAbnormalExit(); /** Emitted immediately after breakpoint is hit, before any commands are sent and before current line indicator is shown. */ void breakpointHit(int id); /** Emitted for watchpoint hit, after line indicator is shown. */ void watchpointHit(int id, const TQString& oldValue, const TQString& newValue); private: int currentFrame_; int viewedThread_; // The output from gdb that was not parsed yet TQCString gdbOutput_; // The output from gdb that arrived where we was // parsing the previous output. To avoid messing // things up, it's not directly added to // gdbOutput_ but stored for future use. // VP: It's not clear why the previous code was doing // this, and holdingZone_ won't be processed until // next output arrives, so probably should be just removed. TQCString holdingZone_; TQPtrList cmdList_; GDBCommand* currentCmd_; STTY* tty_; TQString badCore_; TQString application_; // Gdb command that should be issued when we stop on breakpoint // with the given gdb breakpoint id. TQMap tracedBreakpoints_; // Some state variables int state_; bool programHasExited_; // Configuration values TQDomDocument &dom; bool config_breakOnLoadingLibrary_; bool config_forceBPSet_; bool config_displayStaticMembers_; bool config_asmDemangle_; bool config_dbgTerminal_; TQString config_gdbPath_; TQString config_dbgShell_; TQCString config_configGdbScript_; TQCString config_runShellScript_; TQCString config_runGdbScript_; int config_outputRadix_; MIParser mi_parser_; // As of gdb 6.3, the *stopped packet does not contain // full file name. So we need to send another command to // fetch that, to highlight current line. // After highting current line we need to do something more, // like announcing write watchpoints, and so need to have // access to the stop packet. So store it here. std::auto_ptr last_stop_result; // Gdb 6.4 (and 6.3) does not support "character" format with MI, // so the only way it can work is via the "print" command. As gdb // outputs things, we'll grep for lines that look like output from // print, and store such lines in this variable, so later use. TQCString print_command_result; bool state_reload_needed; TQTime commandExecutionTime; bool stateReloadInProgress_; /** Commands issues as result of the 'program_state_changed' event. */ std::set stateReloadingCommands_; bool saw_gdb_prompt_; /** Does the used GDB supports pending breakpoints in MI? */ bool mi_pending_breakpoints_; }; } #endif