summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/gdbcontroller.h
blob: 6a9e3385f760c47bdf5ae7e06dd552c9fe1e349d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/***************************************************************************
                          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 <qcstring.h>
#include <qdom.h>
#include <qobject.h>
#include <qptrlist.h>
#include <qstring.h>
#include <qmap.h>
#include <qdatetime.h>

#include <memory>
#include <set>

class KProcess;

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(QDomDocument &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 QString& 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 QString& shell, 
               const DomUtil::PairList& run_envvars, 
               const QString& run_directory, 
               const QString &application, 
               const QString& 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 QString&);

    /** 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 QString &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 QString& shell, const QString &application);
    void slotCoreFile(const QString &coreFile);
    void slotAttachTo(int pid);

    void slotStopDebugger();

    void slotRun();
    void slotKill();
    void slotRunUntil(const QString &filename, int lineNum);
    void slotJumpTo(const QString &filename, int lineNum);
    void slotStepInto();
    void slotStepOver();
    void slotStepIntoIns();
    void slotStepOverIns();
    void slotStepOutOff();

    void slotBreakInto();

    void slotUserGDBCmd(const QString&);

    // 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(KProcess *proc, char *buf, int buflen);
    void slotDbgStderr(KProcess *proc, char *buf, int buflen);
    void slotDbgWroteStdin(KProcess *proc);
    void slotDbgProcessExited(KProcess *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 QString& oldValue, const QString& newValue);

private:
    int               currentFrame_;
    int               viewedThread_;

    // The output from gdb that was not parsed yet
    QCString          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.
    QCString          holdingZone_;

    QPtrList<GDBCommand> cmdList_;
    GDBCommand*       currentCmd_;

    STTY*             tty_;
    QString           badCore_;
    QString           application_;

    // Gdb command that should be issued when we stop on breakpoint
    // with the given gdb breakpoint id.
    QMap<int, const Breakpoint*> tracedBreakpoints_;

    // Some state variables
    int               state_;
    bool              programHasExited_;

    // Configuration values
    QDomDocument &dom;
    bool    config_breakOnLoadingLibrary_;
    bool    config_forceBPSet_;
    bool    config_displayStaticMembers_;
    bool    config_asmDemangle_;
    bool    config_dbgTerminal_;
    QString config_gdbPath_;
    QString config_dbgShell_;
    QCString config_configGdbScript_;
    QCString config_runShellScript_;
    QCString 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<GDBMI::ResultRecord> 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.
    QCString print_command_result;

    bool state_reload_needed;

    QTime commandExecutionTime;

    bool stateReloadInProgress_;

    /** Commands issues as result of the 'program_state_changed'
        event. */
    std::set<GDBCommand*> stateReloadingCommands_;

    bool saw_gdb_prompt_;

    /** Does the used GDB supports pending breakpoints in MI? */
    bool mi_pending_breakpoints_;
};

}

#endif