Extend copyright to 2018.
[kdbg.git] / kdbg / dbgmainwnd.cpp
blob151e31c45c5e7e0231522cb4d1e605e11983b43e
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include <klocalizedstring.h> /* i18n */
8 #include <kmessagebox.h>
9 #include <kconfig.h>
10 #include <kconfiggroup.h>
11 #include <kiconengine.h>
12 #include <kiconloader.h>
13 #include <kstandardaction.h>
14 #include <kstandardshortcut.h>
15 #include <kactioncollection.h>
16 #include <krecentfilesaction.h>
17 #include <kshortcutsdialog.h>
18 #include <kanimatedbutton.h>
19 #include <kwindowsystem.h>
20 #include <ksqueezedtextlabel.h>
21 #include <ktoolbar.h>
22 #include <kxmlguifactory.h>
23 #include <KPageDialog>
24 #include <QListWidget>
25 #include <QFile>
26 #include <QFileDialog>
27 #include <QFileInfo>
28 #include <QGuiApplication>
29 #include <QIcon>
30 #include <QList>
31 #include <QDockWidget>
32 #include <QProcess>
33 #include <QStatusBar>
34 #include <QUrl>
35 #include "dbgmainwnd.h"
36 #include "debugger.h"
37 #include "winstack.h"
38 #include "brkpt.h"
39 #include "threadlist.h"
40 #include "memwindow.h"
41 #include "ttywnd.h"
42 #include "watchwindow.h"
43 #include "procattach.h"
44 #include "prefdebugger.h"
45 #include "prefmisc.h"
46 #include "gdbdriver.h"
47 #include "xsldbgdriver.h"
48 #include "mydebug.h"
49 #include <typeinfo>
50 #include <sys/stat.h> /* mknod(2) */
51 #include <unistd.h> /* getpid */
54 static const char defaultTermCmdStr[] = "xterm -name kdbgio -title %T -e sh -c %C";
55 static const char defaultSourceFilter[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
56 static const char defaultHeaderFilter[] = "*.h *.hh *.hpp *.h++";
58 DebuggerMainWnd::DebuggerMainWnd() :
59 KXmlGuiWindow(),
60 m_debugger(0),
61 #ifdef GDB_TRANSCRIPT
62 m_transcriptFile(GDB_TRANSCRIPT),
63 #endif
64 m_outputTermCmdStr(defaultTermCmdStr),
65 m_outputTermProc(new QProcess),
66 m_ttyLevel(-1), /* no tty yet */
67 m_popForeground(false),
68 m_backTimeout(1000),
69 m_tabWidth(0),
70 m_sourceFilter(defaultSourceFilter),
71 m_headerFilter(defaultHeaderFilter),
72 m_statusActive(i18n("active"))
74 setDockNestingEnabled(true);
76 m_filesWindow = new WinStack(this);
77 setCentralWidget(m_filesWindow);
79 QDockWidget* dw1 = createDockWidget("Stack", i18n("Stack"));
80 m_btWindow = new QListWidget(dw1);
81 dw1->setWidget(m_btWindow);
82 QDockWidget* dw2 = createDockWidget("Locals", i18n("Locals"));
83 m_localVariables = new ExprWnd(dw2, i18n("Variable"));
84 dw2->setWidget(m_localVariables);
85 QDockWidget* dw3 = createDockWidget("Watches", i18n("Watched Expressions"));
86 m_watches = new WatchWindow(dw3);
87 dw3->setWidget(m_watches);
88 QDockWidget* dw4 = createDockWidget("Registers", i18n("Registers"));
89 m_registers = new RegisterView(dw4);
90 dw4->setWidget(m_registers);
91 QDockWidget* dw5 = createDockWidget("Breakpoints", i18n("Breakpoints"));
92 m_bpTable = new BreakpointTable(dw5);
93 dw5->setWidget(m_bpTable);
94 QDockWidget* dw6 = createDockWidget("Output", i18n("Output"));
95 m_ttyWindow = new TTYWindow(dw6);
96 dw6->setWidget(m_ttyWindow);
97 QDockWidget* dw7 = createDockWidget("Threads", i18n("Threads"));
98 m_threads = new ThreadList(dw7);
99 dw7->setWidget(m_threads);
100 QDockWidget* dw8 = createDockWidget("Memory", i18n("Memory"));
101 m_memoryWindow = new MemoryWindow(dw8);
102 dw8->setWidget(m_memoryWindow);
104 m_debugger = new KDebugger(this, m_localVariables, m_watches->watchVariables(), m_btWindow);
106 connect(m_debugger, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
107 connect(m_debugger, SIGNAL(updateUI()), SLOT(updateUI()));
108 connect(m_debugger, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
109 connect(m_debugger, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
110 m_bpTable->setDebugger(m_debugger);
111 m_memoryWindow->setDebugger(m_debugger);
113 setStandardToolBarMenuEnabled(true);
114 initKAction();
115 initStatusBar();
117 connect(m_watches, SIGNAL(addWatch()), SLOT(slotAddWatch()));
118 connect(m_watches, SIGNAL(deleteWatch()), m_debugger, SLOT(slotDeleteWatch()));
119 connect(m_watches, SIGNAL(textDropped(const QString&)), SLOT(slotAddWatch(const QString&)));
121 connect(&m_filesWindow->m_findDlg, SIGNAL(closed()), SLOT(updateUI()));
122 connect(m_filesWindow, SIGNAL(newFileLoaded()),
123 SLOT(slotNewFileLoaded()));
124 connect(m_filesWindow, SIGNAL(toggleBreak(const QString&,int,const DbgAddr&,bool)),
125 this, SLOT(slotToggleBreak(const QString&,int,const DbgAddr&,bool)));
126 connect(m_filesWindow, SIGNAL(enadisBreak(const QString&,int,const DbgAddr&)),
127 this, SLOT(slotEnaDisBreak(const QString&,int,const DbgAddr&)));
128 connect(m_debugger, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
129 m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
130 connect(m_debugger, SIGNAL(executableUpdated()),
131 m_filesWindow, SLOT(reloadAllFiles()));
132 connect(m_debugger, SIGNAL(updatePC(const QString&,int,const DbgAddr&,int)),
133 m_filesWindow, SLOT(updatePC(const QString&,int,const DbgAddr&,int)));
134 // value popup communication
135 connect(m_filesWindow, SIGNAL(initiateValuePopup(const QString&)),
136 m_debugger, SLOT(slotValuePopup(const QString&)));
137 connect(m_debugger, SIGNAL(valuePopup(const QString&)),
138 m_filesWindow, SLOT(slotShowValueTip(const QString&)));
139 // disassembling
140 connect(m_filesWindow, SIGNAL(disassemble(const QString&, int)),
141 m_debugger, SLOT(slotDisassemble(const QString&, int)));
142 connect(m_debugger, SIGNAL(disassembled(const QString&,int,const std::list<DisassembledCode>&)),
143 m_filesWindow, SLOT(slotDisassembled(const QString&,int,const std::list<DisassembledCode>&)));
144 connect(m_filesWindow, SIGNAL(moveProgramCounter(const QString&,int,const DbgAddr&)),
145 m_debugger, SLOT(setProgramCounter(const QString&,int,const DbgAddr&)));
146 // program stopped
147 connect(m_debugger, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
148 connect(&m_backTimer, SIGNAL(timeout()), SLOT(slotBackTimer()));
149 // tab width
150 connect(this, SIGNAL(setTabWidth(int)), m_filesWindow, SIGNAL(setTabWidth(int)));
152 // connect breakpoint table
153 connect(m_bpTable, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
154 m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
155 connect(m_debugger, SIGNAL(updateUI()), m_bpTable, SLOT(updateUI()));
156 connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateBreakList()));
157 connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateUI()));
159 connect(m_debugger, SIGNAL(registersChanged(const std::list<RegisterInfo>&)),
160 m_registers, SLOT(updateRegisters(const std::list<RegisterInfo>&)));
162 connect(m_debugger, SIGNAL(memoryDumpChanged(const QString&, const std::list<MemoryDump>&)),
163 m_memoryWindow, SLOT(slotNewMemoryDump(const QString&, const std::list<MemoryDump>&)));
164 connect(m_debugger, SIGNAL(saveProgramSpecific(KConfigBase*)),
165 m_memoryWindow, SLOT(saveProgramSpecific(KConfigBase*)));
166 connect(m_debugger, SIGNAL(restoreProgramSpecific(KConfigBase*)),
167 m_memoryWindow, SLOT(restoreProgramSpecific(KConfigBase*)));
169 // thread window
170 connect(m_debugger, SIGNAL(threadsChanged(const std::list<ThreadInfo>&)),
171 m_threads, SLOT(updateThreads(const std::list<ThreadInfo>&)));
172 connect(m_threads, SIGNAL(setThread(int)),
173 m_debugger, SLOT(setThread(int)));
175 // popup menu of the local variables window
176 m_localVariables->setContextMenuPolicy(Qt::CustomContextMenu);
177 connect(m_localVariables, SIGNAL(customContextMenuRequested(const QPoint&)),
178 this, SLOT(slotLocalsPopup(const QPoint&)));
180 makeDefaultLayout();
181 setupGUI(KXmlGuiWindow::Default, "kdbgui.rc");
182 restoreSettings(KSharedConfig::openConfig());
184 // The animation button is not part of the restored window state.
185 // We must create it after the toolbar was loaded.
186 initAnimation();
188 updateUI();
189 m_bpTable->updateUI();
192 DebuggerMainWnd::~DebuggerMainWnd()
194 saveSettings(KSharedConfig::openConfig());
195 // must delete m_debugger early since it references our windows
196 delete m_debugger;
197 m_debugger = 0;
199 delete m_memoryWindow;
200 delete m_threads;
201 delete m_ttyWindow;
202 delete m_bpTable;
203 delete m_registers;
204 delete m_watches;
205 delete m_localVariables;
206 delete m_btWindow;
207 delete m_filesWindow;
209 delete m_outputTermProc;
212 QDockWidget* DebuggerMainWnd::createDockWidget(const char* name, const QString& title)
214 QDockWidget* w = new QDockWidget(title, this);
215 w->setObjectName(name);
216 // view menu changes when docking state changes
217 connect(w, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
218 return w;
221 QAction* DebuggerMainWnd::createAction(const QString& text, const char* icon,
222 int shortcut, const QObject* receiver,
223 const char* slot, const char* name)
225 QAction* a = actionCollection()->addAction(name);
226 a->setText(text);
227 a->setIcon(QIcon(new KIconEngine(icon, KIconLoader::global())));
228 if (shortcut)
229 actionCollection()->setDefaultShortcut(a, QKeySequence(shortcut));
230 connect(a, SIGNAL(triggered()), receiver, slot);
231 return a;
234 QAction* DebuggerMainWnd::createAction(const QString& text,
235 int shortcut, const QObject* receiver,
236 const char* slot, const char* name)
238 QAction* a = actionCollection()->addAction(name);
239 a->setText(text);
240 if (shortcut)
241 actionCollection()->setDefaultShortcut(a, QKeySequence(shortcut));
242 connect(a, SIGNAL(triggered()), receiver, slot);
243 return a;
247 void DebuggerMainWnd::initKAction()
249 // file menu
250 QAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
251 actionCollection());
252 open->setText(i18n("&Open Source Code..."));
253 m_closeAction = KStandardAction::close(m_filesWindow, SLOT(slotClose()), actionCollection());
254 m_reloadAction = createAction(i18n("&Reload Source Code"), "view-refresh", 0,
255 m_filesWindow, SLOT(slotFileReload()), "file_reload");
256 m_fileExecAction = createAction(i18n("&Load Executable..."),
257 "document-open-executable", 0,
258 this, SLOT(slotFileExe()), "file_executable");
259 m_recentExecAction = KStandardAction::openRecent(this, SLOT(slotRecentExec(const QUrl&)),
260 actionCollection());
261 m_recentExecAction->setObjectName("file_executable_recent");
262 m_recentExecAction->setText(i18n("Recent E&xecutables"));
263 m_coreDumpAction = createAction(i18n("&Core Dump..."), 0,
264 this, SLOT(slotFileCore()), "file_core_dump");
265 KStandardAction::quit(this, SLOT(close()), actionCollection());
267 // settings menu
268 m_settingsAction = createAction(i18n("This &Program..."), 0,
269 this, SLOT(slotFileProgSettings()), "settings_program");
270 createAction(i18n("&Global Options..."), 0,
271 this, SLOT(slotFileGlobalSettings()), "settings_global");
272 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
273 KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
275 // view menu
276 m_findAction = KStandardAction::find(m_filesWindow, SLOT(slotViewFind()), actionCollection());
277 KStandardAction::findNext(m_filesWindow, SLOT(slotFindForward()), actionCollection());
278 KStandardAction::findPrev(m_filesWindow, SLOT(slotFindBackward()), actionCollection());
280 struct { QWidget* w; QString id; QAction** act; } dw[] = {
281 { m_btWindow, "view_stack", &m_btWindowAction },
282 { m_localVariables, "view_locals", &m_localVariablesAction },
283 { m_watches, "view_watched_expressions", &m_watchesAction },
284 { m_registers, "view_registers", &m_registersAction },
285 { m_bpTable, "view_breakpoints", &m_bpTableAction },
286 { m_threads, "view_threads", &m_threadsAction },
287 { m_ttyWindow, "view_output", &m_ttyWindowAction },
288 { m_memoryWindow, "view_memory", &m_memoryWindowAction }
290 for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) {
291 QDockWidget* d = dockParent(dw[i].w);
292 QAction* action = d->toggleViewAction();
293 actionCollection()->addAction(dw[i].id, action);
294 *dw[i].act = action;
297 // execution menu
298 m_runAction = createAction(i18n("&Run"),
299 "debug-run", Qt::Key_F5,
300 m_debugger, SLOT(programRun()), "exec_run");
301 connect(m_runAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
302 m_stepIntoAction = createAction(i18n("Step &into"),
303 "debug-step-into", Qt::Key_F8,
304 m_debugger, SLOT(programStep()), "exec_step_into");
305 connect(m_stepIntoAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
306 m_stepOverAction = createAction(i18n("Step &over"),
307 "debug-step-over", Qt::Key_F10,
308 m_debugger, SLOT(programNext()), "exec_step_over");
309 connect(m_stepOverAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
310 m_stepOutAction = createAction(i18n("Step o&ut"),
311 "debug-step-out", Qt::Key_F6,
312 m_debugger, SLOT(programFinish()), "exec_step_out");
313 connect(m_stepOutAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
314 m_toCursorAction = createAction(i18n("Run to &cursor"),
315 "debug-execute-to-cursor", Qt::Key_F7,
316 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
317 connect(m_toCursorAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
318 m_stepIntoIAction = createAction(i18n("Step i&nto by instruction"),
319 "debug-step-into-instruction", Qt::SHIFT+Qt::Key_F8,
320 m_debugger, SLOT(programStepi()), "exec_step_into_by_insn");
321 connect(m_stepIntoIAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
322 m_stepOverIAction = createAction(i18n("Step o&ver by instruction"),
323 "debug-step-instruction", Qt::SHIFT+Qt::Key_F10,
324 m_debugger, SLOT(programNexti()), "exec_step_over_by_insn");
325 connect(m_stepOverIAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
326 m_execMovePCAction = createAction(i18n("&Program counter to current line"),
327 "debug-run-cursor", 0,
328 m_filesWindow, SLOT(slotMoveProgramCounter()), "exec_movepc");
329 m_breakAction = createAction(i18n("&Break"), 0,
330 m_debugger, SLOT(programBreak()), "exec_break");
331 m_killAction = createAction(i18n("&Kill"), 0,
332 m_debugger, SLOT(programKill()), "exec_kill");
333 m_restartAction = createAction(i18n("Re&start"), 0,
334 m_debugger, SLOT(programRunAgain()), "exec_restart");
335 m_attachAction = createAction(i18n("A&ttach..."), 0,
336 this, SLOT(slotExecAttach()), "exec_attach");
337 m_detachAction = createAction(i18n("&Detach"), 0,
338 m_debugger, SLOT(programDetach()), "exec_detach");
339 m_argumentsAction = createAction(i18n("&Arguments..."), 0,
340 this, SLOT(slotExecArgs()), "exec_arguments");
342 // breakpoint menu
343 m_bpSetAction = createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9,
344 m_filesWindow, SLOT(slotBrkptSet()), "breakpoint_set");
345 m_bpSetTempAction = createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT+Qt::Key_F9,
346 m_filesWindow, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
347 m_bpEnableAction = createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL+Qt::Key_F9,
348 m_filesWindow, SLOT(slotBrkptEnable()), "breakpoint_enable");
350 // only in popup menus
351 createAction(i18n("Watch Expression"), 0,
352 this, SLOT(slotLocalsToWatch()), "watch_expression");
353 m_editValueAction = createAction(i18n("Edit Value"), Qt::Key_F2,
354 this, SLOT(slotEditValue()), "edit_value");
356 // all actions force an UI update
357 QList<QAction*> actions = actionCollection()->actions();
358 foreach(QAction* action, actions) {
359 connect(action, SIGNAL(triggered()), this, SLOT(updateUI()));
363 void DebuggerMainWnd::initAnimation()
365 KToolBar* toolbar = toolBar("mainToolBar");
366 m_animation = new KAnimatedButton(toolbar);
367 toolbar->addWidget(m_animation);
368 m_animation->setAnimationPath(KIconLoader::global()->moviePath("pulse", KIconLoader::Toolbar));
369 connect(m_animation, SIGNAL(clicked(bool)), m_debugger, SLOT(programBreak()));
370 m_animRunning = false;
373 void DebuggerMainWnd::initStatusBar()
375 QStatusBar* statusbar = statusBar();
376 m_statusActiveLabel = new KSqueezedTextLabel(statusbar);
377 m_statusActiveLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
378 statusbar->addPermanentWidget(m_statusActiveLabel);
379 m_statusActiveLabel->show();
380 m_lastActiveStatusText = m_statusActive;
382 /* message pane */
383 m_statusMsgLabel = new KSqueezedTextLabel(statusbar);
384 m_statusMsgLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
385 statusbar->addPermanentWidget(m_statusMsgLabel);
386 m_statusMsgLabel->show();
388 // reserve some translations
389 i18n("Restart");
390 i18n("Core dump");
393 bool DebuggerMainWnd::queryClose()
395 if (m_debugger != 0) {
396 m_debugger->shutdown();
398 return true;
402 // instance properties
403 void DebuggerMainWnd::saveProperties(KConfigGroup& cg)
405 // session management
406 QString executable = "";
407 if (m_debugger != 0) {
408 executable = m_debugger->executable();
410 cg.writeEntry("executable", executable);
413 void DebuggerMainWnd::readProperties(const KConfigGroup& cg)
415 // session management
416 QString execName = cg.readEntry("executable");
418 TRACE("readProperties: executable=" + execName);
419 if (!execName.isEmpty()) {
420 debugProgram(execName, "");
424 static const char RecentExecutables[] = "RecentExecutables";
425 static const char LastSession[] = "LastSession";
426 static const char OutputWindowGroup[] = "OutputWindow";
427 static const char TermCmdStr[] = "TermCmdStr";
428 static const char KeepScript[] = "KeepScript";
429 static const char DebuggerGroup[] = "Debugger";
430 static const char DebuggerCmdStr[] = "DebuggerCmdStr";
431 static const char PreferencesGroup[] = "Preferences";
432 static const char PopForeground[] = "PopForeground";
433 static const char BackTimeout[] = "BackTimeout";
434 static const char TabWidth[] = "TabWidth";
435 static const char SourceFileFilter[] = "SourceFileFilter";
436 static const char HeaderFileFilter[] = "HeaderFileFilter";
438 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config)
440 m_recentExecAction->saveEntries(config->group(RecentExecutables));
442 KConfigGroup lg = config->group(LastSession);
443 lg.writeEntry("Width0Locals", m_localVariables->columnWidth(0));
444 lg.writeEntry("Width0Watches", m_watches->columnWidth(0));
446 if (m_debugger != 0) {
447 m_debugger->saveSettings(config.data());
450 config->group(OutputWindowGroup).writeEntry(TermCmdStr, m_outputTermCmdStr);
451 config->group(DebuggerGroup).writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
453 KConfigGroup pg(config->group(PreferencesGroup));
454 pg.writeEntry(PopForeground, m_popForeground);
455 pg.writeEntry(BackTimeout, m_backTimeout);
456 pg.writeEntry(TabWidth, m_tabWidth);
457 pg.writeEntry(SourceFileFilter, m_sourceFilter);
458 pg.writeEntry(HeaderFileFilter, m_headerFilter);
461 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config)
463 m_recentExecAction->loadEntries(config->group(RecentExecutables));
465 KConfigGroup lg = config->group(LastSession);
466 int w;
467 w = lg.readEntry("Width0Locals", -1);
468 if (w >= 0 && w < 30000)
469 m_localVariables->setColumnWidth(0, w);
470 w = lg.readEntry("Width0Watches", -1);
471 if (w >= 0 && w < 30000)
472 m_watches->setColumnWidth(0, w);
474 if (m_debugger != 0) {
475 m_debugger->restoreSettings(config.data());
478 KConfigGroup og(config->group(OutputWindowGroup));
480 * For debugging and emergency purposes, let the config file override
481 * the shell script that is used to keep the output window open. This
482 * string must have EXACTLY 1 %s sequence in it.
484 setTerminalCmd(og.readEntry(TermCmdStr, defaultTermCmdStr));
485 m_outputTermKeepScript = og.readEntry(KeepScript);
487 setDebuggerCmdStr(config->group(DebuggerGroup).readEntry(DebuggerCmdStr));
489 KConfigGroup pg(config->group(PreferencesGroup));
490 m_popForeground = pg.readEntry(PopForeground, false);
491 m_backTimeout = pg.readEntry(BackTimeout, 1000);
492 m_tabWidth = pg.readEntry(TabWidth, 0);
493 m_sourceFilter = pg.readEntry(SourceFileFilter, m_sourceFilter);
494 m_headerFilter = pg.readEntry(HeaderFileFilter, m_headerFilter);
496 emit setTabWidth(m_tabWidth);
499 void DebuggerMainWnd::updateUI()
501 m_findAction->setChecked(m_filesWindow->m_findDlg.isVisible());
502 m_findAction->setEnabled(m_filesWindow->hasWindows());
503 m_bpSetAction->setEnabled(m_debugger->canChangeBreakpoints());
504 m_bpSetTempAction->setEnabled(m_debugger->canChangeBreakpoints());
505 m_bpEnableAction->setEnabled(m_debugger->canChangeBreakpoints());
507 m_fileExecAction->setEnabled(m_debugger->isIdle());
508 m_settingsAction->setEnabled(m_debugger->haveExecutable());
509 m_coreDumpAction->setEnabled(m_debugger->canStart());
510 m_closeAction->setEnabled(m_filesWindow->hasWindows());
511 m_reloadAction->setEnabled(m_filesWindow->hasWindows());
512 m_stepIntoAction->setEnabled(m_debugger->canSingleStep());
513 m_stepIntoIAction->setEnabled(m_debugger->canSingleStep());
514 m_stepOverAction->setEnabled(m_debugger->canSingleStep());
515 m_stepOverIAction->setEnabled(m_debugger->canSingleStep());
516 m_stepOutAction->setEnabled(m_debugger->canSingleStep());
517 m_toCursorAction->setEnabled(m_debugger->canSingleStep());
518 m_execMovePCAction->setEnabled(m_debugger->canSingleStep());
519 m_restartAction->setEnabled(m_debugger->canSingleStep());
520 m_attachAction->setEnabled(m_debugger->isReady());
521 m_detachAction->setEnabled(m_debugger->canSingleStep());
522 m_runAction->setEnabled(m_debugger->canStart() || m_debugger->canSingleStep());
523 m_killAction->setEnabled(m_debugger->haveExecutable() && m_debugger->isProgramActive());
524 m_breakAction->setEnabled(m_debugger->isProgramRunning());
525 m_argumentsAction->setEnabled(m_debugger->haveExecutable());
526 m_editValueAction->setEnabled(m_debugger->canSingleStep());
528 // animation
529 if (m_debugger->isIdle()) {
530 if (m_animRunning && m_animation) {
531 m_animation->stop();
532 m_animRunning = false;
534 } else {
535 if (!m_animRunning && m_animation) {
536 m_animation->start();
537 m_animRunning = true;
541 // update statusbar
542 QString newStatus;
543 if (m_debugger->isProgramActive())
544 newStatus = m_statusActive;
545 if (newStatus != m_lastActiveStatusText) {
546 m_statusActiveLabel->setText(newStatus);
547 m_lastActiveStatusText = newStatus;
551 void DebuggerMainWnd::updateLineItems()
553 m_filesWindow->updateLineItems(m_debugger);
556 void DebuggerMainWnd::slotAddWatch()
558 if (m_debugger != 0) {
559 QString t = m_watches->watchText();
560 m_debugger->addWatch(t);
564 void DebuggerMainWnd::slotAddWatch(const QString& text)
566 if (m_debugger != 0) {
567 m_debugger->addWatch(text);
571 void DebuggerMainWnd::slotNewFileLoaded()
573 // updates program counter in the new file
574 if (m_debugger != 0)
575 m_filesWindow->updateLineItems(m_debugger);
578 QDockWidget* DebuggerMainWnd::dockParent(QWidget* w)
580 while ((w = w->parentWidget()) != 0) {
581 if (QDockWidget* dock = qobject_cast<QDockWidget*>(w))
582 return dock;
584 return 0;
587 void DebuggerMainWnd::makeDefaultLayout()
589 // +---------------+---------+
590 // | Source | Locals |
591 // | | |
592 // |---------------+---------+
593 // |Stack, Brkpts, | Watches |
594 // |Output,... | |
595 // +---------------+---------+
597 addDockWidget(Qt::RightDockWidgetArea, dockParent(m_localVariables));
598 addDockWidget(Qt::BottomDockWidgetArea, dockParent(m_memoryWindow));
599 splitDockWidget(dockParent(m_memoryWindow), dockParent(m_threads), Qt::Horizontal);
600 tabifyDockWidget(dockParent(m_memoryWindow), dockParent(m_registers));
601 tabifyDockWidget(dockParent(m_registers), dockParent(m_bpTable));
602 tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow));
603 tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow));
604 tabifyDockWidget(dockParent(m_threads), dockParent(m_watches));
605 dockParent(m_localVariables)->setVisible(true);
606 dockParent(m_ttyWindow)->setVisible(true);
607 dockParent(m_watches)->setVisible(true);
608 dockParent(m_btWindow)->setVisible(true);
609 dockParent(m_bpTable)->setVisible(true);
612 bool DebuggerMainWnd::debugProgram(const QString& exe, const QString& lang)
614 // check the file name
615 QFileInfo fi(exe);
617 bool success = fi.isFile();
618 if (!success)
620 QString msg = i18n("`%1' is not a file or does not exist");
621 KMessageBox::sorry(this, msg.arg(exe));
623 else
625 success = startDriver(fi.absoluteFilePath(), lang);
628 if (success)
630 m_recentExecAction->addUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
632 // keep the directory
633 m_lastDirectory = fi.absolutePath();
634 m_filesWindow->setExtraDirectory(m_lastDirectory);
636 // set caption to basename part of executable
637 QString caption = fi.fileName();
638 setCaption(caption);
640 else
642 m_recentExecAction->removeUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
645 return success;
648 static const char GeneralGroup[] = "General";
650 bool DebuggerMainWnd::startDriver(const QString& executable, QString lang)
652 assert(m_debugger != 0);
654 TRACE(QString("trying language '%1'...").arg(lang));
655 DebuggerDriver* driver = driverFromLang(lang);
657 if (driver == 0)
659 // see if there is a language in the per-program config file
660 QString configName = m_debugger->getConfigForExe(executable);
661 if (QFile::exists(configName))
663 KConfig c(configName, KConfig::SimpleConfig);
665 // Using "GDB" as default here is for backwards compatibility:
666 // The config file exists but doesn't have an entry,
667 // so it must have been created by an old version of KDbg
668 // that had only the GDB driver.
669 lang = c.group(GeneralGroup)
670 .readEntry(KDebugger::DriverNameEntry, "GDB");
672 TRACE(QString("...bad, trying config driver %1...").arg(lang));
673 driver = driverFromLang(lang);
677 if (driver == 0)
679 QString name = driverNameFromFile(executable);
681 TRACE(QString("...no luck, trying %1 derived"
682 " from file contents").arg(name));
683 driver = driverFromLang(name);
685 if (driver == 0)
687 // oops
688 QString msg = i18n("Don't know how to debug language `%1'");
689 KMessageBox::sorry(this, msg.arg(lang));
690 return false;
692 if (typeid(*driver) == typeid(XsldbgDriver)) {
693 KMessageBox::information(this, i18n("XSL debugging is no longer supported and will be removed in a future version of KDbg"));
696 driver->setLogFileName(m_transcriptFile);
698 bool success = m_debugger->debugProgram(executable, driver);
700 if (!success)
702 delete driver;
704 QString msg = i18n("Could not start the debugger process.\n"
705 "Please shut down KDbg and resolve the problem.");
706 KMessageBox::sorry(this, msg);
709 return success;
712 // derive driver from language
713 DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
715 // lang is needed in all lowercase
716 lang = lang.toLower();
718 // The following table relates languages and debugger drivers
719 static const struct L {
720 const char* shortest; // abbreviated to this is still unique
721 const char* full; // full name of language
722 int driver;
723 } langs[] = {
724 { "c", "c++", 1 },
725 { "f", "fortran", 1 },
726 { "p", "python", 3 },
727 { "x", "xslt", 2 },
728 // the following are actually driver names
729 { "gdb", "gdb", 1 },
730 { "xsldbg", "xsldbg", 2 },
732 const int N = sizeof(langs)/sizeof(langs[0]);
734 // lookup the language name
735 int driverID = 0;
736 for (int i = 0; i < N; i++)
738 const L& l = langs[i];
740 // shortest must match
741 if (!lang.startsWith(l.shortest))
742 continue;
744 // lang must not be longer than the full name, and it must match
745 if (QString(l.full).startsWith(lang))
747 driverID = l.driver;
748 break;
751 DebuggerDriver* driver = 0;
752 switch (driverID) {
753 case 1:
755 GdbDriver* gdb = new GdbDriver;
756 gdb->setDefaultInvocation(m_debuggerCmdStr);
757 driver = gdb;
759 break;
760 case 2:
761 driver = new XsldbgDriver;
762 break;
763 default:
764 // unknown language
765 break;
767 return driver;
771 * Try to guess the language to use from the contents of the file.
773 QString DebuggerMainWnd::driverNameFromFile(const QString& exe)
775 /* Inprecise but simple test to see if file is in XSLT language */
776 if (exe.right(4).toLower() == ".xsl")
777 return "XSLT";
779 return "GDB";
782 void DebuggerMainWnd::setCoreFile(const QString& corefile)
784 assert(m_debugger != 0);
785 m_debugger->useCoreFile(corefile, true);
788 void DebuggerMainWnd::setRemoteDevice(const QString& remoteDevice)
790 if (m_debugger != 0) {
791 m_debugger->setRemoteDevice(remoteDevice);
795 void DebuggerMainWnd::overrideProgramArguments(const QString& args)
797 assert(m_debugger != 0);
798 m_debugger->overrideProgramArguments(args);
801 void DebuggerMainWnd::setTranscript(const QString& name)
803 m_transcriptFile = name;
804 if (m_debugger != 0 && m_debugger->driver() != 0)
805 m_debugger->driver()->setLogFileName(m_transcriptFile);
808 void DebuggerMainWnd::setAttachPid(const QString& pid)
810 assert(m_debugger != 0);
811 m_debugger->setAttachPid(pid);
814 void DebuggerMainWnd::slotNewStatusMsg()
816 QString msg = m_debugger->statusMessage();
817 m_statusMsgLabel->setText(msg.trimmed());
820 void DebuggerMainWnd::slotFileGlobalSettings()
822 int oldTabWidth = m_tabWidth;
824 KPageDialog dlg(this);
825 dlg.setWindowTitle(i18n("Global Options"));
827 PrefDebugger prefDebugger(&dlg);
828 prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty() ?
829 GdbDriver::defaultGdb() : m_debuggerCmdStr);
830 prefDebugger.setTerminal(m_outputTermCmdStr);
832 PrefMisc prefMisc(&dlg);
833 prefMisc.setPopIntoForeground(m_popForeground);
834 prefMisc.setBackTimeout(m_backTimeout);
835 prefMisc.setTabWidth(m_tabWidth);
836 prefMisc.setSourceFilter(m_sourceFilter);
837 prefMisc.setHeaderFilter(m_headerFilter);
839 dlg.addPage(&prefDebugger, i18n("Debugger"));
840 dlg.addPage(&prefMisc, i18n("Miscellaneous"));
841 if (dlg.exec() == QDialog::Accepted)
843 setDebuggerCmdStr(prefDebugger.debuggerCmd());
844 setTerminalCmd(prefDebugger.terminal());
845 m_popForeground = prefMisc.popIntoForeground();
846 m_backTimeout = prefMisc.backTimeout();
847 m_tabWidth = prefMisc.tabWidth();
848 m_sourceFilter = prefMisc.sourceFilter();
849 if (m_sourceFilter.isEmpty())
850 m_sourceFilter = defaultSourceFilter;
851 m_headerFilter = prefMisc.headerFilter();
852 if (m_headerFilter.isEmpty())
853 m_headerFilter = defaultHeaderFilter;
856 if (m_tabWidth != oldTabWidth) {
857 emit setTabWidth(m_tabWidth);
861 void DebuggerMainWnd::setTerminalCmd(const QString& cmd)
863 m_outputTermCmdStr = cmd;
864 // revert to default if empty
865 if (m_outputTermCmdStr.isEmpty()) {
866 m_outputTermCmdStr = defaultTermCmdStr;
870 void DebuggerMainWnd::setDebuggerCmdStr(const QString& cmd)
872 m_debuggerCmdStr = cmd;
873 // make empty if it is the default
874 if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
875 m_debuggerCmdStr = QString();
879 void DebuggerMainWnd::slotDebuggerStarting()
881 if (m_debugger == 0) /* paranoia check */
882 return;
884 if (m_ttyLevel == m_debugger->ttyLevel())
885 return;
887 // shut down terminal emulations we will not need
888 switch (m_ttyLevel) {
889 case KDebugger::ttySimpleOutputOnly:
890 m_ttyWindow->deactivate();
891 break;
892 case KDebugger::ttyFull:
893 m_outputTermProc->kill();
894 break;
895 default: break;
898 m_ttyLevel = m_debugger->ttyLevel();
900 QString ttyName;
901 switch (m_ttyLevel) {
902 case KDebugger::ttySimpleOutputOnly:
903 ttyName = m_ttyWindow->activate();
904 break;
905 case KDebugger::ttyFull:
906 // create an output window
907 ttyName = createOutputWindow();
908 TRACE(ttyName.isEmpty() ?
909 "createOuputWindow failed" : "successfully created output window");
910 break;
911 default: break;
914 m_debugger->setTerminal(ttyName);
917 void DebuggerMainWnd::slotToggleBreak(const QString& fileName, int lineNo,
918 const DbgAddr& address, bool temp)
920 // lineNo is zero-based
921 if (m_debugger != 0) {
922 m_debugger->setBreakpoint(fileName, lineNo, address, temp);
926 void DebuggerMainWnd::slotEnaDisBreak(const QString& fileName, int lineNo,
927 const DbgAddr& address)
929 // lineNo is zero-based
930 if (m_debugger != 0) {
931 m_debugger->enableDisableBreakpoint(fileName, lineNo, address);
935 QString DebuggerMainWnd::createOutputWindow()
937 // create a name for a fifo
938 QString fifoName;
939 fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
941 // create a fifo that will pass in the tty name
942 QFile::remove(fifoName); // remove remnants
943 #ifdef HAVE_MKFIFO
944 if (::mkfifo(fifoName.toLocal8Bit(), S_IRUSR|S_IWUSR) < 0) {
945 // failed
946 TRACE("mkfifo " + fifoName + " failed");
947 return QString();
949 #else
950 if (::mknod(fifoName.toLocal8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
951 // failed
952 TRACE("mknod " + fifoName + " failed");
953 return QString();
955 #endif
958 * Spawn an xterm that in turn runs a shell script that passes us
959 * back the terminal name and then only sits and waits.
961 static const char shellScriptFmt[] =
962 "tty>%s;"
963 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
964 "exec<&-;exec>&-;" /* close stdin and stdout */
965 "while :;do sleep 3600;done";
966 // let config file override this script
967 QString shellScript;
968 if (!m_outputTermKeepScript.isEmpty()) {
969 shellScript = m_outputTermKeepScript;
970 } else {
971 shellScript = shellScriptFmt;
974 shellScript.replace("%s", fifoName);
975 TRACE("output window script is " + shellScript);
977 QString title = QGuiApplication::applicationDisplayName();
978 title += i18n(": Program output");
980 // parse the command line specified in the preferences
981 QStringList cmdParts = m_outputTermCmdStr.split(' ');
984 * Build the argv array. Thereby substitute special sequences:
986 struct {
987 char seq[4];
988 QString replace;
989 } substitute[] = {
990 { "%T", title },
991 { "%C", shellScript }
994 for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
996 QString& str = *i;
997 for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
998 int pos = str.indexOf(substitute[j].seq);
999 if (pos >= 0) {
1000 str.replace(pos, 2, substitute[j].replace);
1001 break; /* substitute only one sequence */
1006 QString tty, pgm = cmdParts.takeFirst();
1008 m_outputTermProc->start(pgm, cmdParts);
1009 if (m_outputTermProc->waitForStarted())
1011 // read the ttyname from the fifo
1012 QFile f(fifoName);
1013 if (f.open(QIODevice::ReadOnly))
1015 QByteArray t = f.readAll();
1016 tty = QString::fromLocal8Bit(t, t.size());
1017 f.close();
1019 f.remove();
1021 // remove whitespace
1022 tty = tty.trimmed();
1023 TRACE("tty=" + tty);
1025 else
1027 // error, could not start xterm
1028 TRACE("fork failed for fifo " + fifoName);
1029 QFile::remove(fifoName);
1032 return tty;
1035 void DebuggerMainWnd::slotProgramStopped()
1037 // when the program stopped, move the window to the foreground
1038 if (m_popForeground) {
1039 // unfortunately, this requires quite some force to work :-(
1040 KWindowSystem::raiseWindow(winId());
1041 KWindowSystem::forceActiveWindow(winId());
1043 m_backTimer.stop();
1046 void DebuggerMainWnd::intoBackground()
1048 if (m_popForeground) {
1049 m_backTimer.setSingleShot(true);
1050 m_backTimer.start(m_backTimeout);
1054 void DebuggerMainWnd::slotBackTimer()
1056 lower();
1059 void DebuggerMainWnd::slotRecentExec(const QUrl& url)
1061 QString exe = url.toLocalFile();
1062 debugProgram(exe, "");
1065 QString DebuggerMainWnd::makeSourceFilter()
1067 QString f;
1068 f = i18n("All source files") + " (" + m_sourceFilter + " " + m_headerFilter + ")";
1069 f += ";;" + i18n("Source files") + " (" + m_sourceFilter + ")";
1070 f += ";;" + i18n("Header files") + " (" + m_headerFilter + ")";
1071 f += ";;" + i18n("All files") + " (*)";
1072 return f;
1076 * Pop up the context menu in the locals window
1078 void DebuggerMainWnd::slotLocalsPopup(const QPoint& pt)
1080 QMenu* popup = static_cast<QMenu*>(factory()->container("popup_locals", this));
1081 if (popup == 0) {
1082 return;
1084 if (popup->isVisible()) {
1085 popup->hide();
1086 } else {
1087 popup->popup(m_localVariables->viewport()->mapToGlobal(pt));
1092 * Copies the currently selected item to the watch window.
1094 void DebuggerMainWnd::slotLocalsToWatch()
1096 VarTree* item = m_localVariables->selectedItem();
1098 if (item != 0 && m_debugger != 0) {
1099 QString text = item->computeExpr();
1100 m_debugger->addWatch(text);
1105 * Starts editing a value in a value display
1107 void DebuggerMainWnd::slotEditValue()
1109 // does one of the value trees have the focus
1110 QWidget* f = QApplication::focusWidget();
1111 ExprWnd* wnd;
1112 if (f == m_localVariables) {
1113 wnd = m_localVariables;
1114 } else if (f == m_watches->watchVariables()) {
1115 wnd = m_watches->watchVariables();
1116 } else {
1117 return;
1120 if (m_localVariables->isEditing() ||
1121 m_watches->watchVariables()->isEditing())
1123 return; /* don't edit twice */
1126 VarTree* expr = wnd->selectedItem();
1127 if (expr != 0 && m_debugger != 0 && m_debugger->canSingleStep())
1129 TRACE("edit value");
1130 // determine the text to edit
1131 QString text = m_debugger->driver()->editableValue(expr);
1132 wnd->editValue(expr, text);
1136 void DebuggerMainWnd::slotFileOpen()
1138 // start browsing in the active file's directory
1139 // fall back to last used directory (executable)
1140 QString dir = m_lastDirectory;
1141 QString fileName = m_filesWindow->activeFileName();
1142 if (!fileName.isEmpty()) {
1143 QFileInfo fi(fileName);
1144 dir = fi.path();
1147 fileName = QFileDialog::getOpenFileName(this,
1148 i18n("Open Source Code"), dir, makeSourceFilter());
1150 if (!fileName.isEmpty())
1152 QFileInfo fi(fileName);
1153 m_lastDirectory = fi.path();
1154 m_filesWindow->setExtraDirectory(m_lastDirectory);
1155 m_filesWindow->activateFile(fileName);
1159 void DebuggerMainWnd::slotFileExe()
1161 if (m_debugger->isIdle())
1163 // open a new executable
1164 QString executable = QFileDialog::getOpenFileName(this,
1165 i18n("Select the Executable to Debug"),
1166 m_lastDirectory);
1167 if (executable.isEmpty())
1168 return;
1170 debugProgram(executable, "");
1174 void DebuggerMainWnd::slotFileCore()
1176 if (m_debugger->canStart())
1178 QString corefile = QFileDialog::getOpenFileName(this,
1179 i18n("Select Core Dump"),
1180 m_lastDirectory);
1181 if (!corefile.isEmpty()) {
1182 m_debugger->useCoreFile(corefile, false);
1187 void DebuggerMainWnd::slotFileProgSettings()
1189 if (m_debugger != 0) {
1190 m_debugger->programSettings(this);
1194 void DebuggerMainWnd::slotViewStatusbar()
1196 if (statusBar()->isVisible())
1197 statusBar()->hide();
1198 else
1199 statusBar()->show();
1200 setSettingsDirty();
1203 void DebuggerMainWnd::slotExecUntil()
1205 if (m_debugger != 0)
1207 QString file;
1208 int lineNo;
1209 if (m_filesWindow->activeLine(file, lineNo))
1210 m_debugger->runUntil(file, lineNo);
1214 void DebuggerMainWnd::slotExecAttach()
1216 #ifdef PS_COMMAND
1217 ProcAttachPS dlg(this);
1218 // seed filter with executable name
1219 QFileInfo fi = m_debugger->executable();
1220 dlg.setFilterText(fi.fileName());
1221 #else
1222 ProcAttach dlg(this);
1223 dlg.setText(m_debugger->attachedPid());
1224 #endif
1225 if (dlg.exec()) {
1226 m_debugger->attachProgram(dlg.text());
1230 void DebuggerMainWnd::slotExecArgs()
1232 if (m_debugger != 0) {
1233 m_debugger->programArgs(this);
1237 void DebuggerMainWnd::slotConfigureKeys()
1239 KShortcutsDialog::configure(actionCollection());
1242 #include "dbgmainwnd.moc"