summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger')
-rw-r--r--languages/cpp/debugger/DESIGN.txt113
-rw-r--r--languages/cpp/debugger/Makefile.am33
-rw-r--r--languages/cpp/debugger/TODO.txt218
-rw-r--r--languages/cpp/debugger/breakpoint.cpp719
-rw-r--r--languages/cpp/debugger/breakpoint.h313
-rw-r--r--languages/cpp/debugger/dbgcontroller.cpp40
-rw-r--r--languages/cpp/debugger/dbgcontroller.h128
-rw-r--r--languages/cpp/debugger/dbgpsdlg.cpp203
-rw-r--r--languages/cpp/debugger/dbgpsdlg.h59
-rw-r--r--languages/cpp/debugger/dbgtoolbar.cpp498
-rw-r--r--languages/cpp/debugger/dbgtoolbar.h85
-rw-r--r--languages/cpp/debugger/debuggerconfigwidget.cpp138
-rw-r--r--languages/cpp/debugger/debuggerconfigwidget.h43
-rw-r--r--languages/cpp/debugger/debuggerconfigwidgetbase.ui453
-rw-r--r--languages/cpp/debugger/debuggerdcopinterface.h29
-rw-r--r--languages/cpp/debugger/debuggerpart.cpp1272
-rw-r--r--languages/cpp/debugger/debuggerpart.h164
-rw-r--r--languages/cpp/debugger/debuggertracingdialog.cpp104
-rw-r--r--languages/cpp/debugger/debuggertracingdialog.h42
-rw-r--r--languages/cpp/debugger/debuggertracingdialogbase.ui141
-rw-r--r--languages/cpp/debugger/disassemblewidget.cpp173
-rw-r--r--languages/cpp/debugger/disassemblewidget.h69
-rw-r--r--languages/cpp/debugger/framestackwidget.cpp645
-rw-r--r--languages/cpp/debugger/framestackwidget.h183
-rw-r--r--languages/cpp/debugger/gdbbreakpointwidget.cpp1262
-rw-r--r--languages/cpp/debugger/gdbbreakpointwidget.h174
-rw-r--r--languages/cpp/debugger/gdbcommand.cpp142
-rw-r--r--languages/cpp/debugger/gdbcommand.h271
-rw-r--r--languages/cpp/debugger/gdbcontroller.cpp1860
-rw-r--r--languages/cpp/debugger/gdbcontroller.h358
-rw-r--r--languages/cpp/debugger/gdboutputwidget.cpp376
-rw-r--r--languages/cpp/debugger/gdboutputwidget.h137
-rw-r--r--languages/cpp/debugger/gdbparser.cpp432
-rw-r--r--languages/cpp/debugger/gdbparser.h59
-rw-r--r--languages/cpp/debugger/gdbtable.cpp55
-rw-r--r--languages/cpp/debugger/gdbtable.h39
-rw-r--r--languages/cpp/debugger/hi16-action-breakpoint_add.pngbin0 -> 225 bytes
-rw-r--r--languages/cpp/debugger/hi16-action-breakpoint_delete.pngbin0 -> 239 bytes
-rw-r--r--languages/cpp/debugger/hi16-action-breakpoint_delete_all.pngbin0 -> 239 bytes
-rw-r--r--languages/cpp/debugger/hi16-action-breakpoint_edit.pngbin0 -> 277 bytes
-rw-r--r--languages/cpp/debugger/kdevdebugger.desktop86
-rw-r--r--languages/cpp/debugger/kdevdebugger.rc93
-rw-r--r--languages/cpp/debugger/label_with_double_click.cpp14
-rw-r--r--languages/cpp/debugger/label_with_double_click.h20
-rw-r--r--languages/cpp/debugger/memviewdlg.cpp486
-rw-r--r--languages/cpp/debugger/memviewdlg.h118
-rw-r--r--languages/cpp/debugger/mi/Makefile.am12
-rw-r--r--languages/cpp/debugger/mi/gdbmi.cpp128
-rw-r--r--languages/cpp/debugger/mi/gdbmi.h221
-rw-r--r--languages/cpp/debugger/mi/milexer.cpp290
-rw-r--r--languages/cpp/debugger/mi/milexer.h147
-rw-r--r--languages/cpp/debugger/mi/miparser.cpp345
-rw-r--r--languages/cpp/debugger/mi/miparser.h82
-rw-r--r--languages/cpp/debugger/mi/tokens.h34
-rw-r--r--languages/cpp/debugger/stty.cpp386
-rw-r--r--languages/cpp/debugger/stty.h72
-rw-r--r--languages/cpp/debugger/tests/README.txt4
-rw-r--r--languages/cpp/debugger/tests/breakpoints/Makefile4
-rw-r--r--languages/cpp/debugger/tests/breakpoints/README.txt2
-rw-r--r--languages/cpp/debugger/tests/breakpoints/breakpoints.kdevelop163
-rw-r--r--languages/cpp/debugger/tests/breakpoints/foo.cpp10
-rw-r--r--languages/cpp/debugger/tests/breakpoints/main.cpp38
-rw-r--r--languages/cpp/debugger/tests/dll/Makefile8
-rw-r--r--languages/cpp/debugger/tests/dll/README.txt3
-rw-r--r--languages/cpp/debugger/tests/dll/dll.kdevelop158
-rw-r--r--languages/cpp/debugger/tests/dll/helper.cpp6
-rw-r--r--languages/cpp/debugger/tests/dll/main.cpp15
-rw-r--r--languages/cpp/debugger/tests/infinite_loop/Makefile4
-rw-r--r--languages/cpp/debugger/tests/infinite_loop/README.txt3
-rw-r--r--languages/cpp/debugger/tests/infinite_loop/infinite_loop.cpp17
-rw-r--r--languages/cpp/debugger/tests/infinite_loop/infinite_loop.kdevelop109
-rw-r--r--languages/cpp/debugger/tests/print_pointers/Makefile3
-rw-r--r--languages/cpp/debugger/tests/print_pointers/print_pointers.cpp95
-rw-r--r--languages/cpp/debugger/tests/print_pointers/print_pointers.kdevelop218
-rw-r--r--languages/cpp/debugger/tests/segfault/Makefile4
-rw-r--r--languages/cpp/debugger/tests/segfault/README.txt3
-rw-r--r--languages/cpp/debugger/tests/segfault/segfault.cpp7
-rw-r--r--languages/cpp/debugger/tests/segfault/segfault.kdevelop163
-rw-r--r--languages/cpp/debugger/tests/threads/Makefile4
-rw-r--r--languages/cpp/debugger/tests/threads/README.txt4
-rw-r--r--languages/cpp/debugger/tests/threads/threads.cpp26
-rw-r--r--languages/cpp/debugger/tests/threads/threads.kdevelop101
-rw-r--r--languages/cpp/debugger/tests/tracing/Makefile4
-rw-r--r--languages/cpp/debugger/tests/tracing/main.cpp15
-rw-r--r--languages/cpp/debugger/tests/tracing/tracing.kdevelop163
-rw-r--r--languages/cpp/debugger/tests/two_module/Makefile4
-rw-r--r--languages/cpp/debugger/tests/two_module/README.txt2
-rw-r--r--languages/cpp/debugger/tests/two_module/main.cpp8
-rw-r--r--languages/cpp/debugger/tests/two_module/src/foo.cpp12
-rw-r--r--languages/cpp/debugger/tests/two_module/two_module.kdevelop163
-rw-r--r--languages/cpp/debugger/variablewidget.cpp2002
-rw-r--r--languages/cpp/debugger/variablewidget.h466
92 files changed, 17575 insertions, 0 deletions
diff --git a/languages/cpp/debugger/DESIGN.txt b/languages/cpp/debugger/DESIGN.txt
new file mode 100644
index 00000000..627fd403
--- /dev/null
+++ b/languages/cpp/debugger/DESIGN.txt
@@ -0,0 +1,113 @@
+
+This document describes the design of KDevelop's debugger part. Not that it's
+work in progress, and sometimes describes desired design, not the actual
+one.
+
+== Components and lifecycle ==
+
+Debugger part consists of low-lever "controller" that handles talking
+with gdb and guess what state gdb is in, a number of view widgets, showing
+the state of the program, and a number of places where user can click to
+affect the program.
+
+What makes them all work together are "events" that controller sends
+to all interested parties. They are:
+
+ - Debugger exited. All view classes and actions become disabled and hidden
+ - Program exited. All view classes that can't be used without program
+ become disabled.
+ - Debugger is busy executing a command. All actions become disabled.
+ - Debugger is waiting for command. All actions becomes enabled.
+ - Program state changed. All views flush all cached data and
+ reload the content.
+ - Current thread/stack frame changed. All views switch to showing that
+ thread/frame.
+
+The distinction between "program state change" and "thread/frame" changed is
+that the latter does not imply that any *data* changed, and so it's not
+necessary to clear already cached data for other threads.
+
+== Command execution ==
+
+The controller has a queue of commands to send to gdb. A command typically
+has a callback (pair of QObject* and a member pointer) to be called when
+command is done.
+
+When the queue is non-empty, and debugger is not busy executing the previous
+command, the controller will send the command from the queue top to the gdb.
+The command being executed is remembed in the currentCmd_ member variable.
+Gdb will reply with a number of "out-of-band" responses, followed by one
+"done" or "error" response.
+
+The "done"/"error" response, when using MI interface, is a tree-line structure
+that's parsed with into GDBMI::ResultRecord structure, that is then passed
+to callback assocaited with the current command. Say, for "get me value of
+expression" command, MI response includes textual "value" field that can be
+used by any part of GUI to show the value. After callback is called,
+controller deletes the current command and then tries to execute next one from
+the queue, if there's one.
+
+The commands related to running program (continue/step/etc) are handled in
+a bit special way. Instead of calling any callbacks, controller performs
+predefined set of steps:
+
+ - Decide what's the reason for stop, and maybe do something special
+
+ - For stop on shared lib load, just continue
+
+ - For stop on breakpoint, run the breakpoint commands if any.
+
+ - Set a flag that program state might have changed, and must be reloaded
+
+ - Since hitting tracepoint adds extra commands, including possibly
+ "continue", we don't start reloading widgets immediately, instead
+ we wait for all commands currently in queue to get executed.
+
+ - Once there are no commands in queue, and "reload_program_state" flag is
+ set, we raise the 'program_state_changed' event. All widgets react to
+ that by queueing commands for realoding their state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Note that all commands are executed in the order they were queued, so if you
+add several commands at the same time, they are executed "automically". Each
+one sees the gdb state that the previous one has left it in.
+
+The MI protocol is stateful, that is there are things like current thread
+and current stack that affect the meaning of commands. Take care to never
+switch such "current" variables unless it's explicitly asked by the user.
+This means that if you have to switch thread/frame, always switch it back
+as the last command in a sequences.
+
+
+== Breakpoints handling ==
+
+Whenever a new breakpoint is added, or an existing breakpoint is modified,
+we immediately try to send the proper commands to gdb. Note that we
+don't try to check which properties of breakpoint were modified, we
+just send all breakpoint data.
+
+This is not always possible, because debugger might be busy, or just
+not started yet. In this case, we set 'pending' flag for breakpoint. Each time
+the debugger becomes free, we try to send the pending breakpoint again.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/languages/cpp/debugger/Makefile.am b/languages/cpp/debugger/Makefile.am
new file mode 100644
index 00000000..0ac84358
--- /dev/null
+++ b/languages/cpp/debugger/Makefile.am
@@ -0,0 +1,33 @@
+# Here resides the debugger part.
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+SUBDIRS = mi
+INCLUDES = -I$(top_srcdir)/languages/lib/debugger \
+ -I$(top_srcdir)/lib/interfaces -I$(top_srcdir)/lib/interfaces/extensions -I$(top_srcdir)/lib/util \
+ -I$(top_srcdir)/lib/widgets $(all_includes)
+
+kde_module_LTLIBRARIES = libkdevdebugger.la
+libkdevdebugger_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN)
+libkdevdebugger_la_LIBADD = $(top_builddir)/lib/libkdevelop.la $(top_builddir)/lib/widgets/libkdevwidgets.la $(LIB_KHTML) \
+ $(top_builddir)/languages/lib/debugger/liblang_debugger.la \
+ $(top_builddir)/languages/cpp/debugger/mi/libgdbmi_parser.la
+
+libkdevdebugger_la_SOURCES = debuggerdcopinterface.skel debuggerpart.cpp \
+ dbgcontroller.cpp gdbcontroller.cpp gdbcommand.cpp \
+ gdbparser.cpp stty.cpp breakpoint.cpp variablewidget.cpp \
+ gdbbreakpointwidget.cpp framestackwidget.cpp disassemblewidget.cpp \
+ memviewdlg.cpp dbgpsdlg.cpp dbgtoolbar.cpp debuggerconfigwidget.cpp \
+ debuggerconfigwidgetbase.ui debuggertracingdialogbase.ui \
+ gdboutputwidget.cpp gdbtable.cpp debuggertracingdialog.cpp \
+ label_with_double_click.cpp
+
+METASOURCES = AUTO
+KDE_ICON = AUTO
+
+servicedir = $(kde_servicesdir)
+service_DATA = kdevdebugger.desktop
+
+rcdir = $(kde_datadir)/kdevdebugger
+rc_DATA = kdevdebugger.rc
+noinst_HEADERS = gdbtable.h
diff --git a/languages/cpp/debugger/TODO.txt b/languages/cpp/debugger/TODO.txt
new file mode 100644
index 00000000..38aca109
--- /dev/null
+++ b/languages/cpp/debugger/TODO.txt
@@ -0,0 +1,218 @@
+
+KDEV4 Debugger:
+
+ - Launch framework
+
+ - On fly launch types -- "dbus call to connect to a given app"
+
+ - Advanced data visualization
+
+ - Customizable by the user
+
+ - Assembler display that's good
+
+ - Hex display that's good
+
+ - Register display that is good.
+
+ - Debugger scripts? Remembering and replaying a set of
+ commands?
+
+ - Debugger as a visualizer tool?
+
+
+TODO:
+
+ - Check gdb version at startup.
+
+ - P1:
+
+ - Breakpoints duplicated on editing.
+
+ - Global radix
+
+ - Hiding of static members.
+
+ - No horizonal scrollbar in the variables widget.
+
+ - Debugging optimized binaries
+
+ - Debugged application somtimes not killed.
+
+ - Fix moving breakpoints on editing files
+
+ - Pressing tab in memory range dialog modified the edited file.
+
+ - Testing stepping into code for which gdb can't find the file or fullname.
+ Test stepping (or stepi) into undebuggable code.
+
+ - Test files without debug info
+
+ - Reconsider fixed font in variable widget
+
+ - Investigate 'stop on shlib load breaking next" on Dario's project.
+
+ - Incoming bug reports:
+
+ - Check core files usage.
+
+ - Fix remote target dialog, which is confusing and requires
+ to specify tree scripts!
+
+ - Big projects
+
+ - Add support for other special types (STL)
+
+ - Breakpoints
+
+ - Add shortcuts to context menu.
+
+ - Add icons
+
+ - Status display column is just ugly
+
+ - Handle "out of breakpoints" message.
+
+ - Check that changing breakpoint when program is running works sanely.
+ Need to either disable breakpoints widget when debugger is busy, or
+ stop debugger when we add new breakpoint.
+
+ - Implement gdb -> KDevelop breakpoint addition for all existing
+ breakpoint types.
+
+
+
+ - For function breakpoints, the 'file' property of breakpoint from
+ gdb is not a fullname (gdb 6.4), so we don't find the file.
+
+ - For function breakpoints with gdb CVS, clicking on marker corresponding
+ to function breakpoint does not clear it, but adds a new one.
+
+ - "Immediately leave function" breakpoint type.
+
+
+ - Watchpoints redux:
+
+ - Fix status display for watchpoints
+
+ - Test loading of read watchpoints from session file.
+
+ - Change "toogle watchpoint" into checkbox.
+
+ - "Read watchpoint triggered" message disappears too soon.
+
+
+ - Fix up the mess with relative vs. full names of files for breakpoints.
+
+
+ UI cleanup:
+
+ - The dialog box on watchpoint hit draws slowly initially.
+
+ - Cntrl-Enter for "add watch".
+
+ - Close all opened thread when opening other?
+
+ - The dialog box shown in MI errors is ugly. Often, it contains names
+ of internal gdb functions, or no interest to outsiders.
+
+ - Should strip formatting when copying from gdb output window.
+
+ Console command interaction:
+
+ - Handle "Program exited" messages from CLI "continue".
+
+
+ Code cleanup:
+
+ - Rename FileLine to Code.
+
+ - Kill raw char* manipulation.
+
+ - Fix hardcoded color in framestack widget
+
+ - Kill 'trimmableItem'.
+
+ Minor tweaks:
+
+ - Need some "scope" for error reporting. Say, we can try to set invalid
+ breakpoint condition from session file, on debugger startup. Need to
+ produce message like:
+ "Error detected when setting condition for breakpoint 1",
+ not a pretty opaque error we get now.
+
+ - Highlight type changes in variable widget
+
+ - Highlight composite types changes?
+
+ - Test that modifying breakpoint while application is running works.
+
+ - If remembered expresion includes dereferences pointer as a child,
+ that child is still updated as we step.
+
+ - Error in 'finish' command (e.g. on the outer frame) hides the
+ current line indicator.
+
+ - Should disable the 'finish' command on the outer frame.
+
+ Optimizations:
+
+ - If we're in some function and looked at frame 0 and frame 1, and
+ then run "finish", we need to reuse VarFrameRoot for previous frame
+ 1, which now became frame 0, no need to recreate it. Generally, need
+ to preserve open/closed state of all variables for all scopes.
+
+
+
+
+
+
+BUGS/ISSUES found:
+
+ - "set edit off" breaks MI
+ - no stop reason for stop on shared library load
+ - using "interpreter mi -whatever" when already in MI
+ mode causes gdb to output two "^done" messages and
+ it confuses us.
+ - No support for "character" format in -data-evaluate-expression
+ or -var-set-format
+ - Some of the -stack* command operate on current frame unless one is
+ specified, but -stack-list-arguments will print all frames.
+
+ - Output of -thread-list-ids uses the following syntax
+
+ {thread-id="1",thread-id="2"}
+
+ which is neither tuple nor list.
+
+ - Pending breakpoits broken in MI.
+
+
+ - Varobj broken:
+
+ - When entering new scope, we need to issue -stack-list-locals
+ to get names of new varaibles in that scope.
+
+ - When stopping inside undebuggable code (say, on watchpoint hit),
+ -file-list-exec-source-file reports the last valid source file.
+
+ - It's not possible to find if intefiour is running or not.
+
+
+
+
+
+Advantages of MI
+
+
+- The information is easier to extract.
+
+ - For watchpoint, getting the old and new value of
+ watched expression for display is trivial. For CLI,
+ this is tricky and not done in current code.
+
+
+
+
+
+
diff --git a/languages/cpp/debugger/breakpoint.cpp b/languages/cpp/debugger/breakpoint.cpp
new file mode 100644
index 00000000..7bcf674f
--- /dev/null
+++ b/languages/cpp/debugger/breakpoint.cpp
@@ -0,0 +1,719 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 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 "breakpoint.h"
+#include "gdbcontroller.h"
+#include "gdbcommand.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfileinfo.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+#include <stdio.h>
+#include <typeinfo>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+static int BPKey_ = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Breakpoint::Breakpoint(bool temporary, bool enabled)
+ : s_pending_(true),
+ s_actionAdd_(true),
+ s_actionClear_(false),
+ s_actionModify_(false),
+ s_actionDie_(false),
+ s_dbgProcessing_(false),
+ s_enabled_(enabled),
+ s_temporary_(temporary),
+ s_hardwareBP_(false),
+ s_tracingEnabled_(false),
+ s_traceFormatStringEnabled_(false),
+
+ dbgId_(-1),
+ hits_(0),
+ key_(BPKey_++),
+ active_(-1),
+ ignoreCount_(0),
+ condition_("")
+{
+}
+
+/***************************************************************************/
+
+Breakpoint::~Breakpoint()
+{
+}
+
+void Breakpoint::sendToGdb(GDBController* controller)
+{
+ // Need to issue 'modifyBreakpoint' when setting breakpoint is done
+ controller_ = controller;
+
+ // FIXME: should either make sure this widget is disabled
+ // when needed, or implement simular logic.
+ if (controller->stateIsOn(s_dbgNotStarted))
+ {
+ // Can't modify breakpoint now, will try again later.
+ setPending(true);
+ return;
+ }
+
+ setPending(false);
+
+ bool restart = false;
+ // FIXME: this will only catch command for which gdb
+ // produces the "^running" marker.
+ // FIXME: this probably won't work if there are other
+ // run commands in the thread already.
+ if (controller->stateIsOn(s_appRunning)
+ && !controller->stateIsOn(s_explicitBreakInto))
+ {
+ kdDebug(9012) << "PAUSING APP\n";
+ controller->pauseApp();
+ restart = true;
+ }
+
+ if (isActionAdd())
+ {
+ // This prevents us from sending breakpoint command to
+ // gdb for empty breakpoints, when user haven't even
+ // typed function name, or address, or variable.
+ //
+ // Check for isDbgProcessing makes sure we don't issue
+ // several -break-insert commands before getting
+ // output from the first one.
+ if (isValid() && !isDbgProcessing())
+ {
+ setBreakpoint(controller);
+ }
+ }
+ else
+ {
+ if (isActionClear())
+ {
+ clearBreakpoint(controller);
+ }
+ else
+ {
+ if (isActionModify())
+ {
+ modifyBreakpoint(controller);
+ }
+ }
+ }
+
+ if (restart) {
+ kdDebug(9012) << "RESTARING APP\n";
+ GDBCommand *cmd = new GDBCommand("-exec-continue");
+ cmd->setRun(true);
+ controller->addCommand(cmd);
+ }
+}
+
+void Breakpoint::clearBreakpoint(GDBController* /*c*/)
+{
+ controller()->addCommandBeforeRun(
+ new GDBCommand(dbgRemoveCommand(),
+ this,
+ &Breakpoint::handleDeleted));
+}
+
+void Breakpoint::applicationExited(GDBController*)
+{
+}
+
+
+void Breakpoint::setBreakpoint(GDBController* controller)
+{
+ setDbgProcessing(true);
+
+ // Don't use handler mechanism yet, because the reply
+ // should contain internal id of breakpoint (not gdb id), so that we
+ // can match gdb id with the breakpoint instance we've set.
+
+ // Note that at startup we issue several breakpoint commands, so can't
+ // just store last breakpoint. Need to stack of last breakpoint commands,
+ // but that for later.
+ //
+ // When this command is finished, slotParseGDBBreakpointSet
+ // will be called by the controller.
+ controller->addCommandBeforeRun(
+ new GDBCommand(dbgSetCommand(controller),
+ this,
+ &Breakpoint::handleSet, true));
+}
+
+
+void Breakpoint::modifyBreakpoint(GDBController* controller)
+{
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(QString("-break-condition %1 ") +
+ conditional(), this));
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(QString("-break-after %1 ") +
+ QString::number(ignoreCount()), this));
+
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(isEnabled() ?
+ QString("-break-enable %1")
+ : QString("-break-disable %1"), this));
+}
+
+void Breakpoint::removedInGdb()
+{
+ setActionDie();
+ emit modified(this);
+}
+
+
+bool Breakpoint::match(const Breakpoint* breakpoint) const
+{
+ // simple case
+ if (this == breakpoint)
+ return true;
+
+ // Type case
+ if (typeid(*this) != typeid(*breakpoint))
+ return false;
+
+ return match_data(breakpoint);
+}
+
+/***************************************************************************/
+
+QString Breakpoint::dbgRemoveCommand() const
+{
+ if (dbgId_>0)
+ return QString("-break-delete %1").arg(dbgId_); // gdb command - not translatable
+
+ return QString();
+}
+
+
+/***************************************************************************/
+
+// called when debugger ends
+void Breakpoint::reset()
+{
+ dbgId_ = -1;
+ s_pending_ = true;
+ s_actionAdd_ = true; // waiting for the debugger to start
+ s_actionClear_ = false;
+ // All breakpoint properties will be automatically sent to
+ // gdb when breakpoint is first added, no matter what value
+ // this field has.
+ s_actionModify_ = false;
+ s_dbgProcessing_ = false;
+ s_hardwareBP_ = false;
+ hits_ = 0;
+ active_ = -1;
+}
+
+/***************************************************************************/
+
+void Breakpoint::setActive(int active, int id)
+{
+ active_ = active;
+ dbgId_ = id;
+
+ if (s_pending_ && !(s_actionAdd_ && s_actionModify_)) {
+ s_pending_ = false;
+ s_actionModify_ = false;
+ }
+
+ s_actionAdd_ = false;
+ s_actionClear_ = false;
+ s_actionDie_ = false;
+ s_dbgProcessing_ = false;
+}
+
+/***************************************************************************/
+
+QString Breakpoint::statusDisplay(int activeFlag) const
+{
+ QString status="";
+ if (!s_enabled_)
+ status = i18n("Disabled");
+ else
+ if (s_pending_)
+ {
+ if (s_actionAdd_)
+ status = i18n("Pending (add)");
+ if (s_actionClear_)
+ status = i18n("Pending (clear)");
+ if (s_actionModify_)
+ status = i18n("Pending (modify)");
+ }
+ else
+ if (isActive(activeFlag))
+ status = i18n("Active");
+
+ return status;
+}
+
+QString Breakpoint::traceRealFormatString() const
+{
+ QString result;
+
+ if (traceFormatStringEnabled())
+ {
+ result = traceFormatString();
+ }
+ else
+ {
+ result = "Tracepoint";
+ if (const FilePosBreakpoint* fb
+ = dynamic_cast<const FilePosBreakpoint*>(this))
+ {
+ result += " at " + fb->location() + ": ";
+ }
+ else
+ {
+ result += " " + QString::number(key()) + ": ";
+ }
+ for(QStringList::const_iterator i = tracedExpressions_.begin(),
+ e = tracedExpressions_.end(); i != e; ++i)
+ {
+ result += " " + *i + " = %d";
+ }
+ }
+
+ // Quote the thing
+ result = "\"" + result + "\\n\"";
+
+ for(QStringList::const_iterator i = tracedExpressions_.begin(),
+ e = tracedExpressions_.end(); i != e; ++i)
+ {
+ result += ", " + *i;
+ }
+
+ return result;
+}
+
+void Breakpoint::handleSet(const GDBMI::ResultRecord& r)
+{
+ // Try to find gdb id. It's a bit harder that it should be,
+ // because field names differ depending on breakpoint type.
+
+ int id = -1;
+
+ if (r.hasField("bkpt"))
+ id = r["bkpt"]["number"].literal().toInt();
+ else if (r.hasField("wpt"))
+ id = r["wpt"]["number"].literal().toInt();
+ else if (r.hasField("hw-rwpt"))
+ id = r["hw-rwpt"]["number"].literal().toInt();
+ // We don't have access watchpoints in UI yet, but
+ // for future.
+ else if (r.hasField("hw-awpt"))
+ id = r["hw-awpt"]["number"].literal().toInt();
+
+ if (id == -1)
+ {
+ // If can't set because file not found yet,
+ // will need to try later.
+ setPending(true);
+ }
+ else
+ {
+ setActive(0 /* unused m_activeFlag */, id);
+ }
+
+ // Need to do this so that if breakpoint is not set
+ // (because the file is not found)
+ // we unset isDbgProcessing flag, so that breakpoint can
+ // be set on next stop.
+ setDbgProcessing(false);
+
+ // Immediately call modifyBreakpoint to set all breakpoint
+ // properties, such as condition.
+ modifyBreakpoint(controller_);
+
+ emit modified(this);
+}
+
+void Breakpoint::handleDeleted(const GDBMI::ResultRecord& /*r*/)
+{
+ kdDebug(9012) << "inside handleDeleted\n";
+ setActionDie();
+ if (FilePosBreakpoint* fp = dynamic_cast<FilePosBreakpoint*>(this))
+ {
+ kdDebug(9012) << "handleDeleted, line is " << fp->lineNum() << "\n";
+ }
+ emit modified(this);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+FilePosBreakpoint::FilePosBreakpoint()
+: subtype_(filepos),
+ line_(-1)
+{}
+
+FilePosBreakpoint::FilePosBreakpoint(const QString &fileName, int lineNum,
+ bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled)
+{
+ // Sets 'subtype'
+ setLocation(QString("%1:%2").arg(fileName).arg(lineNum));
+}
+
+FilePosBreakpoint::~FilePosBreakpoint()
+{
+}
+
+QString FilePosBreakpoint::dbgSetCommand(GDBController *c) const
+{
+ QString cmdStr = "-break-insert";
+
+ if (isTemporary())
+ cmdStr = cmdStr + " -t";
+
+ if (c->miPendingBreakpoints())
+ cmdStr = cmdStr + " -f";
+
+ return cmdStr + " " + location_;
+}
+
+bool FilePosBreakpoint::match_data(const Breakpoint *xb) const
+{
+ const FilePosBreakpoint* b = static_cast<const FilePosBreakpoint*>(xb);
+
+ if (b)
+ return location_ == b->location_;
+ else
+ return false;
+}
+
+QString FilePosBreakpoint::displayType() const
+{
+ return i18n("Code breakpoint", "Code");
+}
+
+bool FilePosBreakpoint::isValid() const
+{
+ return !location_.isEmpty();
+}
+
+bool FilePosBreakpoint::hasFileAndLine() const
+{
+ return line_ != -1;
+}
+
+QString FilePosBreakpoint::fileName() const
+{
+ return fileName_;
+}
+
+unsigned FilePosBreakpoint::lineNum() const
+{
+ return line_;
+}
+
+
+
+QString FilePosBreakpoint::location(bool compact) const
+{
+ if (subtype_ == filepos && hasFileAndLine() && compact)
+ {
+ return QFileInfo(fileName_).fileName()+":"+QString::number(line_);
+ }
+ else
+ {
+ return location_;
+ }
+}
+
+/***************************************************************************/
+
+void FilePosBreakpoint::setLocation(const QString& location)
+{
+ location_ = location;
+
+ QRegExp regExp1("(.*):(\\d+)$");
+ regExp1.setMinimal(true);
+ if ( regExp1.search(location, 0) >= 0 )
+ {
+ subtype_ = filepos;
+
+ QString t = regExp1.cap(1);
+ QString dirPath = QFileInfo(t).dirPath();
+ if ( dirPath == "." )
+ {
+ QString existingDirPath = QFileInfo(fileName_).dirPath();
+ if (existingDirPath != ".")
+ fileName_ = existingDirPath+"/"+regExp1.cap(1);
+ else
+ fileName_ = regExp1.cap(1);
+ }
+ else
+ fileName_ = regExp1.cap(1);
+
+ line_ = regExp1.cap(2).toInt();
+
+ location_ = QString("%1:%2").arg(fileName_).arg(regExp1.cap(2));
+ }
+ else
+ {
+ // Could be address as well, but it's treated absolutely
+ // the same everywhere.
+ subtype_ = function;
+ }
+}
+
+void FilePosBreakpoint::handleSet(const GDBMI::ResultRecord& r)
+{
+ // Below logic gets filename and line from gdb response, and
+ // allows us to show breakpoint marker even for function
+ // breakpoints. Unfortunately, 'fullname' field is available only in
+ // post-6.4 versions of gdb and if we try to use 'file', then
+ // KDevelop won't be able to find that file to show the marker.
+ if (r.hasField("bkpt"))
+ {
+ const GDBMI::Value& v = r["bkpt"];
+ if (v.hasField("fullname") && v.hasField("line"))
+ {
+ fileName_ = v["fullname"].literal();
+ line_ = v["line"].literal().toInt();
+ }
+ }
+
+ Breakpoint::handleSet(r);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Watchpoint::Watchpoint(const QString& varName, bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ varName_(varName)
+{
+}
+
+/***************************************************************************/
+
+Watchpoint::~Watchpoint()
+{
+}
+
+void Watchpoint::setBreakpoint(GDBController* controller)
+{
+ if (isEnabled())
+ {
+ setDbgProcessing(true);
+
+ controller->addCommandBeforeRun(
+ new GDBCommand(
+ QString("-data-evaluate-expression &%1").arg(varName_),
+ this,
+ &Watchpoint::handleAddressComputed));
+ }
+}
+
+void Watchpoint::handleAddressComputed(const GDBMI::ResultRecord& r)
+{
+ address_ = r["value"].literal().toULongLong(0, 16);
+ controller()->addCommandBeforeRun(
+ new GDBCommand(
+ QString("-break-watch *%1").arg(r["value"].literal()),
+ static_cast<Breakpoint*>(this),
+ &Watchpoint::handleSet));
+}
+
+void Watchpoint::applicationExited(GDBController* c)
+{
+ if (!c->stateIsOn(s_dbgNotStarted))
+ {
+ // Note: not using 'clearBreakpoint' as it will delete breakpoint
+ // completely.
+
+ controller()->addCommand(
+ new GDBCommand(dbgRemoveCommand()));
+ setDbgId(-1);
+ setEnabled(false);
+ setActionAdd(true);
+ address_ = static_cast<unsigned long long>(-1);
+ emit modified(this);
+ }
+}
+
+void Watchpoint::removedInGdb()
+{
+ // Do nothing. Watchpoints must be preserved
+ // even if they are gone in gdb.
+}
+
+/***************************************************************************/
+
+QString Watchpoint::dbgSetCommand(GDBController *) const
+{
+ return QString("-break-watch ")+varName_; // gdb command - not translatable
+}
+
+/***************************************************************************/
+
+bool Watchpoint::match_data(const Breakpoint* xb) const
+{
+ const Watchpoint* b = static_cast<const Watchpoint*>(xb);
+
+ return (varName_ == b->varName_);
+}
+
+ReadWatchpoint::ReadWatchpoint(const QString& varName, bool temporary, bool enabled)
+ : Watchpoint(varName, temporary, enabled)
+{
+}
+
+QString ReadWatchpoint::dbgSetCommand(GDBController *) const
+{
+ return QString("-break-watch -r ")+varName();
+}
+
+bool ReadWatchpoint::match_data(const Breakpoint* xb) const
+{
+ const ReadWatchpoint* b = static_cast<const ReadWatchpoint*>(xb);
+
+ return (varName() == b->varName());
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+//ExitBreakpoint::ExitBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//ExitBreakpoint::~ExitBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString ExitBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+//bool ExitBreakpoint::match(const Breakpoint* brkpt) const
+//{
+// // simple case
+// if (this == brkpt)
+// return true;
+//
+// // Type case
+// const ExitBreakpoint* check = dynamic_cast<const ExitBreakpoint*>(brkpt);
+// if (!check)
+// return false;
+//
+// // member case
+// return true;
+//}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+//
+// These are implemented in gdb but can cause a lot of breakpoints
+// to be set. This needs more thought before being implemented
+
+//RegExpBreakpoint::RegExpBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//RegExpBreakpoint::~RegExpBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString RegExpBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+////QString RegExpBreakpoint::dbgRemoveCommand() const
+////{
+//// return "";
+////}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+// Most catch options arn't implemented in gdb so ignore this for now.
+
+//CatchBreakpoint::CatchBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//CatchBreakpoint::~CatchBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString CatchBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+////QString CatchBreakpoint::dbgRemoveCommand() const
+////{
+//// return "";
+////}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#include "breakpoint.moc"
diff --git a/languages/cpp/debugger/breakpoint.h b/languages/cpp/debugger/breakpoint.h
new file mode 100644
index 00000000..f06fc3d3
--- /dev/null
+++ b/languages/cpp/debugger/breakpoint.h
@@ -0,0 +1,313 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 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 _BREAKPOINT_H_
+#define _BREAKPOINT_H_
+
+#include <klocale.h>
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBMI
+{
+ class ResultRecord;
+}
+
+namespace GDBDebugger
+{
+
+ class GDBController;
+
+enum BP_TYPES
+{
+ BP_TYPE_Invalid,
+ BP_TYPE_FilePos,
+ BP_TYPE_Watchpoint,
+ BP_TYPE_ReadWatchpoint
+};
+
+class Breakpoint : public QObject
+{
+ Q_OBJECT
+public:
+ Breakpoint(bool temporary=false, bool enabled=true);
+ virtual ~Breakpoint();
+
+ void sendToGdb(GDBController* c);
+
+ // Called whenever this breakpoint is removed on gdb side.
+ virtual void removedInGdb();
+
+ virtual void applicationExited(GDBController*);
+
+
+
+ virtual QString dbgSetCommand(GDBController *) const = 0;
+ virtual QString dbgRemoveCommand() const;
+ /** Returns true if 'breakpoint' is identical to *this.
+ Checks for trival cases like pointer equality and
+ differing typeid() and then calls virtual
+ match_data.
+ */
+ bool match(const Breakpoint* breakpoint) const;
+ /** Returns true if essential data in 'breakpoint' is equivalent
+ to *this. The caller should guarantee that dynamic type
+ of *this and *breakpoint is the same.
+ */
+ virtual bool match_data(const Breakpoint* breakpoint) const = 0;
+
+ virtual bool hasFileAndLine() const { return false; }
+
+
+ virtual void reset();
+
+ void setActive(int active, int id);
+ bool isActive(int active) const { return (active_ == active) ||
+ (s_pending_ && !s_actionClear_); }
+
+ void setEnabled(bool enabled) { s_enabled_ = enabled; }
+ bool isEnabled() const { return s_enabled_; }
+
+ void setTemporary(bool temporary) { s_temporary_ = temporary; }
+ bool isTemporary() const { return s_temporary_; }
+
+ void setHardwareBP(bool hardwareBP) { s_hardwareBP_ = hardwareBP; }
+ bool isHardwareBP() const { return s_hardwareBP_; }
+
+ void setIgnoreCount(int ignoreCount) { ignoreCount_ = ignoreCount; }
+ int ignoreCount() const { return ignoreCount_; }
+
+ void setAddress(const QString &address) { address_ = address; }
+ QString address() const { return address_; }
+
+ void setConditional(const QString &condition) { condition_ = condition; }
+ QString conditional() const { return condition_; }
+
+ void setPending(bool pending) { s_pending_ = pending; }
+ bool isPending() const { return s_pending_; }
+
+ void setActionAdd(bool actionAdd) { s_actionDie_ = false;
+ s_actionAdd_ = actionAdd; }
+ bool isActionAdd() const { return s_actionAdd_; }
+
+ void setActionClear(bool actionClear) { s_actionClear_ = actionClear; }
+ bool isActionClear() const { return s_actionClear_; }
+
+ void setActionModify(bool actionModify) { s_actionDie_ = false;
+ s_actionModify_ = actionModify; }
+ bool isActionModify() const { return s_actionModify_; }
+
+ void setDbgProcessing(bool dbgProcessing) { s_dbgProcessing_ = dbgProcessing; }
+ bool isDbgProcessing() const { return s_dbgProcessing_; }
+ void setActionDie() { s_actionDie_ = true;
+ s_actionClear_ = false; }
+ bool isActionDie() const { return s_actionDie_; }
+
+ int key() const { return key_; }
+ void setDbgId(int dbgId) { dbgId_ = dbgId; }
+ int dbgId() const { return dbgId_; }
+ void setHits(int hits) { hits_ = hits; }
+ int hits() const { return hits_; }
+
+ virtual QString statusDisplay(int activeFlag) const;
+ virtual BP_TYPES type() const { return BP_TYPE_Invalid; }
+ virtual QString displayType() const { return i18n( "Invalid" ); }
+
+
+ bool tracingEnabled() const { return s_tracingEnabled_; }
+ void setTracingEnabled(bool enable) { s_tracingEnabled_ = enable; }
+
+ const QStringList& tracedExpressions() const { return tracedExpressions_; }
+ void setTracedExpressions(const QStringList& l) { tracedExpressions_ = l; }
+
+ bool traceFormatStringEnabled() const { return s_traceFormatStringEnabled_; }
+ void setTraceFormatStringEnabled(bool en) { s_traceFormatStringEnabled_ = en; }
+
+ const QString& traceFormatString() const { return traceFormatString_; }
+ void setTraceFormatString(const QString& s) { traceFormatString_ = s; }
+
+ QString traceRealFormatString() const;
+
+ virtual QString location(bool compact=true) const = 0;
+ virtual void setLocation(const QString& ) = 0;
+ virtual bool isValid() const = 0;
+
+signals:
+ /** Emitted whenever this breakpoint is modified from gdb side,
+ say when it's first created, or when gdb reports that any
+ property has changes.
+ */
+ void modified(Breakpoint*);
+
+private:
+ void handleDeleted(const GDBMI::ResultRecord&);
+ virtual void setBreakpoint(GDBController* controller);
+ void modifyBreakpoint(GDBController* controller);
+
+protected:
+ GDBController* controller() const { return controller_; }
+ virtual void handleSet(const GDBMI::ResultRecord&);
+ void clearBreakpoint(GDBController* c);
+
+private:
+ bool s_pending_ :1;
+ bool s_actionAdd_ :1;
+ bool s_actionClear_ :1;
+ bool s_actionModify_ :1;
+ bool s_actionDie_ :1;
+ bool s_dbgProcessing_ :1;
+ bool s_enabled_ :1;
+ bool s_temporary_ :1;
+ bool s_hardwareBP_ :1; // assigned by gdb
+ bool s_tracingEnabled_ :1;
+ bool s_traceFormatStringEnabled_ :1;
+
+ int dbgId_; // assigned by gdb
+ int hits_; // assigned by gdb
+
+ int key_; // internal unique key
+ int active_; // counter incremented on receipt of all BP's
+
+ int ignoreCount_;
+ QString address_;
+ QString condition_;
+ QStringList tracedExpressions_;
+ QString traceFormatString_;
+
+ GDBController* controller_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class FilePosBreakpoint : public Breakpoint
+{
+public:
+ FilePosBreakpoint();
+
+ FilePosBreakpoint(const QString &fileName, int lineNum,
+ bool temporary=false, bool enabled=true);
+ virtual ~FilePosBreakpoint();
+ virtual QString dbgSetCommand(GDBController *) const;
+ virtual bool match_data(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_FilePos; }
+ QString displayType() const;
+ QString location(bool compact=true) const;
+ void setLocation(const QString& location);
+ bool isValid() const;
+
+ bool hasFileAndLine() const;
+ QString fileName() const;
+ unsigned lineNum() const;
+
+protected:
+ void handleSet(const GDBMI::ResultRecord&);
+
+
+private:
+
+ enum subtype { filepos = 1, function, address };
+ subtype subtype_;
+
+ QString location_;
+ QString fileName_;
+ int line_;
+};
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+//class RegExpBreakpoint : public Breakpoint
+//{
+//public:
+// RegExpBreakpoint(bool temporary=false, bool enabled=true);
+// virtual ~RegExpBreakpoint();
+// virtual QString dbgSetCommand() const;
+//};
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+//class CatchBreakpoint : public Breakpoint
+//{
+//public:
+// CatchBreakpoint(bool temporary=false, bool enabled=true);
+// virtual ~CatchBreakpoint();
+// virtual QString dbgSetCommand() const;
+// virtual CatchBreakpoint& operator=(const CatchBreakpoint& rhs);
+//};
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+//class ExitBreakpoint : public Breakpoint
+//{
+//public:
+// ExitBreakpoint(bool temporary=false, bool enabled=true);
+// virtual ~ExitBreakpoint();
+// virtual QString dbgSetCommand() const;
+// bool match(const Breakpoint* brkpt) const;
+// virtual void configureDisplay();
+//};
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class Watchpoint : public Breakpoint
+{
+public:
+ Watchpoint(const QString &varName, bool temporary=false, bool enabled=true);
+ virtual ~Watchpoint();
+ virtual QString dbgSetCommand(GDBController *) const;
+
+ void applicationExited(GDBController*);
+ void removedInGdb();
+
+ bool match_data(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_Watchpoint; }
+ QString displayType() const { return i18n("Watchpoint"); }
+ void setVarName(const QString& varName) { varName_ = varName; }
+ QString varName() const { return varName_; }
+ unsigned long long address() const { return address_; }
+ QString location(bool) const { return varName_; }
+ void setLocation(const QString& location) { varName_ = location; }
+ bool isValid() const { return !varName_.isEmpty(); }
+
+private:
+ void setBreakpoint(GDBController* controller);
+ void handleAddressComputed(const GDBMI::ResultRecord&);
+
+ QString varName_;
+ unsigned long long address_;
+};
+
+class ReadWatchpoint : public Watchpoint
+{
+public:
+ ReadWatchpoint(const QString &varName, bool temporary=false, bool enabled=true);
+ virtual QString dbgSetCommand(GDBController *) const;
+ bool match_data(const Breakpoint *brkpt) const;
+
+ BP_TYPES type () const { return BP_TYPE_ReadWatchpoint; }
+ QString displayType() const { return i18n("Read Watchpoint"); }
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/dbgcontroller.cpp b/languages/cpp/debugger/dbgcontroller.cpp
new file mode 100644
index 00000000..6ca94543
--- /dev/null
+++ b/languages/cpp/debugger/dbgcontroller.cpp
@@ -0,0 +1,40 @@
+/***************************************************************************
+ 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 "dbgcontroller.h"
+#include <kprocess.h>
+
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+DbgController::DbgController()
+ : dbgProcess_(0)
+{
+}
+
+/***************************************************************************/
+
+DbgController::~DbgController()
+{
+ delete dbgProcess_;
+}
+
+/***************************************************************************/
+
+}
+
+#include "dbgcontroller.moc"
diff --git a/languages/cpp/debugger/dbgcontroller.h b/languages/cpp/debugger/dbgcontroller.h
new file mode 100644
index 00000000..8d13f2d0
--- /dev/null
+++ b/languages/cpp/debugger/dbgcontroller.h
@@ -0,0 +1,128 @@
+/***************************************************************************
+ 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 _DBGCONTROLLER_H_
+#define _DBGCONTROLLER_H_
+
+#include "mi/gdbmi.h"
+
+#include <qobject.h>
+#include <domutil.h>
+
+
+
+class KProcess;
+class QString;
+class QStrList;
+
+namespace GDBDebugger
+{
+
+class Breakpoint;
+class DbgCommand;
+class TrimmableItem;
+class VarItem;
+
+
+/***************************************************************************/
+/**
+ * @author jbb
+ */
+/***************************************************************************/
+// sigh - namespace's don't work on some of the older compilers
+enum DBGStateFlags
+{
+ s_dbgNotStarted = 1,
+ s_appNotStarted = 2,
+ s_waitForWrite = 8,
+ s_programExited = 16,
+ s_viewBT = 128,
+ s_viewBP = 256,
+ s_attached = 512,
+ s_core = 1024,
+ s_waitTimer = 2048,
+ // Set when 'slotStopDebugger' started executing, to avoid
+ // entering that function several times.
+ s_shuttingDown = 4096,
+ s_explicitBreakInto = (s_shuttingDown << 1),
+ s_dbgBusy = (s_explicitBreakInto << 1),
+ s_appRunning = (s_dbgBusy << 1),
+ s_lastDbgState = (s_appRunning << 1)
+
+};
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+class DbgController : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ DbgController();
+ virtual ~DbgController();
+
+ virtual bool stateIsOn( int state ) = 0;
+
+public slots:
+ virtual void configure() = 0;
+
+ virtual void slotCoreFile(const QString &coreFile) = 0;
+ virtual void slotAttachTo(int pid) = 0;
+
+ virtual void slotStopDebugger() = 0;
+
+ virtual void slotRun() = 0;
+ // Kills the application but does not stop the debugger itself.
+ virtual void slotKill() = 0;
+ virtual void slotRunUntil(const QString &fileName, int lineNum) = 0;
+ virtual void slotJumpTo(const QString &fileName, int lineNum) = 0;
+ virtual void slotStepInto() = 0;
+ virtual void slotStepOver() = 0;
+ virtual void slotStepIntoIns() = 0;
+ virtual void slotStepOverIns() = 0;
+ virtual void slotStepOutOff() = 0;
+
+ virtual void slotBreakInto() = 0;
+
+ // jw - for optional additional commands and initialization
+ virtual void slotVarItemConstructed(VarItem */*item*/) {}
+
+protected slots:
+ virtual void slotDbgStdout(KProcess *proc, char *buf, int buflen) = 0;
+ virtual void slotDbgStderr(KProcess*, char*, int) {} ;
+ virtual void slotDbgWroteStdin(KProcess *proc) = 0;
+ virtual void slotDbgProcessExited(KProcess *proc) = 0;
+
+signals:
+ void gotoSourcePosition (const QString &fileName, int lineNum);
+ void rawGDBMemoryDump (char *buf);
+ void rawGDBRegisters (char *buf);
+ void rawGDBLibraries (char *buf);
+ void ttyStdout (const char *output);
+ void ttyStderr (const char *output);
+ void gdbInternalCommandStdout (const char *output);
+ void gdbUserCommandStdout (const char *output);
+ void gdbStderr (const char *output);
+ void showStepInSource (const QString &fileName, int lineNum, const QString &address);
+ void dbgStatus (const QString &status, int statusFlag);
+
+protected:
+ KProcess *dbgProcess_;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/dbgpsdlg.cpp b/languages/cpp/debugger/dbgpsdlg.cpp
new file mode 100644
index 00000000..524e4539
--- /dev/null
+++ b/languages/cpp/debugger/dbgpsdlg.cpp
@@ -0,0 +1,203 @@
+/***************************************************************************
+ begin : Mon Sep 20 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 "dbgpsdlg.h"
+
+#include <kbuttonbox.h>
+#include <kdialog.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kstdguiitem.h>
+#include <kdeversion.h>
+#include <klistview.h>
+#include <klistviewsearchline.h>
+#include <kmessagebox.h>
+
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <qtoolbutton.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qheader.h>
+#include <qtimer.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+namespace GDBDebugger
+{
+
+/***************************************************************************/
+
+// Display a list of processes for the user to select one
+// only display processes that they can do something with so if the user
+// is root then display all processes
+// For use with the internal debugger, but this dialog doesn't know anything
+// about why it's doing it.
+
+Dbg_PS_Dialog::Dbg_PS_Dialog(QWidget *parent, const char *name)
+ : KDialog(parent, name, true), // modal
+ psProc_(0),
+ pids_(new KListView(this)),
+ pidLines_(QString())
+{
+ setCaption(i18n("Attach to Process"));
+
+ pids_->addColumn("PID");
+ pids_->addColumn("TTY");
+ pids_->addColumn("STAT");
+ pids_->addColumn("TIME");
+ pids_->addColumn("COMMAND");
+
+
+ QBoxLayout *topLayout = new QVBoxLayout(this, 5);
+
+ searchLineWidget_ = new KListViewSearchLineWidget(pids_, this);
+ topLayout->addWidget(searchLineWidget_);
+
+ topLayout->addWidget(pids_);
+ pids_->setFont(KGlobalSettings::fixedFont());
+
+ KButtonBox *buttonbox = new KButtonBox(this, Qt::Horizontal);
+ buttonbox->addStretch();
+ QPushButton *ok = buttonbox->addButton(KStdGuiItem::ok());
+ QPushButton *cancel = buttonbox->addButton(KStdGuiItem::cancel());
+ buttonbox->layout();
+ topLayout->addWidget(buttonbox);
+
+ connect(ok, SIGNAL(clicked()), SLOT(accept()));
+ connect(cancel, SIGNAL(clicked()), SLOT(reject()));
+
+ // Default display to 40 chars wide, default height is okay
+ resize( ((KGlobalSettings::fixedFont()).pointSize())*40, height());
+ topLayout->activate();
+
+ QTimer::singleShot(0, this, SLOT(slotInit()));
+
+}
+
+/***************************************************************************/
+
+Dbg_PS_Dialog::~Dbg_PS_Dialog()
+{
+ delete psProc_;
+}
+
+/***************************************************************************/
+
+int Dbg_PS_Dialog::pidSelected()
+{
+ return pids_->currentItem()->text(0).toInt();
+}
+
+/***************************************************************************/
+void Dbg_PS_Dialog::slotInit()
+{
+ psProc_ = new KShellProcess("/bin/sh");
+#ifdef USE_SOLARIS
+ *psProc_ << "ps";
+ *psProc_ << "-opid";
+ *psProc_ << "-otty";
+ *psProc_ << "-os";
+ *psProc_ << "-otime";
+ *psProc_ << "-oargs";
+ pidCmd_ = "ps -opid -otty -os -otime -oargs";
+
+ if (getuid() == 0) {
+ *psProc_ << "-e";
+ pidCmd_ += " -e";
+ }
+#else
+ *psProc_ << "ps";
+ *psProc_ << "x";
+ pidCmd_ = "ps x";
+
+ if (getuid() == 0) {
+ *psProc_ << "a";
+ pidCmd_ += " a";
+ }
+#endif
+
+ connect( psProc_, SIGNAL(processExited(KProcess *)), SLOT(slotProcessExited()) );
+ connect( psProc_, SIGNAL(receivedStdout(KProcess *, char *, int)), SLOT(slotReceivedOutput(KProcess *, char *, int)) );
+
+ psProc_->start(KProcess::NotifyOnExit, KProcess::Stdout);
+}
+
+/***************************************************************************/
+
+void Dbg_PS_Dialog::slotReceivedOutput(KProcess */*proc*/, char *buffer, int buflen)
+{
+ pidLines_ += QString::fromLocal8Bit(buffer, buflen);
+}
+
+/***************************************************************************/
+
+void Dbg_PS_Dialog::slotProcessExited()
+{
+ delete psProc_;
+ psProc_ = 0;
+
+ pidLines_ += '\n';
+
+ int start = pidLines_.find('\n', 0); // Skip the first line (header line)
+ int pos;
+
+ static QRegExp ps_output_line("^\\s*(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(.+)");
+ while ( (pos = pidLines_.find('\n', start)) != -1) {
+
+ QString item = pidLines_.mid(start, pos-start);
+ if (!item.isEmpty() && item.find(pidCmd_) == -1)
+ {
+ if(ps_output_line.search(item) == -1)
+ {
+ KMessageBox::information(
+ this,
+ // FIXME: probably should XML-escape 'item' before passing it
+ // to 'arg'.
+ i18n("<b>Could not parse output from the <tt>ps</tt> command.</b>"
+ "<p>The following line could not be parsed:"
+ "<b><tt>%1</tt>").arg(item),
+ i18n("Internal error"), "gdb_error" );
+ break;
+ }
+
+ new QListViewItem(pids_,
+ ps_output_line.cap(1),
+ ps_output_line.cap(2),
+ ps_output_line.cap(3),
+ ps_output_line.cap(4),
+ ps_output_line.cap(5));
+ }
+
+ start = pos+1;
+ }
+ // Need to set focus here too, as KListView will
+ // 'steal' it otherwise.
+ searchLineWidget_->searchLine()->setFocus();
+}
+
+void Dbg_PS_Dialog::focusIn(QFocusEvent*)
+{
+ searchLineWidget_->searchLine()->setFocus();
+}
+
+}
+
+/***************************************************************************/
+#include "dbgpsdlg.moc"
diff --git a/languages/cpp/debugger/dbgpsdlg.h b/languages/cpp/debugger/dbgpsdlg.h
new file mode 100644
index 00000000..d88eb72f
--- /dev/null
+++ b/languages/cpp/debugger/dbgpsdlg.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ begin : Mon Sep 20 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 _DBGPSDLG_H_
+#define _DBGPSDLG_H_
+
+#include <kdialog.h>
+
+class QListBox;
+class KProcess;
+class KListView;
+class KListViewSearchLineWidget;
+
+namespace GDBDebugger
+{
+
+/***************************************************************************/
+
+class Dbg_PS_Dialog : public KDialog
+{
+ Q_OBJECT
+
+public:
+ Dbg_PS_Dialog( QWidget *parent=0, const char *name=0 );
+ ~Dbg_PS_Dialog();
+
+ int pidSelected();
+
+private slots:
+ void slotInit();
+ void slotReceivedOutput(KProcess *proc, char *buffer, int buflen);
+ void slotProcessExited();
+
+protected:
+ void focusIn(QFocusEvent*);
+
+private:
+ KProcess* psProc_;
+ KListView* pids_;
+ KListViewSearchLineWidget* searchLineWidget_;
+ QString pidLines_;
+ QString pidCmd_;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/dbgtoolbar.cpp b/languages/cpp/debugger/dbgtoolbar.cpp
new file mode 100644
index 00000000..a3e58343
--- /dev/null
+++ b/languages/cpp/debugger/dbgtoolbar.cpp
@@ -0,0 +1,498 @@
+/***************************************************************************
+ begin : Thu Dec 23 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 QT_MAC
+#include "dbgtoolbar.h"
+#include "debuggerpart.h"
+#include "dbgcontroller.h"
+
+#include <kdockwindow.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <kwinmodule.h>
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// Implements a floating toolbar for the debugger.
+
+// Unfortunately, I couldn't get the KToolBar to work nicely when it
+// was floating, so I was forced to write these classes. I'm not sure whether
+// I didn't try hard enough or ... and I've forgotten what the problems were
+// now.
+
+// The problem with using this is that it will not dock as a normal toolbar.
+// I'm not convince that this is a real problem though.
+
+// So, if you can get it to work as a KToolBar, and it works well when the
+// app is running, then all these classes can be removed.
+
+// This code is very specific to the internal debugger in kdevelop.
+
+namespace GDBDebugger
+{
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// This just allows the user to click on the toolbar and drag it somewhere else.
+// I would have preferred to use normal decoration on the toolbar and removed
+// the iconify, close, etc buttons from the window title but again I kept running
+// into problems. Instead, I used no decoration and this class. Also this looks
+// similar to the KToolBar floating style.
+class DbgMoveHandle : public QFrame
+{
+public:
+ DbgMoveHandle(DbgToolBar *parent=0, const char * name=0, WFlags f=0);
+ virtual ~DbgMoveHandle();
+
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+
+private:
+ DbgToolBar* toolBar_;
+ QPoint offset_;
+ bool moving_;
+};
+
+// **************************************************************************
+
+DbgMoveHandle::DbgMoveHandle(DbgToolBar *parent, const char * name, WFlags f)
+ : QFrame(parent, name, f),
+ toolBar_(parent),
+ offset_(QPoint(0,0)),
+ moving_(false)
+{
+ setFrameStyle(QFrame::Panel|QFrame::Raised);
+ setFixedHeight(12);
+}
+
+// **************************************************************************
+
+DbgMoveHandle::~DbgMoveHandle()
+{
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mousePressEvent(QMouseEvent *e)
+{
+ QFrame::mousePressEvent(e);
+ if (moving_)
+ return;
+
+ if (e->button() == RightButton) {
+ KPopupMenu *menu = new KPopupMenu(this);
+ menu->insertTitle(i18n("Debug Toolbar"));
+ menu->insertItem(i18n("Dock to Panel"),
+ parent(), SLOT(slotDock()));
+ menu->insertItem(i18n("Dock to Panel && Iconify KDevelop"),
+ parent(), SLOT(slotIconifyAndDock()));
+ menu->popup(e->globalPos());
+ } else {
+ moving_ = true;
+ offset_ = parentWidget()->pos() - e->globalPos();
+ setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ QApplication::setOverrideCursor(QCursor(sizeAllCursor));
+ setPalette(QPalette(colorGroup().background()));
+ repaint();
+ }
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mouseReleaseEvent(QMouseEvent *e)
+{
+ QFrame::mouseReleaseEvent(e);
+ moving_ = false;
+ offset_ = QPoint(0,0);
+ setFrameStyle(QFrame::Panel|QFrame::Raised);
+ QApplication::restoreOverrideCursor();
+ setPalette(QPalette(colorGroup().background()));
+ repaint();
+}
+
+// **************************************************************************
+
+void DbgMoveHandle::mouseMoveEvent(QMouseEvent *e)
+{
+ QFrame::mouseMoveEvent(e);
+ if (!moving_)
+ return;
+
+ toolBar_->move(e->globalPos() + offset_);
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+// This class adds text _and_ a pixmap to a button. Why doesn't QPushButton
+// support that? It only allowed text _or_ pixmap.
+class DbgButton : public QPushButton
+{
+public:
+ DbgButton(const QPixmap &pixmap, const QString &text,
+ DbgToolBar *parent, const char *name=0);
+ virtual ~DbgButton() {};
+ void drawButtonLabel(QPainter *painter);
+ QSize sizeHint() const;
+
+private:
+ QPixmap pixmap_;
+};
+
+// **************************************************************************
+
+DbgButton::DbgButton(const QPixmap& pixmap, const QString& text,
+ DbgToolBar* parent, const char* name)
+ : QPushButton(parent, name),
+ pixmap_(pixmap)
+{
+ setText(text);
+}
+
+// **************************************************************************
+
+void DbgButton::drawButtonLabel(QPainter *painter)
+{
+ // We always have a pixmap (today...)
+ // Centre it if there's no text
+
+ bool hasText = !text().isEmpty();
+ int x = ((hasText ? height() : width()) - pixmap_.width()) / 2;
+ int y = (height() - pixmap_.height()) / 2;
+ painter->drawPixmap(x, y, pixmap_);
+
+ if (hasText) {
+ painter->setPen(colorGroup().text());
+ painter->drawText(height()+2, 0, width()-(height()+2), height(), AlignLeft|AlignVCenter, text());
+ }
+}
+
+// **************************************************************************
+
+QSize DbgButton::sizeHint() const
+{
+ if (text().isEmpty())
+ return pixmap_.size();
+ else
+ {
+ QSize ps = pixmap_.size();
+ QSize bs = QPushButton::sizeHint();
+ QSize result;
+ result.setWidth( ps.width() + bs.width()+10 );
+ result.setHeight( ps.height() > bs.height() ? ps.height() : bs.height() );
+ return result;
+ }
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+DbgDocker::DbgDocker(QWidget* parent, DbgToolBar* toolBar, const QPixmap& pixmap) :
+ KSystemTray(parent, "DbgDocker"),
+ toolBar_(toolBar)
+{
+ setPixmap(pixmap);
+ QToolTip::add( this, i18n("KDevelop debugger: Click to execute one line of code (\"step\")") );
+}
+
+// **************************************************************************
+
+void DbgDocker::mousePressEvent(QMouseEvent *e)
+{
+ if (!rect().contains( e->pos()))
+ return;
+
+ switch (e->button()) {
+ case LeftButton:
+ {
+ // Not really a click, but it'll hold for the time being !!!
+ emit clicked();
+ break;
+ }
+ case RightButton:
+ {
+ KPopupMenu* menu = new KPopupMenu(this);
+ menu->insertTitle(i18n("Debug Toolbar"));
+ menu->insertItem(i18n("Activate"), toolBar_, SLOT(slotUndock()));
+ menu->insertItem(i18n("Activate (KDevelop gets focus)"), toolBar_, SLOT(slotActivateAndUndock()));
+ menu->popup(e->globalPos());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+DbgToolBar::DbgToolBar(DebuggerPart* part,
+ QWidget* parent, const char* name)
+ : QFrame(0, name),
+ part_(part),
+ activeWindow_(0),
+ winModule_(0),
+ bKDevFocus_(0),
+ bPrevFocus_(0),
+ appIsActive_(false),
+ docked_(false),
+ docker_(0),
+ dockWindow_(new KSystemTray(parent))
+{
+ winModule_ = new KWinModule(this);
+ docker_ = new DbgDocker(parent, this, BarIcon("dbgnext"));
+ connect(docker_, SIGNAL(clicked()), part_, SLOT(slotStepOver()));
+
+ // Must have noFocus set so that we can see what window was active.
+ // see slotDbgKdevFocus() for more comments
+ // I do not want the user to be able to "close" this widget. If we have any
+ // decoration then they can and that is bad.
+ // This widget is closed when the debugger finishes i.e. they press "Stop"
+
+ // Do we need NoFocus???
+ KWin::setState(winId(), NET::StaysOnTop | NET::SkipTaskbar);
+// KWin::setType(winId(), NET::Override); // So it has no decoration
+ KWin::setType(winId(), NET::Dock);
+
+ setFocusPolicy(NoFocus);
+ setFrameStyle( QFrame::Box | QFrame::Plain );
+ setLineWidth(4);
+ setMidLineWidth(0);
+
+ QBoxLayout* topLayout = new QVBoxLayout(this);
+
+ QBoxLayout* nextLayout = new QHBoxLayout();
+ QBoxLayout* stepLayout = new QHBoxLayout();
+ QBoxLayout* focusLayout = new QHBoxLayout();
+
+ DbgMoveHandle* moveHandle= new DbgMoveHandle(this);
+
+ QPushButton* bRun = new DbgButton(BarIcon("dbgrun"), i18n("Run"), this);
+ QPushButton* bInterrupt = new DbgButton(BarIcon("player_pause"), i18n("Interrupt"), this);
+ QPushButton* bNext = new DbgButton(BarIcon("dbgnext"), QString::null, this);
+ QPushButton* bNexti = new DbgButton(BarIcon("dbgnextinst"), QString::null, this);
+ QPushButton* bStep = new DbgButton(BarIcon("dbgstep"), QString::null, this);
+ QPushButton* bStepi = new DbgButton(BarIcon("dbgstepinst"), QString::null, this);
+ QPushButton* bFinish = new DbgButton(BarIcon("dbgstepout"), i18n("Step Out"), this);
+ QPushButton* bRunTo = new DbgButton(BarIcon("dbgrunto"), i18n("Run to Cursor"), this);
+ QPushButton* bView = new DbgButton(BarIcon("dbgmemview"), i18n("Viewers"), this);
+ bKDevFocus_ = new DbgButton(BarIcon("kdevelop"), QString::null, this);
+ bPrevFocus_ = new DbgButton(BarIcon("dbgmemview"), QString::null, this);
+
+ connect(bRun, SIGNAL(clicked()), part_, SLOT(slotRun()));
+ connect(bInterrupt, SIGNAL(clicked()), part_, SLOT(slotPause()));
+ connect(bNext, SIGNAL(clicked()), part_, SLOT(slotStepOver()));
+ connect(bNexti, SIGNAL(clicked()), part_, SLOT(slotStepOverInstruction()));
+ connect(bStep, SIGNAL(clicked()), part_, SLOT(slotStepInto()));
+ connect(bStepi, SIGNAL(clicked()), part_, SLOT(slotStepIntoInstruction()));
+ connect(bFinish, SIGNAL(clicked()), part_, SLOT(slotStepOut()));
+ connect(bRunTo, SIGNAL(clicked()), part_, SLOT(slotRunToCursor()));
+ connect(bView, SIGNAL(clicked()), part_, SLOT(slotMemoryView()));
+ connect(bKDevFocus_, SIGNAL(clicked()), this, SLOT(slotKdevFocus()));
+ connect(bPrevFocus_, SIGNAL(clicked()), this, SLOT(slotPrevFocus()));
+
+ QToolTip::add( bRun, i18n("Continue with application execution, may start the application") );
+ QToolTip::add( bInterrupt, i18n("Interrupt the application execution") );
+ QToolTip::add( bNext, i18n("Execute one line of code, but run through functions") );
+ QToolTip::add( bNexti, i18n("Execute one assembler instruction, but run through functions") );
+ QToolTip::add( bStep, i18n("Execute one line of code, stepping into functions if appropriate") );
+ QToolTip::add( bStepi, i18n("Execute one assembler instruction, stepping into functions if appropriate") );
+ QToolTip::add( bFinish, i18n("Execute to end of current stack frame") );
+ QToolTip::add( bRunTo, i18n("Continues execution until the cursor position is reached.") );
+ QToolTip::add( bView, i18n("Memory, dissemble, registers, library viewers") );
+ QToolTip::add( bKDevFocus_, i18n("Set focus on KDevelop") );
+ QToolTip::add( bPrevFocus_, i18n("Set focus on window that had focus when KDevelop got focus") );
+
+ QWhatsThis::add( bRun, i18n("Continue with application execution. May start the application.") );
+ QWhatsThis::add( bInterrupt, i18n("Interrupt the application execution.") );
+ QWhatsThis::add( bNext, i18n("Execute one line of code, but run through functions.") );
+ QWhatsThis::add( bNexti, i18n("Execute one assembler instruction, but run through functions.") );
+ QWhatsThis::add( bStep, i18n("Execute one line of code, stepping into functions if appropriate.") );
+ QWhatsThis::add( bStepi, i18n("Execute one assembler instruction, stepping into functions if appropriate.") );
+ QWhatsThis::add( bFinish, i18n("Execute to end of current stack frame.") );
+ QWhatsThis::add( bRunTo, i18n("Continues execution until the cursor position is reached.") );
+ QWhatsThis::add( bView, i18n("Memory, dissemble, registers, library viewers.") );
+ QWhatsThis::add( bKDevFocus_, i18n("Set focus on KDevelop.") );
+ QWhatsThis::add( bPrevFocus_, i18n("Set focus on window that had focus when KDevelop got focus.") );
+
+ topLayout->addWidget(moveHandle);
+ topLayout->addWidget(bRun);
+ topLayout->addLayout(nextLayout);
+ topLayout->addLayout(stepLayout);
+ topLayout->addWidget(bFinish);
+ topLayout->addWidget(bRunTo);
+ topLayout->addWidget(bView);
+ topLayout->addWidget(bInterrupt);
+ topLayout->addLayout(focusLayout);
+
+ focusLayout->addWidget(bKDevFocus_);
+ focusLayout->addWidget(bPrevFocus_);
+
+ stepLayout->addWidget(bStep);
+ stepLayout->addWidget(bStepi);
+
+ nextLayout->addWidget(bNext);
+ nextLayout->addWidget(bNexti);
+
+// int w = QMAX(bRun->sizeHint().width(), bFinish->sizeHint().width());
+// w = QMAX(w, bInterrupt->sizeHint().width());
+// w = QMAX(w, bView->sizeHint().width());
+
+ // they should have the same height, so don't be too fussy
+// int h = bFinish->sizeHint().height();
+//
+// bNext->setMinimumHeight(h);
+// bNexti->setMinimumHeight(h);
+// bStep->setMinimumHeight(h);
+// bStepi->setMinimumHeight(h);
+// bKDevFocus_->setMinimumHeight(h);
+// bPrevFocus_->setMinimumHeight(h);
+
+// setMinimumSize(w+10, h*7);
+// setMaximumSize(w+10, h*7);
+
+ setAppIndicator(appIsActive_);
+ topLayout->activate();
+}
+
+// **************************************************************************
+
+DbgToolBar::~DbgToolBar()
+{
+ slotUndock();
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotKdevFocus()
+{
+ // I really want to be able to set the focus on the _application_ being debugged
+ // but this is the best compromise I can come up with. All we do is save the
+ // window that had focus when they switch to the kdevelop window. To do this
+ // the toolbar _cannot_ accept focus.
+ // If anyone has a way of determining what window the app is _actually_ running on
+ // then please fix and send a patch.
+
+ if (winModule_->activeWindow() != topLevelWidget()->winId())
+ activeWindow_ = winModule_->activeWindow();
+
+ KWin::activateWindow(topLevelWidget()->winId());
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotPrevFocus()
+{
+ KWin::activateWindow(activeWindow_);
+}
+
+// **************************************************************************
+
+// If the app is active then the app button is highlighted, otherwise
+// kdev button is highlighted.
+void DbgToolBar::slotDbgStatus(const QString&, int state)
+{
+ bool appIndicator = state & s_dbgBusy;
+ if (appIndicator != appIsActive_) {
+ setAppIndicator(appIndicator);
+ appIsActive_ = appIndicator;
+ }
+}
+
+// **************************************************************************
+
+void DbgToolBar::setAppIndicator(bool appIndicator)
+{
+ if (appIndicator) {
+ bPrevFocus_->setPalette(QPalette(colorGroup().mid()));
+ bKDevFocus_->setPalette(QPalette(colorGroup().background()));
+ } else {
+ bPrevFocus_->setPalette(QPalette(colorGroup().background()));
+ bKDevFocus_->setPalette(QPalette(colorGroup().mid()));
+ }
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotDock()
+{
+ if (docked_)
+ return;
+
+ // Q_ASSERT(!docker_);
+ hide();
+
+ docker_->show();
+ docked_ = true;
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotIconifyAndDock()
+{
+ if (docked_)
+ return;
+
+ // KWin::iconifyWindow(ckDevelop_->winId(), true);
+ slotDock();
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotUndock()
+{
+ if (!docked_)
+ return;
+
+ show();
+ docker_->hide();
+ docked_ = false;
+}
+
+// **************************************************************************
+
+void DbgToolBar::slotActivateAndUndock()
+{
+ if (!docked_)
+ return;
+
+ KWin::activateWindow(topLevelWidget()->winId());
+ slotUndock();
+}
+
+}
+
+// **************************************************************************
+#include "dbgtoolbar.moc"
+#endif
diff --git a/languages/cpp/debugger/dbgtoolbar.h b/languages/cpp/debugger/dbgtoolbar.h
new file mode 100644
index 00000000..4f284701
--- /dev/null
+++ b/languages/cpp/debugger/dbgtoolbar.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ begin : Thu Dec 23 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 *q
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#ifndef QT_MAC
+#ifndef _DBGTOOLBAR_H_
+#define _DBGTOOLBAR_H_
+
+class KWinModule;
+
+#include <ksystemtray.h>
+#include <kwin.h> // needed for WId :(
+
+#include <qframe.h>
+
+namespace GDBDebugger
+{
+
+class DbgButton;
+class DbgToolBar;
+class DebuggerPart;
+
+class DbgDocker : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ DbgDocker(QWidget *parent, DbgToolBar *toolBar, const QPixmap &pixmap);
+ virtual ~DbgDocker() {};
+ virtual void mousePressEvent(QMouseEvent *e);
+
+signals:
+ void clicked();
+
+private:
+ DbgToolBar* toolBar_;
+};
+
+
+class DbgToolBar : public QFrame
+{
+ Q_OBJECT
+
+public:
+ DbgToolBar(DebuggerPart *part, QWidget* parent, const char* name=0);
+ virtual ~DbgToolBar();
+
+private slots:
+ void slotDbgStatus(const QString&, int);
+ void slotDock();
+ void slotUndock();
+ void slotIconifyAndDock();
+ void slotActivateAndUndock();
+
+ void slotKdevFocus();
+ void slotPrevFocus();
+
+private:
+ void setAppIndicator(bool appIndicator);
+
+ DebuggerPart* part_;
+ WId activeWindow_;
+ KWinModule* winModule_;
+ DbgButton* bKDevFocus_;
+ DbgButton* bPrevFocus_;
+ bool appIsActive_;
+ bool docked_;
+ DbgDocker* docker_;
+ KSystemTray* dockWindow_;
+};
+
+}
+
+#endif
+#endif
diff --git a/languages/cpp/debugger/debuggerconfigwidget.cpp b/languages/cpp/debugger/debuggerconfigwidget.cpp
new file mode 100644
index 00000000..93e52e64
--- /dev/null
+++ b/languages/cpp/debugger/debuggerconfigwidget.cpp
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@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 "debuggerconfigwidget.h"
+
+#include "debuggerpart.h"
+#include "kdevproject.h"
+
+#include "domutil.h"
+#include <kurlrequester.h>
+#include <klineedit.h>
+
+#include <qcheckbox.h>
+#include <qfileinfo.h>
+#include <qradiobutton.h>
+
+namespace GDBDebugger
+{
+
+DebuggerConfigWidget::DebuggerConfigWidget(DebuggerPart* part, QWidget *parent, const char *name)
+ : DebuggerConfigWidgetBase(parent, name), dom(*part->projectDom())
+{
+ gdbPath_edit->setMode(KFile::File|KFile::ExistingOnly|KFile::LocalOnly);
+
+ gdbPath_edit->setURL( DomUtil::readEntry(dom, "/kdevdebugger/general/gdbpath"));
+
+ QString shell = DomUtil::readEntry(dom, "/kdevdebugger/general/dbgshell","no_value");
+ if( shell == QString("no_value") )
+ {
+ QFileInfo info( part->project()->buildDirectory() + "/libtool" );
+ if( info.exists() ) {
+ shell = "libtool";
+ } else {
+ // Try one directory up.
+ info.setFile( part->project()->buildDirectory() + "/../libtool" );
+ if( info.exists() ) {
+ shell = "../libtool";
+ } else {
+ // Give up.
+ shell = QString::null;
+ }
+ }
+ }
+ debuggingShell_edit->setURL( shell );
+
+ // Use setFile instead?
+ configGdbScript_edit->setURL( DomUtil::readEntry(dom, "/kdevdebugger/general/configGdbScript"));
+ runShellScript_edit ->setURL( DomUtil::readEntry(dom, "/kdevdebugger/general/runShellScript"));
+ runGdbScript_edit ->setURL( DomUtil::readEntry(dom, "/kdevdebugger/general/runGdbScript"));
+
+ displayStaticMembers_box->setChecked( DomUtil::readBoolEntry(dom, "/kdevdebugger/display/staticmembers", false));
+ asmDemangle_box->setChecked( DomUtil::readBoolEntry(dom, "/kdevdebugger/display/demanglenames", true));
+ breakOnLoadingLibrary_box->setChecked( DomUtil::readBoolEntry(dom, "/kdevdebugger/general/breakonloadinglibs", true));
+ dbgTerminal_box->setChecked( DomUtil::readBoolEntry(dom, "/kdevdebugger/general/separatetty", false));
+ enableFloatingToolBar_box->setChecked( DomUtil::readBoolEntry(dom, "/kdevdebugger/general/floatingtoolbar", false));
+ int outputRadix = DomUtil::readIntEntry(dom, "/kdevdebugger/display/outputradix", 10);
+
+ switch (outputRadix)
+ {
+ case 8:
+ outputRadixOctal->setChecked(true);
+ break;
+ case 16:
+ outputRadixHexadecimal->setChecked(true);
+ break;
+ case 10:
+ default:
+ outputRadixDecimal->setChecked(true);
+ break;
+ }
+
+
+ if( DomUtil::readBoolEntry( dom, "/kdevdebugger/general/raiseGDBOnStart", false ) )
+ {
+ radioGDB->setChecked(true);
+ }else
+ {
+ radioFramestack->setChecked(true);
+ }
+
+ // ??? DomUtil::readEntry(dom, "/kdevdebugger/general/allowforcedbpset");
+
+ resize(sizeHint());
+}
+
+
+DebuggerConfigWidget::~DebuggerConfigWidget()
+{}
+
+
+void DebuggerConfigWidget::accept()
+{
+ DomUtil::writeEntry(dom, "/kdevdebugger/general/gdbpath", gdbPath_edit->url());
+ DomUtil::writeEntry(dom, "/kdevdebugger/general/dbgshell", debuggingShell_edit->url());
+
+ DomUtil::writeEntry(dom, "/kdevdebugger/general/configGdbScript", configGdbScript_edit->url());
+ DomUtil::writeEntry(dom, "/kdevdebugger/general/runShellScript", runShellScript_edit ->url());
+ DomUtil::writeEntry(dom, "/kdevdebugger/general/runGdbScript", runGdbScript_edit ->url());
+
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/display/staticmembers", displayStaticMembers_box->isChecked());
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/display/demanglenames", asmDemangle_box->isChecked());
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/general/breakonloadinglibs", breakOnLoadingLibrary_box->isChecked());
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/general/separatetty", dbgTerminal_box->isChecked());
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/general/floatingtoolbar", enableFloatingToolBar_box->isChecked());
+
+ int outputRadix;
+ if (outputRadixOctal->isChecked())
+ outputRadix = 8;
+ else if (outputRadixHexadecimal->isChecked())
+ outputRadix = 16;
+ else
+ outputRadix = 10;
+
+ DomUtil::writeIntEntry(dom, "/kdevdebugger/display/outputradix", outputRadix);
+
+ if( radioGDB->isChecked() )
+ {
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/general/raiseGDBOnStart", true);
+ }else
+ {
+ DomUtil::writeBoolEntry(dom, "/kdevdebugger/general/raiseGDBOnStart", false);
+ }
+
+}
+
+}
+
+#include "debuggerconfigwidget.moc"
diff --git a/languages/cpp/debugger/debuggerconfigwidget.h b/languages/cpp/debugger/debuggerconfigwidget.h
new file mode 100644
index 00000000..dbf63d4b
--- /dev/null
+++ b/languages/cpp/debugger/debuggerconfigwidget.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@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 _DEBUGGERCONFIGWIDGET_H_
+#define _DEBUGGERCONFIGWIDGET_H_
+
+#include "debuggerconfigwidgetbase.h"
+
+#include <qdom.h>
+
+namespace GDBDebugger
+{
+
+class DebuggerPart;
+
+class DebuggerConfigWidget : public DebuggerConfigWidgetBase
+{
+ Q_OBJECT
+
+public:
+ DebuggerConfigWidget( DebuggerPart* part, QWidget *parent=0, const char *name=0 );
+ ~DebuggerConfigWidget();
+
+public slots:
+ void accept();
+
+private:
+ QDomDocument &dom;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/debuggerconfigwidgetbase.ui b/languages/cpp/debugger/debuggerconfigwidgetbase.ui
new file mode 100644
index 00000000..bf81b2d3
--- /dev/null
+++ b/languages/cpp/debugger/debuggerconfigwidgetbase.ui
@@ -0,0 +1,453 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>DebuggerConfigWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>debugger_config_widget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>558</width>
+ <height>627</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Debugger Configuration</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Debug arguments can be set on the Run Options page
+ or directly in the project manager</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>gdbPath_label</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Debugger executable:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>gdbPath_edit</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>gdbPath_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Gdb executable</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>To run "gdb" binary from $PATH, leave this field empty. To run custom gdb, for example, for a different architecture, enter the executable name here. You can either run gdb from $PATH, but with a different name (say, "arm-gdb"), by typing the name here, or specify full path to the gdb executable.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>debuggingShell_label</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Debugging &amp;shell:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>debuggingShell_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Run gdb in a special shell (mainly for automake projects)</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you want gdb to be executed by a special shell or tool insert it here. The main use-case is for Automake based projects where the application is actually only a script and libtool is needed to get it to run inside gdb.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>debuggingShell_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Run gdb in a special shell (mainly for automake projects)</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you want gdb to be executed by a special shell or tool insert it here. The main use-case is for Automake based projects where the application is actually only a script and &lt;b&gt;libtool&lt;/b&gt; is needed to get it to run inside gdb.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>asmDemangle_box</cstring>
+ </property>
+ <property name="text">
+ <string>Display &amp;demangled names</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When displaying the disassembled code you
+can select to see the methods' mangled names.
+However, non-mangled names are easier to read.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>breakOnLoadingLibrary_box</cstring>
+ </property>
+ <property name="text">
+ <string>Try settings &amp;breakpoints on library loading</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If GDB has not seen a library that will be loaded via
+"dlopen" then it will refuse to set a breakpoint in that code.
+We can get GDB to stop on a library load and hence
+try to set the pending breakpoints. See the documentation
+for more details relating to this behavior.
+
+If you are not "dlopen"ing libs then leave this unchecked.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>dbgTerminal_box</cstring>
+ </property>
+ <property name="text">
+ <string>Enable separate terminal for application &amp;IO</string>
+ </property>
+ <property name="accel">
+ <string>Alt+I</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This allows you to enter terminal input when your
+application contains terminal input code (e.g. cin, fgets, etc.).
+If you use terminal input in your application then check this option.
+Otherwise leave it unchecked.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>enableFloatingToolBar_box</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;nable floating toolbar</string>
+ </property>
+ <property name="accel">
+ <string>Alt+N</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use the floating toolbar. This toolbar always stays
+on top of all windows so that if the app covers KDevelop
+you have control of the app through the small toolbar. It
+can also be docked into the panel.
+
+This toolbar is in addition to the toolbar in KDevelop.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>displayStaticMembers_box</cstring>
+ </property>
+ <property name="text">
+ <string>Display static &amp;members</string>
+ </property>
+ <property name="accel">
+ <string>Alt+M</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Displaying static members makes GDB slower in
+producing data within KDE and Qt.
+It may change the "signature" of the data
+which QString and friends rely on,
+but if you need to debug into these values then
+check this option.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>globalOutputRadix</cstring>
+ </property>
+ <property name="title">
+ <string>Global Output Radix</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>outputRadixOctal</cstring>
+ </property>
+ <property name="text">
+ <string>Oct&amp;al</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>outputRadixHexadecimal</cstring>
+ </property>
+ <property name="text">
+ <string>He&amp;xadecimal</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>outputRadixDecimal</cstring>
+ </property>
+ <property name="text">
+ <string>Decimal</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Start Debugger With</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioFramestack</cstring>
+ </property>
+ <property name="text">
+ <string>Framestack</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioGDB</cstring>
+ </property>
+ <property name="text">
+ <string>GDB Output</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Remote Debugging</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This script is intended for the actual commands needed to connect to a remotely running executable.
+ shell sleep 5 wait for remote program to start
+ target remote ... connect to the remote debugger
+ continue [optional] run debugging to the first breakpoint.</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>runShellScript_label</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;un shell script:</string>
+ <comment>Shell script to be executed at run time</comment>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>runShellScript_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>runGdbScript_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Script to connect with remote application</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This script is sourced by gdb after the two preceding scripts have been executed.
+This script is intended for the actual commands needed to connect to a remotely running executable.
+ shell sleep 5 wait for remote program to start
+ target remote ... connect to the remote debugger
+ continue [optional] run debugging to the first breakpoint.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>runShellScript_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Script to start remote application</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This shell script is run after the Config gdb script has been sourced by gdb.
+When debugging remotely this script is intended to actually start the remote process.
+[It is expected that the debug executable can be reached on the target, maybe by downloading it as a final build step]
+1) Find a way to execute a command remotely - rsh, ssh, telnet, ...
+2a) Execute "gdbserver ... application" on target.
+or if your executable contains the gdb stub
+2b) Execute "application" on target.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>runGdbScript_label</cstring>
+ </property>
+ <property name="text">
+ <string>Run &amp;gdb script:</string>
+ <comment>Gdb script to be executed at run time.</comment>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>runGdbScript_edit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>configGdbScript_label</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Config gdb script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>configGdbScript_edit</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>configGdbScript_edit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Gdb configure script</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This script is sourced by gdb when the debugging starts.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>debuggingShell_edit</tabstop>
+ <tabstop>gdbPath_edit</tabstop>
+ <tabstop>displayStaticMembers_box</tabstop>
+ <tabstop>asmDemangle_box</tabstop>
+ <tabstop>breakOnLoadingLibrary_box</tabstop>
+ <tabstop>enableFloatingToolBar_box</tabstop>
+ <tabstop>dbgTerminal_box</tabstop>
+ <tabstop>outputRadixOctal</tabstop>
+ <tabstop>outputRadixDecimal</tabstop>
+ <tabstop>outputRadixHexadecimal</tabstop>
+ <tabstop>configGdbScript_edit</tabstop>
+ <tabstop>runShellScript_edit</tabstop>
+ <tabstop>runGdbScript_edit</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/languages/cpp/debugger/debuggerdcopinterface.h b/languages/cpp/debugger/debuggerdcopinterface.h
new file mode 100644
index 00000000..4a9bcf30
--- /dev/null
+++ b/languages/cpp/debugger/debuggerdcopinterface.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ * Copyright (C) 2003 Hamish Rodda <rodda@kde.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 DEBUGGERDCOPINTERFACE_H
+#define DEBUGGERDCOPINTERFACE_H
+
+#include <dcopobject.h>
+
+/**
+ * Enables dcop communication with the debugger part.
+ */
+class DebuggerDCOPInterface : public DCOPObject
+{
+ K_DCOP
+public:
+
+k_dcop:
+ virtual ASYNC slotDebugExternalProcess() = 0;
+ virtual ASYNC slotDebugCommandLine(const QString& command) = 0;
+};
+
+#endif
diff --git a/languages/cpp/debugger/debuggerpart.cpp b/languages/cpp/debugger/debuggerpart.cpp
new file mode 100644
index 00000000..c8c3c1a1
--- /dev/null
+++ b/languages/cpp/debugger/debuggerpart.cpp
@@ -0,0 +1,1272 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@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 "debuggerpart.h"
+#include "label_with_double_click.h"
+
+#include <qdir.h>
+#include <qvbox.h>
+#include <qwhatsthis.h>
+#include <qpopupmenu.h>
+#include <qtooltip.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kdevgenericfactory.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kstatusbar.h>
+#include <kparts/part.h>
+#include <ktexteditor/viewcursorinterface.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <qtimer.h>
+#include <kstringhandler.h>
+#include <kdockwidget.h>
+
+#include "kdevcore.h"
+#include "kdevproject.h"
+#include "kdevmainwindow.h"
+#include "kdevappfrontend.h"
+#include "kdevpartcontroller.h"
+#include "kdevdebugger.h"
+#include "domutil.h"
+#include "variablewidget.h"
+#include "gdbbreakpointwidget.h"
+#include "framestackwidget.h"
+#include "disassemblewidget.h"
+#include "processwidget.h"
+#include "gdbcontroller.h"
+#include "breakpoint.h"
+#include "dbgpsdlg.h"
+#include "dbgtoolbar.h"
+#include "memviewdlg.h"
+#include "gdbparser.h"
+#include "gdboutputwidget.h"
+#include "debuggerconfigwidget.h"
+#include "processlinemaker.h"
+
+#include <iostream>
+
+#include <kdevplugininfo.h>
+#include <debugger.h>
+
+
+
+
+
+namespace GDBDebugger
+{
+
+static const KDevPluginInfo data("kdevdebugger");
+
+typedef KDevGenericFactory<DebuggerPart> DebuggerFactory;
+K_EXPORT_COMPONENT_FACTORY( libkdevdebugger, DebuggerFactory( data ) )
+
+DebuggerPart::DebuggerPart( QObject *parent, const char *name, const QStringList & ) :
+ KDevPlugin( &data, parent, name ? name : "DebuggerPart" ),
+ controller(0), previousDebuggerState_(s_dbgNotStarted),
+ justRestarted_(false), needRebuild_(true),
+ running_(false)
+{
+ setObjId("DebuggerInterface");
+ setInstance(DebuggerFactory::instance());
+
+ setXMLFile("kdevdebugger.rc");
+
+ m_debugger = new Debugger( partController() );
+
+ statusBarIndicator = new LabelWithDoubleClick(
+ " ", mainWindow()->statusBar());
+ statusBarIndicator->setFixedWidth(15);
+ statusBarIndicator->setAlignment(Qt::AlignCenter);
+ mainWindow()->statusBar()->addWidget(statusBarIndicator, 0, true);
+ statusBarIndicator->show();
+
+ // Setup widgets and dbgcontroller
+
+ controller = new GDBController(*projectDom());
+
+
+ gdbBreakpointWidget = new GDBBreakpointWidget( controller,
+ 0, "gdbBreakpointWidget" );
+ gdbBreakpointWidget->setCaption(i18n("Breakpoint List"));
+ QWhatsThis::add
+ (gdbBreakpointWidget, i18n("<b>Breakpoint list</b><p>"
+ "Displays a list of breakpoints with "
+ "their current status. Clicking on a "
+ "breakpoint item allows you to change "
+ "the breakpoint and will take you "
+ "to the source in the editor window."));
+ gdbBreakpointWidget->setIcon( SmallIcon("stop") );
+ mainWindow()->embedOutputView(gdbBreakpointWidget, i18n("Breakpoints"), i18n("Debugger breakpoints"));
+
+ variableWidget = new VariableWidget( controller,
+ gdbBreakpointWidget,
+ 0, "variablewidget");
+ mainWindow()->embedSelectView(variableWidget, i18n("Variables"),
+ i18n("Debugger variable-view"));
+ mainWindow()->setViewAvailable(variableWidget, false);
+
+ framestackWidget = new FramestackWidget( controller, 0, "framestackWidget" );
+ framestackWidget->setEnabled(false);
+ framestackWidget->setCaption(i18n("Frame Stack"));
+ QWhatsThis::add
+ (framestackWidget, i18n("<b>Frame stack</b><p>"
+ "Often referred to as the \"call stack\", "
+ "this is a list showing what function is "
+ "currently active and who called each "
+ "function to get to this point in your "
+ "program. By clicking on an item you "
+ "can see the values in any of the "
+ "previous calling functions."));
+ framestackWidget->setIcon( SmallIcon("table") );
+ mainWindow()->embedOutputView(framestackWidget, i18n("Frame Stack"), i18n("Debugger function call stack"));
+ mainWindow()->setViewAvailable(framestackWidget, false);
+
+ disassembleWidget = new DisassembleWidget( controller, 0, "disassembleWidget" );
+ disassembleWidget->setEnabled(false);
+ disassembleWidget->setCaption(i18n("Machine Code Display"));
+ QWhatsThis::add
+ (disassembleWidget, i18n("<b>Machine code display</b><p>"
+ "A machine code view into your running "
+ "executable with the current instruction "
+ "highlighted. You can step instruction by "
+ "instruction using the debuggers toolbar "
+ "buttons of \"step over\" instruction and "
+ "\"step into\" instruction."));
+ disassembleWidget->setIcon( SmallIcon("gear") );
+ mainWindow()->embedOutputView(disassembleWidget, i18n("Disassemble"),
+ i18n("Debugger disassemble view"));
+ mainWindow()->setViewAvailable(disassembleWidget, false);
+
+ gdbOutputWidget = new GDBOutputWidget( 0, "gdbOutputWidget" );
+ gdbOutputWidget->setEnabled(false);
+ gdbOutputWidget->setIcon( SmallIcon("inline_image") );
+ gdbOutputWidget->setCaption(i18n("GDB Output"));
+ QWhatsThis::add
+ (gdbOutputWidget, i18n("<b>GDB output</b><p>"
+ "Shows all gdb commands being executed. "
+ "You can also issue any other gdb command while debugging."));
+ mainWindow()->embedOutputView(gdbOutputWidget, i18n("GDB"),
+ i18n("GDB output"));
+ mainWindow()->setViewAvailable(gdbOutputWidget, false);
+
+ // gdbBreakpointWidget -> this
+ connect( gdbBreakpointWidget, SIGNAL(refreshBPState(const Breakpoint&)),
+ this, SLOT(slotRefreshBPState(const Breakpoint&)));
+ connect( gdbBreakpointWidget, SIGNAL(publishBPState(const Breakpoint&)),
+ this, SLOT(slotRefreshBPState(const Breakpoint&)));
+ connect( gdbBreakpointWidget, SIGNAL(gotoSourcePosition(const QString&, int)),
+ this, SLOT(slotGotoSource(const QString&, int)) );
+
+
+ viewerWidget = new ViewerWidget( controller, 0, "viewerWidget");
+ mainWindow()->embedSelectView(viewerWidget,
+ i18n("Debug views"),
+ i18n("Special debugger views"));
+ mainWindow()->setViewAvailable(viewerWidget, false);
+ connect(viewerWidget, SIGNAL(setViewShown(bool)),
+ this, SLOT(slotShowView(bool)));
+
+ // Now setup the actions
+ KAction *action;
+
+// action = new KAction(i18n("&Start"), "1rightarrow", CTRL+SHIFT+Key_F9,
+ action = new KAction(i18n("&Start"), "dbgrun", Key_F9,
+ this, SLOT(slotRun()),
+ actionCollection(), "debug_run");
+ action->setToolTip( i18n("Start in debugger") );
+ action->setWhatsThis( i18n("<b>Start in debugger</b><p>"
+ "Starts the debugger with the project's main "
+ "executable. You may set some breakpoints "
+ "before this, or you can interrupt the program "
+ "while it is running, in order to get information "
+ "about variables, frame stack, and so on.") );
+
+ action = new KAction(i18n("&Restart"), "dbgrestart", 0,
+ this, SLOT(slotRestart()),
+ actionCollection(), "debug_restart");
+ action->setToolTip( i18n("Restart program") );
+ action->setWhatsThis( i18n("<b>Restarts application</b><p>"
+ "Restarts applications from the beginning."
+ ) );
+ action->setEnabled(false);
+
+
+ action = new KAction(i18n("Sto&p"), "stop", 0,
+ this, SLOT(slotStop()),
+ actionCollection(), "debug_stop");
+ action->setToolTip( i18n("Stop debugger") );
+ action->setWhatsThis(i18n("<b>Stop debugger</b><p>Kills the executable and exits the debugger."));
+
+ action = new KAction(i18n("Interrupt"), "player_pause", 0,
+ this, SLOT(slotPause()),
+ actionCollection(), "debug_pause");
+ action->setToolTip( i18n("Interrupt application") );
+ action->setWhatsThis(i18n("<b>Interrupt application</b><p>Interrupts the debugged process or current GDB command."));
+
+ action = new KAction(i18n("Run to &Cursor"), "dbgrunto", 0,
+ this, SLOT(slotRunToCursor()),
+ actionCollection(), "debug_runtocursor");
+ action->setToolTip( i18n("Run to cursor") );
+ action->setWhatsThis(i18n("<b>Run to cursor</b><p>Continues execution until the cursor position is reached."));
+
+
+ action = new KAction(i18n("Set E&xecution Position to Cursor"), "dbgjumpto", 0,
+ this, SLOT(slotJumpToCursor()),
+ actionCollection(), "debug_jumptocursor");
+ action->setToolTip( i18n("Jump to cursor") );
+ action->setWhatsThis(i18n("<b>Set Execution Position </b><p>Set the execution pointer to the current cursor position."));
+
+
+ action = new KAction(i18n("Step &Over"), "dbgnext", Key_F10,
+ this, SLOT(slotStepOver()),
+ actionCollection(), "debug_stepover");
+ action->setToolTip( i18n("Step over the next line") );
+ action->setWhatsThis( i18n("<b>Step over</b><p>"
+ "Executes one line of source in the current source file. "
+ "If the source line is a call to a function the whole "
+ "function is executed and the app will stop at the line "
+ "following the function call.") );
+
+
+ action = new KAction(i18n("Step over Ins&truction"), "dbgnextinst", 0,
+ this, SLOT(slotStepOverInstruction()),
+ actionCollection(), "debug_stepoverinst");
+ action->setToolTip( i18n("Step over instruction") );
+ action->setWhatsThis(i18n("<b>Step over instruction</b><p>Steps over the next assembly instruction."));
+
+
+ action = new KAction(i18n("Step &Into"), "dbgstep", Key_F11,
+ this, SLOT(slotStepInto()),
+ actionCollection(), "debug_stepinto");
+ action->setToolTip( i18n("Step into the next statement") );
+ action->setWhatsThis( i18n("<b>Step into</b><p>"
+ "Executes exactly one line of source. If the source line "
+ "is a call to a function then execution will stop after "
+ "the function has been entered.") );
+
+
+ action = new KAction(i18n("Step into I&nstruction"), "dbgstepinst", 0,
+ this, SLOT(slotStepIntoInstruction()),
+ actionCollection(), "debug_stepintoinst");
+ action->setToolTip( i18n("Step into instruction") );
+ action->setWhatsThis(i18n("<b>Step into instruction</b><p>Steps into the next assembly instruction."));
+
+
+ action = new KAction(i18n("Step O&ut"), "dbgstepout", Key_F12,
+ this, SLOT(slotStepOut()),
+ actionCollection(), "debug_stepout");
+ action->setToolTip( i18n("Steps out of the current function") );
+ action->setWhatsThis( i18n("<b>Step out</b><p>"
+ "Executes the application until the currently executing "
+ "function is completed. The debugger will then display "
+ "the line after the original call to that function. If "
+ "program execution is in the outermost frame (i.e. in "
+ "main()) then this operation has no effect.") );
+
+
+ action = new KAction(i18n("Viewers"), "dbgmemview", 0,
+ this, SLOT(slotMemoryView()),
+ actionCollection(), "debug_memview");
+ action->setToolTip( i18n("Debugger viewers") );
+ action->setWhatsThis(i18n("<b>Debugger viewers</b><p>Various information about application being executed. There are 4 views available:<br>"
+ "<b>Memory</b><br>"
+ "<b>Disassemble</b><br>"
+ "<b>Registers</b><br>"
+ "<b>Libraries</b>"));
+
+
+ action = new KAction(i18n("Examine Core File..."), "core", 0,
+ this, SLOT(slotExamineCore()),
+ actionCollection(), "debug_core");
+ action->setToolTip( i18n("Examine core file") );
+ action->setWhatsThis( i18n("<b>Examine core file</b><p>"
+ "This loads a core file, which is typically created "
+ "after the application has crashed, e.g. with a "
+ "segmentation fault. The core file contains an "
+ "image of the program memory at the time it crashed, "
+ "allowing you to do a post-mortem analysis.") );
+
+
+ action = new KAction(i18n("Attach to Process"), "connect_creating", 0,
+ this, SLOT(slotAttachProcess()),
+ actionCollection(), "debug_attach");
+ action->setToolTip( i18n("Attach to process") );
+ action->setWhatsThis(i18n("<b>Attach to process</b><p>Attaches the debugger to a running process."));
+
+ action = new KAction(i18n("Toggle Breakpoint"), 0, 0,
+ this, SLOT(toggleBreakpoint()),
+ actionCollection(), "debug_toggle_breakpoint");
+ action->setToolTip(i18n("Toggle breakpoint"));
+ action->setWhatsThis(i18n("<b>Toggle breakpoint</b><p>Toggles the breakpoint at the current line in editor."));
+
+ connect( mainWindow()->main()->guiFactory(), SIGNAL(clientAdded(KXMLGUIClient*)),
+ this, SLOT(guiClientAdded(KXMLGUIClient*)) );
+
+ connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)),
+ this, SLOT(projectConfigWidget(KDialogBase*)) );
+
+ connect( partController(), SIGNAL(loadedFile(const KURL &)),
+ gdbBreakpointWidget, SLOT(slotRefreshBP(const KURL &)) );
+ connect( debugger(), SIGNAL(toggledBreakpoint(const QString &, int)),
+ gdbBreakpointWidget, SLOT(slotToggleBreakpoint(const QString &, int)) );
+ connect( debugger(), SIGNAL(editedBreakpoint(const QString &, int)),
+ gdbBreakpointWidget, SLOT(slotEditBreakpoint(const QString &, int)) );
+ connect( debugger(), SIGNAL(toggledBreakpointEnabled(const QString &, int)),
+ gdbBreakpointWidget, SLOT(slotToggleBreakpointEnabled(const QString &, int)) );
+
+ connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
+ this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
+
+ connect( core(), SIGNAL(stopButtonClicked(KDevPlugin*)),
+ this, SLOT(slotStop(KDevPlugin*)) );
+ connect( core(), SIGNAL(projectClosed()),
+ this, SLOT(projectClosed()) );
+
+ connect( partController(), SIGNAL(activePartChanged(KParts::Part*)),
+ this, SLOT(slotActivePartChanged(KParts::Part*)) );
+
+ procLineMaker = new ProcessLineMaker();
+
+ connect( procLineMaker, SIGNAL(receivedStdoutLine(const QCString&)),
+ appFrontend(), SLOT(insertStdoutLine(const QCString&)) );
+ connect( procLineMaker, SIGNAL(receivedStderrLine(const QCString&)),
+ appFrontend(), SLOT(insertStderrLine(const QCString&)) );
+
+ connect( procLineMaker, SIGNAL(receivedPartialStdoutLine(const QCString&)),
+ appFrontend(), SLOT(addPartialStdoutLine(const QCString&)));
+ connect( procLineMaker, SIGNAL(receivedPartialStderrLine(const QCString&)),
+ appFrontend(), SLOT(addPartialStderrLine(const QCString&)));
+
+ // The output from tracepoints goes to "application" window, because
+ // we don't have any better alternative, and using yet another window
+ // is undesirable. Besides, this makes tracepoint look even more similar
+ // to printf debugging.
+ connect( gdbBreakpointWidget, SIGNAL(tracingOutput(const char*)),
+ procLineMaker, SLOT(slotReceivedStdout(const char*)));
+
+
+ connect(partController(), SIGNAL(savedFile(const KURL &)),
+ this, SLOT(slotFileSaved()));
+
+ if (project())
+ connect(project(), SIGNAL(projectCompiled()),
+ this, SLOT(slotProjectCompiled()));
+
+ setupController();
+ QTimer::singleShot(0, this, SLOT(setupDcop()));
+}
+
+void DebuggerPart::setupDcop()
+{
+ QCStringList objects = kapp->dcopClient()->registeredApplications();
+ for (QCStringList::Iterator it = objects.begin(); it != objects.end(); ++it)
+ if ((*it).find("drkonqi-") == 0)
+ slotDCOPApplicationRegistered(*it);
+
+ connect(kapp->dcopClient(), SIGNAL(applicationRegistered(const QCString&)), SLOT(slotDCOPApplicationRegistered(const QCString&)));
+ kapp->dcopClient()->setNotifications(true);
+}
+
+void DebuggerPart::slotDCOPApplicationRegistered(const QCString& appId)
+{
+ if (appId.find("drkonqi-") == 0) {
+ QByteArray answer;
+ QCString replyType;
+
+ kapp->dcopClient()->call(appId, "krashinfo", "appName()", QByteArray(), replyType, answer, true, 5000);
+
+ QDataStream d(answer, IO_ReadOnly);
+ QCString appName;
+ d >> appName;
+
+ if (appName.length() && project() && project()->mainProgram().endsWith(appName)) {
+ kapp->dcopClient()->send(appId, "krashinfo", "registerDebuggingApplication(QString)", i18n("Debug in &KDevelop"));
+ connectDCOPSignal(appId, "krashinfo", "acceptDebuggingApplication()", "slotDebugExternalProcess()", true);
+ }
+ }
+}
+
+ASYNC DebuggerPart::slotDebugExternalProcess()
+{
+ QByteArray answer;
+ QCString replyType;
+
+ kapp->dcopClient()->call(kapp->dcopClient()->senderId(), "krashinfo", "pid()", QByteArray(), replyType, answer, true, 5000);
+
+ QDataStream d(answer, IO_ReadOnly);
+ int pid;
+ d >> pid;
+
+ if (attachProcess(pid) && m_drkonqi.isEmpty()) {
+ m_drkonqi = kapp->dcopClient()->senderId();
+ QTimer::singleShot(15000, this, SLOT(slotCloseDrKonqi()));
+ mainWindow()->raiseView(framestackWidget);
+ }
+
+ mainWindow()->main()->raise();
+}
+
+ASYNC DebuggerPart::slotDebugCommandLine(const QString& /*command*/)
+{
+ KMessageBox::information(0, "Asked to debug command line");
+}
+
+void DebuggerPart::slotCloseDrKonqi()
+{
+ kapp->dcopClient()->send(m_drkonqi, "MainApplication-Interface", "quit()", QByteArray());
+ m_drkonqi = "";
+}
+
+DebuggerPart::~DebuggerPart()
+{
+ kapp->dcopClient()->setNotifications(false);
+
+ if (variableWidget)
+ mainWindow()->removeView(variableWidget);
+ if (gdbBreakpointWidget)
+ mainWindow()->removeView(gdbBreakpointWidget);
+ if (framestackWidget)
+ mainWindow()->removeView(framestackWidget);
+ if (disassembleWidget)
+ mainWindow()->removeView(disassembleWidget);
+ if(gdbOutputWidget)
+ mainWindow()->removeView(gdbOutputWidget);
+
+ delete variableWidget;
+ delete gdbBreakpointWidget;
+ delete framestackWidget;
+ delete disassembleWidget;
+ delete gdbOutputWidget;
+ delete controller;
+ delete floatingToolBar;
+ delete statusBarIndicator;
+ delete procLineMaker;
+
+ GDBParser::destroy();
+}
+
+
+void DebuggerPart::guiClientAdded( KXMLGUIClient* client )
+{
+ // Can't change state until after XMLGUI has been loaded...
+ // Anyone know of a better way of doing this?
+ if( client == this )
+ stateChanged( QString("stopped") );
+}
+
+void DebuggerPart::contextMenu(QPopupMenu *popup, const Context *context)
+{
+ if (!context->hasType( Context::EditorContext ))
+ return;
+
+ const EditorContext *econtext = static_cast<const EditorContext*>(context);
+ m_contextIdent = econtext->currentWord();
+
+ bool running = !(previousDebuggerState_ & s_dbgNotStarted);
+
+ // If debugger is running, we insert items at the top.
+ // The reason is user has explicitly run the debugger, so he's
+ // surely debugging, not editing code or something. So, first
+ // menu items should be about debugging, not some copy/paste/cut
+ // things.
+ if (!running)
+ popup->insertSeparator();
+
+ int index = running ? 0 : -1;
+ if (running)
+ {
+ // Too bad we can't add QAction to popup menu in Qt3.
+ KAction* act = actionCollection()->action("debug_runtocursor");
+ Q_ASSERT(act);
+ if (act)
+ {
+ int id = popup->insertItem( act->iconSet(), i18n("Run to &Cursor"),
+ this, SLOT(slotRunToCursor()),
+ 0, -1, index);
+
+ popup->setWhatsThis(id, act->whatsThis());
+ index += running;
+ }
+ }
+ if (econtext->url().isLocalFile())
+ {
+ int id = popup->insertItem( i18n("Toggle Breakpoint"),
+ this, SLOT(toggleBreakpoint()),
+ 0, -1, index);
+ index += running;
+ popup->setWhatsThis(id, i18n("<b>Toggle breakpoint</b><p>Toggles breakpoint at the current line."));
+ }
+ if (!m_contextIdent.isEmpty())
+ {
+ QString squeezed = KStringHandler::csqueeze(m_contextIdent, 30);
+ int id = popup->insertItem( i18n("Evaluate: %1").arg(squeezed),
+ this, SLOT(contextEvaluate()),
+ 0, -1, index);
+ index += running;
+ popup->setWhatsThis(id, i18n("<b>Evaluate expression</b><p>Shows the value of the expression under the cursor."));
+ int id2 = popup->insertItem( i18n("Watch: %1").arg(squeezed),
+ this, SLOT(contextWatch()),
+ 0, -1, index);
+ index += running;
+ popup->setWhatsThis(id2, i18n("<b>Watch expression</b><p>Adds an expression under the cursor to the Variables/Watch list."));
+ }
+ if (running)
+ popup->insertSeparator(index);
+}
+
+
+void DebuggerPart::toggleBreakpoint()
+{
+ KParts::ReadWritePart *rwpart
+ = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
+ KTextEditor::ViewCursorInterface *cursorIface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(partController()->activeWidget());
+
+ if (!rwpart || !cursorIface)
+ return;
+
+ uint line, col;
+ cursorIface->cursorPositionReal(&line, &col);
+
+ gdbBreakpointWidget->slotToggleBreakpoint(rwpart->url().path(), line);
+}
+
+
+void DebuggerPart::contextWatch()
+{
+ variableWidget->slotAddWatchVariable(m_contextIdent);
+}
+
+void DebuggerPart::contextEvaluate()
+{
+ variableWidget->slotEvaluateExpression(m_contextIdent);
+}
+
+void DebuggerPart::projectConfigWidget(KDialogBase *dlg)
+{
+ QVBox *vbox = dlg->addVBoxPage(i18n("Debugger"), i18n("Debugger"), BarIcon( info()->icon(), KIcon::SizeMedium) );
+ DebuggerConfigWidget *w = new DebuggerConfigWidget(this, vbox, "debugger config widget");
+ connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
+ connect( dlg, SIGNAL(finished()), controller, SLOT(configure()) );
+}
+
+
+void DebuggerPart::setupController()
+{
+ VariableTree *variableTree = variableWidget->varTree();
+
+ // variableTree -> gdbBreakpointWidget
+ connect( variableTree, SIGNAL(toggleWatchpoint(const QString &)),
+ gdbBreakpointWidget, SLOT(slotToggleWatchpoint(const QString &)));
+
+ // gdbOutputWidget -> controller
+ connect( gdbOutputWidget, SIGNAL(userGDBCmd(const QString &)),
+ controller, SLOT(slotUserGDBCmd(const QString&)));
+ connect( gdbOutputWidget, SIGNAL(breakInto()),
+ controller, SLOT(slotBreakInto()));
+
+ connect( controller, SIGNAL(breakpointHit(int)),
+ gdbBreakpointWidget, SLOT(slotBreakpointHit(int)));
+
+ // controller -> disassembleWidget
+ connect( controller, SIGNAL(showStepInSource(const QString&, int, const QString&)),
+ disassembleWidget, SLOT(slotShowStepInSource(const QString&, int, const QString&)));
+
+ // controller -> this
+ connect( controller, SIGNAL(dbgStatus(const QString&, int)),
+ this, SLOT(slotStatus(const QString&, int)));
+ connect( controller, SIGNAL(showStepInSource(const QString&, int, const QString&)),
+ this, SLOT(slotShowStep(const QString&, int)));
+ connect( controller, SIGNAL(debuggerAbnormalExit()),
+ this, SLOT(slotDebuggerAbnormalExit()));
+
+ connect(controller, SIGNAL(event(GDBController::event_t)),
+ this, SLOT(slotEvent(GDBController::event_t)));
+
+ // controller -> procLineMaker
+ connect( controller, SIGNAL(ttyStdout(const char*)),
+ procLineMaker, SLOT(slotReceivedStdout(const char*)));
+ connect( controller, SIGNAL(ttyStderr(const char*)),
+ procLineMaker, SLOT(slotReceivedStderr(const char*)));
+
+ // controller -> gdbOutputWidget
+ connect( controller, SIGNAL(gdbInternalCommandStdout(const char*)),
+ gdbOutputWidget, SLOT(slotInternalCommandStdout(const char*)) );
+ connect( controller, SIGNAL(gdbUserCommandStdout(const char*)),
+ gdbOutputWidget, SLOT(slotUserCommandStdout(const char*)) );
+
+ connect( controller, SIGNAL(gdbStderr(const char*)),
+ gdbOutputWidget, SLOT(slotReceivedStderr(const char*)) );
+ connect( controller, SIGNAL(dbgStatus(const QString&, int)),
+ gdbOutputWidget, SLOT(slotDbgStatus(const QString&, int)));
+
+ // controller -> viewerWidget
+ connect( controller, SIGNAL(dbgStatus(const QString&, int)),
+ viewerWidget, SLOT(slotDebuggerState(const QString&, int)));
+
+
+ connect(statusBarIndicator, SIGNAL(doubleClicked()),
+ controller, SLOT(explainDebuggerStatus()));
+
+}
+
+
+bool DebuggerPart::startDebugger()
+{
+ QString build_dir; // Currently selected build directory
+ DomUtil::PairList run_envvars; // List with the environment variables
+ QString run_directory; // Directory from where the program should be run
+ QString program; // Absolute path to application
+ QString run_arguments; // Command line arguments to be passed to the application
+
+ if (project()) {
+ build_dir = project()->buildDirectory();
+ run_envvars = project()->runEnvironmentVars();
+ run_directory = project()->runDirectory();
+ program = project()->mainProgram();
+ run_arguments = project()->debugArguments();
+ }
+
+ QString shell = DomUtil::readEntry(*projectDom(), "/kdevdebugger/general/dbgshell");
+ if( !shell.isEmpty() )
+ {
+ shell = shell.simplifyWhiteSpace();
+ QString shell_without_args = QStringList::split(QChar(' '), shell ).first();
+
+ QFileInfo info( shell_without_args );
+ if( info.isRelative() )
+ {
+ shell_without_args = build_dir + "/" + shell_without_args;
+ info.setFile( shell_without_args );
+ }
+ if( !info.exists() )
+ {
+ KMessageBox::information(
+ mainWindow()->main(),
+ i18n("Could not locate the debugging shell '%1'.").arg( shell_without_args ),
+ i18n("Debugging Shell Not Found"), "gdb_error" );
+ return false;
+ }
+ }
+
+ if (controller->start(shell, run_envvars, run_directory,
+ program, run_arguments))
+ {
+ core()->running(this, true);
+
+ stateChanged( QString("active") );
+
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Continue") );
+
+ ac->action("debug_run")->setToolTip(
+ i18n("Continues the application execution") );
+ ac->action("debug_run")->setWhatsThis(
+ i18n("Continue application execution\n\n"
+ "Continues the execution of your application in the "
+ "debugger. This only takes effect when the application "
+ "has been halted by the debugger (i.e. a breakpoint has "
+ "been activated or the interrupt was pressed).") );
+
+ mainWindow()->setViewAvailable(framestackWidget, true);
+ mainWindow()->setViewAvailable(disassembleWidget, true);
+ mainWindow()->setViewAvailable(gdbOutputWidget, true);
+ mainWindow()->setViewAvailable(variableWidget, true);
+
+ framestackWidget->setEnabled(true);
+ disassembleWidget->setEnabled(true);
+
+ gdbOutputWidget->setEnabled(true);
+
+
+ if (DomUtil::readBoolEntry(*projectDom(), "/kdevdebugger/general/floatingtoolbar", false))
+ {
+#ifndef QT_MAC
+ floatingToolBar = new DbgToolBar(this, mainWindow()->main());
+ floatingToolBar->show();
+#endif
+ }
+
+ running_ = true;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void DebuggerPart::slotStopDebugger()
+{
+ running_ = false;
+ controller->slotStopDebugger();
+ debugger()->clearExecutionPoint();
+
+ delete floatingToolBar;
+ floatingToolBar = 0;
+
+ gdbBreakpointWidget->reset();
+ disassembleWidget->clear();
+ gdbOutputWidget->clear();
+ disassembleWidget->slotActivate(false);
+
+// variableWidget->setEnabled(false);
+ framestackWidget->setEnabled(false);
+ disassembleWidget->setEnabled(false);
+ gdbOutputWidget->setEnabled(false);
+
+
+ mainWindow()->setViewAvailable(variableWidget, false);
+ mainWindow()->setViewAvailable(framestackWidget, false);
+ mainWindow()->setViewAvailable(disassembleWidget, false);
+ mainWindow()->setViewAvailable(gdbOutputWidget, false);
+
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Start") );
+// ac->action("debug_run")->setIcon( "1rightarrow" );
+ ac->action("debug_run")->setToolTip( i18n("Runs the program in the debugger") );
+ ac->action("debug_run")->setWhatsThis( i18n("Start in debugger\n\n"
+ "Starts the debugger with the project's main "
+ "executable. You may set some breakpoints "
+ "before this, or you can interrupt the program "
+ "while it is running, in order to get information "
+ "about variables, frame stack, and so on.") );
+
+ stateChanged( QString("stopped") );
+
+ core()->running(this, false);
+}
+
+void DebuggerPart::slotShowView(bool show)
+{
+ const QWidget* s = static_cast<const QWidget*>(sender());
+ QWidget* ncs = const_cast<QWidget*>(s);
+ mainWindow()->setViewAvailable(ncs, show);
+ if (show)
+ mainWindow()->raiseView(ncs);
+}
+
+void DebuggerPart::slotDebuggerAbnormalExit()
+{
+ mainWindow()->raiseView(gdbOutputWidget);
+
+ KMessageBox::information(
+ mainWindow()->main(),
+ i18n("<b>GDB exited abnormally</b>"
+ "<p>This is likely a bug in GDB. "
+ "Examine the gdb output window and then stop the debugger"),
+ i18n("GDB exited abnormally"), "gdb_error");
+
+ // Note: we don't stop the debugger here, becuse that will hide gdb
+ // window and prevent the user from finding the exact reason of the
+ // problem.
+}
+
+void DebuggerPart::slotFileSaved()
+{
+ needRebuild_ = true;
+}
+
+void DebuggerPart::slotProjectCompiled()
+{
+ needRebuild_ = false;
+}
+
+void DebuggerPart::projectClosed()
+{
+ slotStopDebugger();
+}
+
+void DebuggerPart::slotRun()
+{
+ if( controller->stateIsOn( s_dbgNotStarted ) ||
+ controller->stateIsOn( s_appNotStarted ) )
+ {
+ if (running_ && controller->stateIsOn(s_dbgNotStarted))
+ {
+ // User has already run the debugger, but it's not running.
+ // Most likely, the debugger has crashed, and the debuggerpart
+ // was left in 'running' state so that the user can examine
+ // gdb output or something. But now, need to fully shut down
+ // previous debug session.
+ slotStopDebugger();
+ }
+
+ // We're either starting gdb for the first time,
+ // or starting the application under gdb. In both
+ // cases, might need to rebuild the application.
+
+ // Note that this logic somewhat duplicates the
+ // isDirty method present in a number of project plugins.
+ // But there, it's a private method we can't conveniently
+ // access. Besides, the custom makefiles project manager won't
+ // care about a file unless it's explicitly added, so it can
+ // miss dependencies.
+
+ needRebuild_ |= haveModifiedFiles();
+
+ bool rebuild = false;
+ if (needRebuild_ && project())
+ {
+ // We don't add "Don't ask again" checkbox to the
+ // message because it's not clear if one cooked
+ // decision will be right for all cases when we're starting
+ // debugging with modified code, and because it's not clear
+ // how user can reset this "don't ask again" setting.
+ int r = KMessageBox::questionYesNoCancel(
+ 0,
+ "<b>" + i18n("Rebuild the project?") + "</b>" +
+ i18n("<p>The project is out of date. Rebuild it?"),
+ i18n("Rebuild the project?"));
+ if (r == KMessageBox::Cancel)
+ {
+ return;
+ }
+ if (r == KMessageBox::Yes)
+ {
+ rebuild = true;
+ }
+ else
+ {
+ // If the user said don't rebuild, try to avoid
+ // asking the same question again.
+ // Note that this only affects 'were any files changed'
+ // check, if a file is changed but not saved we'll
+ // still ask the user again. That's bad, but I don't know
+ // a better solution -- it's hard to check that
+ // the file has the same content as it had when the user
+ // last answered 'no, don't rebuild'.
+ needRebuild_ = false;
+ }
+
+ if (rebuild)
+ {
+ disconnect(SIGNAL(buildProject()));
+ // The KDevProject has no method to build the project,
+ // so try connecting to a slot has is present to all
+ // existing project managers.
+ // Note: this assumes that 'slotBuild' will save
+ // modified files.
+
+ if (connect(this, SIGNAL(buildProject()),
+ project(), SLOT(slotBuild())))
+ {
+ connect(project(), SIGNAL(projectCompiled()),
+ this, SLOT(slotRun_part2()));
+
+ emit buildProject();
+ rebuild = true;
+ }
+ }
+ }
+ if (!rebuild)
+ {
+ slotRun_part2();
+ }
+ return;
+ }
+ else
+ {
+ // When continuing the program, don't try to rebuild -- user
+ // has explicitly asked to "continue".
+ mainWindow()->statusBar()->message(i18n("Continuing program"), 1000);
+ }
+ controller->slotRun();
+}
+
+void DebuggerPart::slotRun_part2()
+{
+ needRebuild_ = false;
+
+ disconnect(project(), SIGNAL(projectCompiled()),
+ this, SLOT(slotRun_part2()));
+
+ if (controller->stateIsOn( s_dbgNotStarted ))
+ {
+ mainWindow()->statusBar()->message(i18n("Debugging program"), 1000);
+ if ( DomUtil::readBoolEntry( *projectDom(), "/kdevdebugger/general/raiseGDBOnStart", false ) )
+ {
+ mainWindow()->raiseView( gdbOutputWidget );
+ }else
+ {
+ mainWindow()->raiseView( framestackWidget );
+ }
+ appFrontend()->clearView();
+ startDebugger();
+ }
+ else if (controller->stateIsOn( s_appNotStarted ) )
+ {
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("&Continue") );
+ ac->action("debug_run")->setToolTip( i18n("Continues the application execution") );
+ ac->action("debug_run")->setWhatsThis( i18n("Continue application execution\n\n"
+ "Continues the execution of your application in the "
+ "debugger. This only takes effect when the application "
+ "has been halted by the debugger (i.e. a breakpoint has "
+ "been activated or the interrupt was pressed).") );
+
+ mainWindow()->statusBar()->message(i18n("Running program"), 1000);
+
+ appFrontend()->clearView();
+ }
+
+ controller->slotRun();
+}
+
+
+void DebuggerPart::slotRestart()
+{
+ // We implement restart as kill + slotRun, as opposed as plain "run"
+ // command because kill + slotRun allows any special logic in slotRun
+ // to apply for restart.
+ //
+ // That includes:
+ // - checking for out-of-date project
+ // - special setup for remote debugging.
+ //
+ // Had we used plain 'run' command, restart for remote debugging simply
+ // would not work.
+ controller->slotKill();
+ slotRun();
+}
+
+void DebuggerPart::slotExamineCore()
+{
+ mainWindow()->statusBar()->message(i18n("Choose a core file to examine..."), 1000);
+
+ QString dirName = project()? project()->projectDirectory() : QDir::homeDirPath();
+ QString coreFile = KFileDialog::getOpenFileName(dirName);
+ if (coreFile.isNull())
+ return;
+
+ mainWindow()->statusBar()->message(i18n("Examining core file %1").arg(coreFile), 1000);
+
+ startDebugger();
+ controller->slotCoreFile(coreFile);
+}
+
+
+void DebuggerPart::slotAttachProcess()
+{
+ mainWindow()->statusBar()->message(i18n("Choose a process to attach to..."), 1000);
+
+ Dbg_PS_Dialog dlg;
+ if (!dlg.exec() || !dlg.pidSelected())
+ return;
+
+ int pid = dlg.pidSelected();
+ attachProcess(pid);
+}
+
+bool DebuggerPart::attachProcess(int pid)
+{
+ mainWindow()->statusBar()->message(i18n("Attaching to process %1").arg(pid), 1000);
+
+ bool ret = startDebugger();
+ controller->slotAttachTo(pid);
+ return ret;
+}
+
+
+void DebuggerPart::slotStop(KDevPlugin* which)
+{
+ if( which != 0 && which != this )
+ return;
+
+// if( !controller->stateIsOn( s_dbgNotStarted ) && !controller->stateIsOn( s_shuttingDown ) )
+ slotStopDebugger();
+}
+
+
+void DebuggerPart::slotPause()
+{
+ controller->slotBreakInto();
+}
+
+
+void DebuggerPart::slotRunToCursor()
+{
+ KParts::ReadWritePart *rwpart
+ = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
+ KTextEditor::ViewCursorInterface *cursorIface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(partController()->activeWidget());
+
+ if (!rwpart || !rwpart->url().isLocalFile() || !cursorIface)
+ return;
+
+ uint line, col;
+ cursorIface->cursorPosition(&line, &col);
+
+ controller->slotRunUntil(rwpart->url().path(), ++line);
+}
+
+void DebuggerPart::slotJumpToCursor()
+{
+ KParts::ReadWritePart *rwpart
+ = dynamic_cast<KParts::ReadWritePart*>(partController()->activePart());
+ KTextEditor::ViewCursorInterface *cursorIface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(partController()->activeWidget());
+
+ if (!rwpart || !rwpart->url().isLocalFile() || !cursorIface)
+ return;
+
+ uint line, col;
+ cursorIface->cursorPositionReal(&line, &col);
+
+ controller->slotJumpTo(rwpart->url().path(), ++line);
+}
+
+void DebuggerPart::slotStepOver()
+{
+ controller->slotStepOver();
+}
+
+
+void DebuggerPart::slotStepOverInstruction()
+{
+ controller->slotStepOverIns();
+}
+
+
+void DebuggerPart::slotStepIntoInstruction()
+{
+ controller->slotStepIntoIns();
+}
+
+
+void DebuggerPart::slotStepInto()
+{
+ controller->slotStepInto();
+}
+
+
+void DebuggerPart::slotStepOut()
+{
+ controller->slotStepOutOff();
+}
+
+
+void DebuggerPart::slotMemoryView()
+{
+ viewerWidget->slotAddMemoryView();
+}
+
+void DebuggerPart::slotRefreshBPState( const Breakpoint& BP)
+{
+ if (BP.hasFileAndLine())
+ {
+ const FilePosBreakpoint& bp = dynamic_cast<const FilePosBreakpoint&>(BP);
+ if (bp.isActionDie())
+ {
+ debugger()->setBreakpoint(bp.fileName(), bp.lineNum()-1, -1, true, false);
+ }
+ else if (bp.isActionClear())
+ {
+ // Do nothing. This is always a result of breakpoint deletion,
+ // either via click on gutter, or via breakpoints window.
+ // We should not add marker for a breakpoint that's being deleted,
+ // because if user removes marker, and we re-add it here until
+ // we see 'actionDie' this can confuse the code.
+ // And no need to clear the marker, since we'll soon see 'actionDie'
+ // and clear it for good.
+ }
+ else
+ debugger()->setBreakpoint(bp.fileName(), bp.lineNum()-1,
+ 1/*bp->id()*/, bp.isEnabled(), bp.isPending() );
+ }
+}
+
+void DebuggerPart::slotStatus(const QString &msg, int state)
+{
+ QString stateIndicator, stateIndicatorFull;
+
+ if (state & s_dbgNotStarted)
+ {
+ stateIndicator = " ";
+ stateIndicatorFull = "Debugger not started";
+ stateChanged( QString("stopped") );
+ }
+ else if (state & s_dbgBusy)
+ {
+ stateIndicator = "R";
+ stateIndicatorFull = "Debugger is busy";
+ stateChanged( QString("active") );
+ }
+ else if (state & s_programExited)
+ {
+ stateIndicator = "E";
+ stateIndicatorFull = "Application has exited";
+ stateChanged( QString("stopped") );
+ }
+ else
+ {
+ stateIndicator = "P";
+ stateIndicatorFull = "Application is paused";
+ stateChanged( QString("paused") );
+ // On the first stop, show the variables view.
+ // We do it on first stop, and not at debugger start, because
+ // a program might just successfully run till completion. If we show
+ // the var views on start and hide on stop, this will look like flicker.
+ // On the other hand, if application is paused, it's very
+ // likely that the user wants to see variables.
+ if (justRestarted_)
+ {
+ justRestarted_ = false;
+ mainWindow()->setViewAvailable(variableWidget, true);
+ mainWindow()->raiseView(variableWidget);
+ }
+ }
+
+ if (state & s_appNotStarted)
+ {
+ KActionCollection *ac = actionCollection();
+ ac->action("debug_run")->setText( i18n("To start something","Start") );
+ ac->action("debug_run")->setToolTip( i18n("Restart the program in the debugger") );
+ ac->action("debug_run")->setWhatsThis( i18n("Restart in debugger\n\n"
+ "Restarts the program in the debugger") );
+ }
+
+
+ bool program_running = !(state & s_appNotStarted);
+ bool attached_or_core = (state & s_attached) || (state & s_core);
+
+ // If program is started, enable the 'restart' comand.
+ actionCollection()->action("debug_restart")->setEnabled(
+ program_running && !attached_or_core);
+
+
+ // As soon as debugger clears 's_appNotStarted' flag, we
+ // set 'justRestarted' variable.
+ // The other approach would be to set justRestarted in slotRun, slotCore
+ // and slotAttach.
+ // Note that setting this var in startDebugger is not OK, because the
+ // initial state of debugger is exactly the same as state after pause,
+ // so we'll always show varaibles view.
+ if ((previousDebuggerState_ & s_appNotStarted) &&
+ !(state & s_appNotStarted))
+ {
+ justRestarted_ = true;
+ }
+ if (state & s_appNotStarted)
+ {
+ justRestarted_ = false;
+ }
+
+ // And now? :-)
+ kdDebug(9012) << "Debugger state: " << stateIndicator << ": " << endl;
+ kdDebug(9012) << " " << msg << endl;
+
+ statusBarIndicator->setText(stateIndicator);
+ QToolTip::add(statusBarIndicator, stateIndicatorFull);
+ if (!msg.isEmpty())
+ mainWindow()->statusBar()->message(msg, 3000);
+
+
+ previousDebuggerState_ = state;
+}
+
+void DebuggerPart::slotEvent(GDBController::event_t e)
+{
+ if (e == GDBController::program_running ||
+ e == GDBController::program_exited ||
+ e == GDBController::debugger_exited)
+ {
+ debugger()->clearExecutionPoint();
+ }
+}
+
+
+void DebuggerPart::slotShowStep(const QString &fileName, int lineNum)
+{
+ if ( ! fileName.isEmpty() )
+ {
+ // Debugger counts lines from 1
+ debugger()->gotoExecutionPoint(KURL( fileName ), lineNum-1);
+ }
+ else
+ {
+ debugger()->clearExecutionPoint();
+ }
+}
+
+
+void DebuggerPart::slotGotoSource(const QString &fileName, int lineNum)
+{
+ if ( ! fileName.isEmpty() )
+ partController()->editDocument(KURL( fileName ), lineNum);
+}
+
+
+void DebuggerPart::slotActivePartChanged( KParts::Part* part )
+{
+ KAction* action = actionCollection()->action("debug_toggle_breakpoint");
+ if(!action)
+ return;
+
+ if(!part)
+ {
+ action->setEnabled(false);
+ return;
+ }
+ KTextEditor::ViewCursorInterface *iface
+ = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
+ action->setEnabled( iface != 0 );
+}
+
+void DebuggerPart::restorePartialProjectSession(const QDomElement* el)
+{
+ gdbBreakpointWidget->restorePartialProjectSession(el);
+ gdbOutputWidget->restorePartialProjectSession(el);
+}
+
+void DebuggerPart::savePartialProjectSession(QDomElement* el)
+{
+ gdbBreakpointWidget->savePartialProjectSession(el);
+ gdbOutputWidget->savePartialProjectSession(el);
+}
+
+bool DebuggerPart::haveModifiedFiles()
+{
+ bool have_modified = false;
+ KURL::List const& filelist = partController()->openURLs();
+ KURL::List::ConstIterator it = filelist.begin();
+ while ( it != filelist.end() )
+ {
+ if (partController()->documentState(*it) != Clean)
+ have_modified = true;
+
+ ++it;
+ }
+
+ return have_modified;
+}
+
+}
+
+KDevAppFrontend * GDBDebugger::DebuggerPart::appFrontend( )
+{
+ return extension<KDevAppFrontend>("KDevelop/AppFrontend");
+}
+
+KDevDebugger * GDBDebugger::DebuggerPart::debugger()
+{
+ return m_debugger;
+}
+
+#include "debuggerpart.moc"
diff --git a/languages/cpp/debugger/debuggerpart.h b/languages/cpp/debugger/debuggerpart.h
new file mode 100644
index 00000000..afb3b340
--- /dev/null
+++ b/languages/cpp/debugger/debuggerpart.h
@@ -0,0 +1,164 @@
+/***************************************************************************
+ * Copyright (C) 1999-2001 by John Birch *
+ * jbb@kdevelop.org *
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * bernd@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 _DEBUGGERPART_H_
+#define _DEBUGGERPART_H_
+
+#include <qguardedptr.h>
+#include "kdevplugin.h"
+#include "kdevcore.h"
+
+#include "gdbcontroller.h"
+#include "debuggerdcopinterface.h"
+
+namespace KParts { class Part; }
+
+class QLabel;
+class QPopupMenu;
+class KDialogBase;
+class ProcessWidget;
+class ProcessLineMaker;
+class KDevAppFrontend;
+class KDevDebugger;
+
+namespace GDBDebugger
+{
+
+class GDBBreakpointWidget;
+class FramestackWidget;
+class DisassembleWidget;
+class Breakpoint;
+class GDBController;
+class DbgToolBar;
+class VariableWidget;
+class GDBOutputWidget;
+class ViewerWidget;
+
+class DebuggerPart : public KDevPlugin, virtual public DebuggerDCOPInterface
+{
+ Q_OBJECT
+
+public:
+ DebuggerPart( QObject *parent, const char *name, const QStringList & );
+ ~DebuggerPart();
+ virtual void restorePartialProjectSession(const QDomElement* el);
+ virtual void savePartialProjectSession(QDomElement* el);
+
+k_dcop:
+ virtual ASYNC slotDebugExternalProcess();
+ virtual ASYNC slotDebugCommandLine(const QString& command);
+
+private slots:
+ void setupDcop();
+ void guiClientAdded(KXMLGUIClient*);
+ void contextMenu(QPopupMenu *popup, const Context *context);
+ void toggleBreakpoint();
+ void contextEvaluate();
+ void contextWatch();
+// void projectOpened();
+ void projectClosed();
+ void projectConfigWidget(KDialogBase *dlg);
+ void slotActivePartChanged(KParts::Part*);
+
+ void slotRun();
+ // Called to finish run operation in the case when we're
+ // starting the debugger. Called either directly from
+ // slotRun, if no rebuilding of project is needed, or
+ // indirectly from project()->projectCompiled() after project
+ // is rebuilt.
+ void slotRun_part2();
+ void slotRestart();
+ void slotExamineCore();
+ void slotAttachProcess();
+ void slotStopDebugger();
+ void slotStop(KDevPlugin* which = 0);
+ void slotPause();
+ void slotRunToCursor();
+ void slotJumpToCursor();
+ void slotStepOver();
+ void slotStepOverInstruction();
+ void slotStepIntoInstruction();
+ void slotStepInto();
+ void slotStepOut();
+ void slotMemoryView();
+
+ void slotRefreshBPState(const Breakpoint&);
+
+ void slotStatus(const QString &msg, int state);
+ void slotShowStep(const QString &fileName, int lineNum);
+ void slotGotoSource(const QString &fileName, int lineNum);
+
+ void slotDCOPApplicationRegistered(const QCString &appId);
+ void slotCloseDrKonqi();
+
+ // Hide or show the view that's the sender of this signal.
+ void slotShowView(bool enabled);
+
+ void slotDebuggerAbnormalExit();
+
+ // Called when some file in the project was saved.
+ // Sets 'needRebuild_' to true.
+ void slotFileSaved();
+
+ void slotProjectCompiled();
+
+ void slotEvent(GDBController::event_t);
+
+private:
+ KDevAppFrontend *appFrontend();
+ KDevDebugger *debugger();
+
+ bool attachProcess(int pid);
+ bool startDebugger();
+ void setupController();
+ bool haveModifiedFiles();
+
+ QGuardedPtr<VariableWidget> variableWidget;
+ QGuardedPtr<GDBBreakpointWidget> gdbBreakpointWidget;
+ QGuardedPtr<FramestackWidget> framestackWidget;
+ QGuardedPtr<DisassembleWidget> disassembleWidget;
+ QGuardedPtr<GDBOutputWidget> gdbOutputWidget;
+ QGuardedPtr<ViewerWidget> viewerWidget;
+ GDBController *controller;
+ QGuardedPtr<QLabel> statusBarIndicator;
+ QGuardedPtr<DbgToolBar> floatingToolBar;
+ ProcessLineMaker* procLineMaker;
+ ProcessLineMaker* gdbLineMaker;
+
+ QString m_contextIdent;
+ QCString m_drkonqi;
+
+ KDevDebugger *m_debugger;
+ int previousDebuggerState_;
+ // Set to true after each debugger restart
+ // Currently used to auto-show variables view
+ // on the first pause.
+ bool justRestarted_;
+
+ // Flag that specifies in project rebuild is necessary
+ // before running the debugger. Set to 'true' in constructor
+ // because we have no idea if project is 'dirty' or not
+ // when it's opened, and then set to 'true' each time a file is
+ // modified.
+ bool needRebuild_;
+
+ // Set by 'startDebugger' and cleared by 'slotStopDebugger'.
+ bool running_;
+
+signals:
+ void buildProject();
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/debuggertracingdialog.cpp b/languages/cpp/debugger/debuggertracingdialog.cpp
new file mode 100644
index 00000000..217f0b88
--- /dev/null
+++ b/languages/cpp/debugger/debuggertracingdialog.cpp
@@ -0,0 +1,104 @@
+
+#include "debuggertracingdialog.h"
+#include "breakpoint.h"
+
+#include <qbutton.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <klineedit.h>
+#include <keditlistbox.h>
+#include <kmessagebox.h>
+
+namespace GDBDebugger
+{
+
+ DebuggerTracingDialog
+ ::DebuggerTracingDialog(Breakpoint* bp,
+ QWidget* parent, const char* name)
+ : DebuggerTracingDialogBase(parent, name), bp_(bp)
+ {
+ expressions->setButtons(KEditListBox::Add | KEditListBox::Remove);
+
+ connect(enable, SIGNAL(stateChanged(int)),
+ this, SLOT(enableOrDisable(int)));
+
+ connect(enableCustomFormat, SIGNAL(stateChanged(int)),
+ this, SLOT(enableOrDisableCustomFormat(int)));
+
+ enable->setChecked(bp_->tracingEnabled());
+ expressions->setItems(bp_->tracedExpressions());
+ enableCustomFormat->setChecked(bp_->traceFormatStringEnabled());
+ customFormat->setText(bp_->traceFormatString());
+
+ enableOrDisable(enable->state());
+ }
+
+ void DebuggerTracingDialog::enableOrDisable(int state)
+ {
+ bool enable = (state == QButton::On);
+
+ expressionsLabel->setEnabled(enable);
+ expressions->setEnabled(enable);
+ enableCustomFormat->setEnabled(enable);
+ customFormat->setEnabled(enable && enableCustomFormat->isOn());
+ }
+
+ void DebuggerTracingDialog::enableOrDisableCustomFormat(int state)
+ {
+ customFormat->setEnabled(state == QButton::On);
+ }
+
+ void DebuggerTracingDialog::accept()
+ {
+ // Check that if we use format string,
+ // the number of expression is not larget than the number of
+ // format specifiers
+ bool ok = true;
+
+ if (enableCustomFormat->isOn())
+ {
+ QString s = customFormat->text();
+ unsigned percent_count = 0;
+ for (unsigned i = 0; i < s.length(); ++i)
+ if (s[i] == '%')
+ {
+ if (i+1 < s.length())
+ {
+ if (s[i+1] != '%')
+ {
+ ++percent_count;
+ }
+ else
+ {
+ // Double %
+ ++i;
+ }
+ }
+ }
+
+ if (percent_count < expressions->items().count())
+ {
+ ok = false;
+
+ KMessageBox::error(
+ this,
+ "<b>Not enough format specifiers</b>"
+ "<p>The number of format specifiers in the custom format "
+ "string is less then the number of expressions. Either remove "
+ "some expressions or edit the format string.",
+ "Not enough format specifiers");
+ }
+
+ }
+
+ if (ok)
+ {
+ bp_->setTracingEnabled(enable->isOn());
+ bp_->setTracedExpressions(expressions->items());
+ bp_->setTraceFormatStringEnabled(enableCustomFormat->isOn());
+ bp_->setTraceFormatString(customFormat->text());
+ DebuggerTracingDialogBase::accept();
+ }
+ }
+
+}
diff --git a/languages/cpp/debugger/debuggertracingdialog.h b/languages/cpp/debugger/debuggertracingdialog.h
new file mode 100644
index 00000000..1379f684
--- /dev/null
+++ b/languages/cpp/debugger/debuggertracingdialog.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * 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 DEBUGGER_TRACING_DIALOG_VP_2005_08_22
+#define DEBUGGER_TRACING_DIALOG_VP_2005_08_22
+
+#include "debuggertracingdialogbase.h"
+
+
+namespace GDBDebugger
+{
+ class Breakpoint;
+
+ class DebuggerTracingDialog : public DebuggerTracingDialogBase
+ {
+ Q_OBJECT
+ public:
+ DebuggerTracingDialog(Breakpoint* bp,
+ QWidget* parent, const char* name = 0);
+
+ private slots:
+ void enableOrDisable(int);
+ void enableOrDisableCustomFormat(int);
+
+ private:
+ void accept();
+
+ private:
+ Breakpoint* bp_;
+ };
+}
+
+#endif
diff --git a/languages/cpp/debugger/debuggertracingdialogbase.ui b/languages/cpp/debugger/debuggertracingdialogbase.ui
new file mode 100644
index 00000000..333dc4de
--- /dev/null
+++ b/languages/cpp/debugger/debuggertracingdialogbase.ui
@@ -0,0 +1,141 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>DebuggerTracingDialogBase</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>DebuggerTracingDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>348</width>
+ <height>409</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Tracing Configuration</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>enable</cstring>
+ </property>
+ <property name="text">
+ <string>Enable tracing</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Enable tracing&lt;/b&gt;
+&lt;p&gt;Tracing is a mechanism to automatically print values of the choosed expressions and continue execution when breakpoint is hit. You can think of it as printf debugging that does not require modifying the source.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="5" column="2">
+ <property name="name">
+ <cstring>cancelButton</cstring>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>enableCustomFormat</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Custom format string</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="5" column="1">
+ <property name="name">
+ <cstring>okButton</cstring>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="5" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>customFormat</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Custom format string&lt;/b&gt;
+&lt;p&gt;Specify a C-style format string that will be used when printing the choosen expression. For example:
+&lt;p align="center"&gt; &lt;tt&gt;Tracepoint 1: g = %d&lt;/tt&gt;&lt;/p&gt;
+If custom format string is not enabled, names and values of all expressions will be printed, using "%d" as format specifier for all expressions.</string>
+ </property>
+ </widget>
+ <widget class="KEditListBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>expressions</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>expressionsLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Expressions to print:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>okButton</sender>
+ <signal>clicked()</signal>
+ <receiver>DebuggerTracingDialogBase</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>DebuggerTracingDialogBase</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>enableOrDisable()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>keditlistbox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/languages/cpp/debugger/disassemblewidget.cpp b/languages/cpp/debugger/disassemblewidget.cpp
new file mode 100644
index 00000000..759393f5
--- /dev/null
+++ b/languages/cpp/debugger/disassemblewidget.cpp
@@ -0,0 +1,173 @@
+/***************************************************************************
+ begin : Tues Jan 3 2000
+ copyright : (C) 2000 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 "disassemblewidget.h"
+#include "gdbcontroller.h"
+#include "gdbcommand.h"
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <ktextedit.h>
+#include <kglobalsettings.h>
+
+#include <qdict.h>
+#include <qheader.h>
+#include <qtextedit.h>
+
+#include <stdlib.h>
+
+namespace GDBDebugger
+{
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+DisassembleWidget::DisassembleWidget(GDBController* controller, QWidget *parent, const char *name)
+ : QTextEdit(parent, name), controller_(controller),
+ active_(false),
+ lower_(0),
+ upper_(0),
+ address_(0)
+{
+ setFont(KGlobalSettings::fixedFont());
+ setReadOnly(true);
+}
+
+/***************************************************************************/
+
+DisassembleWidget::~DisassembleWidget()
+{}
+
+/***************************************************************************/
+
+bool DisassembleWidget::displayCurrent()
+{
+ Q_ASSERT(address_ >= lower_ || address_ <= upper_);
+
+ int line;
+ for (line=0; line < paragraphs(); line++)
+ {
+ unsigned long address = strtoul(text(line).latin1(), 0, 0);
+ if (address == address_)
+ {
+ // put cursor at start of line and highlight the line
+ setCursorPosition(line, 0);
+ setSelection(line,0,line+1,0,0);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/***************************************************************************/
+
+void DisassembleWidget::slotActivate(bool activate)
+{
+ kdDebug(9012) << "Disassemble widget active: " << activate << endl;
+
+ if (active_ != activate)
+ {
+ active_ = activate;
+ if (active_ && address_)
+ {
+ if (address_ < lower_ || address_ > upper_ || !displayCurrent())
+ getNextDisplay();
+ }
+ }
+}
+
+/***************************************************************************/
+
+void DisassembleWidget::slotShowStepInSource( const QString &, int,
+ const QString &currentAddress)
+{
+ kdDebug(9012) << "DisasssembleWidget::slotShowStepInSource()" << endl;
+
+ currentAddress_ = currentAddress;
+ address_ = strtoul(currentAddress.latin1(), 0, 0);
+ if (!active_)
+ return;
+
+ if (address_ < lower_ || address_ > upper_ || !displayCurrent())
+ getNextDisplay();
+}
+
+/***************************************************************************/
+
+void DisassembleWidget::getNextDisplay()
+{
+ kdDebug(9012) << "DisasssembleWidget::getNextDisplay()" << endl;
+
+ if (address_)
+ {
+ Q_ASSERT(!currentAddress_.isNull());
+
+ QString cmd = QString("-data-disassemble -s $pc -e \"$pc + 128\" -- 0");
+ controller_->addCommandToFront(
+ new GDBCommand( cmd, this, &DisassembleWidget::memoryRead ) );
+ }
+}
+
+/***************************************************************************/
+
+void DisassembleWidget::memoryRead(const GDBMI::ResultRecord& r)
+{
+ const GDBMI::Value& content = r["asm_insns"];
+ QString rawdata;
+
+ clear();
+
+ for(unsigned i = 0; i < content.size(); ++i)
+ {
+ const GDBMI::Value& line = content[i];
+
+ QString addr = line["address"].literal();
+ QString fct = line["func-name"].literal();
+ QString offs = line["offset"].literal();
+ QString inst = line["inst"].literal();
+
+ rawdata += QString(addr + " " + fct+"+"+offs + " " + inst + "\n");
+
+ if (i == 0) {
+ lower_ = strtoul(addr.latin1(), 0, 0);
+ } else if (i == content.size()-1) {
+ upper_ = strtoul(addr.latin1(), 0, 0);
+ }
+ }
+
+ append(rawdata);
+
+ displayCurrent();
+}
+
+
+void DisassembleWidget::showEvent(QShowEvent*)
+{
+ slotActivate(true);
+}
+
+
+void DisassembleWidget::hideEvent(QHideEvent*)
+{
+ slotActivate(false);
+}
+
+/***************************************************************************/
+
+}
+
+#include "disassemblewidget.moc"
diff --git a/languages/cpp/debugger/disassemblewidget.h b/languages/cpp/debugger/disassemblewidget.h
new file mode 100644
index 00000000..6b2fdd3a
--- /dev/null
+++ b/languages/cpp/debugger/disassemblewidget.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ 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 _DISASSEMBLEWIDGET_H_
+#define _DISASSEMBLEWIDGET_H_
+
+#include "mi/gdbmi.h"
+
+#include <qtextedit.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+class Breakpoint;
+class GDBController;
+
+class DisassembleWidget : public QTextEdit
+{
+ Q_OBJECT
+
+public:
+ DisassembleWidget( GDBController* controller, QWidget *parent=0, const char *name=0 );
+ virtual ~DisassembleWidget();
+
+public slots:
+ void slotActivate(bool activate);
+ void slotShowStepInSource(const QString &fileName, int lineNum, const QString &address);
+
+private:
+ virtual void showEvent(QShowEvent*);
+ virtual void hideEvent(QHideEvent*);
+
+ bool displayCurrent();
+ void getNextDisplay();
+
+ /// callback for GDBCommand
+ void memoryRead(const GDBMI::ResultRecord& r);
+
+ GDBController* controller_;
+ bool active_;
+ unsigned long lower_;
+ unsigned long upper_;
+ unsigned long address_;
+ QString currentAddress_;
+};
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+#endif
diff --git a/languages/cpp/debugger/framestackwidget.cpp b/languages/cpp/debugger/framestackwidget.cpp
new file mode 100644
index 00000000..e731a1ee
--- /dev/null
+++ b/languages/cpp/debugger/framestackwidget.cpp
@@ -0,0 +1,645 @@
+/***************************************************************************
+ 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 "framestackwidget.h"
+#include "gdbparser.h"
+#include "gdbcommand.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+#include <qheader.h>
+#include <qlistbox.h>
+#include <qregexp.h>
+#include <qstrlist.h>
+#include <qpainter.h>
+
+
+#include <ctype.h>
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+FramestackWidget::FramestackWidget(GDBController* controller,
+ QWidget *parent,
+ const char *name, WFlags f)
+ : QListView(parent, name, f),
+ viewedThread_(0),
+ controller_(controller),
+ mayUpdate_( false )
+{
+ setRootIsDecorated(true);
+ setSorting(-1);
+ setSelectionMode(Single);
+ addColumn(QString::null); // Frame number
+ addColumn(QString::null); // function name/address
+ addColumn(QString::null); // source
+ header()->hide();
+
+
+ // FIXME: maybe, all debugger components should derive from
+ // a base class that does this connect.
+ connect(controller, SIGNAL(event(GDBController::event_t)),
+ this, SLOT(slotEvent(GDBController::event_t)));
+
+ connect( this, SIGNAL(clicked(QListViewItem*)),
+ this, SLOT(slotSelectionChanged(QListViewItem*)) );
+}
+
+
+/***************************************************************************/
+
+FramestackWidget::~FramestackWidget()
+{}
+
+/***************************************************************************/
+
+QListViewItem *FramestackWidget::lastChild() const
+{
+ QListViewItem* child = firstChild();
+ if (child)
+ while (QListViewItem* nextChild = child->nextSibling())
+ child = nextChild;
+
+ return child;
+}
+
+// **************************************************************************
+
+void FramestackWidget::clear()
+{
+ viewedThread_ = 0;
+
+ QListView::clear();
+}
+
+/***************************************************************************/
+
+void FramestackWidget::slotSelectionChanged(QListViewItem *thisItem)
+{
+ ThreadStackItem *thread = dynamic_cast<ThreadStackItem*> (thisItem);
+ if (thread)
+ {
+ controller_->selectFrame(0, thread->threadNo());
+ }
+ else
+ {
+ FrameStackItem *frame = dynamic_cast<FrameStackItem*> (thisItem);
+ if (frame)
+ {
+ if (frame->text(0) == "...")
+ {
+ // Switch to the target thread.
+ if (frame->threadNo() != -1)
+ controller_->addCommand(
+ new GDBCommand(QString("-thread-select %1")
+ .arg(frame->threadNo()).ascii()));
+
+ viewedThread_ = findThread(frame->threadNo());
+ getBacktrace(frame->frameNo(), frame->frameNo() + frameChunk_);
+ }
+ else
+ {
+ controller_->
+ selectFrame(frame->frameNo(), frame->threadNo());
+ }
+ }
+ }
+}
+
+/***************************************************************************/
+
+void FramestackWidget::slotEvent(GDBController::event_t e)
+{
+ switch(e)
+ {
+ case GDBController::program_state_changed:
+
+ kdDebug(9012) << "Clearning framestack\n";
+ clear();
+
+ if ( isVisible() )
+ {
+ controller_->addCommand(
+ new GDBCommand("-thread-list-ids",
+ this, &FramestackWidget::handleThreadList));
+ mayUpdate_ = false;
+ }
+ else mayUpdate_ = true;
+
+ break;
+
+
+ case GDBController::thread_or_frame_changed:
+
+ if (viewedThread_)
+ {
+ // For non-threaded programs frame switch is no-op
+ // as far as framestack is concerned.
+ // FIXME: but need to highlight the current frame.
+
+ if (ThreadStackItem* item
+ = findThread(controller_->currentThread()))
+ {
+ viewedThread_ = item;
+
+ if (!item->firstChild())
+ {
+ // No backtrace for this thread yet.
+ getBacktrace();
+ }
+ }
+ }
+
+ break;
+
+ case GDBController::program_exited:
+ case GDBController::debugger_exited:
+ {
+ clear();
+ break;
+ }
+ case GDBController::debugger_busy:
+ case GDBController::debugger_ready:
+ case GDBController::shared_library_loaded:
+ case GDBController::program_running:
+ case GDBController::connected_to_program:
+ break;
+ }
+}
+
+void FramestackWidget::showEvent(QShowEvent*)
+{
+ if (controller_->stateIsOn(s_appRunning|s_dbgBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ if ( mayUpdate_ )
+ {
+ clear();
+
+ controller_->addCommand(
+ new GDBCommand( "-thread-list-ids", this, &FramestackWidget::handleThreadList ) );
+
+ mayUpdate_ = false;
+ }
+}
+
+void FramestackWidget::getBacktrace(int min_frame, int max_frame)
+{
+ minFrame_ = min_frame;
+ maxFrame_ = max_frame;
+
+ controller_->addCommand(
+ new GDBCommand(QString("-stack-info-depth %1").arg(max_frame+1),
+ this,
+ &FramestackWidget::handleStackDepth));
+}
+
+void FramestackWidget::handleStackDepth(const GDBMI::ResultRecord& r)
+{
+ int existing_frames = r["depth"].literal().toInt();
+
+ has_more_frames = (existing_frames > maxFrame_);
+
+ if (existing_frames < maxFrame_)
+ maxFrame_ = existing_frames;
+ //add the following command to the front, so noone switches threads in between
+ controller_->addCommandToFront(
+ new GDBCommand(QString("-stack-list-frames %1 %2")
+ .arg(minFrame_).arg(maxFrame_),
+ this, &FramestackWidget::parseGDBBacktraceList));
+}
+
+void FramestackWidget::getBacktraceForThread(int threadNo)
+{
+ unsigned currentThread = controller_->currentThread();
+ if (viewedThread_)
+ {
+ // Switch to the target thread.
+ controller_->addCommand(
+ new GDBCommand(QString("-thread-select %1")
+ .arg(threadNo).ascii()));
+
+ viewedThread_ = findThread(threadNo);
+ }
+
+ getBacktrace();
+
+ if (viewedThread_)
+ {
+ // Switch back to the original thread.
+ controller_->addCommand(
+ new GDBCommand(QString("-thread-select %1")
+ .arg(currentThread).ascii()));
+ }
+}
+
+void FramestackWidget::handleThreadList(const GDBMI::ResultRecord& r)
+{
+ // Gdb reply is:
+ // ^done,thread-ids={thread-id="3",thread-id="2",thread-id="1"},
+ // which syntactically is a tuple, but one has to access it
+ // by index anyway.
+ const GDBMI::TupleValue& ids =
+ dynamic_cast<const GDBMI::TupleValue&>(r["thread-ids"]);
+
+ if (ids.results.size() > 1)
+ {
+ // Need to iterate over all threads to figure out where each one stands.
+ // Note that this sequence of command will be executed in strict
+ // sequences, so no other view can add its command in between and
+ // get state for a wrong thread.
+
+ // Really threaded program.
+ for(unsigned i = 0, e = ids.results.size(); i != e; ++i)
+ {
+ QString id = ids.results[i]->value->literal();
+
+ controller_->addCommand(
+ new GDBCommand(QString("-thread-select %1").arg(id).ascii(),
+ this, &FramestackWidget::handleThread));
+ }
+
+ controller_->addCommand(
+ new GDBCommand(QString("-thread-select %1")
+ .arg(controller_->currentThread()).ascii()));
+ }
+
+ // Get backtrace for the current thread. We need to do this
+ // here, and not in event handler for program_state_changed,
+ // viewedThread_ is initialized by 'handleThread' before
+ // backtrace handler is called.
+ getBacktrace();
+}
+
+void FramestackWidget::handleThread(const GDBMI::ResultRecord& r)
+{
+ QString id = r["new-thread-id"].literal();
+ int id_num = id.toInt();
+
+ QString name_column;
+ QString func_column;
+ QString args_column;
+ QString source_column;
+
+ formatFrame(r["frame"], func_column, source_column);
+
+ ThreadStackItem* thread = new ThreadStackItem(this, id_num);
+ thread->setText(1, func_column);
+ thread->setText(2, source_column);
+
+ // The thread with a '*' is always the viewedthread
+
+ if (id_num == controller_->currentThread())
+ {
+ viewedThread_ = thread;
+ setSelected(viewedThread_, true);
+ }
+}
+
+
+void FramestackWidget::parseGDBBacktraceList(const GDBMI::ResultRecord& r)
+{
+ if (!r.hasField("stack"))
+ return;
+
+ const GDBMI::Value& frames = r["stack"];
+
+ if (frames.empty())
+ return;
+
+ Q_ASSERT(dynamic_cast<const GDBMI::ListValue*>(&frames));
+
+ // Remove "..." item, if there's one.
+ QListViewItem* last;
+ if (viewedThread_)
+ {
+ last = viewedThread_->firstChild();
+ if (last)
+ while(last->nextSibling())
+ last = last->nextSibling();
+ }
+ else
+ {
+ last = lastItem();
+ }
+ if (last && last->text(0) == "...")
+ delete last;
+
+ int lastLevel;
+ for(unsigned i = 0, e = frames.size(); i != e; ++i)
+ {
+ const GDBMI::Value& frame = frames[i];
+
+ // For now, just produce string simular to gdb
+ // console output. In future we might have a table,
+ // or something better.
+ QString frameDesc;
+
+ QString name_column;
+ QString func_column;
+ QString source_column;
+
+ QString level_s = frame["level"].literal();
+ int level = level_s.toInt();
+
+ name_column = "#" + level_s;
+
+ formatFrame(frame, func_column, source_column);
+
+ FrameStackItem* item;
+ if (viewedThread_)
+ item = new FrameStackItem(viewedThread_, level, name_column);
+ else
+ item = new FrameStackItem(this, level, name_column);
+ lastLevel = level;
+
+ item->setText(1, func_column);
+ item->setText(2, source_column);
+ }
+ if (has_more_frames)
+ {
+ QListViewItem* item;
+ if (viewedThread_)
+ item = new FrameStackItem(viewedThread_, lastLevel+1, "...");
+ else
+ item = new FrameStackItem(this, lastLevel+1, "...");
+ item->setText(1, "(click to get more frames)");
+ }
+
+ currentFrame_ = 0;
+ // Make sure the first frame in the stopped backtrace is selected
+ // and open
+ if (viewedThread_)
+ viewedThread_->setOpen(true);
+ else
+ {
+ if (FrameStackItem* frame = (FrameStackItem*) firstChild())
+ {
+ frame->setOpen(true);
+ setSelected(frame, true);
+ }
+ }
+}
+
+// **************************************************************************
+
+ThreadStackItem *FramestackWidget::findThread(int threadNo)
+{
+ QListViewItem *sibling = firstChild();
+ while (sibling)
+ {
+ ThreadStackItem *thread = dynamic_cast<ThreadStackItem*> (sibling);
+ if (thread && thread->threadNo() == threadNo)
+ {
+ return thread;
+ }
+ sibling = sibling->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+
+FrameStackItem *FramestackWidget::findFrame(int frameNo, int threadNo)
+{
+ QListViewItem* frameItem = 0;
+
+ if (threadNo != -1)
+ {
+ ThreadStackItem *thread = findThread(threadNo);
+ if (thread == 0)
+ return 0; // no matching thread?
+ frameItem = thread->firstChild();
+ }
+ else
+ frameItem = firstChild();
+
+ while (frameItem)
+ {
+ if (((FrameStackItem*)frameItem)->frameNo() == frameNo)
+ break;
+
+ frameItem = frameItem->nextSibling();
+ }
+ return (FrameStackItem*)frameItem;
+}
+
+void FramestackWidget::formatFrame(const GDBMI::Value& frame,
+ QString& func_column,
+ QString& source_column)
+{
+ func_column = source_column = "";
+
+ if (frame.hasField("func"))
+ {
+ func_column += " " + frame["func"].literal();
+ }
+ else
+ {
+ func_column += " " + frame["address"].literal();
+ }
+
+
+ if (frame.hasField("file"))
+ {
+ source_column = frame["file"].literal();
+
+ if (frame.hasField("line"))
+ {
+ source_column += ":" + frame["line"].literal();
+ }
+ }
+ else if (frame.hasField("from"))
+ {
+ source_column = frame["from"].literal();
+ }
+}
+
+
+void FramestackWidget::drawContentsOffset( QPainter * p, int ox, int oy,
+ int cx, int cy, int cw, int ch )
+{
+ QListView::drawContentsOffset(p, ox, oy, cx, cy, cw, ch);
+
+ int s1_x = header()->sectionPos(1);
+ int s1_w = header()->sectionSize(1);
+
+ QRect section1(s1_x, contentsHeight(), s1_w, viewport()->height());
+
+ p->fillRect(section1, KGlobalSettings::alternateBackgroundColor());
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+FrameStackItem::FrameStackItem(FramestackWidget *parent,
+ unsigned frameNo,
+ const QString &name)
+ : QListViewItem(parent, parent->lastChild()),
+ frameNo_(frameNo),
+ threadNo_(-1)
+{
+ setText(0, name);
+}
+
+// **************************************************************************
+
+FrameStackItem::FrameStackItem(ThreadStackItem *parent,
+ unsigned frameNo,
+ const QString &name)
+ : QListViewItem(parent, parent->lastChild()),
+ frameNo_(frameNo),
+ threadNo_(parent->threadNo())
+{
+ setText(0, name);
+}
+
+// **************************************************************************
+
+FrameStackItem::~FrameStackItem()
+{}
+
+// **************************************************************************
+
+QListViewItem *FrameStackItem::lastChild() const
+{
+ QListViewItem* child = firstChild();
+ if (child)
+ while (QListViewItem* nextChild = child->nextSibling())
+ child = nextChild;
+
+ return child;
+}
+
+// **************************************************************************
+
+void FrameStackItem::setOpen(bool open)
+{
+#if 0
+ if (open)
+ {
+ FramestackWidget* owner = (FramestackWidget*)listView();
+ if (this->threadNo() != owner->viewedThread() &&
+ this->frameNo() != owner->currentFrame_)
+ {
+ ((FramestackWidget*)listView())->slotSelectFrame(0, threadNo());
+ }
+ }
+#endif
+ QListViewItem::setOpen(open);
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+ThreadStackItem::ThreadStackItem(FramestackWidget *parent, unsigned threadNo)
+: QListViewItem(parent),
+ threadNo_(threadNo)
+{
+ setText(0, i18n("Thread %1").arg(threadNo_));
+ setExpandable(true);
+}
+
+// **************************************************************************
+
+ThreadStackItem::~ThreadStackItem()
+{}
+
+// **************************************************************************
+
+QListViewItem *ThreadStackItem::lastChild() const
+{
+ QListViewItem* child = firstChild();
+ if (child)
+ while (QListViewItem* nextChild = child->nextSibling())
+ child = nextChild;
+
+ return child;
+}
+
+// **************************************************************************
+
+void ThreadStackItem::setOpen(bool open)
+{
+ // If we're openining, and have no child yet, get backtrace from
+ // gdb.
+ if (open && !firstChild())
+ {
+ // Not that this will not switch to another thread (and won't show
+ // position in that other thread). This will only get the frames.
+
+ // Imagine you have 20 frames and you want to find one blocked on
+ // mutex. You don't want a new source file to be opened for each
+ // thread you open to find if that's the one you want to debug.
+ ((FramestackWidget*)listView())->getBacktraceForThread(threadNo());
+ }
+
+ if (open)
+ {
+ savedFunc_ = text(1);
+ setText(1, "");
+ savedSource_ = text(2);
+ setText(2, "");
+ }
+ else
+ {
+ setText(1, savedFunc_);
+ setText(2, savedSource_);
+ }
+
+ QListViewItem::setOpen(open);
+}
+
+void FrameStackItem::paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align )
+{
+ QColorGroup cg2(cg);
+ if (column % 2)
+ {
+ cg2.setColor(QColorGroup::Base,
+ KGlobalSettings::alternateBackgroundColor());
+ }
+ QListViewItem::paintCell(p, cg2, column, width, align);
+}
+
+void ThreadStackItem::paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align )
+{
+ QColorGroup cg2(cg);
+ if (column % 2)
+ {
+ cg2.setColor(QColorGroup::Base,
+ KGlobalSettings::alternateBackgroundColor());
+ }
+ QListViewItem::paintCell(p, cg2, column, width, align);
+}
+
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+#include "framestackwidget.moc"
diff --git a/languages/cpp/debugger/framestackwidget.h b/languages/cpp/debugger/framestackwidget.h
new file mode 100644
index 00000000..36cac1ba
--- /dev/null
+++ b/languages/cpp/debugger/framestackwidget.h
@@ -0,0 +1,183 @@
+/***************************************************************************
+ 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 _FRAMESTACKWIDGET_H_
+#define _FRAMESTACKWIDGET_H_
+
+#include <qlistview.h>
+#include <qstringlist.h>
+
+#include "gdbcontroller.h"
+#include "mi/miparser.h"
+
+#include <vector>
+
+namespace GDBDebugger
+{
+
+class FramestackWidget;
+
+
+class ThreadStackItem : public QListViewItem
+{
+public:
+ ThreadStackItem(FramestackWidget *parent,
+ unsigned threadNo);
+ virtual ~ThreadStackItem();
+
+ void setOpen(bool open);
+ QListViewItem *lastChild() const;
+
+ void paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align );
+
+ int threadNo()
+ { return threadNo_; }
+
+private:
+ int threadNo_;
+ QString savedFunc_;
+ QString savedSource_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class FrameStackItem : public QListViewItem
+{
+public:
+ FrameStackItem(FramestackWidget *parent,
+ unsigned frameNo,
+ const QString &name);
+ FrameStackItem(ThreadStackItem *parent,
+ unsigned frameNo,
+ const QString &name);
+ virtual ~FrameStackItem();
+
+ void setOpen(bool open);
+ QListViewItem *lastChild() const;
+
+ void paintCell(QPainter * p, const QColorGroup & cg,
+ int column, int width, int align );
+
+ int frameNo()
+ { return frameNo_; }
+ int threadNo()
+ { return threadNo_; }
+private:
+ int frameNo_;
+ int threadNo_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+
+/**
+ * @author John Birch
+ */
+class FramestackWidget : public QListView
+{
+ Q_OBJECT
+
+public:
+ FramestackWidget( GDBController* controller,
+ QWidget *parent=0,
+ const char *name=0, WFlags f=0 );
+ virtual ~FramestackWidget();
+
+ QListViewItem *lastChild() const;
+
+ ThreadStackItem *findThread(int threadNo);
+ FrameStackItem *findFrame(int frameNo, int threadNo);
+
+ int viewedThread()
+ { return viewedThread_ ? viewedThread_->threadNo() : -1; }
+
+protected:
+
+ void drawContentsOffset( QPainter * p, int ox, int oy,
+ int cx, int cy, int cw, int ch );
+
+
+
+private:
+ /** Given gdb's 'frame' information, compute decent
+ textual representation for display.
+
+ The function is used both for frames and threads.
+ */
+ void formatFrame(const GDBMI::Value& frame,
+ QString& func_column,
+ QString& source_column);
+
+ /** Cause gdb to produce backtrace for the current thread.
+
+ GDB reply will be route to parseArg and parseGDBBacktraceList,
+ and will show up under viewedThread_ (if there are threads), or
+ on top-level.
+ */
+ void getBacktrace(int min_frame = 0, int max_frame = frameChunk_);
+
+ /** Obtains backtrace for the specified thread without chaning the current
+ thread in gdb.
+
+ Switches viewedThread_ to the specified thread, switches gdb thread,
+ call getBacktrace(), and switches the current thread back.
+ */
+ void getBacktraceForThread(int threadNo);
+ friend class ThreadStackItem;
+
+
+ void handleThreadList(const GDBMI::ResultRecord&);
+ void handleThread(const GDBMI::ResultRecord&);
+ void parseGDBBacktraceList(const GDBMI::ResultRecord&);
+ void handleStackDepth(const GDBMI::ResultRecord& r);
+
+public slots:
+ void slotEvent(GDBController::event_t e);
+ void slotSelectionChanged(QListViewItem *thisItem);
+
+#if QT_VERSION < 300
+private:
+ QListViewItem* findItemWhichBeginsWith(const QString& text) const;
+#endif
+ virtual void showEvent(QShowEvent*);
+
+private:
+
+ void clear();
+
+private:
+
+ ThreadStackItem *viewedThread_;
+ int currentFrame_;
+ GDBController* controller_;
+
+ // Data to pass from 'getBacktrace' to 'handleStackDepth'
+ int minFrame_;
+ int maxFrame_;
+ bool has_more_frames;
+
+ bool mayUpdate_;
+
+ friend class FrameStackItem;
+
+ static const int frameChunk_ = 5;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/gdbbreakpointwidget.cpp b/languages/cpp/debugger/gdbbreakpointwidget.cpp
new file mode 100644
index 00000000..f9a83d6c
--- /dev/null
+++ b/languages/cpp/debugger/gdbbreakpointwidget.cpp
@@ -0,0 +1,1262 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 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 "gdbbreakpointwidget.h"
+#include "gdbtable.h"
+#include "debuggertracingdialog.h"
+#include "gdbcommand.h"
+#include "gdbcontroller.h"
+
+#include "breakpoint.h"
+#include "domutil.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurl.h>
+#include <kmessagebox.h>
+
+#include <qvbuttongroup.h>
+#include <qfileinfo.h>
+#include <qheader.h>
+#include <qtable.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+enum Column {
+ Control = 0,
+ Enable = 1,
+ Type = 2,
+ Status = 3,
+ Location = 4,
+ Condition = 5,
+ IgnoreCount = 6,
+ Hits = 7,
+ Tracing = 8
+};
+
+
+#define numCols 9
+
+enum BW_ITEMS { BW_ITEM_Show, BW_ITEM_Edit, BW_ITEM_Disable, BW_ITEM_Delete,
+ BW_ITEM_DisableAll, BW_ITEM_EnableAll, BW_ITEM_DeleteAll};
+
+static int m_activeFlag = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class BreakpointTableRow : public QTableItem
+{
+public:
+
+ BreakpointTableRow(QTable* table, EditType editType, Breakpoint* bp);
+ ~BreakpointTableRow();
+
+ bool match (Breakpoint* bp) const;
+ void reset ();
+ void setRow();
+
+ Breakpoint* breakpoint() { return m_breakpoint; }
+
+private:
+ void appendEmptyRow();
+
+private:
+ Breakpoint* m_breakpoint;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+BreakpointTableRow::BreakpointTableRow(QTable* parent, EditType editType,
+ Breakpoint* bp) :
+ QTableItem(parent, editType, ""),
+ m_breakpoint(bp)
+{
+ appendEmptyRow();
+ setRow();
+}
+
+/***************************************************************************/
+
+BreakpointTableRow::~BreakpointTableRow()
+{
+ m_breakpoint->deleteLater();
+}
+
+/***************************************************************************/
+
+bool BreakpointTableRow::match(Breakpoint* breakpoint) const
+{
+ return m_breakpoint->match(breakpoint);
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::reset()
+{
+ m_breakpoint->reset();
+ setRow();
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::appendEmptyRow()
+{
+ int row = table()->numRows();
+ table()->setNumRows(row+1);
+
+ table()->setItem(row, Control, this);
+
+ QCheckTableItem* cti = new QCheckTableItem( table(), "");
+ table()->setItem(row, Enable, cti);
+
+ ComplexEditCell* act = new ComplexEditCell(table());
+ table()->setItem(row, Tracing, act);
+ QObject::connect(act, SIGNAL(edit(QTableItem*)),
+ table()->parent(), SLOT(editTracing(QTableItem*)));
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::setRow()
+{
+ if ( m_breakpoint )
+ {
+ QTableItem *item = table()->item ( row(), Enable );
+ Q_ASSERT(item->rtti() == 2);
+ ((QCheckTableItem*)item)->setChecked(m_breakpoint->isEnabled());
+
+ QString status=m_breakpoint->statusDisplay(m_activeFlag);
+
+ table()->setText(row(), Status, status);
+ table()->setText(row(), Condition, m_breakpoint->conditional());
+ table()->setText(row(), IgnoreCount, QString::number(m_breakpoint->ignoreCount() ));
+ table()->setText(row(), Hits, QString::number(m_breakpoint->hits() ));
+
+ QString displayType = m_breakpoint->displayType();
+ table()->setText(row(), Location, m_breakpoint->location());
+
+
+ QTableItem* ce = table()->item( row(), Tracing );
+ ce->setText(breakpoint()->tracingEnabled() ? "Enabled" : "Disabled");
+ // In case there's editor open in this cell, update it too.
+ static_cast<ComplexEditCell*>(ce)->updateValue();
+
+
+ if (m_breakpoint->isTemporary())
+ displayType = i18n(" temporary");
+ if (m_breakpoint->isHardwareBP())
+ displayType += i18n(" hw");
+
+ table()->setText(row(), Type, displayType);
+ table()->adjustColumn(Type);
+ table()->adjustColumn(Status);
+ table()->adjustColumn(Location);
+ table()->adjustColumn(Hits);
+ table()->adjustColumn(IgnoreCount);
+ table()->adjustColumn(Condition);
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+GDBBreakpointWidget::GDBBreakpointWidget(GDBController* controller,
+ QWidget *parent, const char *name) :
+QHBox(parent, name),
+controller_(controller)
+{
+ m_table = new GDBTable(0, numCols, this, name);
+ m_table->setSelectionMode(QTable::SingleRow);
+ m_table->setShowGrid (false);
+ m_table->setLeftMargin(0);
+ m_table->setFocusStyle(QTable::FollowStyle);
+
+ m_table->hideColumn(Control);
+ m_table->setColumnReadOnly(Type, true);
+ m_table->setColumnReadOnly(Status, true);
+ m_table->setColumnReadOnly(Hits, true);
+ m_table->setColumnWidth( Enable, 20);
+
+ QHeader *header = m_table->horizontalHeader();
+
+ header->setLabel( Enable, "" );
+ header->setLabel( Type, i18n("Type") );
+ header->setLabel( Status, i18n("Status") );
+ header->setLabel( Location, i18n("Location") );
+ header->setLabel( Condition, i18n("Condition") );
+ header->setLabel( IgnoreCount, i18n("Ignore Count") );
+ header->setLabel( Hits, i18n("Hits") );
+ header->setLabel( Tracing, i18n("Tracing") );
+
+ QPopupMenu* newBreakpoint = new QPopupMenu(this);
+ newBreakpoint->insertItem(i18n("Code breakpoint", "Code"),
+ BP_TYPE_FilePos);
+ newBreakpoint->insertItem(i18n("Data breakpoint", "Data write"),
+ BP_TYPE_Watchpoint);
+ newBreakpoint->insertItem(i18n("Data read breakpoint", "Data read"),
+ BP_TYPE_ReadWatchpoint);
+
+
+ m_ctxMenu = new QPopupMenu( this );
+ m_ctxMenu->insertItem( i18n("New breakpoint", "New"),
+ newBreakpoint);
+ m_ctxMenu->insertItem( i18n( "Show text" ), BW_ITEM_Show );
+ int edit_id =
+ m_ctxMenu->insertItem( i18n( "Edit" ), BW_ITEM_Edit );
+ m_ctxMenu->setAccel(Qt::Key_Enter, edit_id);
+ m_ctxMenu->insertItem( i18n( "Disable" ), BW_ITEM_Disable );
+ int del_id =
+ m_ctxMenu->insertItem( SmallIcon("breakpoint_delete"),
+ i18n( "Delete" ), BW_ITEM_Delete );
+ m_ctxMenu->setAccel(Qt::Key_Delete, del_id);
+ m_ctxMenu->insertSeparator();
+ m_ctxMenu->insertItem( i18n( "Disable all"), BW_ITEM_DisableAll );
+ m_ctxMenu->insertItem( i18n( "Enable all"), BW_ITEM_EnableAll );
+ m_ctxMenu->insertItem( i18n( "Delete all"), BW_ITEM_DeleteAll );
+
+ m_table->show();
+
+ connect( newBreakpoint, SIGNAL(activated(int)),
+ this, SLOT(slotAddBlankBreakpoint(int)) );
+
+ connect( m_table, SIGNAL(contextMenuRequested(int, int, const QPoint &)),
+ this, SLOT(slotContextMenuShow(int, int, const QPoint & )) );
+ connect( m_ctxMenu, SIGNAL(activated(int)),
+ this, SLOT(slotContextMenuSelect(int)) );
+
+ connect( m_table, SIGNAL(doubleClicked(int, int, int, const QPoint &)),
+ this, SLOT(slotRowDoubleClicked(int, int, int, const QPoint &)));
+
+ connect( m_table, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotNewValue(int, int)));
+
+ connect( m_table, SIGNAL(returnPressed()),
+ this, SLOT(slotEditBreakpoint()));
+// connect( m_table, SIGNAL(f2Pressed()),
+// this, SLOT(slotEditBreakpoint()));
+ connect( m_table, SIGNAL(deletePressed()),
+ this, SLOT(slotRemoveBreakpoint()));
+// This slot doesn't exist anymore
+// connect( m_table, SIGNAL(insertPressed()),
+// this, SLOT(slotAddBlankBreakpoint()));
+
+ // FIXME: maybe, all debugger components should derive from
+ // a base class that does this connect.
+ connect(controller, SIGNAL(event(GDBController::event_t)),
+ this, SLOT(slotEvent(GDBController::event_t)));
+
+ connect(controller,
+ SIGNAL(watchpointHit(int, const QString&, const QString&)),
+ this,
+ SLOT(slotWatchpointHit(int, const QString&, const QString&)));
+}
+
+/***************************************************************************/
+
+GDBBreakpointWidget::~GDBBreakpointWidget()
+{
+ delete m_table;
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::reset()
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ btr->reset();
+ sendToGdb(*(btr->breakpoint()));
+ }
+ }
+}
+
+/***************************************************************************/
+
+// When a file is loaded then we need to tell the editor (display window)
+// which lines contain a breakpoint.
+void GDBBreakpointWidget::slotRefreshBP(const KURL &filename)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp && bp->hasFileAndLine()
+ && (bp->fileName() == filename.path()))
+ emit refreshBPState(*bp);
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotBreakpointHit(int id)
+{
+ BreakpointTableRow* br = findId(id);
+
+ // FIXME: should produce an message, this is most likely
+ // an error.
+ if (!br)
+ return;
+
+ Breakpoint* b = br->breakpoint();
+
+ if (b->tracingEnabled())
+ {
+ controller_->addCommand(
+ new CliCommand(("printf "
+ + b->traceRealFormatString()).latin1(),
+ this,
+ &GDBBreakpointWidget::handleTracingPrintf));
+
+ controller_->addCommand(new
+ GDBCommand("-exec-continue"));
+
+ }
+ else
+ {
+ controller_->demandAttention();
+ }
+}
+
+void GDBBreakpointWidget::slotWatchpointHit(int id,
+ const QString& oldValue,
+ const QString& newValue)
+{
+ BreakpointTableRow* br = findId(id);
+
+ // FIXME: should produce an message, this is most likely
+ // an error.
+ if (!br)
+ return;
+
+ Watchpoint* b = dynamic_cast<Watchpoint*>(br->breakpoint());
+
+
+ KMessageBox::information(
+ 0,
+ i18n("<b>Data write breakpoint</b><br>"
+ "Expression: %1<br>"
+ "Address: 0x%2<br>"
+ "Old value: %3<br>"
+ "New value: %4")
+ .arg(b->varName())
+ .arg(b->address(), 0, 16)
+ .arg(oldValue)
+ .arg(newValue));
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* GDBBreakpointWidget::find(Breakpoint *breakpoint)
+{
+ // NOTE:- The match doesn't have to be equal. Each type of bp
+ // must decide on the match criteria.
+ Q_ASSERT (breakpoint);
+
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->match(breakpoint))
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The Id is supplied by the debugger
+BreakpointTableRow* GDBBreakpointWidget::findId(int dbgId)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->dbgId() == dbgId)
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The key is a unique number supplied by us
+BreakpointTableRow* GDBBreakpointWidget::findKey(int BPKey)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->key() == BPKey)
+ return btr;
+ }
+
+ return 0;
+}
+
+bool GDBBreakpointWidget::hasWatchpointForAddress(
+ unsigned long long address) const
+{
+ for(int i = 0; i < m_table->numRows(); ++i)
+ {
+ BreakpointTableRow* br = (BreakpointTableRow*)
+ m_table->item(i, Control);
+
+ Watchpoint* w = dynamic_cast<Watchpoint*>(br->breakpoint());
+ if (w && w->address() == address)
+ return true;
+ }
+ return false;
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* GDBBreakpointWidget::addBreakpoint(Breakpoint *bp)
+{
+ BreakpointTableRow* btr =
+ new BreakpointTableRow( m_table, QTableItem::WhenCurrent, bp );
+
+ connect(bp, SIGNAL(modified(Breakpoint*)),
+ this, SLOT(slotBreakpointModified(Breakpoint*)));
+
+ sendToGdb(*bp);
+
+ return btr;
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::removeBreakpoint(BreakpointTableRow* btr)
+{
+ if (!btr)
+ return;
+
+ // Pending but the debugger hasn't started processing this bp so
+ // we can just remove it.
+ Breakpoint* bp = btr->breakpoint();
+ // No gdb breakpoint, and no breakpoint addition command in the
+ // queue. Just remove.
+ if (bp->dbgId() == -1 && !bp->isDbgProcessing())
+ {
+ bp->setActionDie();
+ sendToGdb(*bp);
+ m_table->removeRow(btr->row());
+ }
+ else
+ {
+ bp->setActionClear(true);
+ sendToGdb(*bp);
+ btr->setRow();
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ if (btr)
+ {
+ removeBreakpoint(btr);
+ }
+ else
+ addBreakpoint(fpBP);
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleBreakpointEnabled(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+ if (btr)
+ {
+ Breakpoint* bp=btr->breakpoint();
+ bp->setEnabled(!bp->isEnabled());
+ sendToGdb(*bp);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleWatchpoint(const QString &varName)
+{
+ Watchpoint *watchpoint = new Watchpoint(varName, false, true);
+ BreakpointTableRow* btr = find(watchpoint);
+ if (btr)
+ {
+ removeBreakpoint(btr);
+ delete watchpoint;
+ }
+ else
+ addBreakpoint(watchpoint);
+}
+
+void GDBBreakpointWidget::handleBreakpointList(const GDBMI::ResultRecord& r)
+{
+ m_activeFlag++;
+
+ const GDBMI::Value& blist = r["BreakpointTable"]["body"];
+
+ for(unsigned i = 0, e = blist.size(); i != e; ++i)
+ {
+ const GDBMI::Value& b = blist[i];
+
+ int id = b["number"].literal().toInt();
+ BreakpointTableRow* btr = findId(id);
+ if (btr)
+ {
+ Breakpoint *bp = btr->breakpoint();
+ bp->setActive(m_activeFlag, id);
+ bp->setHits(b["times"].toInt());
+ if (b.hasField("ignore"))
+ bp->setIgnoreCount(b["ignore"].toInt());
+ else
+ bp->setIgnoreCount(0);
+ if (b.hasField("cond"))
+ bp->setConditional(b["cond"].literal());
+ else
+ bp->setConditional(QString::null);
+ btr->setRow();
+ emit publishBPState(*bp);
+ }
+ else
+ {
+ // It's a breakpoint added outside, most probably
+ // via gdb console. Add it now.
+ QString type = b["type"].literal();
+
+ if (type == "breakpoint" || type == "hw breakpoint")
+ {
+ if (b.hasField("fullname") && b.hasField("line"))
+ {
+ Breakpoint* bp = new FilePosBreakpoint(
+ b["fullname"].literal(),
+ b["line"].literal().toInt());
+
+ bp->setActive(m_activeFlag, id);
+ bp->setActionAdd(false);
+ bp->setPending(false);
+
+ new BreakpointTableRow(m_table,
+ QTableItem::WhenCurrent,
+ bp);
+
+ emit publishBPState(*bp);
+ }
+ }
+
+ }
+ }
+
+ // Remove any inactive breakpoints.
+ for ( int row = m_table->numRows()-1; row >= 0 ; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if (!(bp->isActive(m_activeFlag)))
+ {
+ // FIXME: need to review is this happens for
+ // as-yet unset breakpoint.
+ bp->removedInGdb();
+ }
+ }
+ }
+}
+
+void GDBBreakpointWidget::handleTracingPrintf(const QValueVector<QString>& s)
+{
+ // The first line of output is the command itself, which we don't need.
+ for(unsigned i = 1; i < s.size(); ++i)
+ emit tracingOutput(s[i].local8Bit());
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotBreakpointSet(Breakpoint* bp)
+{
+ // FIXME: why 'key' is used here?
+ BreakpointTableRow* btr = findKey(bp->key());
+ if (!btr)
+ {
+ kdDebug(9012) << "Early return\n";
+ return;
+ }
+
+ btr->setRow();
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotAddBlankBreakpoint(int idx)
+{
+ BreakpointTableRow* btr = 0;
+ switch (idx)
+ {
+ case BP_TYPE_FilePos:
+ btr = addBreakpoint(new FilePosBreakpoint());
+ break;
+
+ case BP_TYPE_Watchpoint:
+ btr = addBreakpoint(new Watchpoint(""));
+ break;
+
+ case BP_TYPE_ReadWatchpoint:
+ btr = addBreakpoint(new ReadWatchpoint(""));
+ break;
+
+ default:
+ break;
+ }
+
+ if (btr)
+ {
+ m_table->selectRow(btr->row());
+ m_table->editCell(btr->row(), Location, false);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRemoveBreakpoint()
+{
+ int row = m_table->currentRow();
+ if ( row != -1)
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRemoveAllBreakpoints()
+{
+ for ( int row = m_table->numRows()-1; row>=0; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRowDoubleClicked(int row, int col, int btn, const QPoint &)
+{
+ if ( btn == Qt::LeftButton )
+ {
+// kdDebug(9012) << "in slotRowSelected row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp && bp->hasFileAndLine())
+ emit gotoSourcePosition(bp->fileName(), bp->lineNum()-1);
+
+ // put the focus back on the clicked item if appropriate
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotContextMenuShow( int row, int /*col*/, const QPoint &mousePos )
+{
+ BreakpointTableRow *btr = (BreakpointTableRow *)m_table->item(row, Control );
+
+ if (btr == NULL)
+ {
+ btr = (BreakpointTableRow *)m_table->item(m_table->currentRow(),
+ Control );
+ }
+
+ if (btr != NULL)
+ {
+ m_ctxMenu->setItemEnabled(
+ BW_ITEM_Show,
+ btr->breakpoint()->hasFileAndLine());
+
+ if (btr->breakpoint( )->isEnabled( ))
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Disable") );
+ }
+ else
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Enable") );
+ }
+
+ m_ctxMenu->setItemEnabled(BW_ITEM_Disable, true);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, true);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Edit, true);
+ }
+ else
+ {
+ m_ctxMenu->setItemEnabled(BW_ITEM_Show, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Disable, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Edit, false);
+ }
+
+ bool has_bps = (m_table->numRows() != 0);
+ m_ctxMenu->setItemEnabled(BW_ITEM_DisableAll, has_bps);
+ m_ctxMenu->setItemEnabled(BW_ITEM_EnableAll, has_bps);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, has_bps);
+
+ m_ctxMenu->popup( mousePos );
+}
+
+void GDBBreakpointWidget::slotContextMenuSelect( int item )
+{
+ int row, col;
+ BreakpointTableRow *btr;
+ Breakpoint *bp;
+ FilePosBreakpoint *fbp;
+
+ row= m_table->currentRow( );
+ if (row == -1)
+ return;
+ btr = (BreakpointTableRow *)m_table->item( row, Control );
+ if (btr == NULL)
+ return;
+ bp = btr->breakpoint( );
+ if (bp == NULL)
+ return;
+ fbp = dynamic_cast<FilePosBreakpoint*>(bp);
+
+ switch( item )
+ {
+ case BW_ITEM_Show:
+ if (fbp)
+ emit gotoSourcePosition(fbp->fileName(), fbp->lineNum()-1);
+ break;
+ case BW_ITEM_Edit:
+ col = m_table->currentColumn( );
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ break;
+ case BW_ITEM_Disable:
+
+ bp->setEnabled( !bp->isEnabled( ) );
+ btr->setRow( );
+ sendToGdb( *bp );
+ break;
+ case BW_ITEM_Delete:
+ slotRemoveBreakpoint( );
+ break;
+ case BW_ITEM_DeleteAll:
+ slotRemoveAllBreakpoints();
+ break;
+ case BW_ITEM_DisableAll:
+ case BW_ITEM_EnableAll:
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(row, Control);
+
+ if (btr)
+ {
+ btr->breakpoint()->setEnabled(item == BW_ITEM_EnableAll);
+ btr->setRow();
+ sendToGdb(*btr->breakpoint());
+ }
+ }
+ break;
+ default:
+ // oops, check it out! this case is not in sync with the
+ // m_ctxMenu. Check the enum in the header file.
+ return;
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditRow(int row, int col, const QPoint &)
+{
+// kdDebug(9012) << "in slotEditRow row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotNewValue(int row, int col)
+{
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+
+ QString new_value = m_table->text(row, col);
+
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ switch (col)
+ {
+ case Enable:
+ {
+ QCheckTableItem *item =
+ (QCheckTableItem*)m_table->item ( row, Enable );
+ bp->setEnabled(item->isChecked());
+ }
+ break;
+
+ case Location:
+ {
+ if (bp->location() != new_value)
+ {
+ // GDB does not allow to change location of
+ // an existing breakpoint. So, need to remove old
+ // breakpoint and add another.
+
+ // Announce to editor that breakpoit at its
+ // current location is dying.
+ bp->setActionDie();
+ emit publishBPState(*bp);
+
+ // However, we don't want the line in breakpoint
+ // widget to disappear and appear again.
+
+ // Emit delete command. This won't resync breakpoint
+ // table (unlike clearBreakpoint), so we won't have
+ // nasty effect where line in the table first disappears
+ // and then appears again, and won't have internal issues
+ // as well.
+ if (!controller_->stateIsOn(s_dbgNotStarted))
+ controller_->addCommand(bp->dbgRemoveCommand().latin1());
+
+ // Now add new breakpoint in gdb. It will correspond to
+ // the same 'Breakpoint' and 'BreakpointRow' objects in
+ // KDevelop is the previous, deleted, breakpoint.
+
+ // Note: clears 'actionDie' implicitly.
+ bp->setActionAdd(true);
+ bp->setLocation(new_value);
+ }
+ break;
+ }
+
+ case Condition:
+ {
+ bp->setConditional(new_value);
+ break;
+ }
+
+ case IgnoreCount:
+ {
+ bp->setIgnoreCount(new_value.toInt());
+ break;
+ }
+ default:
+ break;
+ }
+
+ bp->setActionModify(true);
+
+
+ // This is not needed for most changes, since we've
+ // just read a value from table cell to breakpoint, and
+ // setRow will write back the same value to the cell.
+ // It's only really needed for tracing column changes,
+ // where tracing config dialog directly changes breakpoint,
+ // so we need to send those changes to the table.
+ btr->setRow();
+
+
+ sendToGdb(*bp);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+
+ if (btr)
+ {
+ QTableSelection ts;
+ ts.init(btr->row(), 0);
+ ts.expandTo(btr->row(), numCols);
+ m_table->addSelection(ts);
+ m_table->editCell(btr->row(), Location, false);
+ }
+
+}
+
+void GDBBreakpointWidget::sendToGdb(Breakpoint& BP)
+{
+ // Announce the change in state. We need to do this before
+ // everything. For example, if debugger is not yet running, we'll
+ // immediate exit after setting pending flag, but we still want changes
+ // in "enabled" flag to be shown on the left border of the editor.
+ emit publishBPState(BP);
+
+ BP.sendToGdb(controller_);
+}
+
+void GDBBreakpointWidget::slotBreakpointModified(Breakpoint* b)
+{
+ emit publishBPState(*b);
+
+ if (BreakpointTableRow* btr = find(b))
+ {
+ if (b->isActionDie())
+ {
+ // Breakpoint was deleted, kill the table row.
+ m_table->removeRow(btr->row());
+ }
+ else
+ {
+ btr->setRow();
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotEvent(GDBController::event_t e)
+{
+ switch(e)
+ {
+ case GDBController::program_state_changed:
+ {
+ controller_->addCommand(
+ new GDBCommand("-break-list",
+ this,
+ &GDBBreakpointWidget::handleBreakpointList));
+ break;
+ }
+
+ case GDBController::shared_library_loaded:
+ case GDBController::connected_to_program:
+ {
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(row, Control);
+
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if ( (bp->dbgId() == -1 || bp->isPending())
+ && !bp->isDbgProcessing()
+ && bp->isValid())
+ {
+ sendToGdb(*bp);
+ }
+ }
+ }
+ break;
+ }
+ case GDBController::program_exited:
+ {
+ for(int row = 0; row < m_table->numRows(); ++row)
+ {
+ Breakpoint* b = static_cast<BreakpointTableRow*>(
+ m_table->item(row, Control))->breakpoint();
+
+ b->applicationExited(controller_);
+ }
+ }
+
+ default:
+ ;
+ }
+}
+
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditBreakpoint()
+{
+ m_table->editCell(m_table->currentRow(), Location, false);
+}
+
+
+void GDBBreakpointWidget::editTracing(QTableItem* item)
+{
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(item->row(), Control);
+
+ DebuggerTracingDialog* d = new DebuggerTracingDialog(
+ btr->breakpoint(), m_table, "");
+
+ int r = d->exec();
+
+ // Note: change cell text here and explicitly call slotNewValue here.
+ // We want this signal to be emitted when we close the tracing dialog
+ // and not when we select some other cell, as happens in Qt by default.
+ if (r == QDialog::Accepted)
+ {
+ // The dialog has modified "btr->breakpoint()" already.
+ // Calling 'slotNewValue' will flush the changes back
+ // to the table.
+ slotNewValue(item->row(), item->col());
+ }
+
+ delete d;
+}
+
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::savePartialProjectSession(QDomElement* el)
+{
+ QDomDocument domDoc = el->ownerDocument();
+ if (domDoc.isNull())
+ return;
+
+ QDomElement breakpointListEl = domDoc.createElement("breakpointList");
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr =
+ (BreakpointTableRow *) m_table->item(row, Control);
+ Breakpoint* bp = btr->breakpoint();
+
+ QDomElement breakpointEl =
+ domDoc.createElement("breakpoint"+QString::number(row));
+
+ breakpointEl.setAttribute("type", bp->type());
+ breakpointEl.setAttribute("location", bp->location(false));
+ breakpointEl.setAttribute("enabled", bp->isEnabled());
+ breakpointEl.setAttribute("condition", bp->conditional());
+ breakpointEl.setAttribute("tracingEnabled",
+ QString::number(bp->tracingEnabled()));
+ breakpointEl.setAttribute("traceFormatStringEnabled",
+ QString::number(bp->traceFormatStringEnabled()));
+ breakpointEl.setAttribute("tracingFormatString",
+ bp->traceFormatString());
+
+ QDomElement tracedExpressions =
+ domDoc.createElement("tracedExpressions");
+
+ QStringList::const_iterator i, e;
+ for(i = bp->tracedExpressions().begin(),
+ e = bp->tracedExpressions().end();
+ i != e; ++i)
+ {
+ QDomElement expr = domDoc.createElement("expression");
+ expr.setAttribute("value", *i);
+ tracedExpressions.appendChild(expr);
+ }
+
+ breakpointEl.appendChild(tracedExpressions);
+
+ breakpointListEl.appendChild(breakpointEl);
+ }
+
+ if (!breakpointListEl.isNull())
+ el->appendChild(breakpointListEl);
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::restorePartialProjectSession(const QDomElement* el)
+{
+ /** Eventually, would be best to make each breakpoint type handle loading/
+ saving it's data. The only problem is that on load, we need to allocate
+ with new different objects, depending on type, and that requires some
+ kind of global registry. Gotta find out if a solution for that exists in
+ KDE (Boost.Serialization is too much dependency, and rolling my own is
+ boring).
+ */
+ QDomElement breakpointListEl = el->namedItem("breakpointList").toElement();
+ if (!breakpointListEl.isNull())
+ {
+ QDomElement breakpointEl;
+ for (breakpointEl = breakpointListEl.firstChild().toElement();
+ !breakpointEl.isNull();
+ breakpointEl = breakpointEl.nextSibling().toElement())
+ {
+ Breakpoint* bp=0;
+ BP_TYPES type = (BP_TYPES) breakpointEl.attribute( "type", "0").toInt();
+ switch (type)
+ {
+ case BP_TYPE_FilePos:
+ {
+ bp = new FilePosBreakpoint();
+ break;
+ }
+ case BP_TYPE_Watchpoint:
+ {
+ bp = new Watchpoint("");
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Common settings for any type of breakpoint
+ if (bp)
+ {
+ bp->setLocation(breakpointEl.attribute( "location", ""));
+ if (type == BP_TYPE_Watchpoint)
+ {
+ bp->setEnabled(false);
+ }
+ else
+ {
+ bp->setEnabled(
+ breakpointEl.attribute( "enabled", "1").toInt());
+ }
+ bp->setConditional(breakpointEl.attribute( "condition", ""));
+
+ bp->setTracingEnabled(
+ breakpointEl.attribute("tracingEnabled", "0").toInt());
+ bp->setTraceFormatString(
+ breakpointEl.attribute("tracingFormatString", ""));
+ bp->setTraceFormatStringEnabled(
+ breakpointEl.attribute("traceFormatStringEnabled", "0")
+ .toInt());
+
+ QDomNode tracedExpr =
+ breakpointEl.namedItem("tracedExpressions");
+
+ if (!tracedExpr.isNull())
+ {
+ QStringList l;
+
+ for(QDomNode c = tracedExpr.firstChild(); !c.isNull();
+ c = c.nextSibling())
+ {
+ QDomElement el = c.toElement();
+ l.push_back(el.attribute("value", ""));
+ }
+ bp->setTracedExpressions(l);
+ }
+
+ // Now add the breakpoint. Don't try to check if
+ // breakpoint already exists.
+ // It's easy to check that breakpoint on the same
+ // line already exists, but it might have different condition,
+ // and checking conditions for equality is too complex thing.
+ // And anyway, it's will be suprising of realoading a project
+ // changes the set of breakpoints.
+ addBreakpoint(bp);
+ }
+ }
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::focusInEvent( QFocusEvent */* e*/ )
+{
+ // Without the following 'if', when we first open the breakpoints
+ // widget, the background is all black. This happens only with
+ // m_table->setFocusStyle(QTable::FollowStyle);
+ // in constructor, so I suspect Qt bug. But anyway, without
+ // current cell keyboard actions like Enter for edit won't work,
+ // so keyboard focus does not makes much sense.
+ if (m_table->currentRow() == -1 ||
+ m_table->currentColumn() == -1)
+ {
+ m_table->setCurrentCell(0, 0);
+ }
+ m_table->setFocus();
+}
+
+ComplexEditCell::
+ComplexEditCell(QTable* table)
+: QTableItem(table, QTableItem::WhenCurrent)
+{
+}
+
+
+QWidget* ComplexEditCell::createEditor() const
+{
+ QHBox* box = new QHBox( table()->viewport() );
+ box->setPaletteBackgroundColor(
+ table()->palette().active().highlight());
+
+ label_ = new QLabel(text(), box, "label");
+ label_->setBackgroundMode(Qt::PaletteHighlight);
+ // Sorry for hardcode, but '2' is already hardcoded in
+ // Qt source, in QTableItem::paint. Since I don't want the
+ // text to jump 2 pixels to the right when editor is activated,
+ // need to set the same indent for label.
+ label_->setIndent(2);
+ QPalette p = label_->palette();
+
+ p.setColor(QPalette::Active, QColorGroup::Foreground,
+ table()->palette().active().highlightedText());
+ p.setColor(QPalette::Inactive, QColorGroup::Foreground,
+ table()->palette().active().highlightedText());
+
+ label_->setPalette(p);
+
+ QPushButton* b = new QPushButton("...", box);
+ // This is exactly what is done in QDesigner source in the
+ // similar context. Haven't had any success making the good look
+ // with layout, I suppose that sizeHint for button is always larger
+ // than 20.
+ b->setFixedWidth( 20 );
+
+ connect(b, SIGNAL(clicked()), this, SLOT(slotEdit()));
+
+ return box;
+}
+
+void ComplexEditCell::updateValue()
+{
+ if (!label_.isNull())
+ {
+ label_->setText(table()->text(row(), col()));
+ }
+}
+
+void ComplexEditCell::slotEdit()
+{
+ emit edit(this);
+}
+
+}
+
+
+#include "gdbbreakpointwidget.moc"
diff --git a/languages/cpp/debugger/gdbbreakpointwidget.h b/languages/cpp/debugger/gdbbreakpointwidget.h
new file mode 100644
index 00000000..91dc40a5
--- /dev/null
+++ b/languages/cpp/debugger/gdbbreakpointwidget.h
@@ -0,0 +1,174 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 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 _GDBBreakpointWidget_H_
+#define _GDBBreakpointWidget_H_
+
+#include <qhbox.h>
+#include <qpopupmenu.h>
+#include <qtable.h>
+#include <qguardedptr.h>
+#include <qvaluevector.h>
+
+#include "mi/gdbmi.h"
+#include "gdbcontroller.h"
+
+class QDomElement;
+class QToolButton;
+class QLabel;
+class KURL;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+class Breakpoint;
+class BreakpointTableRow;
+class GDBTable;
+class GDBController;
+
+class GDBBreakpointWidget : public QHBox
+{
+ Q_OBJECT
+
+public:
+ GDBBreakpointWidget( GDBController* controller,
+ QWidget* parent=0, const char* name=0 );
+ virtual ~GDBBreakpointWidget();
+
+ void reset();
+
+ void savePartialProjectSession(QDomElement* el);
+ void restorePartialProjectSession(const QDomElement* el);
+
+ bool hasWatchpointForAddress(unsigned long long address) const;
+
+
+public slots:
+ // Connected to from the editor widget:
+ void slotToggleBreakpoint(const QString &filename, int lineNum);
+ void slotToggleBreakpointEnabled(const QString &fileName, int lineNum);
+
+ // Connected to from the variable widget:
+ void slotToggleWatchpoint(const QString &varName);
+
+ void slotBreakpointSet(Breakpoint*);
+
+ void slotRefreshBP(const KURL &filename);
+
+ void slotBreakpointHit(int id);
+
+
+protected:
+ virtual void focusInEvent(QFocusEvent *e);
+
+
+ friend class BreakpointActionCell; // for access to slotNewValue
+private slots:
+ void slotRemoveBreakpoint();
+ void slotRemoveAllBreakpoints();
+ void slotEditBreakpoint(const QString &fileName, int lineNum);
+ void slotEditBreakpoint();
+ void slotAddBlankBreakpoint(int idx);
+ void slotRowDoubleClicked(int row, int col, int button, const QPoint & mousePos);
+ void slotContextMenuShow( int row, int col, const QPoint &mousePos );
+ void slotContextMenuSelect( int item );
+ void slotEditRow(int row, int col, const QPoint & mousePos);
+ void slotNewValue(int row, int col);
+ void editTracing(QTableItem* item);
+ void slotBreakpointModified(Breakpoint*);
+
+ void slotEvent(GDBController::event_t);
+ void slotWatchpointHit(int id,
+ const QString& oldValue,
+ const QString& newValue);
+
+signals:
+ void publishBPState(const Breakpoint& brkpt);
+ void refreshBPState(const Breakpoint& brkpt);
+ void gotoSourcePosition(const QString &fileName, int lineNum);
+ // Emitted when output from yet another passed tracepoint is available.
+ void tracingOutput(const char*);
+
+private:
+ BreakpointTableRow* find(Breakpoint *bp);
+ BreakpointTableRow* findId(int id);
+ BreakpointTableRow* findKey(int BPKey);
+
+ void setActive();
+ BreakpointTableRow* addBreakpoint(Breakpoint *bp);
+ void removeBreakpoint(BreakpointTableRow* btr);
+
+ void sendToGdb(Breakpoint &);
+
+ void handleBreakpointList(const GDBMI::ResultRecord&);
+ void handleTracingPrintf(const QValueVector<QString>& s);
+
+private:
+ GDBController* controller_;
+
+ GDBTable* m_table;
+ QPopupMenu* m_ctxMenu;
+};
+
+class BreakpointTableRow;
+
+/** Custom table cell class for cells that require complex editing.
+ When current, the cell shows a "..." on the right. When clicked,
+ the 'edit' signal is emitted that can be be used to pop-up
+ a dialog box.
+
+ When editing is done, the receiver of 'edit' should change the
+ value in the table, and then call the 'updateValue' method.
+ */
+class ComplexEditCell : public QObject, public QTableItem
+{
+ Q_OBJECT
+public:
+
+ ComplexEditCell(QTable* table);
+
+ /** Called by Qt when the current cell should become editable.
+ In our case, when the item becomes current. Creates a widget
+ that will be shown in the cell and should be able to edit cell
+ content. In our case -- text plus "..." button that invokes
+ action dialog.
+ */
+ QWidget* createEditor() const;
+
+ void updateValue();
+
+private slots:
+
+ /** Called when the "..." button is clicked. */
+ void slotEdit();
+
+signals:
+ void edit(QTableItem*);
+
+private:
+ mutable QGuardedPtr<QLabel> label_;
+};
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/gdbcommand.cpp b/languages/cpp/debugger/gdbcommand.cpp
new file mode 100644
index 00000000..de5aeb9d
--- /dev/null
+++ b/languages/cpp/debugger/gdbcommand.cpp
@@ -0,0 +1,142 @@
+/***************************************************************************
+ 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 "gdbcommand.h"
+#include "breakpoint.h"
+#include "variablewidget.h"
+
+namespace GDBDebugger
+{
+
+GDBCommand::GDBCommand(const QString &command)
+: command_(command), run(false), handler_this(0)
+{
+}
+
+QString GDBCommand::cmdToSend()
+{
+ return initialString() + "\n";
+}
+
+QString GDBCommand::initialString() const
+{
+ return command_;
+}
+
+bool GDBCommand::isUserCommand() const
+{
+ return false;
+}
+
+bool
+GDBCommand::invokeHandler(const GDBMI::ResultRecord& r)
+{
+ if (handler_this) {
+ (handler_this->*handler_method)(r);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+void GDBCommand::newOutput(const QString& line)
+{
+ lines.push_back(line);
+}
+
+const QValueVector<QString>& GDBCommand::allStreamOutput() const
+{
+ return lines;
+}
+
+bool GDBCommand::handlesError() const
+{
+ return handlesError_;
+}
+
+GDBCommand::~GDBCommand()
+{
+}
+
+bool GDBCommand::isRun() const
+{
+ return run;
+}
+
+void GDBCommand::setRun(bool run)
+{
+ this->run = run;
+}
+
+
+UserCommand::UserCommand(const QString& s)
+: GDBCommand(s)
+{
+}
+
+bool UserCommand::isUserCommand() const
+{
+ return true;
+}
+
+
+ModifyBreakpointCommand::ModifyBreakpointCommand(
+ const QString& command, const Breakpoint* bp)
+: GDBCommand(command.local8Bit()),
+ bp_(bp)
+{}
+
+QString
+ModifyBreakpointCommand::cmdToSend()
+{
+ if (bp_->dbgId() > 0)
+ {
+ QString s(initialString());
+ s = s.arg(bp_->dbgId()) + "\n";
+ return s.local8Bit();
+ }
+ else
+ {
+ // The ID can be -1 either if breakpoint set command
+ // failed, or if breakpoint is somehow already deleted.
+ // In either case, should not do anything.
+ return "";
+ }
+}
+
+
+bool CliCommand::invokeHandler(const GDBMI::ResultRecord& r)
+{
+ // On error, do nothing.
+ if (r.reason != "done")
+ return true;
+
+ if (cli_handler_this) {
+ (cli_handler_this->*cli_handler_method)(allStreamOutput());
+ return true;
+ }
+ else {
+ return false;
+ }
+
+}
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
diff --git a/languages/cpp/debugger/gdbcommand.h b/languages/cpp/debugger/gdbcommand.h
new file mode 100644
index 00000000..8c928394
--- /dev/null
+++ b/languages/cpp/debugger/gdbcommand.h
@@ -0,0 +1,271 @@
+/***************************************************************************
+ 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 _GDBCOMMAND_H_
+#define _GDBCOMMAND_H_
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+#include "mi/gdbmi.h"
+#include <qguardedptr.h>
+
+namespace GDBDebugger
+{
+
+
+class Breakpoint;
+class VarItem;
+class ValueCallback;
+
+/**
+ * @author John Birch
+ */
+
+class GDBCommand
+{
+public:
+ GDBCommand(const QString& command);
+
+ template<class Handler>
+ GDBCommand(const QString& command,
+ Handler* handler_this,
+ void (Handler::* handler_method)(const GDBMI::ResultRecord&),
+ bool handlesError = false);
+
+ /* The command that should be sent to gdb.
+ This method is virtual so the command can compute this
+ dynamically, possibly using results of the previous
+ commands.
+ If the empty string is returned, nothing is sent. */
+ virtual QString cmdToSend();
+
+ /* Returns the initial string that was specified in
+ ctor invocation. The actual command will be
+ determined by cmdToSend above and the return
+ value of this method is only used in various
+ diagnostic messages emitted before actually
+ sending the command. */
+ QString initialString() const;
+
+ /* Returns true if this is command entered by the user
+ and so should be always shown in the gdb output window. */
+ virtual bool isUserCommand() const;
+
+ // If there's a handler for this command, invokes it and returns true.
+ // Otherwise, returns false.
+ virtual bool invokeHandler(const GDBMI::ResultRecord& r);
+
+ // Returns 'true' if 'invokeHandler' should be invoked even
+ // on MI errors.
+ bool handlesError() const;
+
+ virtual ~GDBCommand();
+
+ // Called by gdbcontroller for each new output string
+ // gdb emits for this command. In MI mode, this includes
+ // all "stream" messages, but does not include MI responses.
+ void newOutput(const QString&);
+
+ const QValueVector<QString>& allStreamOutput() const;
+
+ // True if this command run then target for
+ // unspecified period of time -- that is either 'run' or
+ // 'continue'.
+ bool isRun() const;
+
+ void setRun(bool run);
+
+private:
+ QString command_;
+ QGuardedPtr<QObject> handler_this;
+ typedef void (QObject::* handler_t)(const GDBMI::ResultRecord&);
+ handler_t handler_method;
+ QValueVector<QString> lines;
+ bool run;
+
+protected: // FIXME: should be private, after I kill the first ctor
+ // that is obsolete and no longer necessary.
+ bool handlesError_;
+
+};
+
+class UserCommand : public GDBCommand
+{
+public:
+ UserCommand(const QString& s);
+
+ bool isUserCommand() const;
+};
+
+/** This command is used to change some property of breakpoint.
+ It holds a pointer to a Breakpoint object and will substitute
+ breakpoint id into the command string.
+
+ So, the command can be issued before the breakpoint id is know. That
+ is, it's possible to queue add + modify pair. The add command will
+ set breakpoint id and modify command will use it.
+*/
+class ModifyBreakpointCommand : public GDBCommand
+{
+public:
+ /** The 'comamnd' should include a single format specifier "%1" that
+ will be replaced with the id of breakpoint.
+ */
+ ModifyBreakpointCommand(const QString& command, const Breakpoint* bp);
+
+public: // DbgCommand overrides
+ virtual QString cmdToSend();
+
+private:
+ const Breakpoint* bp_;
+};
+
+/** This is a class for raw CLI commands. Instead of invoking
+ user provided hook with MI response, it invokes the a hook
+ with lists of strings.
+*/
+class CliCommand : public GDBCommand
+{
+public:
+ template<class Handler>
+ CliCommand(const QString& command,
+ Handler* handler_this,
+ void (Handler::* handler_method)(const QValueVector<QString>&),
+ bool handlesError = false);
+
+
+
+public: // GDBCommand overrides
+ bool invokeHandler(const GDBMI::ResultRecord& r);
+
+private:
+ QGuardedPtr<QObject> cli_handler_this;
+ typedef void (QObject::* cli_handler_t)(const QValueVector<QString>&);
+ cli_handler_t cli_handler_method;
+};
+
+/** Command that does nothing and can be just used to invoke
+ a user provided handler when all preceeding commands are
+ executed.
+*/
+class SentinelCommand : public GDBCommand
+{
+public:
+ typedef void (QObject::*handler_method_t)();
+
+ template<class Handler>
+ SentinelCommand(Handler* handler_this,
+ void (Handler::* handler_method)())
+ : GDBCommand(""),
+ handler_this(handler_this),
+ handler_method(static_cast<handler_method_t>(handler_method))
+ {}
+
+ void invokeHandler()
+ {
+ (handler_this->*handler_method)();
+ }
+
+ QString cmdToSend()
+ {
+ return "";
+ }
+
+private:
+ QGuardedPtr<QObject> handler_this;
+ handler_method_t handler_method;
+
+};
+
+/* Command for which we don't want any reply. */
+class ResultlessCommand : public QObject, public GDBCommand
+{
+public:
+ ResultlessCommand(const QString& command, bool handlesError = false)
+ : GDBCommand(command, this, &ResultlessCommand::handle, handlesError)
+ {}
+
+private:
+ void handle(const GDBMI::ResultRecord&)
+ {}
+};
+
+class ExpressionValueCommand : public QObject, public GDBCommand
+{
+public:
+ typedef void (QObject::*handler_method_t)(const QString&);
+
+ template<class Handler>
+ ExpressionValueCommand(
+ const QString& expression,
+ Handler* handler_this,
+ void (Handler::* handler_method)(const QString&))
+ : GDBCommand(("-data-evaluate-expression " + expression).ascii(), this,
+ &ExpressionValueCommand::handleResponse),
+ handler_this(handler_this),
+ handler_method(static_cast<handler_method_t>(handler_method))
+ {}
+
+ void handleResponse(const GDBMI::ResultRecord& r)
+ {
+ (handler_this->*handler_method)(r["value"].literal());
+ }
+
+private:
+ QGuardedPtr<QObject> handler_this;
+ handler_method_t handler_method;
+};
+
+
+
+template<class Handler>
+GDBCommand::GDBCommand(
+ const QString& command,
+ Handler* handler_this,
+ void (Handler::* handler_method)(const GDBMI::ResultRecord&),
+ bool handlesError)
+: command_(command),
+ handler_this(handler_this),
+ handler_method(static_cast<handler_t>(handler_method)),
+ run(false),
+ handlesError_(handlesError)
+{
+}
+
+template<class Handler>
+CliCommand::CliCommand(
+ const QString& command,
+ Handler* handler_this,
+ void (Handler::* handler_method)(const QValueVector<QString>&),
+ bool handlesError)
+: GDBCommand(command.latin1()),
+ cli_handler_this(handler_this),
+ cli_handler_method(static_cast<cli_handler_t>(handler_method))
+{
+ handlesError_ = handlesError;
+}
+
+
+
+
+}
+
+
+
+
+
+#endif
diff --git a/languages/cpp/debugger/gdbcontroller.cpp b/languages/cpp/debugger/gdbcontroller.cpp
new file mode 100644
index 00000000..05954069
--- /dev/null
+++ b/languages/cpp/debugger/gdbcontroller.cpp
@@ -0,0 +1,1860 @@
+// *************************************************************************
+// 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 <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kwin.h>
+
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qdir.h>
+#include <qvaluevector.h>
+#include <qeventloop.h>
+
+#include <iostream>
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <typeinfo>
+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(QString().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(QDomDocument &projectDom)
+ : DbgController(),
+ currentFrame_(0),
+ viewedThread_(-1),
+ holdingZone_(),
+ currentCmd_(0),
+ tty_(0),
+ badCore_(QString()),
+ 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 QStrings? 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(QCString().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 QString& 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("<b>Gdb command sent when debugger is not running</b><br>"
+ "The command was:<br> %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;
+ }
+
+ QString commandText = currentCmd_->cmdToSend();
+ bool bad_command = false;
+ QString 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<SentinelCommand*>(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("<b>Invalid debugger command</b><br>")
+ + message,
+ i18n("Invalid debugger command"), "gdb_error");
+ return;
+ }
+
+ kdDebug(9012) << "SEND: " << commandText;
+
+ dbgProcess_->writeStdin(commandText.local8Bit(),
+ commandText.length());
+ setStateOn(s_waitForWrite);
+
+ QString prettyCmd = currentCmd_->cmdToSend();
+ prettyCmd.replace( QRegExp("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 QValueVector<QString>& 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("<b>Invalid gdb reply</b>"
+ "<p>The 'stopped' packet does not include the 'reason' field'."),
+ i18n("The gdb reply is: bla-bla-bla"),
+ i18n("Invalid gdb reply"));
+ return;
+ }
+
+ QString 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")
+ {
+ QString 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")
+ {
+ QString name = r["signal-name"].literal();
+ QString 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 QString &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, QSocketNotifier
+ // will continiously bomd STTY with signals, so we need to either disable
+ // QSocketNotifier, 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 QString& 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(QString(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
+ }
+
+ QString 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"))
+ {
+ QString 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"];
+
+ QString 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 QString& shell, const DomUtil::PairList& run_envvars, const QString& run_directory, const QString &application, const QString& run_arguments)
+{
+ kdDebug(9012) << "Starting debugger controller\n";
+ badCore_ = QString();
+
+ Q_ASSERT (!dbgProcess_ && !tty_);
+
+ dbgProcess_ = new KProcess;
+
+ connect( dbgProcess_, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(slotDbgStdout(KProcess *, char *, int)) );
+
+ connect( dbgProcess_, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(slotDbgStderr(KProcess *, char *, int)) );
+
+ connect( dbgProcess_, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(slotDbgWroteStdin(KProcess *)) );
+
+ connect( dbgProcess_, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotDbgProcessExited(KProcess*)) );
+
+ application_ = application;
+
+ QString gdb = "gdb";
+ // Prepend path to gdb, if needed. Using QDir,
+ // 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(
+ QString( "/bin/sh -c " + shell + " " + gdb
+ + " " + application
+ + " --interpreter=mi2 -quiet\n" ).latin1());
+ }
+ else
+ {
+ *dbgProcess_ << gdb << application
+ << "-interpreter=mi2" << "-quiet";
+ emit gdbUserCommandStdout(
+ QString( gdb + " " + application +
+ " --interpreter=mi2 -quiet\n" ).latin1());
+ }
+
+ if (!dbgProcess_->start( KProcess::NotifyOnExit,
+ KProcess::Communication(KProcess::All)))
+ {
+ KMessageBox::information(
+ 0,
+ i18n("<b>Could not start debugger.</b>"
+ "<p>Could not run '%1'. "
+ "Make sure that the path name is specified correctly."
+ ).arg(dbgProcess_->args()[0]),
+ 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(QCString().sprintf("set output-radix %d", config_outputRadix_)));
+
+ // Change the "Working directory" to the correct one
+ QCString tmp( "cd " + QFile::encodeName( run_directory ));
+ queueCmd(new GDBCommand(tmp));
+
+ // Set the run arguments
+ if (!run_arguments.isEmpty())
+ queueCmd(
+ new GDBCommand(QCString("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
+ QString 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;
+
+ QTime start;
+ QTime 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 = QTime::currentTime();
+ while (-1)
+ {
+ kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 20 );
+ now = QTime::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 = QTime::currentTime();
+ while (-1)
+ {
+ kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 20 );
+ now = QTime::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 = QTime::currentTime();
+ while (-1)
+ {
+ kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 20 );
+ now = QTime::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 QString &coreFile)
+{
+ setStateOff(s_programExited|s_appNotStarted);
+ setStateOn(s_core);
+
+ queueCmd(new GDBCommand(QCString("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(QString("file")));
+
+ // The MI interface does not implements -target-attach yet,
+ // and we don't recognize whatever gibberish 'attach' pours out, so...
+ queueCmd(new GDBCommand(
+ QCString().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_, SIGNAL(OutOutput(const char*)), SIGNAL(ttyStdout(const char*)) );
+ connect( tty_, SIGNAL(ErrOutput(const char*)), SIGNAL(ttyStderr(const char*)) );
+ }
+
+ QString 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(QCString("tty ")+tty.latin1()));
+
+ if (!config_runShellScript_.isEmpty()) {
+ // Special for remote debug...
+ QCString tty(tty_->getSlave().latin1());
+ QCString options = QCString(">") + tty + QCString(" 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 {
+
+ QFileInfo app(application_);
+
+ if (!app.exists())
+ {
+ KMessageBox::error(
+ 0,
+ i18n("<b>Application does not exist</b>"
+ "<p>The application you are trying to debug,<br>"
+ " %1\n"
+ "<br>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("<b>Could not run application '%1'.</b>"
+ "<p>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 QString &fileName, int lineNum)
+{
+ if (stateIsOn(s_dbgBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ removeStateReloadingCommands();
+
+ if (fileName.isEmpty())
+ queueCmd(new GDBCommand(
+ QCString().sprintf("-exec-until %d", lineNum)));
+ else
+ queueCmd(new GDBCommand(
+ QCString().
+ sprintf("-exec-until %s:%d", fileName.latin1(), lineNum)));
+}
+
+// **************************************************************************
+
+void GDBController::slotJumpTo(const QString &fileName, int lineNum)
+{
+ if (stateIsOn(s_dbgBusy|s_dbgNotStarted|s_shuttingDown))
+ return;
+
+ if (!fileName.isEmpty()) {
+ queueCmd(new GDBCommand(QCString().sprintf("tbreak %s:%d", fileName.latin1(), lineNum)));
+ queueCmd(new GDBCommand(QCString().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(
+ QString("-thread-select %1").arg(threadNo).ascii()));
+ }
+
+ queueCmd(new GDBCommand(
+ QString("-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)
+{
+ QString 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("<b>Debugger error</b>"
+ "<p>Debugger reported the following error:"
+ "<p><tt>") + 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;
+
+ QCString 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_ += QCString(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 Qt 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;
+
+ QCString reply(holdingZone_.left(i));
+ holdingZone_ = holdingZone_.mid(i+1);
+
+ kdDebug(9012) << "REPLY: " << reply << "\n";
+
+ FileSymbol file;
+ file.contents = reply;
+
+ std::auto_ptr<GDBMI::Record> 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<GDBMI::ResultRecord&>(*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<GDBMI::ResultRecord*>(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<GDBMI::StreamRecord&>(*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 QRegExp 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("<b>Internal debugger error</b>",
+ "<p>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),
+ 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) << "UNQUEUE: " << 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: " << QString::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 QString& 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()
+{
+ QString 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_)
+ {
+ QString 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)
+ {
+ QString 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 += QString::number(i);
+ out += " ";
+
+ }
+ }
+ kdDebug(9012) << out << "\n";
+ }
+}
+
+int GDBController::qtVersion( ) const
+{
+ return DomUtil::readIntEntry( dom, "/kdevcppsupport/qt/version", 3 );
+}
+
+void GDBController::demandAttention() const
+{
+ if ( QWidget * w = kapp->mainWidget() )
+ {
+ KWin::demandAttention( w->winId(), true );
+ }
+}
+
+bool GDBController::miPendingBreakpoints() const
+{
+ return mi_pending_breakpoints_;
+}
+
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+#include "gdbcontroller.moc"
diff --git a/languages/cpp/debugger/gdbcontroller.h b/languages/cpp/debugger/gdbcontroller.h
new file mode 100644
index 00000000..6a9e3385
--- /dev/null
+++ b/languages/cpp/debugger/gdbcontroller.h
@@ -0,0 +1,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
diff --git a/languages/cpp/debugger/gdboutputwidget.cpp b/languages/cpp/debugger/gdboutputwidget.cpp
new file mode 100644
index 00000000..817f8692
--- /dev/null
+++ b/languages/cpp/debugger/gdboutputwidget.cpp
@@ -0,0 +1,376 @@
+// *************************************************************************
+// gdboutputwidget.cpp - description
+// -------------------
+// begin : 10th April 2003
+// copyright : (C) 2003 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 "gdboutputwidget.h"
+#include "dbgcontroller.h"
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtextedit.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qdom.h>
+
+
+namespace GDBDebugger
+{
+
+/***************************************************************************/
+
+GDBOutputWidget::GDBOutputWidget( QWidget *parent, const char *name) :
+ QWidget(parent, name),
+ m_userGDBCmdEditor(0),
+ m_Interrupt(0),
+ m_gdbView(0),
+ showInternalCommands_(false),
+ maxLines_(5000)
+{
+
+ m_gdbView = new OutputText(this);
+ m_gdbView->setTextFormat(QTextEdit::LogText);
+
+ QBoxLayout *userGDBCmdEntry = new QHBoxLayout();
+ m_userGDBCmdEditor = new KHistoryCombo (this, "gdb-user-cmd-editor");
+
+ QLabel *label = new QLabel(i18n("&GDB cmd:"), this);
+ label->setBuddy(m_userGDBCmdEditor);
+ userGDBCmdEntry->addWidget(label);
+
+ userGDBCmdEntry->addWidget(m_userGDBCmdEditor);
+ userGDBCmdEntry->setStretchFactor(m_userGDBCmdEditor, 1);
+
+ m_Interrupt = new QToolButton( this, "add breakpoint" );
+ m_Interrupt->setSizePolicy ( QSizePolicy ( (QSizePolicy::SizeType)0,
+ ( QSizePolicy::SizeType)0,
+ 0,
+ 0,
+ m_Interrupt->sizePolicy().hasHeightForWidth())
+ );
+ m_Interrupt->setPixmap ( SmallIcon ( "player_pause" ) );
+ userGDBCmdEntry->addWidget(m_Interrupt);
+ QToolTip::add ( m_Interrupt, i18n ( "Pause execution of the app to enter gdb commands" ) );
+
+ QVBoxLayout *topLayout = new QVBoxLayout(this, 2);
+ topLayout->addWidget(m_gdbView, 10);
+ topLayout->addLayout(userGDBCmdEntry);
+
+ slotDbgStatus( "", s_dbgNotStarted);
+
+ connect( m_userGDBCmdEditor, SIGNAL(returnPressed()), SLOT(slotGDBCmd()) );
+ connect( m_Interrupt, SIGNAL(clicked()), SIGNAL(breakInto()));
+
+ connect( &updateTimer_, SIGNAL(timeout()),
+ this, SLOT(flushPending()));
+}
+
+/***************************************************************************/
+
+GDBOutputWidget::~GDBOutputWidget()
+{
+ delete m_gdbView;
+ delete m_userGDBCmdEditor;
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::clear()
+{
+ if (m_gdbView)
+ m_gdbView->clear();
+
+ userCommands_.clear();
+ allCommands_.clear();
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::slotInternalCommandStdout(const char* line)
+{
+ newStdoutLine(line, true);
+}
+
+void GDBOutputWidget::slotUserCommandStdout(const char* line)
+{
+ newStdoutLine(line, false);
+}
+
+namespace {
+ QString colorify(QString text, const QString& color)
+ {
+ // Make sure the newline is at the end of the newly-added
+ // string. This is so that we can always correctly remove
+ // newline inside 'flushPending'.
+ Q_ASSERT(text.endsWith("\n"));
+ if (text.endsWith("\n"))
+ {
+ text.remove(text.length()-1, 1);
+ }
+ text = "<font color=\"" + color + "\">" + text + "</font>\n";
+ return text;
+ }
+}
+
+
+void GDBOutputWidget::newStdoutLine(const QString& line,
+ bool internal)
+{
+ QString s = html_escape(line);
+ if (s.startsWith("(gdb)"))
+ {
+ s = colorify(s, "blue");
+ }
+
+ allCommands_.append(s);
+ allCommandsRaw_.append(line);
+ trimList(allCommands_, maxLines_);
+ trimList(allCommandsRaw_, maxLines_);
+
+ if (!internal)
+ {
+ userCommands_.append(s);
+ userCommandsRaw_.append(line);
+ trimList(userCommands_, maxLines_);
+ trimList(userCommandsRaw_, maxLines_);
+ }
+
+ if (!internal || showInternalCommands_)
+ showLine(s);
+}
+
+
+void GDBOutputWidget::showLine(const QString& line)
+{
+ pendingOutput_ += line;
+
+ // To improve performance, we update the view after some delay.
+ if (!updateTimer_.isActive())
+ {
+ updateTimer_.start(100, true /* single shot */);
+ }
+}
+
+void GDBOutputWidget::trimList(QStringList& l, unsigned max_size)
+{
+ unsigned int length = l.count();
+ if (length > max_size)
+ {
+ for(int to_delete = length - max_size; to_delete; --to_delete)
+ {
+ l.erase(l.begin());
+ }
+ }
+}
+
+void GDBOutputWidget::setShowInternalCommands(bool show)
+{
+ if (show != showInternalCommands_)
+ {
+ showInternalCommands_ = show;
+
+ // Set of strings to show changes, text edit still has old
+ // set. Refresh.
+ m_gdbView->clear();
+ QStringList& newList =
+ showInternalCommands_ ? allCommands_ : userCommands_;
+
+ QStringList::iterator i = newList.begin(), e = newList.end();
+ for(; i != e; ++i)
+ {
+ // Note that color formatting is already applied to '*i'.
+ showLine(*i);
+ }
+ }
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::slotReceivedStderr(const char* line)
+{
+ QString colored = colorify(html_escape(line), "red");
+ // Errors are shown inside user commands too.
+ allCommands_.append(colored);
+ trimList(allCommands_, maxLines_);
+ userCommands_.append(colored);
+ trimList(userCommands_, maxLines_);
+
+ allCommandsRaw_.append(line);
+ trimList(allCommandsRaw_, maxLines_);
+ userCommandsRaw_.append(line);
+ trimList(userCommandsRaw_, maxLines_);
+
+ showLine(colored);
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::slotGDBCmd()
+{
+ QString GDBCmd(m_userGDBCmdEditor->currentText());
+ if (!GDBCmd.isEmpty())
+ {
+ m_userGDBCmdEditor->addToHistory(GDBCmd);
+ m_userGDBCmdEditor->clearEdit();
+ emit userGDBCmd(GDBCmd);
+ }
+}
+
+void GDBOutputWidget::flushPending()
+{
+ m_gdbView->setUpdatesEnabled(false);
+
+ // QTextEdit adds newline after paragraph automatically.
+ // So, remove trailing newline to avoid double newlines.
+ if (pendingOutput_.endsWith("\n"))
+ pendingOutput_.remove(pendingOutput_.length()-1, 1);
+ Q_ASSERT(!pendingOutput_.endsWith("\n"));
+
+ m_gdbView->append(pendingOutput_);
+ pendingOutput_ = "";
+
+ m_gdbView->scrollToBottom();
+ m_gdbView->setUpdatesEnabled(true);
+ m_gdbView->update();
+ m_userGDBCmdEditor->setFocus();
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::slotDbgStatus(const QString &, int statusFlag)
+{
+ if (statusFlag & s_dbgNotStarted)
+ {
+ m_Interrupt->setEnabled(false);
+ m_userGDBCmdEditor->setEnabled(false);
+ return;
+ }
+ else
+ {
+ m_Interrupt->setEnabled(true);
+ }
+
+ if (statusFlag & s_dbgBusy)
+ {
+ m_userGDBCmdEditor->setEnabled(false);
+ }
+ else
+ {
+ m_userGDBCmdEditor->setEnabled(true);
+ }
+}
+
+/***************************************************************************/
+
+void GDBOutputWidget::focusInEvent(QFocusEvent */*e*/)
+{
+ m_gdbView->scrollToBottom();
+ m_userGDBCmdEditor->setFocus();
+}
+
+QString GDBOutputWidget::html_escape(const QString& s)
+{
+ QString r(s);
+ r.replace("<", "&lt;");
+ r.replace(">", "&gt;");
+ return r;
+}
+
+void GDBOutputWidget::savePartialProjectSession(QDomElement* el)
+{
+ QDomDocument doc = el->ownerDocument();
+
+ QDomElement showInternal = doc.createElement("showInternalCommands");
+ showInternal.setAttribute("value", QString::number(showInternalCommands_));
+
+ el->appendChild(showInternal);
+}
+
+void GDBOutputWidget::restorePartialProjectSession(const QDomElement* el)
+{
+ QDomElement showInternal =
+ el->namedItem("showInternalCommands").toElement();
+
+ if (!showInternal.isNull())
+ {
+ showInternalCommands_ = showInternal.attribute("value", "0").toInt();
+ }
+}
+
+
+//void OutputText::contextMenuEvent(QContextMenuEvent* e)
+QPopupMenu* OutputText::createPopupMenu(const QPoint&)
+{
+ KPopupMenu* popup = new KPopupMenu;
+
+ int id = popup->insertItem(i18n("Show Internal Commands"),
+ this,
+ SLOT(toggleShowInternalCommands()));
+
+ popup->setItemChecked(id, parent_->showInternalCommands_);
+ popup->setWhatsThis(
+ id,
+ i18n(
+ "Controls if commands issued internally by KDevelop "
+ "will be shown or not.<br>"
+ "This option will affect only future commands, it will not "
+ "add or remove already issued commands from the view."));
+
+ popup->insertItem(i18n("Copy All"),
+ this,
+ SLOT(copyAll()));
+
+
+ return popup;
+}
+
+void OutputText::copyAll()
+{
+ /* See comments for allCommandRaw_ for explanations of
+ this complex logic, as opposed to calling text(). */
+ QStringList& raw = parent_->showInternalCommands_ ?
+ parent_->allCommandsRaw_ : parent_->userCommandsRaw_;
+ QString text;
+ for (unsigned i = 0; i < raw.size(); ++i)
+ text += raw[i];
+
+ // Make sure the text is pastable both with Ctrl-C and with
+ // middle click.
+ QApplication::clipboard()->setText(text, QClipboard::Clipboard);
+ QApplication::clipboard()->setText(text, QClipboard::Selection);
+}
+
+void OutputText::toggleShowInternalCommands()
+{
+ parent_->setShowInternalCommands(!parent_->showInternalCommands_);
+}
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+}
+
+
+#include "gdboutputwidget.moc"
+
diff --git a/languages/cpp/debugger/gdboutputwidget.h b/languages/cpp/debugger/gdboutputwidget.h
new file mode 100644
index 00000000..ddf79823
--- /dev/null
+++ b/languages/cpp/debugger/gdboutputwidget.h
@@ -0,0 +1,137 @@
+// *************************************************************************
+// gdboutputwidget.cpp - description
+// -------------------
+// begin : 10th April 2003
+// copyright : (C) 2003 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 _GDBOUTPUTWIDGET_H_
+#define _GDBOUTPUTWIDGET_H_
+
+#include <qwidget.h>
+#include <qtextedit.h>
+#include <qtimer.h>
+#include <qstringlist.h>
+
+class KHistoryCombo;
+
+class QTextEdit;
+class QToolButton;
+class QDomElement;
+
+namespace GDBDebugger
+{
+
+class GDBOutputWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ GDBOutputWidget( QWidget *parent=0, const char *name=0 );
+ ~GDBOutputWidget();
+
+ void savePartialProjectSession(QDomElement* el);
+ void restorePartialProjectSession(const QDomElement* el);
+
+ void clear();
+
+public slots:
+ void slotInternalCommandStdout(const char* line);
+ void slotUserCommandStdout(const char* line);
+ void slotReceivedStderr(const char* line);
+ void slotDbgStatus (const QString &status, int statusFlag);
+
+ void slotGDBCmd();
+
+ void flushPending();
+
+protected:
+ virtual void focusInEvent(QFocusEvent *e);
+
+signals:
+ void userGDBCmd(const QString &cmd);
+ void breakInto();
+
+private:
+
+ QString html_escape(const QString& s);
+
+ void newStdoutLine(const QString& line, bool internal);
+
+ /** Arranges for 'line' to be shown to the user.
+ Adds 'line' to pendingOutput_ and makes sure
+ updateTimer_ is running. */
+ void showLine(const QString& line);
+
+ /** Makes 'l' no longer then 'max_size' by
+ removing excessive elements from the top.
+ */
+ void trimList(QStringList& l, unsigned max_size);
+
+ KHistoryCombo* m_userGDBCmdEditor;
+ QToolButton* m_Interrupt;
+ QTextEdit* m_gdbView;
+
+ void setShowInternalCommands(bool);
+ friend class OutputText;
+
+ /** The output from user commands only and from
+ all commands. We keep it here so that if we switch
+ "Show internal commands" on, we can show previous
+ internal commands.
+ */
+ QStringList userCommands_, allCommands_;
+ /** Same output, without any fancy formatting. Keeping it
+ here because I can't find any way to extract raw text,
+ without formatting, out of QTextEdit except for
+ selecting everything and calling 'copy()'. The latter
+ approach is just ugly. */
+ QStringList userCommandsRaw_, allCommandsRaw_;
+
+
+ /** For performance reasons, we don't immediately add new text
+ to QTExtEdit. Instead we add it to pendingOutput_ and
+ flush it on timer.
+ */
+ QString pendingOutput_;
+ QTimer updateTimer_;
+
+ bool showInternalCommands_;
+
+ int maxLines_;
+};
+
+/** Add popup menu specific to gdb output window to QTextEdit.
+*/
+class OutputText : public QTextEdit
+{
+ Q_OBJECT
+public:
+ OutputText(GDBOutputWidget* parent)
+ : QTextEdit(parent),
+ parent_(parent)
+ {}
+
+ QPopupMenu* createPopupMenu(const QPoint& pos);
+
+private slots:
+ void copyAll();
+ void toggleShowInternalCommands();
+
+private:
+ GDBOutputWidget* parent_;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/gdbparser.cpp b/languages/cpp/debugger/gdbparser.cpp
new file mode 100644
index 00000000..79057af5
--- /dev/null
+++ b/languages/cpp/debugger/gdbparser.cpp
@@ -0,0 +1,432 @@
+// **************************************************************************
+// begin : Tue Aug 17 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 "gdbparser.h"
+#include "variablewidget.h"
+#include <kdebug.h>
+
+#include <qregexp.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+namespace GDBDebugger
+{
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+GDBParser *GDBParser::GDBParser_ = 0;
+
+GDBParser *GDBParser::getGDBParser()
+{
+ if (!GDBParser_)
+ GDBParser_ = new GDBParser();
+
+ return GDBParser_;
+}
+
+// **************************************************************************
+
+void GDBParser::destroy()
+{
+ delete GDBParser_;
+ GDBParser_ = 0;
+}
+
+// **************************************************************************
+
+GDBParser::GDBParser()
+{
+}
+
+// **************************************************************************
+
+GDBParser::~GDBParser()
+{
+}
+
+
+// **************************************************************************
+
+QString GDBParser::getName(const char **buf)
+{
+ const char *start = skipNextTokenStart(*buf);
+ if (*start) {
+ *buf = skipTokenValue(start);
+ return QCString(start, *buf - start + 1);
+ } else
+ *buf = start;
+
+ return QString();
+}
+
+// **************************************************************************
+
+QString GDBParser::getValue(const char **buf)
+{
+ const char *start = skipNextTokenStart(*buf);
+ *buf = skipTokenValue(start);
+
+ QString value(QCString(start, *buf - start + 1).data());
+ return value;
+}
+
+QString GDBParser::undecorateValue(DataType type, const QString& s)
+{
+ QCString l8 = s.local8Bit();
+ const char* start = l8;
+ const char* end = start + s.length();
+
+ if (*start == '{')
+ {
+ // Gdb uses '{' in two cases:
+ // - composites (arrays and structures)
+ // - pointers to functions. In this case type is
+ // enclosed in "{}". Not sure why it's so, as
+ // when printing pointer, type is in parenthesis.
+ if (type == typePointer)
+ {
+ // Looks like type in braces at the beginning. Strip it.
+ start = skipDelim(start, '{', '}');
+ }
+ else
+ {
+ // Looks like composite, strip the braces and return.
+ return QCString(start+1, end - start -1);
+ }
+ }
+ else if (*start == '(')
+ {
+ // Strip the type of the pointer from the value.
+ //
+ // When printing values of pointers, gdb prints the pointer
+ // type as well. This is not necessary for kdevelop -- after
+ // all, there's separate column with value type. But that behaviour
+ // is not configurable. The only way to change it is to explicitly
+ // pass the 'x' format specifier to the 'print' command.
+ //
+ // We probably can achieve that by sending an 'print in hex' request
+ // as soon as we know the type of variable, but that would be complex
+ // and probably conflict with 'toggle hex/decimal' command.
+ // So, address this problem as close to debugger as possible.
+
+ // We can't find the first ')', because type can contain '(' and ')'
+ // characters if its function pointer. So count opening and closing
+ // parentheses.
+
+ start = skipDelim(start, '(', ')');
+ }
+
+ QString value(QCString(start, end - start + 1).data());
+
+ value = value.stripWhiteSpace();
+
+ if (value[0] == '@')
+ {
+ // It's a reference, we need to show just the value.
+ if (int i = value.find(":"))
+ {
+ value = value.mid(i+2);
+ }
+ else
+ {
+ // Just reference, no value at all, remove all
+ value = "";
+ }
+ }
+
+ if (value.find("Cannot access memory") == 0)
+ value = "(inaccessible)";
+
+ return value.stripWhiteSpace();
+}
+
+QString GDBParser::undecorateValue(const QString& s)
+{
+ DataType dataType = determineType(s.local8Bit());
+ QString r = undecorateValue(dataType, s.local8Bit());
+ return r;
+}
+
+// Given a value that starts with 0xNNNNNN determines if
+// it looks more like pointer, or a string value.
+DataType pointerOrValue(const char *buf)
+{
+ while (*buf) {
+ if (!isspace(*buf))
+ buf++;
+ else if (*(buf+1) == '\"')
+ return typeValue;
+ else
+ break;
+ }
+
+ return typePointer;
+}
+
+
+DataType GDBParser::determineType(const char *buf) const
+{
+ if (!buf || !*(buf= skipNextTokenStart(buf)))
+ return typeUnknown;
+
+ // A reference, probably from a parameter value.
+ if (*buf == '@')
+ return typeReference;
+
+ // Structures and arrays - (but which one is which?)
+ // {void (void)} 0x804a944 <__builtin_new+41> - this is a fn pointer
+ // (void (*)(void)) 0x804a944 <f(E *, char)> - so is this - ugly!!!
+ if (*buf == '{') {
+ if (strncmp(buf, "{{", 2) == 0)
+ return typeArray;
+
+ if (strncmp(buf, "{<No data fields>}", 18) == 0)
+ return typeValue;
+
+ buf++;
+ while (*buf) {
+ switch (*buf) {
+ case '=':
+ return typeStruct;
+ case '"':
+ buf = skipString(buf);
+ break;
+ case '\'':
+ buf = skipQuotes(buf, '\'');
+ break;
+ case ',':
+ if (*(buf-1) == '}')
+ Q_ASSERT(false);
+ return typeArray;
+ case '}':
+ if (*(buf+1) == ',' || *(buf+1) == '\n' || !*(buf+1))
+ return typeArray; // Hmm a single element array??
+ if (strncmp(buf+1, " 0x", 3) == 0)
+ return typePointer; // What about references?
+ return typeUnknown; // very odd?
+ case '(':
+ buf = skipDelim(buf, '(', ')');
+ break;
+ case '<':
+ buf = skipDelim(buf, '<', '>');
+ // gdb may produce this output:
+ // $1 = 0x804ddf3 ' ' <repeats 20 times>, "TESTSTRING"
+ // after having finished with the "repeats"-block we need
+ // to check if the string continues
+ if ( buf[0] == ',' && (buf[2] == '\"' || buf[2] == '\'') ) {
+ buf++; //set the buffer behind the comma to indicate that the string continues
+ }
+ break;
+ default:
+ buf++;
+ break;
+ }
+ }
+ return typeUnknown;
+ }
+
+ // some sort of address. We need to sort out if we have
+ // a 0x888888 "this is a char*" type which we'll term a value
+ // or whether we just have an address
+ if (strncmp(buf, "0x", 2) == 0) {
+ return pointerOrValue(buf);
+ }
+
+ // Pointers and references - references are a bit odd
+ // and cause GDB to fail to produce all the local data
+ // if they haven't been initialised. but that's not our problem!!
+ // (void (*)(void)) 0x804a944 <f(E *, char)> - this is a fn pointer
+ if (*buf == '(') {
+ buf = skipDelim(buf, '(', ')');
+ // This 'if' handles values like this:
+ // (int (&)[3]) @0xbffff684: {5, 6, 7}
+ // which is a reference to array.
+ if (buf[1] == '@')
+ return typeReference;
+ // This 'if' handles values like this:
+ // (int (*)[3]) 0xbffff810
+ if (strncmp(buf, " 0x", 3) == 0)
+ {
+ ++buf;
+ return pointerOrValue(buf);
+ }
+
+ switch (*(buf-2)) {
+ case '*':
+ return typePointer;
+ case '&':
+ return typeReference;
+ default:
+ switch (*(buf-8)) {
+ case '*':
+ return typePointer;
+ case '&':
+ return typeReference;
+ }
+ return typeUnknown;
+ }
+ }
+
+ buf = skipTokenValue(buf);
+ if ((strncmp(buf, " = ", 3) == 0) || (*buf == '='))
+ return typeName;
+
+ return typeValue;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipString(const char *buf) const
+{
+ if (buf && *buf == '\"') {
+ buf = skipQuotes(buf, *buf);
+ while (*buf) {
+ if ((strncmp(buf, ", \"", 3) == 0) ||
+ (strncmp(buf, ", '", 3) == 0))
+ buf = skipQuotes(buf+2, *(buf+2));
+ else if (strncmp(buf, " <", 2) == 0) // take care of <repeats
+ buf = skipDelim(buf+1, '<', '>');
+ else
+ break;
+ }
+
+ // If the string is long then it's chopped and has ... after it.
+ while (*buf && *buf == '.')
+ buf++;
+ }
+
+ return buf;
+}
+
+// ***************************************************************************
+
+const char *GDBParser::skipQuotes(const char *buf, char quotes) const
+{
+ if (buf && *buf == quotes) {
+ buf++;
+
+ while (*buf) {
+ if (*buf == '\\')
+ buf++; // skips \" or \' problems
+ else if (*buf == quotes)
+ return buf+1;
+
+ buf++;
+ }
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipDelim(const char *buf, char open, char close) const
+{
+ if (buf && *buf == open) {
+ buf++;
+
+ while (*buf) {
+ if (*buf == open)
+ buf = skipDelim(buf, open, close);
+ else if (*buf == close)
+ return buf+1;
+ else if (*buf == '\"')
+ buf = skipString(buf);
+ else if (*buf == '\'')
+ buf = skipQuotes(buf, *buf);
+ else if (*buf)
+ buf++;
+ }
+ }
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipTokenValue(const char *buf) const
+{
+ if (buf) {
+ while (true) {
+ buf = skipTokenEnd(buf);
+
+ const char *end = buf;
+ while (*end && isspace(*end) && *end != '\n')
+ end++;
+
+ if (*end == 0 || *end == ',' || *end == '\n' || *end == '=' || *end == '}')
+ break;
+
+ if (buf == end)
+ break;
+
+ buf = end;
+ }
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipTokenEnd(const char *buf) const
+{
+ if (buf) {
+ switch (*buf) {
+ case '"':
+ return skipString(buf);
+ case '\'':
+ return skipQuotes(buf, *buf);
+ case '{':
+ return skipDelim(buf, '{', '}');
+ case '<':
+ buf = skipDelim(buf, '<', '>');
+ // gdb may produce this output:
+ // $1 = 0x804ddf3 ' ' <repeats 20 times>, "TESTSTRING"
+ // after having finished with the "repeats"-block we need
+ // to check if the string continues
+ if ( buf[0] == ',' && (buf[2] == '\"' || buf[2] == '\'') ) {
+ buf++; //set the buffer behind the comma to indicate that the string continues
+ }
+ return buf;
+ case '(':
+ return skipDelim(buf, '(', ')');
+ }
+
+ while (*buf && !isspace(*buf) && *buf != ',' && *buf != '}' && *buf != '=')
+ buf++;
+ }
+
+ return buf;
+}
+
+// **************************************************************************
+
+const char *GDBParser::skipNextTokenStart(const char *buf) const
+{
+ if (buf)
+ while (*buf && (isspace(*buf) || *buf == ',' || *buf == '}' || *buf == '='))
+ buf++;
+
+ return buf;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}
diff --git a/languages/cpp/debugger/gdbparser.h b/languages/cpp/debugger/gdbparser.h
new file mode 100644
index 00000000..8972e5df
--- /dev/null
+++ b/languages/cpp/debugger/gdbparser.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ begin : Tue Aug 17 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 _GDBPARSER_H_
+#define _GDBPARSER_H_
+
+#include "variablewidget.h"
+
+namespace GDBDebugger
+{
+
+class GDBParser
+{
+public:
+ DataType determineType(const char *buf) const;
+ QString undecorateValue(const QString& s);
+
+ const char *skipString(const char *buf) const;
+ const char *skipQuotes(const char *buf, char quote) const;
+ const char *skipDelim(const char *buf, char open, char close) const;
+
+ static GDBParser *getGDBParser();
+ static void destroy();
+
+private:
+ void parseArray(TrimmableItem *parent, const char *buf);
+
+ const char *skipTokenEnd(const char *buf) const;
+ const char *skipTokenValue(const char *buf) const;
+ const char *skipNextTokenStart(const char *buf) const;
+
+ QString getName(const char **buf);
+ /** Assuming 'buf' points to a value, return a pointer
+ to the position right after the value.
+ */
+ QString getValue(const char **buf);
+ QString undecorateValue(DataType type, const QString& s);
+
+protected:
+ GDBParser();
+ ~GDBParser();
+ static GDBParser *GDBParser_;
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/gdbtable.cpp b/languages/cpp/debugger/gdbtable.cpp
new file mode 100644
index 00000000..c4d75f3a
--- /dev/null
+++ b/languages/cpp/debugger/gdbtable.cpp
@@ -0,0 +1,55 @@
+/***************************************************************************
+* Copyright (C) 2003 by Alexander Dymo *
+* cloudtemple@mksat.net *
+* *
+* 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 "gdbtable.h"
+
+namespace GDBDebugger {
+
+GDBTable::GDBTable(QWidget *parent, const char *name)
+ : QTable(parent, name)
+{
+}
+
+GDBTable::GDBTable(int nr, int nc, QWidget * parent, const char * name)
+ : QTable(nr, nc, parent, name)
+{
+}
+
+GDBTable::~GDBTable()
+{
+}
+
+void GDBTable::keyPressEvent( QKeyEvent * e )
+{
+ emit keyPressed(e->key());
+
+ if (e->key() == Key_Return)
+ emit returnPressed();
+ else if (e->key() == Key_F2)
+ emit f2Pressed();
+ else if ((e->text() == QString("a")) && (e->state() == AltButton))
+ {
+ emit insertPressed();
+ return;
+ }
+ else if ((e->text() == QString("A")) && (e->state() == AltButton))
+ {
+ emit insertPressed();
+ return;
+ }
+ else if (e->key() == Key_Delete)
+ emit deletePressed();
+
+ QTable::keyPressEvent(e);
+}
+
+}
+
+#include "gdbtable.moc"
+
diff --git a/languages/cpp/debugger/gdbtable.h b/languages/cpp/debugger/gdbtable.h
new file mode 100644
index 00000000..f8741e26
--- /dev/null
+++ b/languages/cpp/debugger/gdbtable.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+* Copyright (C) 2003 by Alexander Dymo *
+* cloudtemple@mksat.net *
+* *
+* 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 GDBDEBUGGERGDBTABLE_H
+#define GDBDEBUGGERGDBTABLE_H
+
+#include <qtable.h>
+
+namespace GDBDebugger {
+
+class GDBTable : public QTable
+{
+Q_OBJECT
+public:
+ GDBTable(QWidget *parent = 0, const char *name = 0);
+ GDBTable( int numRows, int numCols, QWidget * parent = 0, const char * name = 0 );
+ ~GDBTable();
+
+ virtual void keyPressEvent ( QKeyEvent * e );
+
+signals:
+ void keyPressed(int key);
+
+ void returnPressed();
+ void f2Pressed();
+ void insertPressed();
+ void deletePressed();
+};
+
+}
+
+#endif
+
diff --git a/languages/cpp/debugger/hi16-action-breakpoint_add.png b/languages/cpp/debugger/hi16-action-breakpoint_add.png
new file mode 100644
index 00000000..1b41040c
--- /dev/null
+++ b/languages/cpp/debugger/hi16-action-breakpoint_add.png
Binary files differ
diff --git a/languages/cpp/debugger/hi16-action-breakpoint_delete.png b/languages/cpp/debugger/hi16-action-breakpoint_delete.png
new file mode 100644
index 00000000..c35e039f
--- /dev/null
+++ b/languages/cpp/debugger/hi16-action-breakpoint_delete.png
Binary files differ
diff --git a/languages/cpp/debugger/hi16-action-breakpoint_delete_all.png b/languages/cpp/debugger/hi16-action-breakpoint_delete_all.png
new file mode 100644
index 00000000..c35e039f
--- /dev/null
+++ b/languages/cpp/debugger/hi16-action-breakpoint_delete_all.png
Binary files differ
diff --git a/languages/cpp/debugger/hi16-action-breakpoint_edit.png b/languages/cpp/debugger/hi16-action-breakpoint_edit.png
new file mode 100644
index 00000000..ec92ced2
--- /dev/null
+++ b/languages/cpp/debugger/hi16-action-breakpoint_edit.png
Binary files differ
diff --git a/languages/cpp/debugger/kdevdebugger.desktop b/languages/cpp/debugger/kdevdebugger.desktop
new file mode 100644
index 00000000..69b5646c
--- /dev/null
+++ b/languages/cpp/debugger/kdevdebugger.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Type=Service
+Exec=blubb
+Comment=This plugin provides a frontend for GDB, a source-level debugger for C, C++ and more. http://sources.redhat.com/gdb/
+Comment[ca]=Aquest connector proveeix una interfície per a GDB, un depurador a nivell de codi per a C, C++ i d'altres. http://sources.redhat.com/gdb/
+Comment[da]=Dette plugin sørger for en brugerflade til GDB, en fejlretter til C, C++ og mere. http://sources.redhat.com/gdb/
+Comment[de]=Dieses Modul bietet eine Oberfläche für GDB, einen Quellcode-Debugger für C, C++ und andere Sprachen. http://sources.redhat.com/gdb/
+Comment[el]=Αυτό το Ï€Ïόσθετο Ï€ÏοσφέÏει ένα Ï€ÏόγÏαμμα για το GDB, έναν αποσφαλματωτή πηγαίου κώδικα για C, C++ και άλλες γλώσσες. http://sources.redhat.com/gdb/
+Comment[es]=Este complemento proporciona un entorno para GDB, un depurador a nivel de código para C, C++ y otros (http://sources.redhat.com/gdb/)
+Comment[et]=See plugin pakub GDB kasutajaliidest. GDB on C, C++ ja veel mitme keele lähtekoodi tasandil tegutsev siluja. http://sources.redhat.com/gdb/
+Comment[eu]=Plugin honek GDB-rako interfaze bat eskeintzen du. GDB, C eta C++-rako iturburu-mailako araztaile bat da. http://sources.redhat.com/gdb/
+Comment[fa]=این وصله پایانه‌ای برای GDB، اشکال‌زدای سطح منبع برای سی، C++ و بیشتر تولید می‌کند. http://sources.redhat.com/gdb/
+Comment[fr]=Ce module externe fournit une interface pour GDB, un débogueur au niveau source pour C, C++ et autres. http://sources.redhat.com/gdb/
+Comment[gl]=Esta extensión proporciona un frontal para GDB, un depurador a nivel de código para C, C++ e outras linguaxes. http://sources.redhat.com/gdb/
+Comment[hi]=यह पà¥à¤²à¤—इन जीडीबी के लिठफà¥à¤°à¤¨à¥à¤Ÿà¤à¤£à¥à¤¡ पà¥à¤°à¤¦à¤¾à¤¨ करता है, जो सी, सी++ तथा और भी के लिठसà¥à¤°à¥‹à¤¤-सà¥à¤¤à¤° पर डिबगर हैhttp://sources.redhat.com/gdb/
+Comment[hu]=Ez a bővítőmodul grafikus felületet biztosít a GDB nyomkövető használatához, C/C++-hoz és más nyelvekhez, forrásszinten. http://sources.redhat.com/gdb/
+Comment[is]=Þetta íforrit útvegar framhlið fyrir GDB, frumkóða-aflúsara fyrir C, C++ og fleira. http://sources.redhat.com/gdb/
+Comment[it]=Questo plugin fornisce un'interfaccia per GDB, un debugger a livello sorgente per C, C++ e altro. http://sources.redhat.com/gdb/
+Comment[ja]=ã“ã®ãƒ—ラグインã¯ã€C, C++ ãªã©ã®ã‚½ãƒ¼ã‚¹ãƒ¬ãƒ™ãƒ«ãƒ‡ãƒãƒƒã‚¬ GDB ã®ãƒ•ãƒ­ãƒ³ãƒˆã‚¨ãƒ³ãƒ‰ã‚’æä¾›ã—ã¾ã™ã€‚http://sources.redhat.com/gdb/
+Comment[nds]=Dit Moduul stellt en Böversiet för GDB praat, en Bornkode-Fehlersöker för C, C++ un annerswat. http://sources.redhat.com/gdb/
+Comment[ne]=यो पà¥à¤²à¤—इनले C, C++ र बढीका लागि सà¥à¤°à¥‹à¤¤-तह डिबगर,GDB का लागि फà¥à¤°à¤¨à¥à¤Ÿà¤‡à¤¨à¥à¤¡ पà¥à¤°à¤¦à¤¾à¤¨ गरà¥à¤¦à¤› । http://sources.redhat.com/gdb/
+Comment[nl]=Deze plugin biedt een grafische schil voor GDB, een broncode-debugger voor C, C++ en meer. http://sources.redhat.com/gdb/
+Comment[pl]=Ta wtyczka udostępnia interfejs do GDB, debugera poziomu źródłowego dla C, C++ i innych. http://sources.redhat.com/gdb/
+Comment[pt]=Este 'plugin' oferece uma interface para o GDB, um depurador ao nível do código para C, C++ entre outros. http://sources.redhat.com/gdb/
+Comment[pt_BR]=Este plug-in fornece um frontend para o GDB, um depurador a nível de código para C, C++ e mais. http://sources.redhat.com/gdb/
+Comment[ru]=Этот модуль предоÑтавлÑет Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ðº GDB, отладчику иÑходного кода Ð´Ð»Ñ C, C++ и других Ñзыков. http://sources.redhat.com/gdb/
+Comment[sk]=Tento modul poskytuje rozhranie pre GDB, debuger pre C, C++ a ÄalÅ¡ie. http://sources.redhat.com/gdb/
+Comment[sl]=Ta vstavek omogoÄa vmesnik za GDB, razhroÅ¡Äevalnik na ravni izvorne kode za C, C++ in veÄ. http://sourcs.redhat.com/gdb/
+Comment[sr]=Овај прикључак обезбеђује Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð·Ð° GDB, иÑправљач на нивоу изворног кода за C, C++ и више. http://sources.redhat.com/gdb/
+Comment[sr@Latn]=Ovaj prikljuÄak obezbeÄ‘uje interfejs za GDB, ispravljaÄ na nivou izvornog koda za C, C++ i viÅ¡e. http://sources.redhat.com/gdb/
+Comment[sv]=Insticksprogrammet tillhandahåller ett gränssnitt till GDB, en källkodsavlusare för C, C++ med mera. http://sources.redhat.com/gdb/
+Comment[ta]=GDB கà¯à®•à¯ இநà¯à®¤ சொரà¯à®•à¯ ஒர௠மà¯à®©à¯à®¨à®¿à®±à¯à®¤à¯à®¤à®²à¯ , C, C++ மறà¯à®±à¯à®®à¯ பலவறà¯à®±à®¿à®±à¯à®•à¯ ஒர௠மூல-நிலை டிபகà¯à®•à®°à¯
+Comment[tg]=Ин модул дар GDB интерфейÑро ба ихтиёр мегузорад, Ñозгузори код барои C, C++ ибтидоӣва барои дигар забонҳо. http://sources.redhat.com/gdb/
+Comment[tr]=Bu eklenti, C, C++ ve daha fazlası için bir kaynak-düzeyinde hata ayıklayıcı olan GDB için bir önucu sağlar. http://sources.redhat.com/gdb/
+Comment[zh_CN]=这个æ’件是一个 GDB å‰ç«¯ï¼Œä¸€ä¸ª Cã€C++ 和其它语言的æºä»£ç è°ƒè¯•å™¨ã€‚http://sources.redhat.com/gdb/
+Comment[zh_TW]=這個外掛程å¼æä¾› GDB çš„å‰ç«¯ä»‹é¢ã€‚GDB 是一個 C/C++ 還有其它語言的除錯器。http://sources.redhat.com/gdb/
+Name=KDevDebugger
+Name[da]=KDevelop Debugger
+Name[de]=Debugger (KDevelop)
+Name[hi]=के-डेव-डिबगर
+Name[nds]=KDevelop-Fehlersöker
+Name[ne]=केडीई विकास डिबगर
+Name[pl]=KDevDebuger
+Name[sk]=KDev debuger
+Name[sv]=KDevelop avlusare
+Name[ta]=கெடெவ௠டிபகà¯à®•à®°à¯
+Name[zh_TW]=KDevelop 除錯器
+GenericName=Debugger Frontend
+GenericName[ca]=Interfície per al depurador
+GenericName[da]=Fejlsøgningsbrugerflade
+GenericName[de]=Debugger-Oberfläche
+GenericName[el]=ΠÏόγÏαμμα Αποσφαλματωτή
+GenericName[es]=Entorno del depurador
+GenericName[et]=Siluja kasutajaliides
+GenericName[eu]=Araztaile-interfazea
+GenericName[fa]=پایانۀ اشکال‌زدا
+GenericName[fr]=Interface du débogueur
+GenericName[ga]=Comhéadan Dífhabhtóra
+GenericName[gl]=Frontal de depuración
+GenericName[hi]=डिबगर फà¥à¤°à¤¨à¥à¤Ÿà¤à¤£à¥à¤¡
+GenericName[hu]=Grafikus felület nyomkövetéshez
+GenericName[it]=Interfaccia al debugger
+GenericName[ja]=デãƒãƒƒã‚¬ãƒ•ãƒ­ãƒ³ãƒˆã‚¨ãƒ³ãƒ‰
+GenericName[nds]=Fehlersöker-Böversiet
+GenericName[ne]=डिबगर फà¥à¤°à¤¨à¥à¤Ÿà¤‡à¤¨à¥à¤¡
+GenericName[nl]=Grafische schil voor debugger
+GenericName[pl]=Interfejs do debugera
+GenericName[pt]=Interface de Depuração
+GenericName[pt_BR]=Frontend do Depurador
+GenericName[ru]=Интегрированный отладчик
+GenericName[sk]=Debuger rozhranie
+GenericName[sl]=Vmesnik razhroÅ¡Äevalnika
+GenericName[sr]=Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð¸Ñправљача
+GenericName[sr@Latn]=Interfejs ispravljaÄa
+GenericName[sv]=Gränssnitt för avlusare
+GenericName[ta]=டிபகà¯à®•à®°à¯ பிரானà¯à®Ÿà®Ÿà¯
+GenericName[tg]=Интеграл ёфтани Ñозгузор
+GenericName[tr]=Hata Ayıklayıcı Önucu
+GenericName[zh_CN]=调试器å‰ç«¯
+GenericName[zh_TW]=除錯器å‰ç«¯
+Icon=debugger
+ServiceTypes=KDevelop/Plugin
+X-KDE-Library=libkdevdebugger
+X-KDevelop-Version=5
+X-KDevelop-Scope=Project
+X-KDevelop-Properties=CompiledDevelopment
diff --git a/languages/cpp/debugger/kdevdebugger.rc b/languages/cpp/debugger/kdevdebugger.rc
new file mode 100644
index 00000000..801cadb8
--- /dev/null
+++ b/languages/cpp/debugger/kdevdebugger.rc
@@ -0,0 +1,93 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="KDevDebugger" version="11">
+<MenuBar>
+ <Menu name="debug">
+ <text>&amp;Debug</text>
+ <Action name="debug_run" group="debug"/>
+ <Action name="debug_stop" group="debug"/>
+ <Action name="debug_pause" group="debug"/>
+ <Action name="debug_runtocursor" group="debug"/>
+ <Action name="debug_jumptocursor" group="debug"/>
+ <Separator group="debug"/>
+ <Action name="debug_stepover" group="debug"/>
+ <Action name="debug_stepoverinst" group="debug"/>
+ <Action name="debug_stepinto" group="debug"/>
+ <Action name="debug_stepintoinst" group="debug"/>
+ <Action name="debug_stepout" group="debug"/>
+ <Action name="debug_restart" group="debug"/>
+ <Separator group="debug"/>
+ <Action name="debug_toggle_breakpoint" group="debug"/>
+ <Action name="debug_disable_breakpoint" group="debug"/>
+ <Separator group="debug"/>
+ <Action name="debug_memview" group="debug"/>
+ <Action name="debug_core" group="debug"/>
+ <Action name="debug_attach" group="debug"/>
+ <Separator group="debug"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="debugToolBar">
+ <text>Debugger Toolbar</text>
+ <Action name="debug_run"/>
+ <Action name="debug_restart"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepout"/>
+ <Separator/>
+ <Action name="debug_memview"/>
+ <WeakSeparator/>
+</ToolBar>
+<State name="stopped">
+ <enable>
+ <Action name="debug_run"/>
+ <Action name="debug_core"/>
+ <Action name="debug_attach"/>
+ </enable>
+ <disable>
+ <Action name="debug_stop"/>
+ <Action name="debug_pause"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepoverinst"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepintoinst"/>
+ <Action name="debug_stepout"/>
+ <Action name="debug_memview"/>
+ </disable>
+</State>
+<State name="paused">
+ <enable>
+ <Action name="debug_run"/>
+ <Action name="debug_stop"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepoverinst"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepintoinst"/>
+ <Action name="debug_stepout"/>
+ <Action name="debug_memview"/>
+ </enable>
+ <disable>
+ <Action name="debug_core"/>
+ <Action name="debug_attach"/>
+ </disable>
+</State>
+<State name="active">
+ <enable>
+ <Action name="debug_pause"/>
+ <Action name="debug_stop"/>
+ </enable>
+ <disable>
+ <Action name="debug_run"/>
+ <Action name="debug_runtocursor"/>
+ <Action name="debug_stepover"/>
+ <Action name="debug_stepoverinst"/>
+ <Action name="debug_stepinto"/>
+ <Action name="debug_stepintoinst"/>
+ <Action name="debug_stepout"/>
+ <Action name="debug_memview"/>
+ <Action name="debug_core"/>
+ <Action name="debug_attach"/>
+ </disable>
+</State>
+</kpartgui>
+
diff --git a/languages/cpp/debugger/label_with_double_click.cpp b/languages/cpp/debugger/label_with_double_click.cpp
new file mode 100644
index 00000000..a0a2014b
--- /dev/null
+++ b/languages/cpp/debugger/label_with_double_click.cpp
@@ -0,0 +1,14 @@
+
+#include "label_with_double_click.h"
+
+LabelWithDoubleClick::LabelWithDoubleClick(const QString& s, QWidget* parent)
+: QLabel(s, parent)
+{}
+
+void LabelWithDoubleClick::mouseDoubleClickEvent(QMouseEvent*)
+{
+ emit doubleClicked();
+}
+
+
+#include "label_with_double_click.moc"
diff --git a/languages/cpp/debugger/label_with_double_click.h b/languages/cpp/debugger/label_with_double_click.h
new file mode 100644
index 00000000..11dec898
--- /dev/null
+++ b/languages/cpp/debugger/label_with_double_click.h
@@ -0,0 +1,20 @@
+
+#ifndef LABEL_WITH_DOUBLE_CLICK_HPP_VP_2006_04_04
+#define LABEL_WITH_DOUBLE_CLICK_HPP_VP_2006_04_04
+
+#include <qlabel.h>
+
+class LabelWithDoubleClick : public QLabel
+{
+ Q_OBJECT
+public:
+ LabelWithDoubleClick(const QString& s, QWidget* parent);
+
+signals:
+ void doubleClicked();
+
+protected:
+ void mouseDoubleClickEvent(QMouseEvent*);
+};
+
+#endif
diff --git a/languages/cpp/debugger/memviewdlg.cpp b/languages/cpp/debugger/memviewdlg.cpp
new file mode 100644
index 00000000..5316aa91
--- /dev/null
+++ b/languages/cpp/debugger/memviewdlg.cpp
@@ -0,0 +1,486 @@
+/***************************************************************************
+ begin : Tue Oct 5 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 "memviewdlg.h"
+#include "gdbcontroller.h"
+#include "gdbcommand.h"
+
+#include <kbuttonbox.h>
+#include <klineedit.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kstdguiitem.h>
+#include <kdeversion.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qmultilineedit.h>
+#include <qpushbutton.h>
+#include <qvariant.h>
+#include <qpopupmenu.h>
+#include <qhbox.h>
+#include <qtoolbox.h>
+#include <qtextedit.h>
+
+#include <kmessagebox.h>
+
+#include <khexedit/byteseditinterface.h>
+
+#include <ctype.h>
+
+// **************************************************************************
+//
+// Dialog allows the user to enter
+// - A starting address
+// - An ending address
+//
+// this can be in the form
+// functiom/method name
+// variable address (ie &Var, str)
+// Memory address 0x8040abc
+//
+// When disassembling and you enter a method name without an
+// ending address then the whole method is disassembled.
+// No data means disassemble the method we're curently in.(from the
+// start of the method)
+//
+// click ok buton to send the request to gdb
+// the output is returned (some time later) in the raw data slot
+// and displayed as is, so it's rather crude, but it works!
+// **************************************************************************
+
+namespace GDBDebugger
+{
+ /** Container for controls that select memory range.
+
+ The memory range selection is embedded into memory view widget,
+ it's not a standalone dialog. However, we want to have easy way
+ to hide/show all controls, so we group them in this class.
+ */
+ class MemoryRangeSelector : public QWidget
+ {
+ public:
+ KLineEdit* startAddressLineEdit;
+ KLineEdit* amountLineEdit;
+ QPushButton* okButton;
+ QPushButton* cancelButton;
+
+ MemoryRangeSelector(QWidget* parent)
+ : QWidget(parent)
+ {
+ QVBoxLayout* l = new QVBoxLayout(this);
+
+ // Grid layout: labels + address field
+ QGridLayout* gl = new QGridLayout(l);
+
+ gl->setColSpacing(0, 2);
+ gl->setColSpacing(1, 4);
+ gl->setRowSpacing(1, 2);
+
+ QLabel* l1 = new QLabel(i18n("Start"), this);
+ gl->addWidget(l1, 0, 1);
+
+ startAddressLineEdit = new KLineEdit(this);
+ gl->addWidget(startAddressLineEdit, 0, 3);
+
+ QLabel* l2 = new QLabel(i18n("Amount"), this);
+ gl->addWidget(l2, 2, 1);
+
+ amountLineEdit = new KLineEdit(this);
+ gl->addWidget(amountLineEdit, 2, 3);
+
+ l->addSpacing(2);
+
+ QHBoxLayout* hb = new QHBoxLayout(l);
+ hb->addStretch();
+
+ okButton = new QPushButton(i18n("OK"), this);
+ hb->addWidget(okButton);
+
+ cancelButton = new QPushButton(i18n("Cancel"), this);
+ hb->addWidget(cancelButton);
+
+ l->addSpacing(2);
+
+ connect(startAddressLineEdit, SIGNAL(returnPressed()),
+ okButton, SLOT(animateClick()));
+
+ connect(amountLineEdit, SIGNAL(returnPressed()),
+ okButton, SLOT(animateClick()));
+ }
+ };
+
+
+
+ MemoryView::MemoryView(GDBController* controller,
+ QWidget* parent, const char* name)
+ : QWidget(parent, name),
+ controller_(controller),
+ // New memory view can be created only when debugger is active,
+ // so don't set s_appNotStarted here.
+ khexedit2_real_widget(0),
+ amount_(0), data_(0),
+ debuggerState_(0)
+ {
+ setCaption(i18n("Memory view"));
+ emit captionChanged(caption());
+
+ initWidget();
+
+ if (isOk())
+ slotEnableOrDisable();
+ }
+
+ void MemoryView::initWidget()
+ {
+ QVBoxLayout *l = new QVBoxLayout(this, 0, 0);
+
+ khexedit2_widget = KHE::createBytesEditWidget(this);
+
+ bool ok_ = false;
+
+ if (khexedit2_widget)
+ {
+ QWidget* real_widget = (QWidget*)
+ khexedit2_widget->child("BytesEdit");
+
+ if (real_widget)
+ {
+ ok_ = true;
+
+ connect(real_widget, SIGNAL(bufferChanged(int, int)),
+ this, SLOT(memoryEdited(int, int)));
+
+ khexedit2_real_widget = real_widget;
+
+ QVariant resize_style(2); // full size usage.
+ real_widget->setProperty("ResizeStyle", resize_style);
+
+ //QVariant group(8);
+ //real_widget->setProperty("StartOffset", start);
+ //real_widget->setProperty("NoOfBytesPerLine", group);
+
+ // HACK: use hardcoded constant taht should match
+ // khexedit2
+ // 3 -- binary
+ // 1 -- decimal
+ // 0 -- hex
+ //QVariant coding(3);
+ //real_widget->setProperty("Coding", coding);
+
+ //QVariant gap(32);
+ //real_widget->setProperty("BinaryGapWidth", gap);
+
+ }
+ else
+ {
+ delete khexedit2_widget;
+ }
+ }
+
+ if (ok_) {
+
+ rangeSelector_ = new MemoryRangeSelector(this);
+ l->addWidget(rangeSelector_);
+
+ connect(rangeSelector_->okButton, SIGNAL(clicked()),
+ this, SLOT(slotChangeMemoryRange()));
+
+
+ connect(rangeSelector_->cancelButton, SIGNAL(clicked()),
+ this, SLOT(slotHideRangeDialog()));
+
+ connect(rangeSelector_->startAddressLineEdit,
+ SIGNAL(textChanged(const QString&)),
+ this,
+ SLOT(slotEnableOrDisable()));
+
+ connect(rangeSelector_->amountLineEdit,
+ SIGNAL(textChanged(const QString&)),
+ this,
+ SLOT(slotEnableOrDisable()));
+
+ l->addWidget(khexedit2_widget);
+
+ } else {
+
+ QTextEdit* edit = new QTextEdit(this);
+ l->addWidget(edit);
+
+ edit->setText(
+ "<h1>Not available</h1>"
+ "<p>Could not open the khexedit2 library. "
+ "Make sure that the KHexEdit package (part of kdeutils) is installed. "
+ "Specifically, check for the following files:"
+ "<ul><li>libkhexeditcommon.so.0.0.0\n"
+ "<li>libkbyteseditwidget.so\n"
+ "<li>kbyteseditwidget.desktop\n"
+ "</ul>");
+ }
+
+ }
+
+ void MemoryView::debuggerStateChanged(int state)
+ {
+ if (isOk())
+ {
+ debuggerState_ = state;
+ slotEnableOrDisable();
+ }
+ }
+
+
+ void MemoryView::slotHideRangeDialog()
+ {
+ rangeSelector_->hide();
+ }
+
+ void MemoryView::slotChangeMemoryRange()
+ {
+ controller_->addCommand(
+ new ExpressionValueCommand(
+ rangeSelector_->amountLineEdit->text(),
+ this, &MemoryView::sizeComputed));
+ }
+
+ void MemoryView::sizeComputed(const QString& size)
+ {
+ controller_->addCommand(
+ new
+ GDBCommand(
+ QString("-data-read-memory %1 x 1 1 %2")
+ .arg(rangeSelector_->startAddressLineEdit->text())
+ .arg(size).ascii(),
+ this,
+ &MemoryView::memoryRead));
+ }
+
+ void MemoryView::memoryRead(const GDBMI::ResultRecord& r)
+ {
+ const GDBMI::Value& content = r["memory"][0]["data"];
+
+ amount_ = content.size();
+
+ startAsString_ = rangeSelector_->startAddressLineEdit->text();
+ amountAsString_ = rangeSelector_->amountLineEdit->text();
+ start_ = startAsString_.toUInt(0, 0);
+
+ setCaption(QString("%1 (%2 bytes)")
+ .arg(startAsString_).arg(amount_));
+ emit captionChanged(caption());
+
+ KHE::BytesEditInterface* bytesEditor
+ = KHE::bytesEditInterface(khexedit2_widget);
+
+ delete[] this->data_;
+ this->data_ = new char[amount_];
+ for(unsigned i = 0; i < content.size(); ++i)
+ {
+ this->data_[i] = content[i].literal().toInt(0, 16);
+ }
+
+
+ bytesEditor->setData( this->data_, amount_ );
+ bytesEditor->setReadOnly(false);
+ // Overwrite data, not insert new
+ bytesEditor->setOverwriteMode( true );
+ // Not sure this is needed, but prevent
+ // inserting new data.
+ bytesEditor->setOverwriteOnly( true );
+
+ QVariant start_v(start_);
+ khexedit2_real_widget->setProperty("FirstLineOffset", start_v);
+
+ //QVariant bsw(0);
+ //khexedit2_real_widget->setProperty("ByteSpacingWidth", bsw);
+
+ // HACK: use hardcoded constant taht should match
+ // khexedit2
+ // 3 -- binary
+ // 1 -- decimal
+ // 0 -- hex
+ //QVariant coding(1);
+ //khexedit2_real_widget->setProperty("Coding", coding);
+
+
+ slotHideRangeDialog();
+ }
+
+
+ void MemoryView::memoryEdited(int start, int end)
+ {
+ for(int i = start; i <= end; ++i)
+ {
+ controller_->addCommand(
+ new GDBCommand(
+ QString("set *(char*)(%1 + %2) = %3")
+ .arg(start_)
+ .arg(i)
+ .arg(QString::number(data_[i]))));
+ }
+ }
+
+ void MemoryView::contextMenuEvent ( QContextMenuEvent * e )
+ {
+ if (!isOk())
+ return;
+
+ QPopupMenu menu;
+
+ bool app_running = !(debuggerState_ & s_appNotStarted);
+
+ int idRange = menu.insertItem(i18n("Change memory range"));
+ // If address selector is show, 'set memory range' can't
+ // do anything more.
+ menu.setItemEnabled(idRange,
+ app_running && !rangeSelector_->isShown());
+ int idReload = menu.insertItem(i18n("Reload"));
+ // If amount is zero, it means there's not data yet, so
+ // reloading does not make sense.
+ menu.setItemEnabled(idReload, app_running && amount_ != 0);
+ int idClose = menu.insertItem(i18n("Close this view"));
+
+ int result = menu.exec(e->globalPos());
+
+ if (result == idRange)
+ {
+ rangeSelector_->startAddressLineEdit->setText(startAsString_);
+ rangeSelector_->amountLineEdit->setText(amountAsString_);
+
+ rangeSelector_->show();
+ rangeSelector_->startAddressLineEdit->setFocus();
+ }
+ if (result == idReload)
+ {
+ // We use numeric start_ and amount_ stored in this,
+ // not textual startAsString_ and amountAsString_,
+ // because program position might have changes and expressions
+ // are no longer valid.
+ controller_->addCommand(
+ new
+ GDBCommand(
+ QString("-data-read-memory %1 x 1 1 %2")
+ .arg(start_).arg(amount_).ascii(),
+ this,
+ &MemoryView::memoryRead));
+ }
+
+ if (result == idClose)
+ delete this;
+
+
+ }
+
+ bool MemoryView::isOk() const
+ {
+ return khexedit2_real_widget;
+ }
+
+ void MemoryView::slotEnableOrDisable()
+ {
+ bool app_started = !(debuggerState_ & s_appNotStarted);
+
+ bool enabled_ = app_started &&
+ !rangeSelector_->startAddressLineEdit->text().isEmpty() &&
+ !rangeSelector_->amountLineEdit->text().isEmpty();
+
+ rangeSelector_->okButton->setEnabled(enabled_);
+ }
+
+
+ ViewerWidget::ViewerWidget(GDBController* controller,
+ QWidget* parent,
+ const char* name)
+ : QWidget(parent, name),
+ controller_(controller)
+ {
+ setIcon(SmallIcon("math_brace"));
+
+ QVBoxLayout *l = new QVBoxLayout(this, 0, 0);
+
+ toolBox_ = new QToolBox(this);
+ l->addWidget(toolBox_);
+ }
+
+ void ViewerWidget::slotAddMemoryView()
+ {
+ // For unclear reasons, this call, that indirectly
+ // does
+ //
+ // mainWindow()->setViewAvailable(this)
+ // mainWindow()->raiseView(this)
+ //
+ // should be done before creating the child widget.
+ // Otherwise, the child widget won't be freely resizable --
+ // there will be not-so-small minimum size.
+ // Problem exists both with KMDI and S/IDEAL.
+
+ setViewShown(true);
+
+ MemoryView* widget = new MemoryView(controller_, this);
+ toolBox_->addItem(widget, widget->caption());
+ toolBox_->setCurrentItem(widget);
+ memoryViews_.push_back(widget);
+
+ connect(widget, SIGNAL(captionChanged(const QString&)),
+ this, SLOT(slotChildCaptionChanged(const QString&)));
+
+ connect(widget, SIGNAL(destroyed(QObject*)),
+ this, SLOT(slotChildDestroyed(QObject*)));
+ }
+
+ void ViewerWidget::slotDebuggerState(const QString&, int state)
+ {
+ for(unsigned i = 0; i < memoryViews_.size(); ++i)
+ {
+ memoryViews_[i]->debuggerStateChanged(state);
+ }
+ }
+
+ void ViewerWidget::slotChildCaptionChanged(const QString& caption)
+ {
+ const QWidget* s = static_cast<const QWidget*>(sender());
+ QWidget* ncs = const_cast<QWidget*>(s);
+ QString cap = caption;
+ // Prevent intepreting '&' as accelerator specifier.
+ cap.replace("&", "&&");
+ toolBox_->setItemLabel(toolBox_->indexOf(ncs), cap);
+ }
+
+ void ViewerWidget::slotChildDestroyed(QObject* child)
+ {
+ QValueVector<MemoryView*>::iterator i, e;
+ for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i)
+ {
+ if (*i == child)
+ {
+ memoryViews_.erase(i);
+ break;
+ }
+ }
+
+ if (toolBox_->count() == 0)
+ setViewShown(false);
+ }
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}
+
+#include "memviewdlg.moc"
diff --git a/languages/cpp/debugger/memviewdlg.h b/languages/cpp/debugger/memviewdlg.h
new file mode 100644
index 00000000..a29de924
--- /dev/null
+++ b/languages/cpp/debugger/memviewdlg.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ begin : Tue Oct 5 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 _MEMVIEW_H_
+#define _MEMVIEW_H_
+
+#include "mi/gdbmi.h"
+
+#include <kdialog.h>
+
+#include <qvaluevector.h>
+
+class KLineEdit;
+class QMultiLineEdit;
+class QToolBox;
+
+namespace GDBDebugger
+{
+ class MemoryView;
+ class GDBController;
+
+ class ViewerWidget : public QWidget
+ {
+ Q_OBJECT
+ public:
+ ViewerWidget(GDBController* controller,
+ QWidget* parent, const char* name);
+
+ public slots:
+ /** Adds a new memory view to *this, initially showing
+ no data. */
+ void slotAddMemoryView();
+ /** Informs *this about change in debugger state. Should always
+ be connected to, so that *this can disable itself when
+ debugger is not running. */
+ void slotDebuggerState(const QString&, int state);
+
+ signals:
+ void setViewShown(bool shown);
+
+
+ private slots:
+ void slotChildCaptionChanged(const QString& caption);
+ void slotChildDestroyed(QObject* child);
+
+ private: // Data
+ GDBController* controller_;
+ QToolBox* toolBox_;
+ QValueVector<MemoryView*> memoryViews_;
+ };
+
+ class MemoryView : public QWidget
+ {
+ Q_OBJECT
+ public:
+ MemoryView(GDBController* controller,
+ QWidget* parent, const char* name = 0);
+
+ void debuggerStateChanged(int state);
+
+ signals:
+ void captionChanged(const QString& caption);
+
+ private: // Callbacks
+ void sizeComputed(const QString& value);
+
+ void memoryRead(const GDBMI::ResultRecord& r);
+
+ private slots:
+ void memoryEdited(int start, int end);
+
+ private:
+ // Returns true is we successfully created the hexeditor, and so
+ // can work.
+ bool isOk() const;
+
+
+
+ private slots:
+ /** Invoked when user has changed memory range.
+ Gets memory for the new range. */
+ void slotChangeMemoryRange();
+ void slotHideRangeDialog();
+ void slotEnableOrDisable();
+
+ private: // QWidget overrides
+ void contextMenuEvent(QContextMenuEvent* e);
+
+ private:
+ void initWidget();
+
+ private:
+ GDBController* controller_;
+ class MemoryRangeSelector* rangeSelector_;
+ QWidget* khexedit2_widget;
+ QWidget* khexedit2_real_widget;
+
+ uint start_, amount_;
+ QString startAsString_, amountAsString_;
+ char* data_;
+
+ int debuggerState_;
+ };
+}
+
+#endif
diff --git a/languages/cpp/debugger/mi/Makefile.am b/languages/cpp/debugger/mi/Makefile.am
new file mode 100644
index 00000000..d6cdf5f7
--- /dev/null
+++ b/languages/cpp/debugger/mi/Makefile.am
@@ -0,0 +1,12 @@
+
+# We need exceptions since they are used to report all MI access errors.
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+METASOURCES = AUTO
+INCLUDES = $(all_includes)
+
+lib_LTLIBRARIES = libgdbmi_parser.la
+libgdbmi_parser_la_LDFLAGS = $(all_libraries)
+libgdbmi_parser_la_LIBADD = $(LIB_QT)
+libgdbmi_parser_la_SOURCES = gdbmi.cpp miparser.cpp milexer.cpp
+
diff --git a/languages/cpp/debugger/mi/gdbmi.cpp b/languages/cpp/debugger/mi/gdbmi.cpp
new file mode 100644
index 00000000..534a0cad
--- /dev/null
+++ b/languages/cpp/debugger/mi/gdbmi.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "gdbmi.h"
+
+using namespace GDBMI;
+
+
+type_error::type_error()
+: std::logic_error("MI type error")
+{}
+
+QString Value::literal() const
+{
+ throw type_error();
+}
+
+int Value::toInt(int /*base*/) const
+{
+ throw type_error();
+}
+
+bool Value::hasField(const QString&) const
+{
+ throw type_error();
+}
+
+const Value& Value::operator[](const QString&) const
+{
+ throw type_error();
+}
+
+bool Value::empty() const
+{
+ throw type_error();
+}
+
+unsigned Value::size() const
+{
+ throw type_error();
+}
+
+
+const Value& Value::operator[](unsigned) const
+{
+ throw type_error();
+}
+
+QString StringLiteralValue::literal() const
+{
+ return literal_;
+}
+
+int StringLiteralValue::toInt(int base) const
+{
+ bool ok;
+ int result = literal_.toInt(&ok, base);
+ if (!ok)
+ throw type_error();
+ return result;
+}
+
+TupleValue::~TupleValue()
+{
+ for (QValueListIterator<Result*> it=results.begin(); it!=results.end(); ++it)
+ delete *it;
+}
+
+bool TupleValue::hasField(const QString& variable) const
+{
+ return results_by_name.count(variable);
+}
+
+const Value& TupleValue::operator[](const QString& variable) const
+{
+ if (results_by_name.count(variable))
+ return *results_by_name[variable]->value;
+ else
+ throw type_error();
+}
+
+ListValue::~ListValue()
+{
+ for (QValueListIterator<Result*> it=results.begin(); it!=results.end(); ++it)
+ delete *it;
+}
+
+bool ListValue::empty() const
+{
+ return results.isEmpty();
+}
+
+unsigned ListValue::size() const
+{
+ return results.size();
+}
+
+const Value& ListValue::operator[](unsigned index) const
+{
+ if (index < results.size())
+ {
+ return *results[index]->value;
+ }
+ else
+ throw type_error();
+}
+
+
+
+
diff --git a/languages/cpp/debugger/mi/gdbmi.h b/languages/cpp/debugger/mi/gdbmi.h
new file mode 100644
index 00000000..7a193e91
--- /dev/null
+++ b/languages/cpp/debugger/mi/gdbmi.h
@@ -0,0 +1,221 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef GDBMI_H
+#define GDBMI_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include <stdexcept>
+
+/**
+@author Roberto Raggi
+@author Vladimir Prus
+*/
+namespace GDBMI
+{
+ /** Exception that is thrown when we're trying to invoke an
+ operation that is not supported by specific MI value. For
+ example, trying to index a string literal.
+
+ Such errors are conceptually the same as assert, but in GUI
+ we can't use regular assert, and Q_ASSERT, which only prints
+ a message, is not suitable either. We need to break processing,
+ and the higher-level code can report "Internal parsing error",
+ or something.
+
+ Being glorified assert, this exception does not cary any
+ useful information.
+ */
+ class type_error : public std::logic_error
+ {
+ public:
+ type_error();
+ };
+
+ /** Base class for all MI values.
+ MI values are of three kinds:
+ - String literals
+ - Lists (indexed by integer)
+ - Tuple (set of named values, indexed by name)
+
+ The structure of response to a specific gdb command is fixed.
+ While any tuples in response may omit certain named fields, the
+ kind of each item never changes. That is, response to specific
+ command can't contains sometimes string and sometimes tuple in
+ specific position.
+
+ Because of that static structure, it's almost never needed to query
+ dynamic type of a MI value. Most often we know it's say, tuple, and
+ can subscripts it.
+
+ So, the Value class has methods for accessing all kinds of values.
+ Attempting to call a method that is not applicable to specific value
+ will result in exception. The client code will almost never need to
+ cast from 'Value' to its derived classes.
+
+ Note also that all methods in this class are const and return
+ const Value&. That's by design -- there's no need to modify gdb
+ responses in GUI.
+ */
+ struct Value
+ {
+ Value() {}
+ private: // Copy disabled to prevent slicing.
+ Value(const Value&);
+ Value& operator=(const Value&);
+
+ public:
+
+ virtual ~Value() {}
+
+ enum { StringLiteral, Tuple, List } kind;
+
+ /** If this value is a string literals, returns the string value.
+ Othewise, throws type_error.
+ */
+ virtual QString literal() const;
+
+ /** If the value is a string literal, converts it to int and
+ returns. If conversion fails, or the value cannot be
+ converted to int, throws type_error.
+ */
+ virtual int toInt(int base = 10) const;
+
+ /** If this value is a tuple, returns true if the tuple
+ has a field named 'variable'. Otherwise,
+ throws type_error.
+ */
+ virtual bool hasField(const QString& variable) const;
+
+ /** If this value is a tuple, and contains named field 'variable',
+ returns it. Otherwise, throws 'type_error'.
+ This method is virtual, and derived in base class, so that
+ we can save on casting, when we know for sure that instance
+ is TupleValue, or ListValue.
+ */
+ virtual const Value& operator[](const QString& variable) const;
+
+ /** If this value is a list, returns true if the list is empty.
+ If this value is not a list, throws 'type_error'.
+ */
+ virtual bool empty() const;
+
+ /** If this value is a list, returns it's size.
+ Otherwise, throws 'type_error'.
+ */
+ virtual unsigned size() const;
+
+ /** If this value is a list, returns the element at
+ 'index'. Otherwise, throws 'type_error'.
+ */
+ virtual const Value& operator[](unsigned index) const;
+ };
+
+ /** @internal
+ Internal class to represent name-value pair in tuples.
+ */
+ struct Result
+ {
+ Result() : value(0) {}
+ ~Result() { delete value; value = 0; }
+
+ QString variable;
+ Value *value;
+ };
+
+ struct StringLiteralValue : public Value
+ {
+ StringLiteralValue(const QString &lit)
+ : literal_(lit) { Value::kind = StringLiteral; }
+
+ public: // Value overrides
+
+ QString literal() const;
+ int toInt(int base) const;
+
+ private:
+ QString literal_;
+ };
+
+ struct TupleValue : public Value
+ {
+ TupleValue() { Value::kind = Tuple; }
+ ~TupleValue();
+
+ bool hasField(const QString&) const;
+ const Value& operator[](const QString& variable) const;
+
+
+ QValueList<Result*> results;
+ QMap<QString, GDBMI::Result*> results_by_name;
+ };
+
+ struct ListValue : public Value
+ {
+ ListValue() { Value::kind = List; }
+ ~ListValue();
+
+ bool empty() const;
+
+ unsigned size() const;
+
+ const Value& operator[](unsigned index) const;
+
+ QValueList<Result*> results;
+
+ };
+
+ struct Record
+ {
+ virtual ~Record() {}
+ virtual QString toString() const { Q_ASSERT( 0 ); return QString::null; }
+
+ enum { Prompt, Stream, Result } kind;
+ };
+
+ struct ResultRecord : public Record, public TupleValue
+ {
+ ResultRecord() { Record::kind = Result; }
+
+ QString reason;
+ };
+
+ struct PromptRecord : public Record
+ {
+ inline PromptRecord() { Record::kind = Prompt; }
+
+ virtual QString toString() const
+ { return "(prompt)\n"; }
+ };
+
+ struct StreamRecord : public Record
+ {
+ inline StreamRecord() : reason(0) { Record::kind = Stream; }
+
+ char reason;
+ QString message;
+ };
+}
+
+#endif
diff --git a/languages/cpp/debugger/mi/milexer.cpp b/languages/cpp/debugger/mi/milexer.cpp
new file mode 100644
index 00000000..ecf18373
--- /dev/null
+++ b/languages/cpp/debugger/mi/milexer.cpp
@@ -0,0 +1,290 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "milexer.h"
+#include "tokens.h"
+#include <cctype>
+#include <iostream>
+
+bool MILexer::s_initialized = false;
+scan_fun_ptr MILexer::s_scan_table[];
+
+
+MILexer::MILexer()
+{
+ if (!s_initialized)
+ setupScanTable();
+}
+
+MILexer::~MILexer()
+{
+}
+
+void MILexer::setupScanTable()
+{
+ s_initialized = true;
+
+ for (int i=0; i<128; ++i) {
+ switch (i) {
+ case '\n':
+ s_scan_table[i] = &MILexer::scanNewline;
+ break;
+
+ case '"':
+ s_scan_table[i] = &MILexer::scanStringLiteral;
+ break;
+
+ default:
+ if (isspace(i))
+ s_scan_table[i] = &MILexer::scanWhiteSpaces;
+ else if (isalpha(i) || i == '_')
+ s_scan_table[i] = &MILexer::scanIdentifier;
+ else if (isdigit(i))
+ s_scan_table[i] = &MILexer::scanNumberLiteral;
+ else
+ s_scan_table[i] = &MILexer::scanChar;
+ }
+ }
+
+ s_scan_table[128] = &MILexer::scanUnicodeChar;
+}
+
+/*
+
+ m_firstToken = m_tokens.data();
+ m_currentToken = 0;
+
+ m_firstToken = m_tokens.data();
+ m_currentToken = m_firstToken;
+ */
+
+TokenStream *MILexer::tokenize(const FileSymbol *fileSymbol)
+{
+ m_tokensCount = 0;
+ m_tokens.resize(64);
+
+ m_contents = fileSymbol->contents;
+ m_length = m_contents.length();
+ m_ptr = 0;
+
+ m_lines.resize(8);
+ m_line = 0;
+
+ m_lines[m_line++] = 0;
+
+ m_cursor = 0;
+
+ // tokenize
+ int pos, len;
+
+ for (;;) {
+ if (m_tokensCount == (int)m_tokens.size())
+ m_tokens.resize(m_tokensCount * 2);
+
+ Token &tk = m_tokens[m_tokensCount++];
+ tk.kind = nextToken(pos, len);
+ tk.position = pos;
+ tk.length = len;
+
+ if (tk.kind == 0)
+ break;
+ }
+
+ TokenStream *tokenStream = new TokenStream;
+ tokenStream->m_contents = m_contents;
+
+ tokenStream->m_lines = m_lines;
+ tokenStream->m_line = m_line;
+
+ tokenStream->m_tokens = m_tokens;
+ tokenStream->m_tokensCount = m_tokensCount;
+
+ tokenStream->m_firstToken = tokenStream->m_tokens.data();
+ tokenStream->m_currentToken = tokenStream->m_firstToken;;
+
+ tokenStream->m_cursor = m_cursor;
+
+ return tokenStream;
+}
+
+int MILexer::nextToken(int &pos, int &len)
+{
+ int start = 0;
+ int kind = 0;
+ unsigned char ch = 0;
+
+ while (m_ptr < m_length) {
+ start = m_ptr;
+
+ ch = (unsigned char)m_contents[m_ptr];
+ (this->*s_scan_table[ch < 128 ? ch : 128])(&kind);
+
+ switch (kind) {
+ case Token_whitespaces:
+ case '\n':
+ break;
+
+ default:
+ pos = start;
+ len = m_ptr - start;
+ return kind;
+ }
+
+ if (kind == 0)
+ break;
+ }
+
+ return 0;
+}
+
+void MILexer::scanChar(int *kind)
+{
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanWhiteSpaces(int *kind)
+{
+ *kind = Token_whitespaces;
+
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isspace(ch) && ch != '\n'))
+ break;
+
+ ++m_ptr;
+ }
+}
+
+void MILexer::scanNewline(int *kind)
+{
+ if (m_line == (int)m_lines.size())
+ m_lines.resize(m_lines.size() * 2);
+
+ if (m_lines.at(m_line) < m_ptr)
+ m_lines[m_line++] = m_ptr;
+
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanUnicodeChar(int *kind)
+{
+ *kind = m_contents[m_ptr++];
+}
+
+void MILexer::scanStringLiteral(int *kind)
+{
+ ++m_ptr;
+ while (char c = m_contents[m_ptr]) {
+ switch (c) {
+ case '\n':
+ // ### error
+ *kind = Token_string_literal;
+ return;
+ case '\\':
+ {
+ char next = m_contents.at(m_ptr+1);
+ if (next == '"' || next == '\\')
+ m_ptr += 2;
+ else
+ ++m_ptr;
+ }
+ break;
+ case '"':
+ ++m_ptr;
+ *kind = Token_string_literal;
+ return;
+ default:
+ ++m_ptr;
+ break;
+ }
+ }
+
+ // ### error
+ *kind = Token_string_literal;
+}
+
+void MILexer::scanIdentifier(int *kind)
+{
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isalnum(ch) || ch == '-' || ch == '_'))
+ break;
+
+ ++m_ptr;
+ }
+
+ *kind = Token_identifier;
+}
+
+void MILexer::scanNumberLiteral(int *kind)
+{
+ char ch;
+ while (m_ptr < m_length) {
+ ch = m_contents[m_ptr];
+ if (!(isalnum(ch) || ch == '.'))
+ break;
+
+ ++m_ptr;
+ }
+
+ // ### finish to implement me!!
+ *kind = Token_number_literal;
+}
+
+void TokenStream::positionAt(int position, int *line, int *column) const
+{
+ if (!(line && column && !m_lines.isEmpty()))
+ return;
+
+ int first = 0;
+ int len = m_line;
+ int half;
+ int middle;
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+
+ middle += half;
+
+ if (m_lines[middle] < position) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ }
+ else
+ len = half;
+ }
+
+ *line = QMAX(first - 1, 0);
+ *column = position - m_lines.at(*line);
+
+ Q_ASSERT( *column >= 0 );
+}
+
+QCString TokenStream::tokenText(int index) const
+{
+ Token *t = index < 0 ? m_currentToken : m_firstToken + index;
+ const char* data = m_contents;
+ return QCString(data + t->position, t->length+1);
+}
+
diff --git a/languages/cpp/debugger/mi/milexer.h b/languages/cpp/debugger/mi/milexer.h
new file mode 100644
index 00000000..bc0deead
--- /dev/null
+++ b/languages/cpp/debugger/mi/milexer.h
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MILEXER_H
+#define MILEXER_H
+
+#include <qmemarray.h>
+#include <qmap.h>
+#include <qstring.h>
+
+class MILexer;
+class TokenStream;
+
+typedef void (MILexer::*scan_fun_ptr)(int *kind);
+
+struct Token
+{
+ int kind;
+ int position;
+ int length;
+};
+
+struct FileSymbol
+{
+ QCString contents;
+ TokenStream *tokenStream;
+
+ inline FileSymbol()
+ : tokenStream(0) {}
+
+ inline ~FileSymbol();
+};
+
+struct TokenStream
+{
+ inline int lookAhead(int n = 0) const
+ { return (m_currentToken + n)->kind; }
+
+ inline int currentToken() const
+ { return m_currentToken->kind; }
+
+ inline QCString currentTokenText() const
+ { return tokenText(-1); }
+
+ QCString tokenText(int index = 0) const;
+
+ inline int lineOffset(int line) const
+ { return m_lines.at(line); }
+
+ void positionAt(int position, int *line, int *column) const;
+
+ inline void getTokenStartPosition(int index, int *line, int *column) const
+ { positionAt((m_firstToken + index)->position, line, column); }
+
+ inline void getTokenEndPosition(int index, int *line, int *column) const
+ {
+ Token *tk = m_firstToken + index;
+ positionAt(tk->position + tk->length, line, column);
+ }
+
+ inline void rewind(int index)
+ { m_currentToken = m_firstToken + index; }
+
+ inline int cursor() const
+ { return m_currentToken - m_firstToken; }
+
+ inline void nextToken()
+ { m_currentToken++; m_cursor++; }
+
+//private:
+ QCString m_contents;
+
+ QMemArray<int> m_lines;
+ int m_line;
+
+ QMemArray<Token> m_tokens;
+ int m_tokensCount;
+
+ Token *m_firstToken;
+ Token *m_currentToken;
+
+ int m_cursor;
+};
+
+class MILexer
+{
+public:
+ MILexer();
+ ~MILexer();
+
+ TokenStream *tokenize(const FileSymbol *fileSymbol);
+
+private:
+ int nextToken(int &position, int &len);
+
+ void scanChar(int *kind);
+ void scanUnicodeChar(int *kind);
+ void scanNewline(int *kind);
+ void scanWhiteSpaces(int *kind);
+ void scanStringLiteral(int *kind);
+ void scanNumberLiteral(int *kind);
+ void scanIdentifier(int *kind);
+
+ void setupScanTable();
+
+private:
+ static bool s_initialized;
+ static scan_fun_ptr s_scan_table[128 + 1];
+
+ QCString m_contents;
+ int m_ptr;
+ // Cached 'm_contents.length()'
+ int m_length;
+
+ QMemArray<int> m_lines;
+ int m_line;
+
+ QMemArray<Token> m_tokens;
+ int m_tokensCount;
+
+ int m_cursor;
+};
+
+inline FileSymbol::~FileSymbol()
+{
+ delete tokenStream;
+ tokenStream = 0;
+}
+
+
+#endif
diff --git a/languages/cpp/debugger/mi/miparser.cpp b/languages/cpp/debugger/mi/miparser.cpp
new file mode 100644
index 00000000..876bfa47
--- /dev/null
+++ b/languages/cpp/debugger/mi/miparser.cpp
@@ -0,0 +1,345 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * Copyright (C) 2005-2006 by Vladimir Prus *
+ * ghost@cs.msu.su *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "miparser.h"
+#include "tokens.h"
+#include <memory>
+
+using namespace GDBMI;
+
+#define MATCH(tok) \
+ do { \
+ if (lex->lookAhead(0) != (tok)) \
+ return false; \
+ } while (0)
+
+#define ADVANCE(tok) \
+ do { \
+ MATCH(tok); \
+ lex->nextToken(); \
+ } while (0)
+
+MIParser::MIParser()
+ : lex(0)
+{
+}
+
+MIParser::~MIParser()
+{
+}
+
+Record *MIParser::parse(FileSymbol *file)
+{
+ lex = 0;
+
+ Record *record = 0;
+
+ TokenStream *tokenStream = lexer.tokenize(file);
+ if (!tokenStream)
+ return false;
+
+ lex = file->tokenStream = tokenStream;
+
+ switch (lex->lookAhead()) {
+ case '~':
+ case '@':
+ case '&':
+ parseStreamRecord(record);
+ break;
+ case '(':
+ parsePrompt(record);
+ break;
+ case '^':
+ parseResultRecord(record);
+ break;
+ case '*':
+ // Same as result, only differs in start
+ // marker.
+ parseResultRecord(record);
+ break;
+ default:
+ break;
+ }
+
+ return record;
+}
+
+bool MIParser::parsePrompt(Record *&record)
+{
+ ADVANCE('(');
+ MATCH(Token_identifier);
+ if (lex->currentTokenText() != "gdb")
+ return false;
+ lex->nextToken();
+ ADVANCE(')');
+
+ record = new PromptRecord;
+ return true;
+}
+
+bool MIParser::parseStreamRecord(Record *&record)
+{
+ std::auto_ptr<StreamRecord> stream(new StreamRecord);
+
+ switch (lex->lookAhead()) {
+ case '~':
+ case '@':
+ case '&': {
+ stream->reason = lex->lookAhead();
+ lex->nextToken();
+ MATCH(Token_string_literal);
+ stream->message = parseStringLiteral();
+ record = stream.release();
+ }
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool MIParser::parseResultRecord(Record *&record)
+{
+ if (lex->lookAhead() != '^' && lex->lookAhead() != '*')
+ return false;
+ lex->nextToken();
+
+ MATCH(Token_identifier);
+ QString reason = lex->currentTokenText();
+ lex->nextToken();
+
+ std::auto_ptr<ResultRecord> res(new ResultRecord);
+ res->reason = reason;
+
+ if (lex->lookAhead() != ',') {
+ record = res.release();
+ return true;
+ }
+
+ lex->nextToken();
+
+ if (!parseCSV(*res))
+ return false;
+
+ record = res.release();
+ return true;
+}
+
+bool MIParser::parseResult(Result *&result)
+{
+ MATCH(Token_identifier);
+ QString variable = lex->currentTokenText();
+ lex->nextToken();
+
+ std::auto_ptr<Result> res(new Result);
+ res->variable = variable;
+
+ if (lex->lookAhead() != '=')
+ return true;
+
+ lex->nextToken();
+
+ Value *value = 0;
+ if (!parseValue(value))
+ return false;
+
+ res->value = value;
+ result = res.release();
+
+ return true;
+}
+
+bool MIParser::parseValue(Value *&value)
+{
+ value = 0;
+
+ switch (lex->lookAhead()) {
+ case Token_string_literal: {
+ value = new StringLiteralValue(parseStringLiteral());
+ }
+ return true;
+
+ case '{':
+ return parseTuple(value);
+
+ case '[':
+ return parseList(value);
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool MIParser::parseTuple(Value *&value)
+{
+ TupleValue* val;
+
+ if (!parseCSV(&val, '{', '}'))
+ return false;
+
+ value = val;
+ return true;
+}
+
+bool MIParser::parseList(Value *&value)
+{
+ ADVANCE('[');
+
+ std::auto_ptr<ListValue> lst(new ListValue);
+
+ // Note: can't use parseCSV here because of nested
+ // "is this Value or Result" guessing. Too lazy to factor
+ // that out too using function pointers.
+ int tok = lex->lookAhead();
+ while (tok && tok != ']') {
+ Result *result = 0;
+ Value *val = 0;
+
+ if (tok == Token_identifier)
+ {
+ if (!parseResult(result))
+ return false;
+ }
+ else if (!parseValue(val))
+ return false;
+
+ Q_ASSERT(result || val);
+
+ if (!result) {
+ result = new Result;
+ result->value = val;
+ }
+ lst->results.append(result);
+
+ if (lex->lookAhead() == ',')
+ lex->nextToken();
+
+ tok = lex->lookAhead();
+ }
+ ADVANCE(']');
+
+ value = lst.release();
+
+ return true;
+}
+
+bool MIParser::parseCSV(TupleValue** value,
+ char start, char end)
+{
+ std::auto_ptr<TupleValue> tuple(new TupleValue);
+
+ if (!parseCSV(*tuple, start, end))
+ return false;
+
+ *value = tuple.get();
+ tuple.release();
+ return true;
+}
+
+bool MIParser::parseCSV(GDBMI::TupleValue& value,
+ char start, char end)
+{
+ if (start)
+ ADVANCE(start);
+
+ int tok = lex->lookAhead();
+ while (tok) {
+ if (end && tok == end)
+ break;
+
+ Result *result;
+ if (!parseResult(result))
+ return false;
+
+ value.results.append(result);
+ value.results_by_name.insert(result->variable, result);
+
+ if (lex->lookAhead() == ',')
+ lex->nextToken();
+
+ tok = lex->lookAhead();
+ }
+
+ if (end)
+ ADVANCE(end);
+
+ return true;
+}
+
+
+QString MIParser::parseStringLiteral()
+{
+ QCString message = lex->currentTokenText();
+
+ unsigned int length = message.length();
+ QString message2;
+ message2.setLength(length);
+ // The [1,length-1] range removes quotes without extra
+ // call to 'mid'
+ unsigned target_index = 0;
+ for(unsigned i = 1, e = length-1; i != e; ++i)
+ {
+ int translated = -1;
+ if (message[i] == '\\')
+ {
+ if (i+1 < length)
+ {
+ // TODO: implement all the other escapes, maybe
+ if (message[i+1] == 'n')
+ {
+ translated = '\n';
+ }
+ else if (message[i+1] == '\\')
+ {
+ translated = '\\';
+ }
+ else if (message[i+1] == '"')
+ {
+ translated = '"';
+ }
+ else if (message[i+1] == 't')
+ {
+ translated = '\t';
+ }
+
+ }
+ }
+
+ if (translated != -1)
+ {
+ message2[target_index++] = (char)translated;
+ ++i;
+ }
+ else
+ {
+ message2[target_index++] = message[i];
+ }
+ }
+ message2.setLength(target_index);
+
+ lex->nextToken();
+ return message2;
+}
+
diff --git a/languages/cpp/debugger/mi/miparser.h b/languages/cpp/debugger/mi/miparser.h
new file mode 100644
index 00000000..524dba7c
--- /dev/null
+++ b/languages/cpp/debugger/mi/miparser.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MIPARSER_H
+#define MIPARSER_H
+
+#include "milexer.h"
+#include "gdbmi.h"
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+/**
+@author Roberto Raggi
+*/
+class MIParser
+{
+public:
+ MIParser();
+ ~MIParser();
+
+ GDBMI::Record *parse(FileSymbol *file);
+
+protected: // rules
+ bool parseResultRecord(GDBMI::Record *&record);
+ bool parsePrompt(GDBMI::Record *&record);
+ bool parseStreamRecord(GDBMI::Record *&record);
+
+ bool parseResult(GDBMI::Result *&result);
+ bool parseValue(GDBMI::Value *&value);
+ bool parseTuple(GDBMI::Value *&value);
+ bool parseList(GDBMI::Value *&value);
+
+ /** Creates new TupleValue object, writes its address
+ into *value, parses a comma-separated set of values,
+ and adds each new value into (*value)->results.
+ If 'start' and 'end' are not zero, they specify
+ start and end delimiter of the list.
+ Parsing stops when we see 'end' character, or, if
+ 'end' is zero, at the end of input.
+ */
+ bool parseCSV(GDBMI::TupleValue** value,
+ char start = 0, char end = 0);
+
+ /** @overload
+ Same as above, but writes into existing tuple.
+ */
+ bool parseCSV(GDBMI::TupleValue& value,
+ char start = 0, char end = 0);
+
+
+ /** Parses a string literal and returns it. Advances
+ the lexer past the literal. Processes C escape sequences
+ in the string.
+ @pre lex->lookAhead(0) == Token_string_literal
+ */
+ QString parseStringLiteral();
+
+
+
+private:
+ MILexer lexer;
+ TokenStream *lex;
+};
+
+#endif
diff --git a/languages/cpp/debugger/mi/tokens.h b/languages/cpp/debugger/mi/tokens.h
new file mode 100644
index 00000000..c9ab283d
--- /dev/null
+++ b/languages/cpp/debugger/mi/tokens.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright (C) 2004 by Roberto Raggi *
+ * roberto@kdevelop.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef TOKENS_H
+#define TOKENS_H
+
+enum Type
+{
+ Token_eof = 0,
+ Token_identifier = 1000,
+ Token_number_literal,
+ Token_string_literal,
+ Token_whitespaces
+};
+
+#endif
+
diff --git a/languages/cpp/debugger/stty.cpp b/languages/cpp/debugger/stty.cpp
new file mode 100644
index 00000000..f0bc2627
--- /dev/null
+++ b/languages/cpp/debugger/stty.cpp
@@ -0,0 +1,386 @@
+/***************************************************************************
+ begin : Mon Sep 13 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ This code was originally written by Judin Maxim, from the
+ KDEStudio project.
+
+ It was then updated with later code from konsole (KDE).
+
+ It has also been enhanced with an idea from the code in kdbg
+ written by Johannes Sixt<Johannes.Sixt@telecom.at>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __osf__
+#define _XOPEN_SOURCE_EXTENDED
+#define O_NDELAY O_NONBLOCK
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#define _NEW_TTY_CTRL
+#endif
+
+#include <assert.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined (_HPUX_SOURCE)
+#define _TERMIOS_INCLUDED
+#include <bsdtty.h>
+#endif
+
+#include <qintdict.h>
+#include <qsocketnotifier.h>
+#include <qstring.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#include "stty.h"
+
+#define PTY_FILENO 3
+#define BASE_CHOWN "konsole_grantpty"
+
+namespace GDBDebugger
+{
+
+static int chownpty(int fd, int grant)
+// param fd: the fd of a master pty.
+// param grant: 1 to grant, 0 to revoke
+// returns 1 on success 0 on fail
+{
+ void(*tmp)(int) = signal(SIGCHLD,SIG_DFL);
+ pid_t pid = fork();
+ if (pid < 0) {
+ signal(SIGCHLD,tmp);
+ return 0;
+ }
+ if (pid == 0) {
+ /* We pass the master pseudo terminal as file descriptor PTY_FILENO. */
+ if (fd != PTY_FILENO && dup2(fd, PTY_FILENO) < 0)
+ ::exit(1);
+
+ QString path = locate("exe", BASE_CHOWN);
+ execle(QFile::encodeName(path), BASE_CHOWN, grant?"--grant":"--revoke", (void *)0, NULL);
+ ::exit(1); // should not be reached
+ }
+ if (pid > 0) {
+ int w;
+ // retry:
+ int rc = waitpid (pid, &w, 0);
+ if (rc != pid)
+ ::exit(1);
+
+ // { // signal from other child, behave like catchChild.
+ // // guess this gives quite some control chaos...
+ // Shell* sh = shells.find(rc);
+ // if (sh) { shells.remove(rc); sh->doneShell(w); }
+ // goto retry;
+ // }
+ signal(SIGCHLD,tmp);
+ return (rc != -1 && WIFEXITED(w) && WEXITSTATUS(w) == 0);
+ }
+ signal(SIGCHLD,tmp);
+ return 0; //dummy.
+}
+
+// **************************************************************************
+
+STTY::STTY(bool ext, const QString &termAppName)
+ : QObject(),
+ out(0),
+ ttySlave(""),
+ pid_(0),
+ external_(ext)
+{
+ if (ext) {
+ findExternalTTY(termAppName);
+ } else {
+ fout = findTTY();
+ if (fout >= 0) {
+ ttySlave = QString(tty_slave);
+ out = new QSocketNotifier(fout, QSocketNotifier::Read, this);
+ connect( out, SIGNAL(activated(int)), this, SLOT(OutReceived(int)) );
+ }
+ }
+}
+
+// **************************************************************************
+
+STTY::~STTY()
+{
+ if (pid_)
+ ::kill(pid_, SIGTERM);
+
+ if (out) {
+ ::close(fout);
+ delete out;
+ }
+}
+
+// **************************************************************************
+
+int STTY::findTTY()
+{
+ int ptyfd = -1;
+ bool needGrantPty = TRUE;
+
+ // Find a master pty that we can open ////////////////////////////////
+
+#ifdef __sgi__
+ ptyfd = open("/dev/ptmx",O_RDWR);
+ if (ptyfd < 0) {
+ perror("Can't open a pseudo teletype");
+ return(-1);
+ }
+ strncpy(tty_slave, ptsname(ptyfd), 50);
+ grantpt(ptyfd);
+ unlockpt(ptyfd);
+ needGrantPty = FALSE;
+#endif
+
+ // first we try UNIX PTY's
+#ifdef TIOCGPTN
+ strcpy(pty_master,"/dev/ptmx");
+ strcpy(tty_slave,"/dev/pts/");
+ ptyfd = open(pty_master,O_RDWR);
+ if (ptyfd >= 0) { // got the master pty
+ int ptyno;
+ if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) {
+ struct stat sbuf;
+ sprintf(tty_slave,"/dev/pts/%d",ptyno);
+ if (stat(tty_slave,&sbuf) == 0 && S_ISCHR(sbuf.st_mode))
+ needGrantPty = FALSE;
+ else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ } else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+#endif
+
+#if defined(_SCO_DS) || defined(__USLC__) /* SCO OSr5 and UnixWare */
+ if (ptyfd < 0) {
+ for (int idx = 0; idx < 256; idx++)
+ { sprintf(pty_master, "/dev/ptyp%d", idx);
+ sprintf(tty_slave, "/dev/ttyp%d", idx);
+ if (access(tty_slave, F_OK) < 0) { idx = 256; break; }
+ if ((ptyfd = open (pty_master, O_RDWR)) >= 0)
+ { if (access (tty_slave, R_OK|W_OK) == 0) break;
+ close(ptyfd); ptyfd = -1;
+ }
+ }
+ }
+#endif
+ if (ptyfd < 0) { /// \FIXME Linux, Trouble on other systems?
+ for (const char* s3 = "pqrstuvwxyzabcde"; *s3 != 0; s3++) {
+ for (const char* s4 = "0123456789abcdef"; *s4 != 0; s4++) {
+ sprintf(pty_master,"/dev/pty%c%c",*s3,*s4);
+ sprintf(tty_slave,"/dev/tty%c%c",*s3,*s4);
+ if ((ptyfd = open(pty_master, O_RDWR)) >= 0) {
+ if (geteuid() == 0 || access(tty_slave, R_OK|W_OK) == 0)
+ break;
+
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+
+ if (ptyfd >= 0)
+ break;
+ }
+ }
+
+ if (ptyfd >= 0) {
+ if (needGrantPty && !chownpty(ptyfd, TRUE)) {
+ fprintf(stderr,"kdevelop: chownpty failed for device %s::%s.\n",pty_master,tty_slave);
+ fprintf(stderr," : This means the session can be eavesdroped.\n");
+ fprintf(stderr," : Make sure konsole_grantpty is installed and setuid root.\n");
+ }
+
+ ::fcntl(ptyfd, F_SETFL, O_NDELAY);
+#ifdef TIOCSPTLCK
+ int flag = 0;
+ ioctl(ptyfd, TIOCSPTLCK, &flag); // unlock pty
+#endif
+ }
+
+ return ptyfd;
+}
+
+// **************************************************************************
+
+void STTY::OutReceived(int f)
+{
+ char buf[1024];
+ int n;
+
+ // read until socket is empty. We shouldn't be receiving a continuous
+ // stream of data, so the loop is unlikely to cause problems.
+ while ((n = ::read(f, buf, sizeof(buf)-1)) > 0) {
+ *(buf+n) = 0; // a standard string
+ emit OutOutput(buf);
+ }
+ // Note: for some reason, n can be 0 here.
+ // I can understand that non-blocking read returns 0,
+ // but I don't understand how OutRecieved can be even
+ // called when there's no input.
+ if (n == 0 /* eof */
+ || (n == -1 && errno != EAGAIN))
+ {
+ // Found eof or error. Disable socket notifier, otherwise Qt
+ // will repeatedly call this method, eating CPU
+ // cycles.
+ out->setEnabled(false);
+ }
+
+}
+
+void STTY::readRemaining()
+{
+ if (!external_)
+ OutReceived(fout);
+}
+
+// **************************************************************************
+
+#define FIFO_FILE "/tmp/debug_tty.XXXXXX"
+
+bool STTY::findExternalTTY(const QString &termApp)
+{
+ QString appName(termApp.isEmpty() ? QString("xterm") : termApp);
+
+ if ( KStandardDirs::findExe( termApp ).isEmpty() )
+ {
+ return false;
+ }
+
+ char fifo[] = FIFO_FILE;
+ int fifo_fd;
+ if ((fifo_fd = mkstemp(fifo)) == -1)
+ return false;
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // create a fifo that will pass in the tty name
+#ifdef HAVE_MKFIFO
+ if (::mkfifo(fifo, S_IRUSR|S_IWUSR) < 0)
+#else
+ if (::mknod(fifo, S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0)
+#endif
+ return false;
+
+ int pid = ::fork();
+ if (pid < 0) { // No process
+ ::unlink(fifo);
+ return false;
+ }
+
+ if (pid == 0) { // child process
+ /*
+ * Spawn a console that in turn runs a shell script that passes us
+ * back the terminal name and then only sits and waits.
+ */
+
+ const char* prog = appName.latin1();
+ QString script = QString("tty>") + QString(fifo) +
+ QString(";" // fifo name
+ "trap \"\" INT QUIT TSTP;" // ignore various signals
+ "exec<&-;exec>&-;" // close stdin and stdout
+ "while :;do sleep 3600;done");
+ const char* scriptStr = script.latin1();
+ const char* end = 0;
+
+ if ( termApp == "konsole" )
+ {
+ ::execlp( prog, prog,
+ "-caption", i18n("kdevelop: Debug application console").local8Bit().data(),
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+ else
+ {
+ ::execlp( prog, prog,
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+
+ // Should not get here, as above should always work
+ ::exit(1);
+ }
+
+ // parent process
+ if (pid <= 0)
+ ::exit(1);
+
+ // Open the communication between us (the parent) and the
+ // child (the process running on a tty console)
+ fifo_fd = ::open(fifo, O_RDONLY);
+ if (fifo_fd < 0)
+ return false;
+
+ // Get the ttyname from the fifo buffer that the child process
+ // has sent.
+ char ttyname[50];
+ int n = ::read(fifo_fd, ttyname, sizeof(ttyname)-sizeof(char));
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // No name??
+ if (n <= 0)
+ return false;
+
+ // remove whitespace
+ ttyname[n] = 0;
+ if (char* newline = strchr(ttyname, '\n'))
+ *newline = 0; // clobber the new line
+
+ ttySlave = ttyname;
+ pid_ = pid;
+
+ return true;
+}
+
+}
+
+// **************************************************************************
+#include "stty.moc"
diff --git a/languages/cpp/debugger/stty.h b/languages/cpp/debugger/stty.h
new file mode 100644
index 00000000..639e8417
--- /dev/null
+++ b/languages/cpp/debugger/stty.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ begin : Mon Sep 13 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ This code was originally written by Judin Maxim, from the
+ KDEStudio project.
+
+ It was then updated with later code from konsole (KDE).
+
+ It has also been enhanced with an idea from the code in kdbg
+ written by Johannes Sixt<Johannes.Sixt@telecom.at>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 _STTY_H_
+#define _STTY_H_
+
+class QSocketNotifier;
+
+#include <qobject.h>
+#include <qstring.h>
+
+namespace GDBDebugger
+{
+
+class STTY : public QObject
+{
+ Q_OBJECT
+
+public:
+ STTY(bool ext=false, const QString &termAppName=QString());
+ ~STTY();
+
+ QString getSlave() { return ttySlave; };
+ void readRemaining();
+
+private slots:
+ void OutReceived(int);
+
+signals:
+ void OutOutput(const char *);
+ void ErrOutput(const char*);
+
+private:
+ int findTTY();
+ bool findExternalTTY(const QString &termApp);
+
+private:
+ int fout;
+ int ferr;
+ QSocketNotifier *out;
+ QString ttySlave;
+ int pid_;
+ bool external_;
+
+ char pty_master[50]; // "/dev/ptyxx" | "/dev/ptmx"
+ char tty_slave[50]; // "/dev/ttyxx" | "/dev/pts/########..."
+};
+
+}
+
+#endif
diff --git a/languages/cpp/debugger/tests/README.txt b/languages/cpp/debugger/tests/README.txt
new file mode 100644
index 00000000..1764b029
--- /dev/null
+++ b/languages/cpp/debugger/tests/README.txt
@@ -0,0 +1,4 @@
+
+This directory contains some tests for debugger. They are not
+automated, and most test have no description of expected behaviour,
+but still it's better than nothing. \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/breakpoints/Makefile b/languages/cpp/debugger/tests/breakpoints/Makefile
new file mode 100644
index 00000000..77d481f8
--- /dev/null
+++ b/languages/cpp/debugger/tests/breakpoints/Makefile
@@ -0,0 +1,4 @@
+
+breakpoints: main.cpp foo.cpp
+ g++ -g -obreakpoints main.cpp foo.cpp
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/breakpoints/README.txt b/languages/cpp/debugger/tests/breakpoints/README.txt
new file mode 100644
index 00000000..5c441631
--- /dev/null
+++ b/languages/cpp/debugger/tests/breakpoints/README.txt
@@ -0,0 +1,2 @@
+
+Supposed to test various kinds of breakpoints. \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/breakpoints/breakpoints.kdevelop b/languages/cpp/debugger/tests/breakpoints/breakpoints.kdevelop
new file mode 100644
index 00000000..4b3b07c9
--- /dev/null
+++ b/languages/cpp/debugger/tests/breakpoints/breakpoints.kdevelop
@@ -0,0 +1,163 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@ghostwalk</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>breakpoints</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs/>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir/>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin/>
+ <defaulttarget/>
+ <makeoptions/>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ <programargs/>
+ <gdbpath/>
+ <configGdbScript/>
+ <runShellScript/>
+ <runGdbScript/>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>false</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root>/usr/share/qt3</root>
+ </qt>
+ <creategettersetter>
+ <prefixGet/>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <splitheadersource>
+ <enabled>false</enabled>
+ <synchronize>true</synchronize>
+ <orientation>Vertical</orientation>
+ </splitheadersource>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/breakpoints/foo.cpp b/languages/cpp/debugger/tests/breakpoints/foo.cpp
new file mode 100644
index 00000000..a3cf399d
--- /dev/null
+++ b/languages/cpp/debugger/tests/breakpoints/foo.cpp
@@ -0,0 +1,10 @@
+
+#include <stdio.h>
+
+void foo(int a)
+{
+ int i = 10;
+ int i2 = 12;
+ int i3 = i + i2;
+ printf("i3 = %d\n", i3);
+} \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/breakpoints/main.cpp b/languages/cpp/debugger/tests/breakpoints/main.cpp
new file mode 100644
index 00000000..81a5851f
--- /dev/null
+++ b/languages/cpp/debugger/tests/breakpoints/main.cpp
@@ -0,0 +1,38 @@
+
+void foo(int);
+
+void set_value(int* i)
+{
+ *i = 10;
+}
+
+void modify(int* i)
+{
+ *i = 15;
+}
+
+void read(int* i)
+{
+ static int i2;
+ i2 = *i;
+}
+
+int test_main(int* i)
+{
+ foo(5);
+ set_value(i);
+
+ modify(i);
+ read(i);
+
+ for(unsigned j = 0; j < 10; ++j)
+ foo(j);
+
+ return 0;
+}
+
+int main()
+{
+ int var;
+ return test_main(&var);
+}
diff --git a/languages/cpp/debugger/tests/dll/Makefile b/languages/cpp/debugger/tests/dll/Makefile
new file mode 100644
index 00000000..b49c732d
--- /dev/null
+++ b/languages/cpp/debugger/tests/dll/Makefile
@@ -0,0 +1,8 @@
+
+all: main libhelper.so
+
+main: main.cpp
+ g++ -g -o main main.cpp -ldl
+
+libhelper.so: helper.cpp
+ g++ -g -o libhelper.so -fPIC -shared helper.cpp \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/dll/README.txt b/languages/cpp/debugger/tests/dll/README.txt
new file mode 100644
index 00000000..52b39e46
--- /dev/null
+++ b/languages/cpp/debugger/tests/dll/README.txt
@@ -0,0 +1,3 @@
+
+Simple test that we can set breakpoint in dynamic library
+loaded with 'dlopen' before the library is actually loaded. \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/dll/dll.kdevelop b/languages/cpp/debugger/tests/dll/dll.kdevelop
new file mode 100644
index 00000000..dbeb12b3
--- /dev/null
+++ b/languages/cpp/debugger/tests/dll/dll.kdevelop
@@ -0,0 +1,158 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@ghostwalk</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>main</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs/>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir/>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin/>
+ <defaulttarget/>
+ <makeoptions/>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ <programargs/>
+ <gdbpath/>
+ <configGdbScript/>
+ <runShellScript/>
+ <runGdbScript/>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>false</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+ <kdevcppsupport>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root/>
+ </qt>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ <creategettersetter>
+ <prefixGet/>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <references/>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/dll/helper.cpp b/languages/cpp/debugger/tests/dll/helper.cpp
new file mode 100644
index 00000000..eb59e95b
--- /dev/null
+++ b/languages/cpp/debugger/tests/dll/helper.cpp
@@ -0,0 +1,6 @@
+
+extern "C" int helper(int i)
+{
+ int j = i;
+ return j+10;
+}
diff --git a/languages/cpp/debugger/tests/dll/main.cpp b/languages/cpp/debugger/tests/dll/main.cpp
new file mode 100644
index 00000000..ee91d5a2
--- /dev/null
+++ b/languages/cpp/debugger/tests/dll/main.cpp
@@ -0,0 +1,15 @@
+#include <dlfcn.h>
+
+typedef int (*ft)(int);
+
+int main()
+{
+ void* handle = dlopen("./libhelper.so", RTLD_LAZY);
+ void* sym = dlsym(handle, "helper");
+
+ ft f = (ft)sym;
+
+ f(10);
+ f(15);
+ return 0;
+}
diff --git a/languages/cpp/debugger/tests/infinite_loop/Makefile b/languages/cpp/debugger/tests/infinite_loop/Makefile
new file mode 100644
index 00000000..7d32cd85
--- /dev/null
+++ b/languages/cpp/debugger/tests/infinite_loop/Makefile
@@ -0,0 +1,4 @@
+
+infinite_loop: infinite_loop.cpp
+ g++ -g -oinfinite_loop infinite_loop.cpp
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/infinite_loop/README.txt b/languages/cpp/debugger/tests/infinite_loop/README.txt
new file mode 100644
index 00000000..1c008ee6
--- /dev/null
+++ b/languages/cpp/debugger/tests/infinite_loop/README.txt
@@ -0,0 +1,3 @@
+
+Tests that we can stop a program with the "Interrupt" command.
+
diff --git a/languages/cpp/debugger/tests/infinite_loop/infinite_loop.cpp b/languages/cpp/debugger/tests/infinite_loop/infinite_loop.cpp
new file mode 100644
index 00000000..ca90a055
--- /dev/null
+++ b/languages/cpp/debugger/tests/infinite_loop/infinite_loop.cpp
@@ -0,0 +1,17 @@
+
+int foo()
+{
+ int i = 0;
+ for(;;)
+ {
+ i = i+1;
+ }
+ return i;
+}
+
+int main()
+{
+ int r = 10;
+ r += foo();
+ return r;
+} \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/infinite_loop/infinite_loop.kdevelop b/languages/cpp/debugger/tests/infinite_loop/infinite_loop.kdevelop
new file mode 100644
index 00000000..24055c6f
--- /dev/null
+++ b/languages/cpp/debugger/tests/infinite_loop/infinite_loop.kdevelop
@@ -0,0 +1,109 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@zigzag</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>infinite_loop</mainprogram>
+ <directoryradio>executable</directoryradio>
+ </run>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ </general>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ <showOnlyAccessibleItems>false</showOnlyAccessibleItems>
+ <completionBoxItemOrder>0</completionBoxItemOrder>
+ <howEvaluationContextMenu>true</howEvaluationContextMenu>
+ <showCommentWithArgumentHint>true</showCommentWithArgumentHint>
+ <statusBarTypeEvaluation>false</statusBarTypeEvaluation>
+ <namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
+ <processPrimaryTypes>true</processPrimaryTypes>
+ <processFunctionArguments>false</processFunctionArguments>
+ </codecompletion>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/print_pointers/Makefile b/languages/cpp/debugger/tests/print_pointers/Makefile
new file mode 100644
index 00000000..0eafe22f
--- /dev/null
+++ b/languages/cpp/debugger/tests/print_pointers/Makefile
@@ -0,0 +1,3 @@
+
+print_pointers: print_pointers.cpp
+ g++ -g -o print_pointers -I/usr/share/qt3/include print_pointers.cpp -lqt-mt \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/print_pointers/print_pointers.cpp b/languages/cpp/debugger/tests/print_pointers/print_pointers.cpp
new file mode 100644
index 00000000..09053b51
--- /dev/null
+++ b/languages/cpp/debugger/tests/print_pointers/print_pointers.cpp
@@ -0,0 +1,95 @@
+
+#include <qstring.h>
+#include <vector>
+
+struct B { int i; int j; static int k; };
+struct C { int a[3]; };
+struct D { int *ptr; };
+int B::k = 11;
+typedef int (*fp)(int);
+
+int g = 10;
+int g2 = 23;
+
+void func2()
+{
+ int foobar = 123;
+ printf("func2\n");
+}
+
+void func(QString& xs)
+{
+ int ac = 10;
+ std::string s;
+ func2();
+ g = 10;
+ xs = "foo";
+
+}
+
+class Test
+{
+ public:
+ QString n;
+ int b;
+};
+
+int test_main(int ac, char* av[])
+{
+ printf("Hello world\n");
+ int i = 10;
+ int* p1 = 0x00000000;
+ int** p1_p = &p1;
+ p1 = &g;
+
+ B* p2 = (B*)0x12345678;
+ g = 77;
+ int (*p3)(int) = (fp)0x000000AE;
+ B p4 = {1, 3};
+ p2 = &p4;
+ int p5[] = {5, 6, 7};
+ int* p6[] = {&g, &g2};
+ int p7[][2] = {{1,2}, {5,6}};
+ B p8[] = {{1,2}, {3,4}};
+ C p9 = {{7, 8, 9}};
+ g = 77;
+ const D p9_1 = {&g};
+ {
+ B p9_1;
+ int i = 15;
+ printf("p9_1\n");
+ }
+ B& p10 = p4;
+ int& p11 = *p1;
+ int (*p12)[3] = &p5;
+ int (&p13)[3] = p5;
+ char p14[6] = "abc";
+ wchar_t* p15 = L"test1";
+
+
+ QString s = "test test test test";
+ QString* sp = &s;
+ const QString& sr = s;
+ func(s);
+ i = 15;
+
+ std::vector<int> v;
+
+ Test* test = new Test;
+ Test& test2 = *test;
+ test->n = "foo";
+ printf("hi\n");
+ test = 0;
+ printf("hi2\n");
+ printf("hi %d\n", test->b);
+
+
+
+ p5[1] = 14;
+ return 0;
+}
+
+int main(int ac, char* av[])
+{
+ return test_main(ac, av);
+}
diff --git a/languages/cpp/debugger/tests/print_pointers/print_pointers.kdevelop b/languages/cpp/debugger/tests/print_pointers/print_pointers.kdevelop
new file mode 100644
index 00000000..03faba9a
--- /dev/null
+++ b/languages/cpp/debugger/tests/print_pointers/print_pointers.kdevelop
@@ -0,0 +1,218 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@cs.msu.su</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts>
+ <part>kdevsecurity</part>
+ <part>kdevkonsoleview</part>
+ <part>kdevreplace</part>
+ <part>kdevctags2</part>
+ <part>kdevvalgrind</part>
+ </ignoreparts>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description/>
+ <projectname>print_pointers</projectname>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>print_pointers</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs/>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir/>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin/>
+ <defaulttarget/>
+ <makeoptions/>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ <filetypes>
+ <filetype>*.java</filetype>
+ <filetype>*.h</filetype>
+ <filetype>*.H</filetype>
+ <filetype>*.hh</filetype>
+ <filetype>*.hxx</filetype>
+ <filetype>*.hpp</filetype>
+ <filetype>*.c</filetype>
+ <filetype>*.C</filetype>
+ <filetype>*.cc</filetype>
+ <filetype>*.cpp</filetype>
+ <filetype>*.c++</filetype>
+ <filetype>*.cxx</filetype>
+ <filetype>Makefile</filetype>
+ <filetype>CMakeLists.txt</filetype>
+ </filetypes>
+ <blacklist/>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ <programargs/>
+ <gdbpath/>
+ <configGdbScript/>
+ <runShellScript/>
+ <runGdbScript/>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>true</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+ <kdevcppsupport>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ <showOnlyAccessibleItems>false</showOnlyAccessibleItems>
+ <completionBoxItemOrder>0</completionBoxItemOrder>
+ <howEvaluationContextMenu>true</howEvaluationContextMenu>
+ <showCommentWithArgumentHint>true</showCommentWithArgumentHint>
+ <statusBarTypeEvaluation>false</statusBarTypeEvaluation>
+ <namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx&lt;&lt;std</namespaceAliases>
+ <processPrimaryTypes>true</processPrimaryTypes>
+ <processFunctionArguments>false</processFunctionArguments>
+ <preProcessAllHeaders>false</preProcessAllHeaders>
+ <parseMissingHeaders>false</parseMissingHeaders>
+ <resolveIncludePaths>true</resolveIncludePaths>
+ <alwaysParseInBackground>true</alwaysParseInBackground>
+ <usePermanentCaching>true</usePermanentCaching>
+ <alwaysIncludeNamespaces>false</alwaysIncludeNamespaces>
+ <includePaths>.;</includePaths>
+ <parseMissingHeadersExperimental>false</parseMissingHeadersExperimental>
+ <resolveIncludePathsUsingMakeExperimental>false</resolveIncludePathsUsingMakeExperimental>
+ </codecompletion>
+ <creategettersetter>
+ <prefixGet/>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <references/>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root>/usr/share/qt3</root>
+ <includestyle>3</includestyle>
+ <designerintegration>EmbeddedKDevDesigner</designerintegration>
+ <qmake>/usr/bin/qmake-qt3</qmake>
+ <designer>/usr/bin/designer</designer>
+ <designerpluginpaths/>
+ </qt>
+ <splitheadersource>
+ <enabled>false</enabled>
+ <synchronize>true</synchronize>
+ <orientation>Vertical</orientation>
+ </splitheadersource>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <kdevvisualadvance>
+ <emulator>VisualBoyAdvance</emulator>
+ <binary/>
+ <addOptions/>
+ <terminal>false</terminal>
+ <fullscreen>false</fullscreen>
+ <graphicFilter>-f0</graphicFilter>
+ <scaling>-1</scaling>
+ </kdevvisualadvance>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/segfault/Makefile b/languages/cpp/debugger/tests/segfault/Makefile
new file mode 100644
index 00000000..eebc5385
--- /dev/null
+++ b/languages/cpp/debugger/tests/segfault/Makefile
@@ -0,0 +1,4 @@
+
+segfault: segfault.cpp
+ g++ -g -osegfault segfault.cpp
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/segfault/README.txt b/languages/cpp/debugger/tests/segfault/README.txt
new file mode 100644
index 00000000..c36254de
--- /dev/null
+++ b/languages/cpp/debugger/tests/segfault/README.txt
@@ -0,0 +1,3 @@
+
+Tests that we correctly report segfault in a debugged program.
+
diff --git a/languages/cpp/debugger/tests/segfault/segfault.cpp b/languages/cpp/debugger/tests/segfault/segfault.cpp
new file mode 100644
index 00000000..3a77e500
--- /dev/null
+++ b/languages/cpp/debugger/tests/segfault/segfault.cpp
@@ -0,0 +1,7 @@
+
+
+int main()
+{
+ int* ptr = 0;
+ *ptr = 10;
+} \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/segfault/segfault.kdevelop b/languages/cpp/debugger/tests/segfault/segfault.kdevelop
new file mode 100644
index 00000000..0135e84b
--- /dev/null
+++ b/languages/cpp/debugger/tests/segfault/segfault.kdevelop
@@ -0,0 +1,163 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@zigzag</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description></description>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>segfault</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs></programargs>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir></builddir>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin></makebin>
+ <defaulttarget></defaulttarget>
+ <makeoptions></makeoptions>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell></dbgshell>
+ <programargs></programargs>
+ <gdbpath></gdbpath>
+ <configGdbScript></configGdbScript>
+ <runShellScript></runShellScript>
+ <runGdbScript></runGdbScript>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>false</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root>/usr/share/qt3</root>
+ </qt>
+ <creategettersetter>
+ <prefixGet></prefixGet>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <splitheadersource>
+ <enabled>false</enabled>
+ <synchronize>true</synchronize>
+ <orientation>Vertical</orientation>
+ </splitheadersource>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/threads/Makefile b/languages/cpp/debugger/tests/threads/Makefile
new file mode 100644
index 00000000..8db14799
--- /dev/null
+++ b/languages/cpp/debugger/tests/threads/Makefile
@@ -0,0 +1,4 @@
+
+threads: threads.cpp
+ g++ -g -othreads threads.cpp -pthread
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/threads/README.txt b/languages/cpp/debugger/tests/threads/README.txt
new file mode 100644
index 00000000..3d84fae1
--- /dev/null
+++ b/languages/cpp/debugger/tests/threads/README.txt
@@ -0,0 +1,4 @@
+
+Tests with a threaded application. Makes sure that the
+list of threads is shown that that switching threads works.
+
diff --git a/languages/cpp/debugger/tests/threads/threads.cpp b/languages/cpp/debugger/tests/threads/threads.cpp
new file mode 100644
index 00000000..f412ef7c
--- /dev/null
+++ b/languages/cpp/debugger/tests/threads/threads.cpp
@@ -0,0 +1,26 @@
+
+#include <pthread.h>
+
+void runner(int i)
+{
+ for(int i = 0; i < 1000000;)
+ ++i;
+}
+
+void* thread(void* p)
+{
+ runner((int)p);
+}
+
+int main()
+{
+ pthread_t p1, p2;
+
+ pthread_create(&p1, 0, &thread, (void*)1);
+ pthread_create(&p2, 0, &thread, (void*)2);
+
+ pthread_join(p1, 0);
+ pthread_join(p2, 0);
+
+ return 0;
+} \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/threads/threads.kdevelop b/languages/cpp/debugger/tests/threads/threads.kdevelop
new file mode 100644
index 00000000..3cd806b8
--- /dev/null
+++ b/languages/cpp/debugger/tests/threads/threads.kdevelop
@@ -0,0 +1,101 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@zigzag</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>threads</mainprogram>
+ <directoryradio>executable</directoryradio>
+ </run>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ </general>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/tracing/Makefile b/languages/cpp/debugger/tests/tracing/Makefile
new file mode 100644
index 00000000..22333f01
--- /dev/null
+++ b/languages/cpp/debugger/tests/tracing/Makefile
@@ -0,0 +1,4 @@
+
+tracing: main.cpp
+ g++ -g -otracing main.cpp
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/tracing/main.cpp b/languages/cpp/debugger/tests/tracing/main.cpp
new file mode 100644
index 00000000..4a1a38d7
--- /dev/null
+++ b/languages/cpp/debugger/tests/tracing/main.cpp
@@ -0,0 +1,15 @@
+
+// áÌÇÏÒÉÔÍ å×ËÌÉÄÁ ×ÙÞÉÓÌÅÎÉÑ ÎÁÉÂÏÌØÛÅÇÏ ÏÂÝÅÇÏ ÄÅÌÉÔÅÌÑ
+int main()
+{
+ int i, j;
+ i = 157;
+ j = 312;
+
+ while( i != j )
+ {
+ if( i > j ) i = i-j;
+ else j = j-i;
+ }
+ return 0;
+}
diff --git a/languages/cpp/debugger/tests/tracing/tracing.kdevelop b/languages/cpp/debugger/tests/tracing/tracing.kdevelop
new file mode 100644
index 00000000..94d39c6f
--- /dev/null
+++ b/languages/cpp/debugger/tests/tracing/tracing.kdevelop
@@ -0,0 +1,163 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@ghostwalk</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>tracing</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs/>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir/>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin/>
+ <defaulttarget/>
+ <makeoptions/>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ <programargs/>
+ <gdbpath/>
+ <configGdbScript/>
+ <runShellScript/>
+ <runGdbScript/>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>false</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root>/usr/share/qt3</root>
+ </qt>
+ <creategettersetter>
+ <prefixGet/>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <splitheadersource>
+ <enabled>false</enabled>
+ <synchronize>true</synchronize>
+ <orientation>Vertical</orientation>
+ </splitheadersource>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+</kdevelop>
diff --git a/languages/cpp/debugger/tests/two_module/Makefile b/languages/cpp/debugger/tests/two_module/Makefile
new file mode 100644
index 00000000..edd1a242
--- /dev/null
+++ b/languages/cpp/debugger/tests/two_module/Makefile
@@ -0,0 +1,4 @@
+
+two_module: main.cpp src/foo.cpp
+ g++ -g -I/usr/share/qt3/include -otwo_module main.cpp src/foo.cpp -lqt-mt
+ \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/two_module/README.txt b/languages/cpp/debugger/tests/two_module/README.txt
new file mode 100644
index 00000000..31bb0249
--- /dev/null
+++ b/languages/cpp/debugger/tests/two_module/README.txt
@@ -0,0 +1,2 @@
+
+Very basic tests containing two modules. \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/two_module/main.cpp b/languages/cpp/debugger/tests/two_module/main.cpp
new file mode 100644
index 00000000..30863389
--- /dev/null
+++ b/languages/cpp/debugger/tests/two_module/main.cpp
@@ -0,0 +1,8 @@
+
+void foo(int);
+
+int main(int ac, char* av[])
+{
+ foo(5);
+ return 0;
+}
diff --git a/languages/cpp/debugger/tests/two_module/src/foo.cpp b/languages/cpp/debugger/tests/two_module/src/foo.cpp
new file mode 100644
index 00000000..b66f9924
--- /dev/null
+++ b/languages/cpp/debugger/tests/two_module/src/foo.cpp
@@ -0,0 +1,12 @@
+
+#include <stdio.h>
+#include <qstring.h>
+
+void foo(int a)
+{
+ QString s = "foo";
+ int i = 10;
+ int i2 = 12;
+ int i3 = i + i2;
+ printf("i3 = %d\n", i3);
+} \ No newline at end of file
diff --git a/languages/cpp/debugger/tests/two_module/two_module.kdevelop b/languages/cpp/debugger/tests/two_module/two_module.kdevelop
new file mode 100644
index 00000000..a9ecc5ad
--- /dev/null
+++ b/languages/cpp/debugger/tests/two_module/two_module.kdevelop
@@ -0,0 +1,163 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Vladimir Prus</author>
+ <email>ghost@ghostwalk</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C++</primarylanguage>
+ <ignoreparts/>
+ <projectdirectory>.</projectdirectory>
+ <absoluteprojectpath>false</absoluteprojectpath>
+ <description></description>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>two_module</mainprogram>
+ <directoryradio>executable</directoryradio>
+ <customdirectory>/</customdirectory>
+ <programargs></programargs>
+ <terminal>false</terminal>
+ <autocompile>true</autocompile>
+ <envvars/>
+ </run>
+ <build>
+ <buildtool>make</buildtool>
+ <builddir></builddir>
+ </build>
+ <make>
+ <abortonerror>false</abortonerror>
+ <numberofjobs>1</numberofjobs>
+ <prio>0</prio>
+ <dontact>false</dontact>
+ <makebin></makebin>
+ <defaulttarget></defaulttarget>
+ <makeoptions></makeoptions>
+ <selectedenvironment>default</selectedenvironment>
+ <environments>
+ <default/>
+ </environments>
+ </make>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell></dbgshell>
+ <programargs></programargs>
+ <gdbpath></gdbpath>
+ <configGdbScript></configGdbScript>
+ <runShellScript></runShellScript>
+ <runGdbScript></runGdbScript>
+ <breakonloadinglibs>true</breakonloadinglibs>
+ <separatetty>false</separatetty>
+ <floatingtoolbar>false</floatingtoolbar>
+ </general>
+ <display>
+ <staticmembers>false</staticmembers>
+ <demanglenames>true</demanglenames>
+ <outputradix>10</outputradix>
+ </display>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="ui" />
+ <type ext="cpp" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ <qt>
+ <used>false</used>
+ <version>3</version>
+ <root>/usr/share/qt3</root>
+ </qt>
+ <creategettersetter>
+ <prefixGet></prefixGet>
+ <prefixSet>set</prefixSet>
+ <prefixVariable>m_,_</prefixVariable>
+ <parameterName>theValue</parameterName>
+ <inlineGet>true</inlineGet>
+ <inlineSet>true</inlineSet>
+ </creategettersetter>
+ <splitheadersource>
+ <enabled>false</enabled>
+ <synchronize>true</synchronize>
+ <orientation>Vertical</orientation>
+ </splitheadersource>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+ <cppsupportpart>
+ <filetemplates>
+ <interfacesuffix>.h</interfacesuffix>
+ <implementationsuffix>.cpp</implementationsuffix>
+ </filetemplates>
+ </cppsupportpart>
+</kdevelop>
diff --git a/languages/cpp/debugger/variablewidget.cpp b/languages/cpp/debugger/variablewidget.cpp
new file mode 100644
index 00000000..263afdf9
--- /dev/null
+++ b/languages/cpp/debugger/variablewidget.cpp
@@ -0,0 +1,2002 @@
+// **************************************************************************
+// 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 "variablewidget.h"
+#include "gdbparser.h"
+#include "gdbcommand.h"
+#include "gdbbreakpointwidget.h"
+
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klineedit.h>
+#include <kdeversion.h>
+#include <kiconloader.h>
+
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qcursor.h>
+#include <qwhatsthis.h>
+#include <klocale.h>
+
+#include <qpoint.h>
+#include <qclipboard.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+
+#include <cctype>
+#include <set>
+#include <typeinfo>
+#include <cctype>
+
+/** The variables widget is passive, and is invoked by the rest of the
+ code via two main slots:
+ - slotDbgStatus
+ - slotCurrentFrame
+
+ The first is received the program status changes and the second is
+ recieved after current frame in the debugger can possibly changes.
+
+ The widget has a list item for each frame/thread combination, with
+ variables as children. However, at each moment only one item is shown.
+ When handling the slotCurrentFrame, we check if variables for the
+ current frame are available. If yes, we simply show the corresponding item.
+ Otherwise, we fetch the new data from debugger.
+
+ Fetching the data is done by emitting the produceVariablesInfo signal.
+ In response, we get slotParametersReady and slotLocalsReady signal,
+ in that order.
+
+ The data is parsed and changed variables are highlighted. After that,
+ we 'trim' variable items that were not reported by gdb -- that is, gone
+ out of scope.
+*/
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+namespace GDBDebugger
+{
+
+VariableWidget::VariableWidget(GDBController* controller,
+ GDBBreakpointWidget* breakpointWidget,
+ QWidget *parent, const char *name)
+: QWidget(parent, name)
+{
+ setIcon(SmallIcon("math_brace"));
+ setCaption(i18n("Variable Tree"));
+
+ varTree_ = new VariableTree(this, controller, breakpointWidget);
+
+ watchVarEditor_ = new KHistoryCombo( this,
+ "var-to-watch editor");
+
+ QHBoxLayout* buttons = new QHBoxLayout();
+
+ buttons->addStretch();
+
+ QPushButton *evalButton = new QPushButton(i18n("&Evaluate"), this );
+ buttons->addWidget(evalButton);
+
+ QPushButton *addButton = new QPushButton(i18n("&Watch"), this );
+ buttons->addWidget(addButton);
+
+ QVBoxLayout *topLayout = new QVBoxLayout(this, 2);
+ topLayout->addWidget(varTree_, 10);
+ topLayout->addWidget(watchVarEditor_);
+ topLayout->addItem(buttons);
+
+
+ connect( addButton, SIGNAL(clicked()), SLOT(slotAddWatchVariable()) );
+ connect( evalButton, SIGNAL(clicked()), SLOT(slotEvaluateExpression()) );
+
+ connect( watchVarEditor_, SIGNAL(returnPressed()),
+ SLOT(slotEvaluateExpression()) );
+
+ connect(controller, SIGNAL(event(GDBController::event_t)),
+ varTree_, SLOT(slotEvent(GDBController::event_t)));
+
+
+ // Setup help items.
+
+ QWhatsThis::add(this, i18n(
+ "<b>Variable tree</b><p>"
+ "The variable tree allows you to see the values of local "
+ "variables and arbitrary expressions."
+ "<p>Local variables are displayed automatically and are updated "
+ "as you step through your program. "
+ "For each expression you enter, you can either evaluate it once, "
+ "or \"watch\" it (make it auto-updated). Expressions that are not "
+ "auto-updated can be updated manually from the context menu. "
+ "Expressions can be renamed to more descriptive names by clicking "
+ "on the name column."
+ "<p>To change the value of a variable or an expression, "
+ "click on the value."));
+
+ QWhatsThis::add(watchVarEditor_,
+ i18n("<b>Expression entry</b>"
+ "<p>Type in expression to evaluate."));
+
+ QWhatsThis::add(evalButton,
+ i18n("Evaluate the expression."));
+
+ QWhatsThis::add(addButton,
+ i18n("Evaluate the expression and "
+ "auto-update the value when stepping."));
+}
+
+void VariableWidget::slotAddWatchVariable()
+{
+// QString watchVar(watchVarEntry_->text());
+ QString watchVar(watchVarEditor_->currentText());
+ if (!watchVar.isEmpty())
+ {
+ slotAddWatchVariable(watchVar);
+ }
+}
+
+// **************************************************************************
+
+void VariableWidget::slotAddWatchVariable(const QString &ident)
+{
+ if (!ident.isEmpty())
+ {
+ watchVarEditor_->addToHistory(ident);
+ varTree_->slotAddWatchVariable(ident);
+ watchVarEditor_->clearEdit();
+ }
+}
+
+void VariableWidget::slotEvaluateExpression()
+{
+ QString exp(watchVarEditor_->currentText());
+ if (!exp.isEmpty())
+ {
+ slotEvaluateExpression(exp);
+ }
+}
+
+void VariableWidget::slotEvaluateExpression(const QString &ident)
+{
+ if (!ident.isEmpty())
+ {
+ watchVarEditor_->addToHistory(ident);
+ varTree_->slotEvaluateExpression(ident);
+ watchVarEditor_->clearEdit();
+ }
+}
+
+// **************************************************************************
+
+void VariableWidget::focusInEvent(QFocusEvent */*e*/)
+{
+ varTree_->setFocus();
+}
+
+
+
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+VariableTree::VariableTree(VariableWidget *parent,
+ GDBController* controller,
+ GDBBreakpointWidget* breakpointWidget,
+ const char *name)
+ : KListView(parent, name),
+ QToolTip( viewport() ),
+ controller_(controller),
+ breakpointWidget_(breakpointWidget),
+ activeFlag_(0),
+ recentExpressions_(0),
+ currentFrameItem(0),
+ activePopup_(0)
+{
+ setRootIsDecorated(true);
+ setAllColumnsShowFocus(true);
+ setSorting(-1);
+ QListView::setSelectionMode(QListView::Single);
+
+ // Note: it might be reasonable to set width of value
+ // column to 10 characters ('0x12345678'), and rely on
+ // tooltips for showing larger values. Currently, both
+ // columns will get roughly equal width.
+ addColumn(i18n("Variable"));
+ addColumn(i18n("Value"));
+// setResizeMode(AllColumns);
+
+ connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
+ SLOT(slotContextMenu(KListView*, QListViewItem*)) );
+ connect( this, SIGNAL(itemRenamed( QListViewItem*, int, const QString&)),
+ this, SLOT(slotItemRenamed( QListViewItem*, int, const QString&)));
+}
+
+// **************************************************************************
+
+VariableTree::~VariableTree()
+{
+}
+
+// **************************************************************************
+
+void VariableTree::slotContextMenu(KListView *, QListViewItem *item)
+{
+ if (!item)
+ return;
+
+ setSelected(item, true); // Need to select this item.
+
+ if (item->parent())
+ {
+ KPopupMenu popup(this);
+ KPopupMenu format(this);
+
+ int idRemember = -2;
+ int idRemove = -2;
+ int idReevaluate = -2;
+ int idWatch = -2;
+
+ int idNatural = -2;
+ int idHex = -2;
+ int idDecimal = -2;
+ int idCharacter = -2;
+ int idBinary = -2;
+
+#define MAYBE_DISABLE(id) if (!var->isAlive()) popup.setItemEnabled(id, false)
+
+ VarItem* var;
+ if ((var = dynamic_cast<VarItem*>(item)))
+ {
+ popup.insertTitle(var->gdbExpression());
+
+
+ format.setCheckable(true);
+ idNatural = format.insertItem(i18n("Natural"),
+ (int)VarItem::natural);
+ format.setAccel(Qt::Key_N, idNatural);
+ idHex = format.insertItem(i18n("Hexadecimal"),
+ (int)VarItem::hexadecimal);
+ format.setAccel(Qt::Key_X, idHex);
+ idDecimal = format.insertItem(i18n("Decimal"),
+ (int)VarItem::decimal);
+ format.setAccel(Qt::Key_D, idDecimal);
+ idCharacter = format.insertItem(i18n("Character"),
+ (int)VarItem::character);
+ format.setAccel(Qt::Key_C, idCharacter);
+ idBinary = format.insertItem(i18n("Binary"),
+ (int)VarItem::binary);
+ format.setAccel(Qt::Key_T, idBinary);
+
+
+ format.setItemChecked((int)(var->format()), true);
+
+ int id = popup.insertItem(i18n("Format"), &format);
+ MAYBE_DISABLE(id);
+ }
+
+
+ QListViewItem* root = findRoot(item);
+
+ if (root != recentExpressions_)
+ {
+ idRemember = popup.insertItem(
+ SmallIcon("pencil"), i18n("Remember Value"));
+ MAYBE_DISABLE(idRemember);
+ }
+
+ if (dynamic_cast<WatchRoot*>(root)) {
+ idRemove = popup.insertItem(
+ SmallIcon("editdelete"), i18n("Remove Watch Variable") );
+ popup.setAccel(Qt::Key_Delete, idRemove);
+ } else if (root != recentExpressions_) {
+ idWatch = popup.insertItem(
+ i18n("Watch Variable"));
+ MAYBE_DISABLE(idWatch);
+ }
+ if (root == recentExpressions_) {
+ idReevaluate = popup.insertItem(
+ SmallIcon("reload"), i18n("Reevaluate Expression") );
+ MAYBE_DISABLE(idReevaluate);
+ idRemove = popup.insertItem(
+ SmallIcon("editdelete"), i18n("Remove Expression") );
+ popup.setAccel(Qt::Key_Delete, idRemove);
+ }
+
+ if (var)
+ {
+ popup.insertItem( i18n("Data write breakpoint"), idToggleWatch );
+ popup.setItemEnabled(idToggleWatch, false);
+ }
+
+ int idCopyToClipboard = popup.insertItem(
+ SmallIcon("editcopy"), i18n("Copy Value") );
+ popup.setAccel(Qt::CTRL + Qt::Key_C, idCopyToClipboard);
+
+ activePopup_ = &popup;
+ /* This code can be executed when debugger is stopped,
+ and we invoke popup menu on a var under "recent expressions"
+ just to delete it. */
+ if (var && var->isAlive() && !controller()->stateIsOn(s_dbgNotStarted))
+ controller_->addCommand(
+ new GDBCommand(
+ QString("-data-evaluate-expression &%1")
+ .arg(var->gdbExpression()),
+ this,
+ &VariableTree::handleAddressComputed,
+ true /*handles error*/));
+
+
+ int res = popup.exec(QCursor::pos());
+
+ activePopup_ = 0;
+
+
+ if (res == idNatural || res == idHex || res == idDecimal
+ || res == idCharacter || res == idBinary)
+ {
+ // Change format.
+ VarItem* var_item = static_cast<VarItem*>(item);
+ var_item->setFormat(static_cast<VarItem::format_t>(res));
+ }
+ else if (res == idRemember)
+ {
+ if (VarItem *item = dynamic_cast<VarItem*>(currentItem()))
+ {
+ ((VariableWidget*)parent())->
+ slotEvaluateExpression(item->gdbExpression());
+ }
+ }
+ else if (res == idWatch)
+ {
+ if (VarItem *item = dynamic_cast<VarItem*>(currentItem()))
+ {
+ ((VariableWidget*)parent())->
+ slotAddWatchVariable(item->gdbExpression());
+ }
+ }
+ else if (res == idRemove)
+ delete item;
+ else if (res == idCopyToClipboard)
+ {
+ copyToClipboard(item);
+ }
+ else if (res == idToggleWatch)
+ {
+ if (VarItem *item = dynamic_cast<VarItem*>(currentItem()))
+ emit toggleWatchpoint(item->gdbExpression());
+ }
+ else if (res == idReevaluate)
+ {
+ if (VarItem* item = dynamic_cast<VarItem*>(currentItem()))
+ {
+ item->recreate();
+ }
+ }
+ }
+ else if (item == recentExpressions_)
+ {
+ KPopupMenu popup(this);
+ popup.insertTitle(i18n("Recent Expressions"));
+ int idRemove = popup.insertItem(
+ SmallIcon("editdelete"), i18n("Remove All"));
+ int idReevaluate = popup.insertItem(
+ SmallIcon("reload"), i18n("Reevaluate All"));
+ if (controller()->stateIsOn(s_dbgNotStarted))
+ popup.setItemEnabled(idReevaluate, false);
+ int res = popup.exec(QCursor::pos());
+
+ if (res == idRemove)
+ {
+ delete recentExpressions_;
+ recentExpressions_ = 0;
+ }
+ else if (res == idReevaluate)
+ {
+ for(QListViewItem* child = recentExpressions_->firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->recreate();
+ }
+ }
+ }
+}
+
+void VariableTree::slotEvent(GDBController::event_t event)
+{
+ switch(event)
+ {
+ case GDBController::program_exited:
+ case GDBController::debugger_exited:
+ {
+ // Remove all locals.
+ QListViewItem *child = firstChild();
+
+ while (child) {
+ QListViewItem *nextChild = child->nextSibling();
+
+ // don't remove the watch root, or 'recent expressions' root.
+ if (!(dynamic_cast<WatchRoot*> (child))
+ && child != recentExpressions_)
+ {
+ delete child;
+ }
+ child = nextChild;
+ }
+ currentFrameItem = 0;
+
+ if (recentExpressions_)
+ {
+ for(QListViewItem* child = recentExpressions_->firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->unhookFromGdb();
+ }
+ }
+
+ if (WatchRoot* w = findWatch())
+ {
+ for(QListViewItem* child = w->firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->unhookFromGdb();
+ }
+ }
+
+ break;
+ }
+
+ case GDBController::program_state_changed:
+
+ // Fall-through intended.
+
+ case GDBController::thread_or_frame_changed:
+ {
+ VarFrameRoot *frame = demand_frame_root(
+ controller_->currentFrame(), controller_->currentThread());
+
+ if (frame->isOpen())
+ {
+ updateCurrentFrame();
+ }
+ else
+ {
+ frame->setDirty();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void VariableTree::updateCurrentFrame()
+{
+ // In GDB 6.4, the -stack-list-locals command is broken.
+ // If there's any local reference variable which is not
+ // initialized yet, for example because it's in the middle
+ // of function, gdb will still print it and try to dereference
+ // it. If the address in not accessible, the MI command will
+ // exit with an error, and we won't be able to see *any*
+ // locals. A patch is submitted:
+ // http://sourceware.org/ml/gdb-patches/2006-04/msg00069.html
+ // but we need to work with 6.4, not with some future version. So,
+ // we just -stack-list-locals to get just names of the locals,
+ // but not their values.
+ // We'll fetch values separately:
+
+ controller_->addCommand(
+ new GDBCommand(QString("-stack-list-arguments 0 %1 %2")
+ .arg(controller_->currentFrame())
+ .arg(controller_->currentFrame())
+ .ascii(),
+ this,
+ &VariableTree::argumentsReady));
+
+
+ controller_->addCommand(
+ new GDBCommand("-stack-list-locals 0",
+ this,
+ &VariableTree::localsReady));
+
+}
+
+
+// **************************************************************************
+
+void VariableTree::slotAddWatchVariable(const QString &watchVar)
+{
+ VarItem *varItem = 0;
+ varItem = new VarItem(findWatch(), watchVar);
+}
+
+void VariableTree::slotEvaluateExpression(const QString &expression)
+{
+ if (recentExpressions_ == 0)
+ {
+ recentExpressions_ = new TrimmableItem(this);
+ recentExpressions_->setText(0, "Recent");
+ recentExpressions_->setOpen(true);
+ }
+
+ VarItem *varItem = new VarItem(recentExpressions_,
+ expression,
+ true /* freeze */);
+ varItem->setRenameEnabled(0, 1);
+}
+
+// **************************************************************************
+
+QListViewItem *VariableTree::findRoot(QListViewItem *item) const
+{
+ while (item->parent())
+ item = item->parent();
+
+ return item;
+}
+
+// **************************************************************************
+
+VarFrameRoot *VariableTree::findFrame(int frameNo, int threadNo) const
+{
+ QListViewItem *sibling = firstChild();
+
+ // frames only exist on th top level so we only need to
+ // check the siblings
+ while (sibling) {
+ VarFrameRoot *frame = dynamic_cast<VarFrameRoot*> (sibling);
+ if (frame && frame->matchDetails(frameNo, threadNo))
+ return frame;
+
+ sibling = sibling->nextSibling();
+ }
+
+ return 0;
+}
+
+// **************************************************************************
+
+WatchRoot *VariableTree::findWatch()
+{
+ QListViewItem *sibling = firstChild();
+
+ while (sibling) {
+ if (WatchRoot *watch = dynamic_cast<WatchRoot*> (sibling))
+ return watch;
+
+ sibling = sibling->nextSibling();
+ }
+
+ return new WatchRoot(this);
+}
+
+// **************************************************************************
+
+QListViewItem *VariableTree::lastChild() const
+{
+ QListViewItem *child = firstChild();
+ if (child)
+ while (QListViewItem *nextChild = child->nextSibling())
+ child = nextChild;
+
+ return child;
+}
+
+// **************************************************************************
+
+void VariableTree::maybeTip(const QPoint &p)
+{
+ VarItem * item = dynamic_cast<VarItem*>( itemAt( p ) );
+ if ( item )
+ {
+ QRect r = itemRect( item );
+ if ( r.isValid() )
+ tip( r, item->tipText() );
+ }
+}
+
+class ValueSpecialRepresentationCommand : public QObject, public CliCommand
+{
+public:
+ ValueSpecialRepresentationCommand(VarItem* item, const QString& command)
+ : CliCommand(command.ascii(),
+ this,
+ &ValueSpecialRepresentationCommand::handleReply,
+ true),
+ item_(item)
+ {}
+
+private:
+
+ VarItem* item_;
+
+ void handleReply(const QValueVector<QString>& lines)
+ {
+ QString s;
+ for(unsigned i = 1; i < lines.count(); ++i)
+ s += lines[i];
+ item_->updateSpecialRepresentation(s.local8Bit());
+ }
+};
+
+void VariableTree::slotVarobjNameChanged(
+ const QString& from, const QString& to)
+{
+ if (!from.isEmpty())
+ varobj2varitem.erase(from);
+
+ if (!to.isEmpty())
+ varobj2varitem[to] =
+ const_cast<VarItem*>(
+ static_cast<const VarItem*>(sender()));
+}
+
+
+
+VarFrameRoot* VariableTree::demand_frame_root(int frameNo, int threadNo)
+{
+ VarFrameRoot *frame = findFrame(frameNo, threadNo);
+ if (!frame)
+ {
+ frame = new VarFrameRoot(this, frameNo, threadNo);
+ frame->setFrameName(i18n("Locals"));
+ // Make sure "Locals" item is always the top item, before
+ // "watch" and "recent experessions" items.
+ this->takeItem(frame);
+ this->insertItem(frame);
+ frame->setOpen(true);
+ }
+ return frame;
+}
+
+void VariableTree::argumentsReady(const GDBMI::ResultRecord& r)
+{
+ const GDBMI::Value& args = r["stack-args"][0]["args"];
+
+ fetch_time.start();
+
+ locals_and_arguments.clear();
+ for(unsigned i = 0; i < args.size(); ++i)
+ {
+ locals_and_arguments.push_back(args[i].literal());
+ }
+}
+
+void VariableTree::localsReady(const GDBMI::ResultRecord& r)
+{
+ const GDBMI::Value& locals = r["locals"];
+
+ for(unsigned i = 0; i < locals.size(); ++i)
+ {
+ QString val = locals[i].literal();
+
+ // Check ada internal variables like <R45b>, <L23R> ...
+ bool is_ada_variable = (val[0] == '<' && val[val.length() - 1] == '>');
+
+ if (!is_ada_variable)
+ {
+ locals_and_arguments.push_back(val);
+ }
+ }
+
+ controller_->addCommand(new CliCommand("info frame",
+ this,
+ &VariableTree::frameIdReady));
+}
+
+void VariableTree::frameIdReady(const QValueVector<QString>& lines)
+{
+ //kdDebug(9012) << "localAddresses: " << lines[1] << "\n";
+
+ QString frame_info;
+ for(unsigned i = 1; i < lines.size(); ++i)
+ frame_info += lines[i];
+
+ kdDebug(9012) << "frame info: " << frame_info << "\n";
+ frame_info.replace('\n', "");
+
+ static QRegExp frame_base_rx("frame at 0x([0-9a-fA-F]*)");
+ static QRegExp frame_code_rx("saved [a-zA-Z0-9]* 0x([0-9a-fA-F]*)");
+
+ int i = frame_base_rx.search(frame_info);
+ int i2 = frame_code_rx.search(frame_info);
+
+ bool frameIdChanged = false;
+
+ VarFrameRoot *frame = demand_frame_root(
+ controller_->currentFrame(), controller_->currentThread());
+
+ if (frame != currentFrameItem)
+ {
+ if (currentFrameItem)
+ {
+ currentFrameItem->setVisible(false);
+ }
+ }
+ currentFrameItem = frame;
+ currentFrameItem->setVisible(true);
+
+
+ if (i != -1 && i2 != -1)
+ {
+ unsigned long long new_frame_base =
+ frame_base_rx.cap(1).toULongLong(0, 16);
+ unsigned long long new_code_address =
+ frame_code_rx.cap(1).toULongLong(0, 16);
+ kdDebug(9012) << "Frame base = " << QString::number(new_frame_base, 16)
+ << " code = " << QString::number(new_code_address, 16)
+ << "\n";
+ kdDebug(9012) << "Previous frame " <<
+ QString::number(frame->currentFrameBase, 16)
+ << " code = " << QString::number(
+ frame->currentFrameCodeAddress, 16)
+ << "\n";
+
+ frameIdChanged = (new_frame_base != frame->currentFrameBase ||
+ new_code_address != frame->currentFrameCodeAddress);
+
+ frame->currentFrameBase = new_frame_base;
+ frame->currentFrameCodeAddress = new_code_address;
+ }
+ else
+ {
+ KMessageBox::information(
+ 0,
+ "<b>Can't get frame id</b>"
+ "Could not found frame id from output of 'info frame'. "
+ "Further debugging can be unreliable. ",
+ i18n("Internal error"), "gdb_error");
+ }
+
+ if (frameIdChanged)
+ {
+ // Remove all variables.
+ // FIXME: probably, need to do this in all frames.
+ QListViewItem* child = frame->firstChild();
+ QListViewItem* next;
+ for(; child; child = next)
+ {
+ next = child->nextSibling();
+ delete child;
+ }
+ }
+
+ setUpdatesEnabled(false);
+
+ std::set<QListViewItem*> alive;
+
+ for(unsigned i = 0; i < locals_and_arguments.size(); ++i)
+ {
+ QString name = locals_and_arguments[i];
+
+ // See if we've got VarItem for this one already.
+ VarItem* var = 0;
+ for(QListViewItem *child = frame->firstChild();
+ child;
+ child = child->nextSibling())
+ {
+ if (child->text(VarNameCol) == name)
+ {
+ var = dynamic_cast<VarItem*>(child);
+ break;
+ }
+ }
+ if (!var)
+ {
+ var = new VarItem(frame, name);
+ }
+ alive.insert(var);
+
+ var->clearHighlight();
+ }
+
+ // Remove VarItems that don't correspond to any local
+ // variables any longer. Perform type/address updates
+ // for others.
+ for(QListViewItem* child = frame->firstChild(); child;)
+ {
+ QListViewItem* current = child;
+ child = current->nextSibling();
+ if (!alive.count(current))
+ delete current;
+ else
+ static_cast<VarItem*>(current)->recreateLocallyMaybe();
+ }
+
+ for(QListViewItem* child = findWatch()->firstChild();
+ child; child = child->nextSibling())
+ {
+ VarItem* var = static_cast<VarItem*>(child);
+ var->clearHighlight();
+ // For watched expressions, we don't have an easy way
+ // to check if their meaning is still the same, so
+ // unconditionally recreate them.
+ var->recreate();
+ }
+
+ // Note: can't use --all-values in this command, because gdb will
+ // die if there's any uninitialized variable. Ouch!
+ controller_->addCommand(new GDBCommand(
+ "-var-update *",
+ this,
+ &VariableTree::handleVarUpdate));
+
+ controller_->addCommand(new SentinelCommand(
+ this,
+ &VariableTree::variablesFetchDone));
+}
+
+void VariableTree::handleVarUpdate(const GDBMI::ResultRecord& r)
+{
+ const GDBMI::Value& changed = r["changelist"];
+
+ std::set<QString> names_to_update;
+
+ for(unsigned i = 0; i < changed.size(); ++i)
+ {
+ const GDBMI::Value& c = changed[i];
+
+ QString name = c["name"].literal();
+ if (c.hasField("in_scope") && c["in_scope"].literal() == "false")
+ continue;
+
+ names_to_update.insert(name);
+ }
+
+ QMap<QString, VarItem*>::iterator i, e;
+ for (i = varobj2varitem.begin(), e = varobj2varitem.end(); i != e; ++i)
+ {
+ if (names_to_update.count(i.key())
+ || i.data()->updateUnconditionally())
+ {
+ i.data()->updateValue();
+ }
+ }
+}
+
+void VarItem::handleCliPrint(const QValueVector<QString>& lines)
+{
+ static QRegExp r("(\\$[0-9]+)");
+ if (lines.size() >= 2)
+ {
+ int i = r.search(lines[1]);
+ if (i == 0)
+ {
+ controller_->addCommand(
+ new GDBCommand(QString("-var-create %1 * \"%2\"")
+ .arg(varobjName_)
+ .arg(r.cap(1)),
+ this,
+ &VarItem::varobjCreated,
+ // On initial create, errors get reported
+ // by generic code. After then, errors
+ // are swallowed by varobjCreated.
+ initialCreation_ ? false : true));
+ }
+ else
+ {
+ // FIXME: merge all output lines together.
+ // FIXME: add 'debuggerError' to debuggerpart.
+ KMessageBox::information(
+ 0,
+ i18n("<b>Debugger error</b><br>") + lines[1],
+ i18n("Debugger error"), "gdb_error");
+ }
+ }
+}
+
+
+void VariableTree::variablesFetchDone()
+{
+ // During parsing of fetched variable values, we might have issued
+ // extra command to handle 'special values', like QString.
+ // We don't want to enable updates just yet, because this will cause
+ // flicker, so add a sentinel command just to enable updates.
+ //
+ // We need this intermediate hook because commands for special
+ // representation are issues when responses to orginary fetch
+ // values commands are received, so we can add sentinel command after
+ // special representation fetch only when commands for ordinary
+ // fetch are all executed.
+ controller_->addCommand(new SentinelCommand(
+ this,
+ &VariableTree::fetchSpecialValuesDone));
+
+}
+
+void VariableTree::fetchSpecialValuesDone()
+{
+ // FIXME: can currentFrame_ or currentThread_ change between
+ // start of var fetch and call of 'variablesFetchDone'?
+ VarFrameRoot *frame = demand_frame_root(
+ controller_->currentFrame(), controller_->currentThread());
+
+// frame->trim();
+
+ frame->needLocals_ = false;
+
+ setUpdatesEnabled(true);
+ triggerUpdate();
+
+ kdDebug(9012) << "Time to fetch variables: " << fetch_time.elapsed() <<
+ "ms\n";
+}
+
+void
+VariableTree::slotItemRenamed(QListViewItem* item, int col, const QString& text)
+{
+ if (col == ValueCol)
+ {
+ VarItem* v = dynamic_cast<VarItem*>(item);
+ Q_ASSERT(v);
+ if (v)
+ {
+ v->setValue(text);
+ }
+ }
+}
+
+
+void VariableTree::keyPressEvent(QKeyEvent* e)
+{
+ if (VarItem* item = dynamic_cast<VarItem*>(currentItem()))
+ {
+ QString text = e->text();
+
+ if (text == "n" || text == "x" || text == "d" || text == "c"
+ || text == "t")
+ {
+ item->setFormat(
+ item->formatFromGdbModifier(text[0].latin1()));
+ }
+
+ if (e->key() == Qt::Key_Delete)
+ {
+ QListViewItem* root = findRoot(item);
+
+ if (dynamic_cast<WatchRoot*>(root) || root == recentExpressions_)
+ {
+ delete item;
+ }
+ }
+
+ if (e->key() == Qt::Key_C && e->state() == Qt::ControlButton)
+ {
+ copyToClipboard(item);
+ }
+ }
+}
+
+
+void VariableTree::copyToClipboard(QListViewItem* item)
+{
+ QClipboard *qb = KApplication::clipboard();
+ QString text = item->text( 1 );
+
+ qb->setText( text, QClipboard::Clipboard );
+}
+
+void VariableTree::handleAddressComputed(const GDBMI::ResultRecord& r)
+{
+ if (r.reason == "error")
+ {
+ // Not lvalue, leave item disabled.
+ return;
+ }
+
+ if (activePopup_)
+ {
+ activePopup_->setItemEnabled(idToggleWatch, true);
+
+ unsigned long long address = r["value"].literal().toULongLong(0, 16);
+ if (breakpointWidget_->hasWatchpointForAddress(address))
+ {
+ activePopup_->setItemChecked(idToggleWatch, true);
+ }
+ }
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+TrimmableItem::TrimmableItem(VariableTree *parent)
+ : KListViewItem (parent, parent->lastChild())
+{
+}
+
+// **************************************************************************
+
+TrimmableItem::TrimmableItem(TrimmableItem *parent)
+ : KListViewItem (parent, parent->lastChild())
+{
+}
+
+// **************************************************************************
+
+TrimmableItem::~TrimmableItem()
+{
+}
+
+// **************************************************************************
+
+void TrimmableItem::paintCell(QPainter *p, const QColorGroup &cg,
+ int column, int width, int align)
+{
+ if ( !p )
+ return;
+ // make toplevel item (watch and frame items) names bold
+ if (column == 0 && !parent())
+ {
+ QFont f = p->font();
+ f.setBold(true);
+ p->setFont(f);
+ }
+ QListViewItem::paintCell( p, cg, column, width, align );
+}
+
+QListViewItem *TrimmableItem::lastChild() const
+{
+ QListViewItem *child = firstChild();
+ if (child)
+ while (QListViewItem *nextChild = child->nextSibling())
+ child = nextChild;
+
+ return child;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+int VarItem::varobjIndex = 0;
+
+VarItem::VarItem(TrimmableItem *parent,
+ const QString& expression,
+ bool frozen)
+ : TrimmableItem (parent),
+ expression_(expression),
+ highlight_(false),
+ oldSpecialRepresentationSet_(false),
+ format_(natural),
+ numChildren_(0),
+ childrenFetched_(false),
+ updateUnconditionally_(false),
+ frozen_(frozen),
+ initialCreation_(true),
+ baseClassMember_(false),
+ alive_(true)
+{
+ connect(this, SIGNAL(varobjNameChange(const QString&, const QString&)),
+ varTree(),
+ SLOT(slotVarobjNameChanged(const QString&, const QString&)));
+
+
+ // User might have entered format together with expression: like
+ // /x i1+i2
+ // If we do nothing, it will be impossible to watch the variable in
+ // different format, as we'll just add extra format specifier.
+ // So:
+ // - detect initial value of format_
+ // - remove the format specifier from the string.
+
+ static QRegExp explicit_format("^\\s*/(.)\\s*(.*)");
+ if (explicit_format.search(expression_) == 0)
+ {
+ format_ = formatFromGdbModifier(explicit_format.cap(1)[0].latin1());
+ expression_ = explicit_format.cap(2);
+ }
+
+ setText(VarNameCol, expression_);
+ // Allow to change variable name by editing.
+ setRenameEnabled(ValueCol, true);
+
+ // Need to store this locally, since varTree() is 0 in
+ // destructor.
+ controller_ = varTree()->controller();
+
+ createVarobj();
+}
+
+VarItem::VarItem(TrimmableItem *parent, const GDBMI::Value& varobj,
+ format_t format, bool baseClassMember)
+: TrimmableItem (parent),
+ highlight_(false),
+ oldSpecialRepresentationSet_(false),
+ format_(format),
+ numChildren_(0),
+ childrenFetched_(false),
+ updateUnconditionally_(false),
+ frozen_(false),
+ initialCreation_(false),
+ baseClassMember_(baseClassMember),
+ alive_(true)
+{
+ connect(this, SIGNAL(varobjNameChange(const QString&, const QString&)),
+ varTree(),
+ SLOT(slotVarobjNameChanged(const QString&, const QString&)));
+
+ expression_ = varobj["exp"].literal();
+ varobjName_ = varobj["name"].literal();
+
+ varobjNameChange("", varobjName_);
+
+ setText(VarNameCol, displayName());
+
+ // Allow to change variable name by editing.
+ setRenameEnabled(ValueCol, true);
+
+ controller_ = varTree()->controller();
+
+ // Set type and children.
+ originalValueType_ = varobj["type"].literal();
+ numChildren_ = varobj["numchild"].literal().toInt();
+ setExpandable(numChildren_ != 0);
+
+
+ // Get the initial value.
+ updateValue();
+}
+
+void VarItem::createVarobj()
+{
+ QString old = varobjName_;
+ varobjName_ = QString("KDEV%1").arg(varobjIndex++);
+ emit varobjNameChange(old, varobjName_);
+
+ if (frozen_)
+ {
+ // MI has no way to freeze a variable object. So, we
+ // issue print command that returns $NN convenience
+ // variable and we create variable object from that.
+ controller_->addCommand(
+ new CliCommand(
+ QString("print %1").arg(expression_),
+ this,
+ &VarItem::handleCliPrint));
+ }
+ else
+ {
+ controller_->addCommand(
+ new CliCommand(
+ QString("print /x &%1").arg(expression_),
+ this,
+ &VarItem::handleCurrentAddress,
+ true));
+
+ controller_->addCommand(
+ // Need to quote expression, otherwise gdb won't like
+ // spaces inside it.
+ new GDBCommand(QString("-var-create %1 * \"%2\"")
+ .arg(varobjName_)
+ .arg(expression_),
+ this,
+ &VarItem::varobjCreated,
+ initialCreation_ ? false : true));
+ }
+}
+
+void VarItem::varobjCreated(const GDBMI::ResultRecord& r)
+{
+ // If we've tried to recreate varobj (for example for watched expression)
+ // after step, and it's no longer valid, it's fine.
+ if (r.reason == "error")
+ {
+ varobjName_ = "";
+ return;
+ }
+ setAliveRecursively(true);
+
+ QString oldType = originalValueType_;
+ originalValueType_ = r["type"].literal();
+ if (!oldType.isEmpty() && oldType != originalValueType_)
+ {
+ // Type changed, the children might be no longer valid,
+ // so delete them.
+ for(QListViewItem* child = firstChild(); child; )
+ {
+ QListViewItem* cur = child;
+ child = child->nextSibling();
+ delete cur;
+ }
+ }
+
+ if (r.hasField("exp"))
+ expression_ = r["exp"].literal();
+ numChildren_ = r["numchild"].literal().toInt();
+ setExpandable(numChildren_ != 0);
+ currentAddress_ = lastObtainedAddress_;
+
+ setVarobjName(varobjName_);
+}
+
+void VarItem::setVarobjName(const QString& name)
+{
+ if (varobjName_ != name)
+ emit varobjNameChange(varobjName_, name);
+
+ varobjName_ = name;
+
+ if (format_ != natural)
+ {
+ controller_->addCommand(
+ new GDBCommand(QString("-var-set-format \"%1\" %2")
+ .arg(varobjName_).arg(varobjFormatName())));
+ }
+
+ // Get the initial value.
+ updateValue();
+
+ if (isOpen())
+ {
+ // This regets children list.
+ setOpen(true);
+ }
+}
+
+void VarItem::valueDone(const GDBMI::ResultRecord& r)
+{
+ if (r.reason == "done")
+ {
+ QString s = GDBParser::getGDBParser()->undecorateValue(
+ r["value"].literal());
+
+ if (format_ == character)
+ {
+ QString encoded = s;
+ bool ok;
+ int value = s.toInt(&ok);
+ if (ok)
+ {
+ char c = (char)value;
+ encoded += " '";
+ if (std::isprint(c))
+ encoded += c;
+ else {
+ // Try common escape characters.
+ static char backslashed[] = {'a', 'b', 'f', 'n',
+ 'r', 't', 'v', '0'};
+ static char represented[] = "\a\b\f\n\r\t\v";
+
+ const char* ix = strchr (represented, c);
+ if (ix) {
+ encoded += "\\";
+ encoded += backslashed[ix - represented];
+ }
+ else
+ encoded += "\\" + s;
+ }
+ encoded += "'";
+ s = encoded;
+ }
+ }
+
+ if (format_ == binary)
+ {
+ // For binary format, split the value at 4-bit boundaries
+ static QRegExp r("^[01]+$");
+ int i = r.search(s);
+ if (i == 0)
+ {
+ QString split;
+ for(unsigned i = 0; i < s.length(); ++i)
+ {
+ // For string 11111, we should split it as
+ // 1 1111, not as 1111 1.
+
+ // 0 is past the end character
+ int distance = i - s.length();
+
+ if (distance % 4 == 0 && !split.isEmpty())
+ split.append(' ');
+ split.append(s[i]);
+ }
+ s = split;
+ }
+ }
+
+ setText(ValueCol, s);
+ }
+ else
+ {
+ QString s = r["msg"].literal();
+ // Error response.
+ if (s.startsWith("Cannot access memory"))
+ {
+ s = "(inaccessible)";
+ setExpandable(false);
+ }
+ else
+ {
+ setExpandable(numChildren_ != 0);
+ }
+ setText(ValueCol, s);
+ }
+}
+
+void VarItem::createChildren(const GDBMI::ResultRecord& r,
+ bool children_of_fake)
+{
+ const GDBMI::Value& children = r["children"];
+
+ /* In order to figure out which variable objects correspond
+ to base class subobject, we first must detect if *this
+ is a structure type. We use present of 'public'/'private'/'protected'
+ fake child as an indicator. */
+ bool structureType = false;
+ if (!children_of_fake && children.size() > 0)
+ {
+ QString exp = children[0]["exp"].literal();
+ bool ok = false;
+ exp.toInt(&ok);
+ if (!ok || exp[0] != '*')
+ {
+ structureType = true;
+ }
+ }
+
+ for (unsigned i = 0; i < children.size(); ++i)
+ {
+ QString exp = children[i]["exp"].literal();
+ // For artificial accessibility nodes,
+ // fetch their children.
+ if (exp == "public" || exp == "protected" || exp == "private")
+ {
+ QString name = children[i]["name"].literal();
+ controller_->addCommand(new GDBCommand(
+ "-var-list-children \"" +
+ name + "\"",
+ this,
+ &VarItem::childrenOfFakesDone));
+ }
+ else
+ {
+ /* All children of structures that are not artifical
+ are base subobjects. */
+ bool baseObject = structureType;
+
+ VarItem* existing = 0;
+ for(QListViewItem* child = firstChild();
+ child; child = child->nextSibling())
+ {
+ VarItem* v = static_cast<VarItem*>(child);
+ kdDebug(9012) << "Child exp : " << v->expression_ <<
+ " new exp " << exp << "\n";
+
+ if (v->expression_ == exp)
+ {
+ existing = v;
+ }
+ }
+ if (existing)
+ {
+ existing->setVarobjName(children[i]["name"].literal());
+ }
+ else
+ {
+ kdDebug(9012) << "Creating new varobj "
+ << exp << " " << baseObject << "\n";
+ // Propagate format from parent.
+ VarItem* v = 0;
+ v = new VarItem(this, children[i], format_, baseObject);
+ }
+ }
+ }
+}
+
+
+void VarItem::childrenDone(const GDBMI::ResultRecord& r)
+{
+ createChildren(r, false);
+ childrenFetched_ = true;
+}
+
+void VarItem::childrenOfFakesDone(const GDBMI::ResultRecord& r)
+{
+ createChildren(r, true);
+}
+
+void VarItem::handleCurrentAddress(const QValueVector<QString>& lines)
+{
+ lastObtainedAddress_ = "";
+ if (lines.count() > 1)
+ {
+ static QRegExp r("\\$\\d+ = ([^\n]*)");
+ int i = r.search(lines[1]);
+ if (i == 0)
+ {
+ lastObtainedAddress_ = r.cap(1);
+ kdDebug(9012) << "new address " << lastObtainedAddress_ << "\n";
+ }
+ }
+}
+
+void VarItem::handleType(const QValueVector<QString>& lines)
+{
+ bool recreate = false;
+
+ if (lastObtainedAddress_ != currentAddress_)
+ {
+ kdDebug(9012) << "Address changed from " << currentAddress_
+ << " to " << lastObtainedAddress_ << "\n";
+ recreate = true;
+ }
+ else
+ {
+ // FIXME: add error diagnostic.
+ if (lines.count() > 1)
+ {
+ static QRegExp r("type = ([^\n]*)");
+ int i = r.search(lines[1]);
+ if (i == 0)
+ {
+ kdDebug(9012) << "found type: " << r.cap(1) << "\n";
+ kdDebug(9012) << "original Type: " << originalValueType_ << "\n";
+
+ if (r.cap(1) != originalValueType_)
+ {
+ recreate = true;
+ }
+ }
+ }
+ }
+ if (recreate)
+ {
+ this->recreate();
+ }
+}
+
+QString VarItem::displayName() const
+{
+ if (expression_[0] != '*')
+ return expression_;
+
+ if (const VarItem* parent =
+ dynamic_cast<const VarItem*>(TrimmableItem::parent()))
+ {
+ return "*" + parent->displayName();
+ }
+ else
+ {
+ return expression_;
+ }
+}
+
+void VarItem::setAliveRecursively(bool enable)
+{
+ setEnabled(enable);
+ alive_ = true;
+
+ for(QListViewItem* child = firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->setAliveRecursively(enable);
+ }
+}
+
+
+VarItem::~VarItem()
+{
+ unhookFromGdb();
+}
+
+QString VarItem::gdbExpression() const
+{
+ // The expression for this item can be either:
+ // - number, for array element
+ // - identifier, for member,
+ // - ***intentifier, for derefenreced pointer.
+ const VarItem* parent = dynamic_cast<const VarItem*>(TrimmableItem::parent());
+
+ bool ok = false;
+ expression_.toInt(&ok);
+ if (ok)
+ {
+ // Array, parent always exists.
+ return parent->gdbExpression() + "[" + expression_ + "]";
+ }
+ else if (expression_[0] == '*')
+ {
+ if (parent)
+ {
+ // For MI, expression_ can be "*0" (meaing
+ // references 0-th element of some array).
+ // So, we really need to get to the parent to computed the right
+ // gdb expression.
+ return "*" + parent->gdbExpression();
+ }
+ else
+ {
+ // Parent can be null for watched expressions. In that case,
+ // expression_ should be a valid C++ expression.
+ return expression_;
+ }
+ }
+ else
+ {
+ if (parent)
+ /* This is varitem corresponds to a base suboject,
+ the expression should cast parent to the base's
+ type. */
+ if (baseClassMember_)
+ return "((" + expression_ + ")" + parent->gdbExpression() + ")";
+ else
+ return parent->gdbExpression() + "." + expression_;
+ else
+ return expression_;
+ }
+}
+
+// **************************************************************************
+
+
+// FIXME: we have two method to set VarItem: this one
+// and updateValue below. That's bad, must have just one.
+void VarItem::setText(int column, const QString &data)
+{
+ QString strData=data;
+
+ if (column == ValueCol) {
+ QString oldValue(text(column));
+ if (!oldValue.isEmpty()) // Don't highlight new items
+ {
+ highlight_ = (oldValue != QString(data));
+ }
+ }
+
+ QListViewItem::setText(column, strData);
+}
+
+void VarItem::clearHighlight()
+{
+ highlight_ = false;
+
+ for(QListViewItem* child = firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->clearHighlight();
+ }
+}
+
+// **************************************************************************
+
+void VarItem::updateValue()
+{
+ if (handleSpecialTypes())
+ {
+ // 1. Gdb never includes structures in output from -var-update
+ // 2. Even if it did, the internal state of object can be
+ // arbitrary complex and gdb can't detect if pretty-printed
+ // value remains the same.
+ // So, we need to reload value on each step.
+ updateUnconditionally_ = true;
+ return;
+ }
+ updateUnconditionally_ = false;
+
+ controller_->addCommand(
+ new GDBCommand(
+ "-var-evaluate-expression \"" + varobjName_ + "\"",
+ this,
+ &VarItem::valueDone,
+ true /* handle error */));
+}
+
+void VarItem::setValue(const QString& new_value)
+{
+ controller_->addCommand(
+ new GDBCommand(QString("-var-assign \"%1\" %2").arg(varobjName_)
+ .arg(new_value)));
+
+ // And immediately reload it from gdb,
+ // so that it's display format is the one gdb uses,
+ // not the one user has typed. Otherwise, on the next
+ // step, the visible value might change and be highlighted
+ // as changed, which is bogus.
+ updateValue();
+}
+
+void VarItem::updateSpecialRepresentation(const QString& xs)
+{
+ QString s(xs);
+ if (s[0] == '$')
+ {
+ int i = s.find('=');
+ if (i != -1)
+ s = s.mid(i+2);
+ }
+
+ // A hack to nicely display QStrings. The content of QString is unicode
+ // for for ASCII only strings we get ascii character mixed with \000.
+ // Remove those \000 now.
+
+ // This is not very nice, becuse we're doing this unconditionally
+ // and this method can be called twice: first with data that gdb sends
+ // for a variable, and second after we request the string data. In theory
+ // the data sent by gdb might contain \000 that should not be translated.
+ //
+ // What's even worse, ideally we should convert the string data from
+ // gdb into a QString again, handling all other escapes and composing
+ // one QChar from two characters from gdb. But to do that, we *should*
+ // now if the data if generic gdb value, and result of request for string
+ // data. Fixing is is for later.
+ s.replace( QRegExp("\\\\000|\\\\0"), "" );
+
+ // FIXME: for now, assume that all special representations are
+ // just strings.
+
+ s = GDBParser::getGDBParser()->undecorateValue(s);
+
+ setText(ValueCol, s);
+ // On the first stop, when VarItem was just created,
+ // don't show it in red.
+ if (oldSpecialRepresentationSet_)
+ highlight_ = (oldSpecialRepresentation_ != s);
+ else
+ highlight_ = false;
+
+ oldSpecialRepresentationSet_ = true;
+ oldSpecialRepresentation_ = s;
+}
+
+void VarItem::recreateLocallyMaybe()
+{
+ controller_->addCommand(
+ new CliCommand(
+ QString("print /x &%1").arg(expression_),
+ this,
+ &VarItem::handleCurrentAddress,
+ true));
+
+ controller_->addCommand(
+ new CliCommand(
+ QString("whatis %1").arg(expression_),
+ this,
+ &VarItem::handleType));
+}
+
+void VarItem::recreate()
+{
+ unhookFromGdb();
+
+ initialCreation_ = false;
+ createVarobj();
+}
+
+
+// **************************************************************************
+
+void VarItem::setOpen(bool open)
+{
+ QListViewItem::setOpen(open);
+
+ if (open && !childrenFetched_)
+ {
+ controller_->addCommand(new GDBCommand(
+ "-var-list-children \"" + varobjName_ + "\"",
+ this,
+ &VarItem::childrenDone));
+ }
+}
+
+bool VarItem::handleSpecialTypes()
+{
+ kdDebug(9012) << "handleSpecialTypes: " << originalValueType_ << "\n";
+ if (originalValueType_.isEmpty())
+ return false;
+
+ static QRegExp qstring("^(const)?[ ]*QString[ ]*&?$");
+
+ if (qstring.exactMatch(originalValueType_)) {
+
+ VariableTree* varTree = static_cast<VariableTree*>(listView());
+ if( !varTree->controller() )
+ return false;
+ varTree->controller()->addCommand(
+ new ResultlessCommand(QString("print $kdev_d=%1.d")
+ .arg(gdbExpression()),
+ true /* ignore error */));
+
+ if (varTree->controller()->qtVersion() >= 4)
+ varTree->controller()->addCommand(
+ new ResultlessCommand(QString("print $kdev_s=$kdev_d.size"),
+ true));
+ else
+ varTree->controller()->addCommand(
+ new ResultlessCommand(QString("print $kdev_s=$kdev_d.len"),
+ true));
+
+ varTree->controller()->addCommand(
+ new ResultlessCommand(
+ QString("print $kdev_s= ($kdev_s > 0)? ($kdev_s > 100 ? 200 : 2*$kdev_s) : 0"),
+ true));
+
+ if (varTree->controller()->qtVersion() >= 4)
+ varTree->controller()->addCommand(
+ new ValueSpecialRepresentationCommand(
+ this, "print ($kdev_s>0) ? (*((char*)&$kdev_d.data[0])@$kdev_s) : \"\""));
+ else
+ varTree->controller()->addCommand(
+ new ValueSpecialRepresentationCommand(
+ this, "print ($kdev_s>0) ? (*((char*)&$kdev_d.unicode[0])@$kdev_s) : \"\""));
+
+ return true;
+ }
+
+ return false;
+}
+
+// **************************************************************************
+
+VarItem::format_t VarItem::format() const
+{
+ return format_;
+}
+
+void VarItem::setFormat(format_t f)
+{
+ if (f == format_)
+ return;
+
+ format_ = f;
+
+ if (numChildren_)
+ {
+ // If variable has children, change format for children.
+ // - for structures, that's clearly right
+ // - for arrays, that's clearly right
+ // - for pointers, this can be confusing, but nobody ever wants to
+ // see the pointer in decimal!
+ for(QListViewItem* child = firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->setFormat(f);
+ }
+ }
+ else
+ {
+ controller_->addCommand(
+ new GDBCommand(QString("-var-set-format \"%1\" %2")
+ .arg(varobjName_).arg(varobjFormatName())));
+
+ updateValue();
+ }
+}
+
+VarItem::format_t VarItem::formatFromGdbModifier(char c) const
+{
+ format_t nf;
+ switch(c)
+ {
+ case 'n': // Not quite gdb modifier, but used in our UI.
+ nf = natural; break;
+ case 'x':
+ nf = hexadecimal; break;
+ case 'd':
+ nf = decimal; break;
+ case 'c':
+ nf = character; break;
+ case 't':
+ nf = binary; break;
+ default:
+ nf = natural; break;
+ }
+ return nf;
+}
+
+QString VarItem::varobjFormatName() const
+{
+ switch(format_)
+ {
+ case natural:
+ return "natural";
+ break;
+
+ case hexadecimal:
+ return "hexadecimal";
+ break;
+
+ case decimal:
+ return "decimal";
+ break;
+
+ // Note: gdb does not support 'character' natively,
+ // so we'll generate appropriate representation
+ // ourselfs.
+ case character:
+ return "decimal";
+ break;
+
+ case binary:
+ return "binary";
+ break;
+ }
+ return "<undefined>";
+}
+
+
+// **************************************************************************
+
+// Overridden to highlight the changed items
+void VarItem::paintCell(QPainter *p, const QColorGroup &cg,
+ int column, int width, int align)
+{
+ if ( !p )
+ return;
+
+ // Draw values in fixed font. For example, when there are several
+ // pointer variables, it's nicer if they are aligned -- it allows
+ // to easy see the diferrence between the pointers.
+ if (column == ValueCol)
+ {
+ p->setFont(KGlobalSettings::fixedFont());
+ }
+
+ if (!alive_)
+ {
+ /* Draw this as disabled. */
+ QListViewItem::paintCell(p, varTree()->QWidget::palette().disabled(),
+ column, width, align);
+ }
+ else
+ {
+ if (column == ValueCol && highlight_)
+ {
+ QColorGroup hl_cg( cg.foreground(), cg.background(), cg.light(),
+ cg.dark(), cg.mid(), red, cg.base());
+ QListViewItem::paintCell( p, hl_cg, column, width, align );
+ } else
+ QListViewItem::paintCell( p, cg, column, width, align );
+ }
+}
+
+
+VariableTree* VarItem::varTree() const
+{
+ return static_cast<VariableTree*>(listView());
+}
+
+void VarItem::unhookFromGdb()
+{
+ // Unhook children first, so that child varitems are deleted
+ // before parent. Strictly speaking, we can avoid calling
+ // -var-delete on child varitems, but that's a bit cheesy,
+ for(QListViewItem* child = firstChild();
+ child; child = child->nextSibling())
+ {
+ static_cast<VarItem*>(child)->unhookFromGdb();
+ }
+
+ alive_ = false;
+ childrenFetched_ = false;
+
+ emit varobjNameChange(varobjName_, "");
+
+ if (!controller_->stateIsOn(s_dbgNotStarted) && !varobjName_.isEmpty())
+ {
+ controller_->addCommand(
+ new GDBCommand(
+ QString("-var-delete \"%1\"").arg(varobjName_)));
+ }
+
+ varobjName_ = "";
+}
+
+// **************************************************************************
+
+QString VarItem::tipText() const
+{
+ const unsigned int maxTooltipSize = 70;
+ QString tip = text( ValueCol );
+
+ if (tip.length() > maxTooltipSize)
+ tip = tip.mid(0, maxTooltipSize - 1 ) + " [...]";
+
+ if (!tip.isEmpty())
+ tip += "\n" + originalValueType_;
+
+ return tip;
+}
+
+bool VarItem::updateUnconditionally() const
+{
+ return updateUnconditionally_;
+}
+
+bool VarItem::isAlive() const
+{
+ return alive_;
+}
+
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+VarFrameRoot::VarFrameRoot(VariableTree *parent, int frameNo, int threadNo)
+ : TrimmableItem (parent),
+ needLocals_(false),
+ frameNo_(frameNo),
+ threadNo_(threadNo),
+ currentFrameBase((unsigned long long)-1),
+ currentFrameCodeAddress((unsigned long long)-1)
+{
+ setExpandable(true);
+}
+
+// **************************************************************************
+
+VarFrameRoot::~VarFrameRoot()
+{
+}
+
+void VarFrameRoot::setOpen(bool open)
+{
+ bool frameOpened = ( isOpen()==false && open==true );
+ QListViewItem::setOpen(open);
+
+ if (frameOpened && needLocals_)
+ {
+ needLocals_ = false;
+ VariableTree* parent = static_cast<VariableTree*>(listView());
+ parent->updateCurrentFrame();
+ }
+}
+
+// **************************************************************************
+
+bool VarFrameRoot::matchDetails(int frameNo, int threadNo)
+{
+ return frameNo == frameNo_ && threadNo == threadNo_;
+}
+
+void VarFrameRoot::setDirty()
+{
+ needLocals_ = true;
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+WatchRoot::WatchRoot(VariableTree *parent)
+ : TrimmableItem(parent)
+{
+ setText(0, i18n("Watch"));
+ setOpen(true);
+}
+
+// **************************************************************************
+
+WatchRoot::~WatchRoot()
+{
+}
+
+// **************************************************************************
+// **************************************************************************
+// **************************************************************************
+
+}
+
+
+#include "variablewidget.moc"
+
diff --git a/languages/cpp/debugger/variablewidget.h b/languages/cpp/debugger/variablewidget.h
new file mode 100644
index 00000000..369f5748
--- /dev/null
+++ b/languages/cpp/debugger/variablewidget.h
@@ -0,0 +1,466 @@
+/***************************************************************************
+ 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 _VARIABLEWIDGET_H_
+#define _VARIABLEWIDGET_H_
+
+#include "gdbcontroller.h"
+#include "mi/gdbmi.h"
+
+#include <klistview.h>
+#include <kcombobox.h>
+#include <qwidget.h>
+#include <qtooltip.h>
+#include <qvaluevector.h>
+#include <qdatetime.h>
+#include <qguardedptr.h>
+#include <qmap.h>
+
+#include <vector>
+
+class KLineEdit;
+class KPopupMenu;
+
+namespace GDBDebugger
+{
+
+class TrimmableItem;
+class VarFrameRoot;
+class WatchRoot;
+class VarItem;
+class VariableTree;
+class DbgController;
+class GDBBreakpointWidget;
+
+enum { VarNameCol = 0, ValueCol = 1, VarTypeCol = 2};
+enum DataType { typeUnknown, typeValue, typePointer, typeReference,
+ typeStruct, typeArray, typeQString, typeWhitespace,
+ typeName };
+
+class VariableWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ VariableWidget( GDBController* controller,
+ GDBBreakpointWidget* breakpointWidget,
+ QWidget *parent=0, const char *name=0 );
+
+ VariableTree *varTree() const
+ { return varTree_; }
+
+protected: // QWidget overrides
+ void focusInEvent(QFocusEvent *e);
+
+public slots:
+ void slotAddWatchVariable();
+ void slotAddWatchVariable(const QString &ident);
+ void slotEvaluateExpression();
+ void slotEvaluateExpression(const QString &ident);
+
+private:
+ VariableTree *varTree_;
+// KLineEdit *watchVarEntry_;
+ friend class VariableTree;
+
+ KHistoryCombo *watchVarEditor_;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VariableTree : public KListView, public QToolTip
+{
+ Q_OBJECT
+public:
+ VariableTree(VariableWidget *parent,
+ GDBController* controller,
+ GDBBreakpointWidget* breakpointWidget,
+ const char *name=0 );
+ virtual ~VariableTree();
+
+ QListViewItem *lastChild() const;
+
+ QListViewItem *findRoot(QListViewItem *item) const;
+ VarFrameRoot *findFrame(int frameNo, int threadNo) const;
+ WatchRoot *findWatch();
+
+
+ // (from QToolTip) Display a tooltip when the cursor is over an item
+ virtual void maybeTip(const QPoint &);
+
+ GDBController* controller() const { return controller_; }
+
+signals:
+ void toggleWatchpoint(const QString &varName);
+
+public slots:
+ void slotAddWatchVariable(const QString& watchVar);
+ void slotEvaluateExpression(const QString& expression);
+
+ void slotEvent(GDBController::event_t);
+ void slotItemRenamed(QListViewItem* item, int col, const QString& text);
+
+private slots:
+ void slotContextMenu(KListView *, QListViewItem *item);
+ void slotVarobjNameChanged(const QString& from, const QString& to);
+
+private: // Callbacks for gdb commands;
+ void argumentsReady(const GDBMI::ResultRecord&);
+ void localsReady(const GDBMI::ResultRecord&);
+ void frameIdReady(const QValueVector<QString>&);
+ void handleVarUpdate(const GDBMI::ResultRecord&);
+ void handleEvaluateExpression(const QValueVector<QString>&);
+ void variablesFetchDone();
+ void fetchSpecialValuesDone();
+
+ /** This is called when address of expression for which
+ popup is created is known.
+
+ If there's no address (for rvalue), does nothing
+ (leaving "Data breakpoint" item disabled).
+ Otherwise, enabled that item, and check is we
+ have data breakpoint for that address already.
+ */
+ void handleAddressComputed(const GDBMI::ResultRecord& r);
+
+private: // helper functions
+ /** Get (if exists) and create (otherwise) frame root for
+ the specified frameNo/threadNo combination.
+ */
+ VarFrameRoot* demand_frame_root(int frameNo, int threadNo);
+
+ void updateCurrentFrame();
+
+ /** Copies the value (second column) of the specified item to
+ the clipboard.
+ */
+ void copyToClipboard(QListViewItem*);
+
+private: // QWidget overrides
+ void keyPressEvent(QKeyEvent* e);
+
+private:
+ GDBController* controller_;
+ GDBBreakpointWidget* breakpointWidget_;
+
+ int activeFlag_;
+ int iOutRadix;
+ bool justPaused_;
+
+ // Root of all recently printed expressions.
+ TrimmableItem* recentExpressions_;
+ VarFrameRoot* currentFrameItem;
+
+ QTime fetch_time;
+ // Names of locals and arguments as reported by
+ // gdb.
+ std::vector<QString> locals_and_arguments;
+
+ QMap<QString, VarItem*> varobj2varitem;
+
+ KPopupMenu* activePopup_;
+ static const int idToggleWatch = 10;
+
+ friend class VarFrameRoot;
+ friend class VarItem;
+ friend class WatchRoot;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/** List view item that can 'trim' outdated children.
+
+ The instances of this class hold a number of children corresponding
+ to variables. When program state changes, such as after a step in source,
+ some variable values can change, and some variables can go out of scope.
+ We need
+ - highlight modified variables
+ - remove gone variables
+
+ We could just remove all children and repopulate the list from
+ the data from debugger, but then we'd loose information about previous
+ variable values.
+
+ So, we first update the values, highlighting the modified variables, and
+ keeping track which variables were recieved from gdb. After that, the
+ 'trim' method is called, removing all variables which were not recieved
+ from gdbr.
+ */
+class TrimmableItem : public KListViewItem
+{
+public:
+ TrimmableItem(VariableTree *parent);
+ TrimmableItem(TrimmableItem *parent);
+
+ virtual ~TrimmableItem();
+
+ QListViewItem *lastChild() const;
+
+protected:
+
+ void paintCell( QPainter *p, const QColorGroup &cg,
+ int column, int width, int align );
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VarItem : public QObject,
+ public TrimmableItem
+{
+ Q_OBJECT
+public:
+ enum format_t { natural, hexadecimal, decimal, character, binary };
+
+ /** Creates top-level variable item from the specified expression.
+ Optionally, alternative display name can be provided.
+ */
+ VarItem( TrimmableItem *parent,
+ const QString& expression,
+ bool frozen = false);
+
+ VarItem( TrimmableItem *parent, const GDBMI::Value& varobj,
+ format_t format, bool baseClassMember);
+
+ virtual ~VarItem();
+
+ /// Returns the gdb expression for *this.
+ QString gdbExpression() const;
+
+ /** Returns true is this VarItem should be unconditionally
+ updated on each step, not matter what's the result of
+ -var-update command.
+ */
+ bool updateUnconditionally() const;
+
+ void updateValue();
+ void updateSpecialRepresentation(const QString& s);
+
+ /** Creates a fresh gdbs "variable object", if needed.
+ Preconditions:
+ - frame id did not change
+ - this is a root variable
+
+ If the current type of expression, or it's address, it different
+ from it was previously, creates new "variable object" and
+ fetches new value.
+
+ Otherwise, does nothing.
+ */
+ void recreateLocallyMaybe();
+
+ /** Tries to create new gdb variable object for this expression.
+ If successfull, updates all values. Otherwise, makes
+ itself disabled.
+ */
+ void recreate();
+
+ void setOpen(bool open);
+ void setText (int column, const QString& text);
+
+ /** Mark the variable as alive, or not alive.
+ Variables that are not alive a shown as "gray",
+ and nothing can be done about them except for
+ removing. */
+ void setAliveRecursively(bool enable);
+
+ /** Recursively clears the varobjName_ field, making
+ *this completely disconnected from gdb.
+ Automatically makes *this and children disables,
+ since there's no possible interaction with unhooked
+ object.
+ */
+ void unhookFromGdb();
+
+ // Returns the text to be displayed as tooltip (the value)
+ QString tipText() const;
+
+ format_t format() const;
+ void setFormat(format_t f);
+ format_t formatFromGdbModifier(char c) const;
+
+ /** Clears highliting for this variable and
+ all its children. */
+ void clearHighlight();
+
+ /** Sets new top-level textual value of this variable.
+ */
+ void setValue(const QString& new_value);
+
+ bool isAlive() const;
+
+signals:
+ /** Emitted whenever the name of varobj associated with *this changes:
+ - when we've created initial varobj
+ - when we've changed varobj name as part of 'recreate' method
+ - when *this is destroyed and no longer uses any varobj.
+
+ Either 'from' or 'to' can be empty string.
+ */
+ void varobjNameChange(const QString& from, const QString& to);
+
+private:
+
+ /** Creates new gdb "variable object". The controller_,
+ expression_ and format_ member variables should already
+ be set.
+ */
+ void createVarobj();
+
+ /** Precondition: 'name' is a name of existing
+ gdb variable object.
+ Effects:
+ - sets varobjName_ to 'name'
+ - sets format, if it's not default one
+ - gets initial value
+ - if item is open, gets children.
+ */
+ void setVarobjName(const QString& name);
+
+
+ /** Handle types that require special dispay, such as
+ QString. Return true if this is such a type.
+ The 'originalValueType_' is already initialized
+ by the time this method is called.
+ */
+ bool handleSpecialTypes();
+ void paintCell( QPainter *p, const QColorGroup &cg,
+ int column, int width, int align );
+ void varobjCreated(const GDBMI::ResultRecord& r);
+ void valueDone(const GDBMI::ResultRecord& r);
+ void childrenDone(const GDBMI::ResultRecord& r);
+ void childrenOfFakesDone(const GDBMI::ResultRecord& r);
+ void handleCurrentAddress(const QValueVector<QString>& lines);
+ void handleType(const QValueVector<QString>& lines);
+
+ void createChildren(const GDBMI::ResultRecord& r, bool children_of_fake);
+
+ /** Called to handle the output of the cli print command.
+ */
+ void handleCliPrint(const QValueVector<QString>& lines);
+
+ // Assuming 'expression_' is already set, returns the
+ // displayName to use when showing this to the user.
+ // This function exists because if we have item with
+ // gdb expression '$1' and displayName 'P4', we want the child
+ // to show up as *P4, not as '*$1', so we can't uncondionally
+ // use expression gdb reports to us.
+ QString displayName() const;
+
+ VariableTree* varTree() const;
+
+ QString varobjFormatName() const;
+
+private:
+ // The gdb expression for this varItem relatively to
+ // parent VarItem.
+ QString expression_;
+
+ bool highlight_;
+ GDBController* controller_;
+
+ QString varobjName_;
+
+ // the non-cast type of the variable
+ QString originalValueType_;
+ bool oldSpecialRepresentationSet_;
+ QString oldSpecialRepresentation_;
+
+ format_t format_;
+
+ static int varobjIndex;
+
+ int numChildren_;
+ bool childrenFetched_;
+
+ QString currentAddress_;
+ QString lastObtainedAddress_;
+
+ bool updateUnconditionally_;
+ bool frozen_;
+
+ /* Set to true whan calling createVarobj for the
+ first time, and to false other time. */
+ bool initialCreation_;
+
+ /* Set if this VarItem corresponds to base class suboject. */
+ bool baseClassMember_;
+
+ bool alive_;
+};
+
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class VarFrameRoot : public TrimmableItem
+{
+public:
+ VarFrameRoot(VariableTree *parent, int frameNo, int threadNo);
+ virtual ~VarFrameRoot();
+
+ void setOpen(bool open);
+
+ // Marks the frame as dirty, that is as having
+ // out of date values. As soon as we try to open
+ // this item, it will fetch new data.
+ void setDirty();
+
+ void setFrameName(const QString &frameName)
+ { setText(VarNameCol, frameName); setText(ValueCol, ""); }
+
+ bool needLocals() const { return needLocals_; }
+ bool matchDetails(int frameNo, int threadNo);
+
+private:
+ bool needLocals_;
+ int frameNo_;
+ int threadNo_;
+
+ // Frame base and code address of the current inner-most
+ // frame. Needed so that if we can know when 'frame N' no longer
+ // is the same as 'frame N' when this 'VarFrameRoot' was created.
+ unsigned long long currentFrameBase;
+ unsigned long long currentFrameCodeAddress;
+
+ friend class VariableTree;
+
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class WatchRoot : public TrimmableItem
+{
+public:
+ WatchRoot(VariableTree *parent);
+ virtual ~WatchRoot();
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#endif