/*************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #include #include namespace GDBDebugger { static const KDevPluginInfo data("kdevdebugger"); typedef KDevGenericFactory DebuggerFactory; K_EXPORT_COMPONENT_FACTORY( libkdevdebugger, DebuggerFactory( data ) ) DebuggerPart::DebuggerPart( TQObject *parent, const char *name, const TQStringList & ) : 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(TQt::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")); TQWhatsThis::add (gdbBreakpointWidget, i18n("Breakpoint list

" "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("process-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")); TQWhatsThis::add (framestackWidget, i18n("Frame stack

" "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")); TQWhatsThis::add (disassembleWidget, i18n("Machine code display

" "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")); TQWhatsThis::add (gdbOutputWidget, i18n("GDB output

" "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, TQT_SIGNAL(refreshBPState(const Breakpoint&)), this, TQT_SLOT(slotRefreshBPState(const Breakpoint&))); connect( gdbBreakpointWidget, TQT_SIGNAL(publishBPState(const Breakpoint&)), this, TQT_SLOT(slotRefreshBPState(const Breakpoint&))); connect( gdbBreakpointWidget, TQT_SIGNAL(gotoSourcePosition(const TQString&, int)), this, TQT_SLOT(slotGotoSource(const TQString&, int)) ); viewerWidget = new ViewerWidget( controller, 0, "viewerWidget"); mainWindow()->embedSelectView(viewerWidget, i18n("Debug views"), i18n("Special debugger views")); mainWindow()->setViewAvailable(viewerWidget, false); connect(viewerWidget, TQT_SIGNAL(setViewShown(bool)), this, TQT_SLOT(slotShowView(bool))); // Now setup the actions TDEAction *action; // action = new TDEAction(i18n("&Start"), "1rightarrow", CTRL+SHIFT+Key_F9, action = new TDEAction(i18n("&Start"), "dbgrun", Key_F9, this, TQT_SLOT(slotRun()), actionCollection(), "debug_run"); action->setToolTip( i18n("Start in debugger") ); action->setWhatsThis( i18n("Start in debugger

" "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 TDEAction(i18n("&Restart"), "dbgrestart", 0, this, TQT_SLOT(slotRestart()), actionCollection(), "debug_restart"); action->setToolTip( i18n("Restart program") ); action->setWhatsThis( i18n("Restarts application

" "Restarts applications from the beginning." ) ); action->setEnabled(false); action = new TDEAction(i18n("Sto&p"), "process-stop", 0, this, TQT_SLOT(slotStop()), actionCollection(), "debug_stop"); action->setToolTip( i18n("Stop debugger") ); action->setWhatsThis(i18n("Stop debugger

Kills the executable and exits the debugger.")); action = new TDEAction(i18n("Interrupt"), "media-playback-pause", 0, this, TQT_SLOT(slotPause()), actionCollection(), "debug_pause"); action->setToolTip( i18n("Interrupt application") ); action->setWhatsThis(i18n("Interrupt application

Interrupts the debugged process or current GDB command.")); action = new TDEAction(i18n("Run to &Cursor"), "dbgrunto", 0, this, TQT_SLOT(slotRunToCursor()), actionCollection(), "debug_runtocursor"); action->setToolTip( i18n("Run to cursor") ); action->setWhatsThis(i18n("Run to cursor

Continues execution until the cursor position is reached.")); action = new TDEAction(i18n("Set E&xecution Position to Cursor"), "dbgjumpto", 0, this, TQT_SLOT(slotJumpToCursor()), actionCollection(), "debug_jumptocursor"); action->setToolTip( i18n("Jump to cursor") ); action->setWhatsThis(i18n("Set Execution Position

Set the execution pointer to the current cursor position.")); action = new TDEAction(i18n("Step &Over"), "dbgnext", Key_F10, this, TQT_SLOT(slotStepOver()), actionCollection(), "debug_stepover"); action->setToolTip( i18n("Step over the next line") ); action->setWhatsThis( i18n("Step over

" "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 TDEAction(i18n("Step over Ins&truction"), "dbgnextinst", 0, this, TQT_SLOT(slotStepOverInstruction()), actionCollection(), "debug_stepoverinst"); action->setToolTip( i18n("Step over instruction") ); action->setWhatsThis(i18n("Step over instruction

Steps over the next assembly instruction.")); action = new TDEAction(i18n("Step &Into"), "dbgstep", Key_F11, this, TQT_SLOT(slotStepInto()), actionCollection(), "debug_stepinto"); action->setToolTip( i18n("Step into the next statement") ); action->setWhatsThis( i18n("Step into

" "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 TDEAction(i18n("Step into I&nstruction"), "dbgstepinst", 0, this, TQT_SLOT(slotStepIntoInstruction()), actionCollection(), "debug_stepintoinst"); action->setToolTip( i18n("Step into instruction") ); action->setWhatsThis(i18n("Step into instruction

Steps into the next assembly instruction.")); action = new TDEAction(i18n("Step O&ut"), "dbgstepout", Key_F12, this, TQT_SLOT(slotStepOut()), actionCollection(), "debug_stepout"); action->setToolTip( i18n("Steps out of the current function") ); action->setWhatsThis( i18n("Step out

" "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 TDEAction(i18n("Viewers"), "dbgmemview", 0, this, TQT_SLOT(slotMemoryView()), actionCollection(), "debug_memview"); action->setToolTip( i18n("Debugger viewers") ); action->setWhatsThis(i18n("Debugger viewers

Various information about application being executed. There are 4 views available:
" "Memory
" "Disassemble
" "Registers
" "Libraries")); action = new TDEAction(i18n("Examine Core File..."), "application-x-core", 0, this, TQT_SLOT(slotExamineCore()), actionCollection(), "debug_core"); action->setToolTip( i18n("Examine core file") ); action->setWhatsThis( i18n("Examine core file

" "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 TDEAction(i18n("Attach to Process"), "connect_creating", 0, this, TQT_SLOT(slotAttachProcess()), actionCollection(), "debug_attach"); action->setToolTip( i18n("Attach to process") ); action->setWhatsThis(i18n("Attach to process

Attaches the debugger to a running process.")); action = new TDEAction(i18n("Toggle Breakpoint"), 0, 0, this, TQT_SLOT(toggleBreakpoint()), actionCollection(), "debug_toggle_breakpoint"); action->setToolTip(i18n("Toggle breakpoint")); action->setWhatsThis(i18n("Toggle breakpoint

Toggles the breakpoint at the current line in editor.")); connect( mainWindow()->main()->guiFactory(), TQT_SIGNAL(clientAdded(KXMLGUIClient*)), this, TQT_SLOT(guiClientAdded(KXMLGUIClient*)) ); connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)), this, TQT_SLOT(projectConfigWidget(KDialogBase*)) ); connect( partController(), TQT_SIGNAL(loadedFile(const KURL &)), gdbBreakpointWidget, TQT_SLOT(slotRefreshBP(const KURL &)) ); connect( debugger(), TQT_SIGNAL(toggledBreakpoint(const TQString &, int)), gdbBreakpointWidget, TQT_SLOT(slotToggleBreakpoint(const TQString &, int)) ); connect( debugger(), TQT_SIGNAL(editedBreakpoint(const TQString &, int)), gdbBreakpointWidget, TQT_SLOT(slotEditBreakpoint(const TQString &, int)) ); connect( debugger(), TQT_SIGNAL(toggledBreakpointEnabled(const TQString &, int)), gdbBreakpointWidget, TQT_SLOT(slotToggleBreakpointEnabled(const TQString &, int)) ); connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)), this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) ); connect( core(), TQT_SIGNAL(stopButtonClicked(KDevPlugin*)), this, TQT_SLOT(slotStop(KDevPlugin*)) ); connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) ); connect( partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(slotActivePartChanged(KParts::Part*)) ); procLineMaker = new ProcessLineMaker(); connect( procLineMaker, TQT_SIGNAL(receivedStdoutLine(const TQCString&)), appFrontend(), TQT_SLOT(insertStdoutLine(const TQCString&)) ); connect( procLineMaker, TQT_SIGNAL(receivedStderrLine(const TQCString&)), appFrontend(), TQT_SLOT(insertStderrLine(const TQCString&)) ); connect( procLineMaker, TQT_SIGNAL(receivedPartialStdoutLine(const TQCString&)), appFrontend(), TQT_SLOT(addPartialStdoutLine(const TQCString&))); connect( procLineMaker, TQT_SIGNAL(receivedPartialStderrLine(const TQCString&)), appFrontend(), TQT_SLOT(addPartialStderrLine(const TQCString&))); // 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, TQT_SIGNAL(tracingOutput(const char*)), procLineMaker, TQT_SLOT(slotReceivedStdout(const char*))); connect(partController(), TQT_SIGNAL(savedFile(const KURL &)), this, TQT_SLOT(slotFileSaved())); if (project()) connect(project(), TQT_SIGNAL(projectCompiled()), this, TQT_SLOT(slotProjectCompiled())); setupController(); TQTimer::singleShot(0, this, TQT_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(), TQT_SIGNAL(applicationRegistered(const TQCString&)), TQT_SLOT(slotDCOPApplicationRegistered(const TQCString&))); kapp->dcopClient()->setNotifications(true); } void DebuggerPart::slotDCOPApplicationRegistered(const TQCString& appId) { if (appId.find("drkonqi-") == 0) { TQByteArray answer; TQCString replyType; kapp->dcopClient()->call(appId, "krashinfo", "appName()", TQByteArray(), replyType, answer, true, 5000); TQDataStream d(answer, IO_ReadOnly); TQCString appName; d >> appName; if (appName.length() && project() && project()->mainProgram().endsWith(appName)) { kapp->dcopClient()->send(appId, "krashinfo", "registerDebuggingApplication(TQString)", i18n("Debug in &TDevelop")); connectDCOPSignal(appId, "krashinfo", "acceptDebuggingApplication()", "slotDebugExternalProcess()", true); } } } ASYNC DebuggerPart::slotDebugExternalProcess() { TQByteArray answer; TQCString replyType; kapp->dcopClient()->call(kapp->dcopClient()->senderId(), "krashinfo", "pid()", TQByteArray(), replyType, answer, true, 5000); TQDataStream d(answer, IO_ReadOnly); int pid; d >> pid; if (attachProcess(pid) && m_drkonqi.isEmpty()) { m_drkonqi = kapp->dcopClient()->senderId(); TQTimer::singleShot(15000, this, TQT_SLOT(slotCloseDrKonqi())); mainWindow()->raiseView(framestackWidget); } mainWindow()->main()->raise(); } ASYNC DebuggerPart::slotDebugCommandLine(const TQString& /*command*/) { KMessageBox::information(0, "Asked to debug command line"); } void DebuggerPart::slotCloseDrKonqi() { kapp->dcopClient()->send(m_drkonqi, "MainApplication-Interface", "quit()", TQByteArray()); 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( TQString("stopped") ); } void DebuggerPart::contextMenu(TQPopupMenu *popup, const Context *context) { if (!context->hasType( Context::EditorContext )) return; const EditorContext *econtext = static_cast(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 TQAction to popup menu in TQt3. TDEAction* act = actionCollection()->action("debug_runtocursor"); Q_ASSERT(act); if (act) { int id = popup->insertItem( act->iconSet(), i18n("Run to &Cursor"), this, TQT_SLOT(slotRunToCursor()), 0, -1, index); popup->setWhatsThis(id, act->whatsThis()); index += running; } } if (econtext->url().isLocalFile()) { int id = popup->insertItem( i18n("Toggle Breakpoint"), this, TQT_SLOT(toggleBreakpoint()), 0, -1, index); index += running; popup->setWhatsThis(id, i18n("Toggle breakpoint

Toggles breakpoint at the current line.")); } if (!m_contextIdent.isEmpty()) { TQString squeezed = KStringHandler::csqueeze(m_contextIdent, 30); int id = popup->insertItem( i18n("Evaluate: %1").arg(squeezed), this, TQT_SLOT(contextEvaluate()), 0, -1, index); index += running; popup->setWhatsThis(id, i18n("Evaluate expression

Shows the value of the expression under the cursor.")); int id2 = popup->insertItem( i18n("Watch: %1").arg(squeezed), this, TQT_SLOT(contextWatch()), 0, -1, index); index += running; popup->setWhatsThis(id2, i18n("Watch expression

Adds an expression under the cursor to the Variables/Watch list.")); } if (running) popup->insertSeparator(index); } void DebuggerPart::toggleBreakpoint() { KParts::ReadWritePart *rwpart = dynamic_cast(partController()->activePart()); KTextEditor::ViewCursorInterface *cursorIface = dynamic_cast(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) { TQVBox *vbox = dlg->addVBoxPage(i18n("Debugger"), i18n("Debugger"), BarIcon( info()->icon(), TDEIcon::SizeMedium) ); DebuggerConfigWidget *w = new DebuggerConfigWidget(this, vbox, "debugger config widget"); connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) ); connect( dlg, TQT_SIGNAL(finished()), controller, TQT_SLOT(configure()) ); } void DebuggerPart::setupController() { VariableTree *variableTree = variableWidget->varTree(); // variableTree -> gdbBreakpointWidget connect( variableTree, TQT_SIGNAL(toggleWatchpoint(const TQString &)), gdbBreakpointWidget, TQT_SLOT(slotToggleWatchpoint(const TQString &))); // gdbOutputWidget -> controller connect( gdbOutputWidget, TQT_SIGNAL(userGDBCmd(const TQString &)), controller, TQT_SLOT(slotUserGDBCmd(const TQString&))); connect( gdbOutputWidget, TQT_SIGNAL(breakInto()), controller, TQT_SLOT(slotBreakInto())); connect( controller, TQT_SIGNAL(breakpointHit(int)), gdbBreakpointWidget, TQT_SLOT(slotBreakpointHit(int))); // controller -> disassembleWidget connect( controller, TQT_SIGNAL(showStepInSource(const TQString&, int, const TQString&)), disassembleWidget, TQT_SLOT(slotShowStepInSource(const TQString&, int, const TQString&))); // controller -> this connect( controller, TQT_SIGNAL(dbgStatus(const TQString&, int)), this, TQT_SLOT(slotStatus(const TQString&, int))); connect( controller, TQT_SIGNAL(showStepInSource(const TQString&, int, const TQString&)), this, TQT_SLOT(slotShowStep(const TQString&, int))); connect( controller, TQT_SIGNAL(debuggerAbnormalExit()), this, TQT_SLOT(slotDebuggerAbnormalExit())); connect(controller, TQT_SIGNAL(event(GDBController::event_t)), this, TQT_SLOT(slotEvent(GDBController::event_t))); // controller -> procLineMaker connect( controller, TQT_SIGNAL(ttyStdout(const char*)), procLineMaker, TQT_SLOT(slotReceivedStdout(const char*))); connect( controller, TQT_SIGNAL(ttyStderr(const char*)), procLineMaker, TQT_SLOT(slotReceivedStderr(const char*))); // controller -> gdbOutputWidget connect( controller, TQT_SIGNAL(gdbInternalCommandStdout(const char*)), gdbOutputWidget, TQT_SLOT(slotInternalCommandStdout(const char*)) ); connect( controller, TQT_SIGNAL(gdbUserCommandStdout(const char*)), gdbOutputWidget, TQT_SLOT(slotUserCommandStdout(const char*)) ); connect( controller, TQT_SIGNAL(gdbStderr(const char*)), gdbOutputWidget, TQT_SLOT(slotReceivedStderr(const char*)) ); connect( controller, TQT_SIGNAL(dbgStatus(const TQString&, int)), gdbOutputWidget, TQT_SLOT(slotDbgStatus(const TQString&, int))); // controller -> viewerWidget connect( controller, TQT_SIGNAL(dbgStatus(const TQString&, int)), viewerWidget, TQT_SLOT(slotDebuggerState(const TQString&, int))); connect(statusBarIndicator, TQT_SIGNAL(doubleClicked()), controller, TQT_SLOT(explainDebuggerStatus())); } bool DebuggerPart::startDebugger() { TQString build_dir; // Currently selected build directory DomUtil::PairList run_envvars; // List with the environment variables TQString run_directory; // Directory from where the program should be run TQString program; // Absolute path to application TQString 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(); } TQString shell = DomUtil::readEntry(*projectDom(), "/kdevdebugger/general/dbgshell"); if( !shell.isEmpty() ) { shell = shell.simplifyWhiteSpace(); TQString shell_without_args = TQStringList::split(TQChar(' '), shell ).first(); TQFileInfo 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( TQString("active") ); TDEActionCollection *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 TQT_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); TDEActionCollection *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( TQString("stopped") ); core()->running(this, false); } void DebuggerPart::slotShowView(bool show) { const TQWidget* s = static_cast(sender()); TQWidget* ncs = const_cast(s); mainWindow()->setViewAvailable(ncs, show); if (show) mainWindow()->raiseView(ncs); } void DebuggerPart::slotDebuggerAbnormalExit() { mainWindow()->raiseView(gdbOutputWidget); KMessageBox::information( mainWindow()->main(), i18n("GDB exited abnormally" "

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, "" + i18n("Rebuild the project?") + "" + i18n("

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(TQT_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, TQT_SIGNAL(buildProject()), project(), TQT_SLOT(slotBuild()))) { connect(project(), TQT_SIGNAL(projectCompiled()), this, TQT_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(), TQT_SIGNAL(projectCompiled()), this, TQT_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 ) ) { TDEActionCollection *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); TQString dirName = project()? project()->projectDirectory() : TQDir::homeDirPath(); TQString 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(partController()->activePart()); KTextEditor::ViewCursorInterface *cursorIface = dynamic_cast(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(partController()->activePart()); KTextEditor::ViewCursorInterface *cursorIface = dynamic_cast(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(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 TQString &msg, int state) { TQString stateIndicator, stateIndicatorFull; if (state & s_dbgNotStarted) { stateIndicator = " "; stateIndicatorFull = "Debugger not started"; stateChanged( TQString("stopped") ); } else if (state & s_dbgBusy) { stateIndicator = "R"; stateIndicatorFull = "Debugger is busy"; stateChanged( TQString("active") ); } else if (state & s_programExited) { stateIndicator = "E"; stateIndicatorFull = "Application has exited"; stateChanged( TQString("stopped") ); } else { stateIndicator = "P"; stateIndicatorFull = "Application is paused"; stateChanged( TQString("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) { TDEActionCollection *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); TQToolTip::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 TQString &fileName, int lineNum) { if ( ! fileName.isEmpty() ) { // Debugger counts lines from 1 debugger()->gotoExecutionPoint(KURL( fileName ), lineNum-1); } else { debugger()->clearExecutionPoint(); } } void DebuggerPart::slotGotoSource(const TQString &fileName, int lineNum) { if ( ! fileName.isEmpty() ) partController()->editDocument(KURL( fileName ), lineNum); } void DebuggerPart::slotActivePartChanged( KParts::Part* part ) { TDEAction* action = actionCollection()->action("debug_toggle_breakpoint"); if(!action) return; if(!part) { action->setEnabled(false); return; } KTextEditor::ViewCursorInterface *iface = dynamic_cast(part->widget()); action->setEnabled( iface != 0 ); } void DebuggerPart::restorePartialProjectSession(const TQDomElement* el) { gdbBreakpointWidget->restorePartialProjectSession(el); gdbOutputWidget->restorePartialProjectSession(el); } void DebuggerPart::savePartialProjectSession(TQDomElement* 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("TDevelop/AppFrontend"); } KDevDebugger * GDBDebugger::DebuggerPart::debugger() { return m_debugger; } #include "debuggerpart.moc"