diff --git a/debuggers/common/widgets/debuggerconsoleview.cpp b/debuggers/common/widgets/debuggerconsoleview.cpp index 6c83c71064..7071bab168 100644 --- a/debuggers/common/widgets/debuggerconsoleview.cpp +++ b/debuggers/common/widgets/debuggerconsoleview.cpp @@ -1,400 +1,408 @@ /* * Debugger Console View * * Copyright 2003 John Birch * Copyright 2006 Vladimir Prus * Copyright 2007 Hamish Rodda * Copyright 2016 Aetf * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 General Public License * along with this program. If not, see . * */ #include "debuggerconsoleview.h" #include "debuglog.h" #include "midebuggerplugin.h" #include "midebugsession.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevMI; DebuggerConsoleView::DebuggerConsoleView(MIDebuggerPlugin *plugin, QWidget *parent) : QWidget(parent) , m_repeatLastCommand(false) , m_showInternalCommands(false) , m_cmdEditorHadFocus(false) , m_maxLines(5000) { setWindowIcon(QIcon::fromTheme("dialog-scripts")); setWindowTitle(i18n("Debugger Console")); setWhatsThis(i18n("Debugger Console

" "Shows all debugger commands being executed. " "You can also issue any other debugger command while debugging.

")); setupUi(); m_actRepeat = new QAction(QIcon::fromTheme("edit-redo"), i18n("Repeat last command when hit Return"), this); m_actRepeat->setCheckable(true); m_actRepeat->setChecked(m_repeatLastCommand); connect(m_actRepeat, &QAction::toggled, this, &DebuggerConsoleView::toggleRepeat); m_toolBar->insertAction(m_actCmdEditor, m_actRepeat); m_actInterrupt = new QAction(QIcon::fromTheme("media-playback-pause"), i18n("Pause execution of the app to enter gdb commands"), this); connect(m_actInterrupt, &QAction::triggered, this, &DebuggerConsoleView::interruptDebugger); m_toolBar->insertAction(m_actCmdEditor, m_actInterrupt); setShowInterrupt(true); m_actShowInternal = new QAction(i18n("Show Internal Commands"), this); m_actShowInternal->setCheckable(true); m_actShowInternal->setChecked(m_showInternalCommands); m_actShowInternal->setWhatsThis(i18n( "Controls if commands issued internally by KDevelop " "will be shown or not.
" "This option will affect only future commands, it will not " "add or remove already issued commands from the view.")); connect(m_actShowInternal, &QAction::toggled, this, &DebuggerConsoleView::toggleShowInternalCommands); handleDebuggerStateChange(s_none, s_dbgNotStarted); m_updateTimer.setSingleShot(true); connect(&m_updateTimer, &QTimer::timeout, this, &DebuggerConsoleView::flushPending); connect(plugin->core()->debugController(), &KDevelop::IDebugController::currentSessionChanged, this, &DebuggerConsoleView::handleSessionChanged); connect(plugin, &MIDebuggerPlugin::reset, this, &DebuggerConsoleView::clear); connect(plugin, &MIDebuggerPlugin::raiseDebuggerConsoleViews, this, &DebuggerConsoleView::requestRaise); handleSessionChanged(plugin->core()->debugController()->currentSession()); updateColors(); } void DebuggerConsoleView::changeEvent(QEvent *event) { if (event->type() == QEvent::PaletteChange) { updateColors(); } } void DebuggerConsoleView::updateColors() { KColorScheme scheme(QPalette::Active); m_stdColor = scheme.foreground(KColorScheme::LinkText).color(); m_errorColor = scheme.foreground(KColorScheme::NegativeText).color(); } void DebuggerConsoleView::setupUi() { setupToolBar(); m_textView = new QTextEdit; m_textView->setReadOnly(true); m_textView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_textView, &QTextEdit::customContextMenuRequested, this, &DebuggerConsoleView::showContextMenu); auto vbox = new QVBoxLayout; vbox->addWidget(m_textView); vbox->addWidget(m_toolBar); setLayout(vbox); m_cmdEditor = new KHistoryComboBox(this); m_cmdEditor->setDuplicatesEnabled(false); connect(m_cmdEditor, static_cast(&KHistoryComboBox::returnPressed), this, &DebuggerConsoleView::trySendCommand); auto label = new QLabel(i18n("&Command:"), this); label->setBuddy(m_cmdEditor); auto hbox = new QHBoxLayout; hbox->addWidget(label); hbox->addWidget(m_cmdEditor); hbox->setStretchFactor(m_cmdEditor, 1); hbox->setContentsMargins(0, 0, 0, 0); auto cmdEditor = new QWidget(this); cmdEditor->setLayout(hbox); m_actCmdEditor = m_toolBar->addWidget(cmdEditor); } void DebuggerConsoleView::setupToolBar() { m_toolBar = new QToolBar(this); int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); m_toolBar->setIconSize(QSize(iconSize, iconSize)); m_toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); m_toolBar->setFloatable(false); m_toolBar->setMovable(false); m_toolBar->setWindowTitle(i18n("%1 Command Bar", windowTitle())); m_toolBar->setContextMenuPolicy(Qt::PreventContextMenu); // remove margins, to make command editor nicely aligned with the output m_toolBar->layout()->setContentsMargins(0, 0, 0, 0); } void DebuggerConsoleView::focusInEvent(QFocusEvent*) { m_textView->verticalScrollBar()->setValue(m_textView->verticalScrollBar()->maximum()); m_cmdEditor->setFocus(); } DebuggerConsoleView::~DebuggerConsoleView() { } void DebuggerConsoleView::setShowInterrupt(bool enable) { m_actInterrupt->setVisible(enable); } +void DebuggerConsoleView::setReplacePrompt(const QString& prompt) +{ + m_alterPrompt = prompt; +} + void DebuggerConsoleView::setShowInternalCommands(bool enable) { if (enable != m_showInternalCommands) { m_showInternalCommands = enable; // Set of strings to show changes, text edit still has old // set. Refresh. m_textView->clear(); QStringList& newList = m_showInternalCommands ? m_allOutput : m_userOutput; for (const auto &line : newList) { // Note that color formatting is already applied to 'line'. appendLine(line); } } } void DebuggerConsoleView::showContextMenu(const QPoint &pos) { QScopedPointer popup(m_textView->createStandardContextMenu(pos)); popup->addSeparator(); popup->addAction(m_actShowInternal); popup->exec(m_textView->mapToGlobal(pos)); } void DebuggerConsoleView::toggleRepeat(bool checked) { m_repeatLastCommand = checked; } void DebuggerConsoleView::toggleShowInternalCommands(bool checked) { setShowInternalCommands(checked); } void DebuggerConsoleView::appendLine(const QString& line) { m_pendingOutput += line; // To improve performance, we update the view after some delay. if (!m_updateTimer.isActive()) { m_updateTimer.start(100); } } void DebuggerConsoleView::flushPending() { m_textView->setUpdatesEnabled(false); QTextDocument *document = m_textView->document(); QTextCursor cursor(document); cursor.movePosition(QTextCursor::End); cursor.insertHtml(m_pendingOutput); m_pendingOutput.clear(); m_textView->verticalScrollBar()->setValue(m_textView->verticalScrollBar()->maximum()); m_textView->setUpdatesEnabled(true); m_textView->update(); if (m_cmdEditorHadFocus) { m_cmdEditor->setFocus(); } } void DebuggerConsoleView::clear() { if (m_textView) m_textView->clear(); if (m_cmdEditor) m_cmdEditor->clear(); m_userOutput.clear(); m_allOutput.clear(); } void DebuggerConsoleView::handleDebuggerStateChange(DBGStateFlags, DBGStateFlags newStatus) { if (newStatus & s_dbgNotStarted) { m_actInterrupt->setEnabled(false); m_cmdEditor->setEnabled(false); return; } else { m_actInterrupt->setEnabled(true); } if (newStatus & s_dbgBusy) { if (m_cmdEditor->isEnabled()) { m_cmdEditorHadFocus = m_cmdEditor->hasFocus(); } m_cmdEditor->setEnabled(false); } else { m_cmdEditor->setEnabled(true); } } QString DebuggerConsoleView::toHtmlEscaped(QString text) { text = text.toHtmlEscaped(); text.replace('\n', "
"); return text; } QString DebuggerConsoleView::colorify(QString text, const QColor& color) { text = "" + text + ""; return text; } void DebuggerConsoleView::receivedInternalCommandStdout(const QString& line) { receivedStdout(line, true); } void DebuggerConsoleView::receivedUserCommandStdout(const QString& line) { receivedStdout(line, false); } void DebuggerConsoleView::receivedStdout(const QString& line, bool internal) { QString colored = toHtmlEscaped(line); if (colored.startsWith("(gdb)")) { + if (!m_alterPrompt.isEmpty()) { + colored.replace(0, 5, m_alterPrompt); + } colored = colorify(colored, m_stdColor); } m_allOutput.append(colored); trimList(m_allOutput, m_maxLines); if (!internal) { m_userOutput.append(colored); trimList(m_userOutput, m_maxLines); } if (!internal || m_showInternalCommands) appendLine(colored); } void DebuggerConsoleView::receivedStderr(const QString& line) { QString colored = toHtmlEscaped(line); colored = colorify(colored, m_errorColor); // Errors are shown inside user commands too. m_allOutput.append(colored); trimList(m_allOutput, m_maxLines); m_userOutput.append(colored); trimList(m_userOutput, m_maxLines); appendLine(colored); } void DebuggerConsoleView::trimList(QStringList& l, int max_size) { int length = l.count(); if (length > max_size) { for(int to_delete = length - max_size; to_delete; --to_delete) { l.erase(l.begin()); } } } void DebuggerConsoleView::trySendCommand(QString cmd) { if (m_repeatLastCommand && cmd.isEmpty()) { cmd = m_cmdEditor->historyItems().last(); } if (!cmd.isEmpty()) { m_cmdEditor->addToHistory(cmd); m_cmdEditor->clearEditText(); emit sendCommand(cmd); } } void DebuggerConsoleView::handleSessionChanged(KDevelop::IDebugSession* s) { MIDebugSession *session = qobject_cast(s); if (!session) return; connect(this, &DebuggerConsoleView::sendCommand, session, &MIDebugSession::addUserCommand); connect(this, &DebuggerConsoleView::interruptDebugger, session, &MIDebugSession::interruptDebugger); connect(session, &MIDebugSession::debuggerInternalCommandOutput, this, &DebuggerConsoleView::receivedInternalCommandStdout); connect(session, &MIDebugSession::debuggerUserCommandOutput, this, &DebuggerConsoleView::receivedUserCommandStdout); connect(session, &MIDebugSession::debuggerInternalOutput, this, &DebuggerConsoleView::receivedStderr); connect(session, &MIDebugSession::debuggerStateChanged, this, &DebuggerConsoleView::handleDebuggerStateChange); handleDebuggerStateChange(s_none, session->debuggerState()); } diff --git a/debuggers/common/widgets/debuggerconsoleview.h b/debuggers/common/widgets/debuggerconsoleview.h index f8ef6de369..8dc0175516 100644 --- a/debuggers/common/widgets/debuggerconsoleview.h +++ b/debuggers/common/widgets/debuggerconsoleview.h @@ -1,157 +1,165 @@ /* * Debugger Console View * * Copyright 2003 John Birch * Copyright 2007 Hamish Rodda * Copyright 2016 Aetf * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 General Public License * along with this program. If not, see . * */ #ifndef DEBUGGERCONSOLEVIEW_H #define DEBUGGERCONSOLEVIEW_H #include #include #include #include #include "dbgglobal.h" class QMenu; class QTextEdit; class QToolBar; class KHistoryComboBox; namespace KDevelop { class IDebugSession; } namespace KDevMI { class MIDebuggerPlugin; /** * @brief A debugger console gives the user direct access to the debugger command line interface. */ class DebuggerConsoleView : public QWidget { Q_OBJECT public: DebuggerConsoleView(MIDebuggerPlugin *plugin, QWidget *parent = nullptr); ~DebuggerConsoleView(); /** * Whether show a button allowing user to interrput debugger execution. */ void setShowInterrupt(bool enable); + /** + * If set to a nonempty string, the default "(gdb)" prompt will be replaced. + * This only affects output lines after the call. + */ + void setReplacePrompt(const QString &prompt); + void setShowInternalCommands(bool enable); Q_SIGNALS: void requestRaise(); /** * Proxy signals for DebugSession */ void interruptDebugger(); void sendCommand(const QString &cmd); protected: void setupUi(); void setupToolBar(); /** * Arranges for 'line' to be shown to the user. * Adds 'line' to m_pendingOutput and makes sure * m_updateTimer is running. */ void appendLine(const QString &line); void updateColors(); /** * escape html meta characters and handle line break */ QString toHtmlEscaped(QString text); QString colorify(QString text, const QColor &color); /** * Makes 'l' no longer than 'max_size' by * removing excessive elements from the top. */ void trimList(QStringList& l, int max_size); void changeEvent(QEvent *e) override; void focusInEvent(QFocusEvent *e) override; protected Q_SLOTS: void showContextMenu(const QPoint &pos); void toggleRepeat(bool checked); void toggleShowInternalCommands(bool checked); void flushPending(); void clear(); void handleSessionChanged(KDevelop::IDebugSession *session); void handleDebuggerStateChange(DBGStateFlags oldStatus, DBGStateFlags newStatus); void receivedInternalCommandStdout(const QString &line); void receivedUserCommandStdout(const QString &line); void receivedStdout(const QString &line, bool internal); void receivedStderr(const QString &line); void trySendCommand(QString cmd); private: QAction *m_actRepeat; QAction *m_actInterrupt; QAction *m_actShowInternal; QAction *m_actCmdEditor; QTextEdit *m_textView; QMenu *m_contextMenu; QToolBar *m_toolBar; KHistoryComboBox *m_cmdEditor; bool m_repeatLastCommand; bool m_showInternalCommands; bool m_cmdEditorHadFocus; /** * 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 m_allOutput; QStringList m_userOutput; /** * For performance reasons, we don't immediately add new text * to QTExtEdit. Instead we add it to m_pendingOutput and * flush it on timer. */ QString m_pendingOutput; QTimer m_updateTimer; QColor m_stdColor; QColor m_errorColor; int m_maxLines; + + QString m_alterPrompt; }; } // end of namespace KDevMI #endif // DEBUGGERCONSOLEVIEW_H diff --git a/debuggers/lldb/TODO.txt b/debuggers/lldb/TODO.txt index 9c00fe8624..3197dca2d2 100644 --- a/debuggers/lldb/TODO.txt +++ b/debuggers/lldb/TODO.txt @@ -1,145 +1,145 @@ - [DONE] Correct signal handling commands for lldb - [DONE] Correct environment variable setting commands for lldb - [DONE] Port DebugJob - [DONE] Port exam core and attach process to KJob - [DONE] Move DebuggerPlugin to MIDebuggerPlugin - [DONE] Verify how unit test works in test_gdb - [DONE] Create minimal unit test cases to be able to test debugger and debugsession + [DONE] can break on start + [DONE] environment var successfully set + Check if environment-cd works in remote debugging + [DONE] Check if lldb supports unicode correctly * [DONE] a program output in encoding other than utf8 and different from host system default + Make examine core test working with coredumpctl - [DONE] Verify MIParser/MILexer can handle output without any stream prefix: no they can't - [DONE] Make config options in config page actually working - [DONE] Remove duplicate breakpoint: look at breakpointController()->setDeleteDuplicateBreakpoints(true) - [DONE] Rework interrupt action - [DONE] (only workaround) Variables view doesn't update when variable changes - Add lldb actions for attach to process and examine core file - Clean up extra actions provided by gdb/lldb plugins * Global launch configuration for attach to process and examine core file - LLDB data formatters * [DONE] show summary for const char [] * Qt types + [DONE] QString, QChar + [DONE] QByteArray + [DONE] QList, QStringList, QQueue + [DONE] QVector, QStack + [DONE] QLinkedList + [DONE] QMap, QMultiMap + [DONE] QHash, QMultiHash + [DONE] QSet + [DONE] QDate, QTime, QDateTime + [DONE] QUrl + [DONE] QUuid * KDE types + [DONE] KDevelop::Path + [DONE] KTextEditor::Cursor + [DONE] KTextEditor::Range - Finish unit tests for LLDB data formatters - [DONE] Show application exit reason in the Debug View - Clean up tool views * register views + controller + tool view * disassembly widget * memory view - Qt data formatter cause hangs data size is too large * use dynamic caching - [DONE] Find a way to avoid duplicate tool views for GDB and LLDB plugin - Polish debugger console * [DONE] user command output regarded as internal command * [DONE] not correctly raised when starting debug - * correct prompt (from "(gdb)" to "(lldb)") + * [DONE] correct prompt (from "(gdb)" to "(lldb)") - [DONE] Handle error sometime with Command 'exec-run'. Invalid process during debug session. - An unified way to report error - File bug to lldb-mi * HIGH PRIORITY: -break-insert + [SUBMITED] pending breakpoints set with '-f' not got resolved after file loaded - https://llvm.org/bugs/show_bug.cgi?id=28702 + [SUBMITED] pending breakpoints '-f' can only be last flag switch - https://llvm.org/bugs/show_bug.cgi?id=28698 + [SUBMITED] create disabled breakpoint with '-d' not working when combined with '-f' - https://llvm.org/bugs/show_bug.cgi?id=28703 + [SUBMITED] -break-enable has no effect - https://llvm.org/bugs/show_bug.cgi?id=28857 * [SUBMITED] breakpoint hit doesn't generate corresponding breakpoint-modified notification - https://llvm.org/bugs/show_bug.cgi?id=28860 (needed to update hitCount) * -break-watch command not supported + use raw cli command 'break set var' doesn't provides MI response * when hit watch point, nothing is output, which confuses the controller * thread-info returns malformated result + there should be only one 'frame' key for each thread in the list + [FIXED] there shoule be a current-thread-id field (Fixed at least in revision 265858) * [FIXED] lldb-mi crashes when break on a point where multiple threads running. (Fixed at least in revision 265858) * var-update doesn't support * as variable name * can't have space in environment cd * -inferior-tty-set only has dummy implementation * 'process launch' doesn't provide thread-group-started notification * [SUBMITED] sliently stop when attaching to process, which confuses the controller - https://llvm.org/bugs/show_bug.cgi?id=28858 * [SUBMITED] sliently stop when request stop at start - https://llvm.org/bugs/show_bug.cgi?id=25000 * [SUBMITED] [PATCHED] cli output not wrapped in console stream record * [SUBMITED][PATCHED] -data-disassemble start and end address parameter doesn't accept expressions - https://llvm.org/bugs/show_bug.cgi?id=28859 * -gdb-set not fully implemented + environment + [SUBMITED] [PATCHED] disassembly-flavor - https://llvm.org/bugs/show_bug.cgi?id=28718 * -gdb-show not fully implemented + [SUBMITED] [PATCHED] disassembly-flavor - https://llvm.org/bugs/show_bug.cgi?id=28718 * -data-list-register-values output format doesn't conform to spec * [SUBMITED] -stack-list-locals shows empty list - https://llvm.org/bugs/show_bug.cgi?id=28621 * File bug to lldb-mi for other missing commands - Fix TODOs in files - Change test_gdb to avoid direct use of QTest::qWait, which starts event loop, and could cause session to be deleted. Use WAIT_FOR_STATE_AND_IDLE(session, ) instead. Or use QPointer for session. - Known issues * Debugger console + debugger CLI stdout isn't shown, due to bug https://llvm.org/bugs/show_bug.cgi?id=28026 * Remote debugging + When using 'lldb-server gdbserver' as remote server, server exits once debug session ended. + When using 'gdbserver' as remote server - Remote work path can't contain space - Can't actually start inferior * Breakpoints + Pending breakpoints doesn't work. (Break on start now works even without pending breakpoint support) - Can still manually set pending breakpoints + Breakpoint hit count is not updated timely (limitation in lldb-mi) + No watchpoint support - Can still manually add watch point * Threads + lldb-mi crashes when break on a point where multiple threads running. (Fixed in latest lldb trunk version) * Attach to process + works internally, but there's no way to access it in the UI currently. relavent lldb settings: disassembly-format thread-format frame-format diff --git a/debuggers/lldb/debuggerplugin.h b/debuggers/lldb/debuggerplugin.h index bd316da648..e1b86f041c 100644 --- a/debuggers/lldb/debuggerplugin.h +++ b/debuggers/lldb/debuggerplugin.h @@ -1,66 +1,67 @@ /* * LLDB Debugger Support * Copyright 2016 Aetf * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 General Public License * along with this program. If not, see . * */ #ifndef LLDB_DEBUGGERPLUGIN_H #define LLDB_DEBUGGERPLUGIN_H #include "midebuggerplugin.h" #include "debugsession.h" #include "widgets/debuggerconsoleview.h" #include "widgets/disassemblewidget.h" namespace KDevMI { namespace LLDB { class NonInterruptDebuggerConsoleView : public DebuggerConsoleView { public: NonInterruptDebuggerConsoleView(MIDebuggerPlugin *plugin, QWidget *parent = nullptr) : DebuggerConsoleView(plugin, parent) { setShowInterrupt(false); + setReplacePrompt("(lldb)"); } }; class LldbDebuggerPlugin : public MIDebuggerPlugin { Q_OBJECT public: friend class KDevMI::LLDB::DebugSession; LldbDebuggerPlugin(QObject *parent, const QVariantList & = QVariantList()); ~LldbDebuggerPlugin() override; DebugSession *createSession() override; void unloadToolviews() override; void setupToolviews() override; private: DebuggerToolFactory *m_consoleFactory; DebuggerToolFactory *m_disassembleFactory; }; } // end of namespace LLDB } // end of namespace KDevMI #endif // LLDB_DEBUGGERPLUGIN_H