diff --git a/kdbg/CMakeLists.txt b/kdbg/CMakeLists.txt index a976c5a..c5559b5 100644 --- a/kdbg/CMakeLists.txt +++ b/kdbg/CMakeLists.txt @@ -67,6 +67,7 @@ set(kdbg_SRCS prefmisc.cpp pgmsettings.cpp watchwindow.cpp + srcfileswindow.cpp dbgmainwnd.cpp main.cpp ) diff --git a/kdbg/dbgdriver.h b/kdbg/dbgdriver.h index de41931..899b096 100644 --- a/kdbg/dbgdriver.h +++ b/kdbg/dbgdriver.h @@ -55,6 +55,7 @@ enum DbgCommand { DCexamine, DCinfoline, DCinfotarget, + DCinfosources, DCdisassemble, DCsetdisassflavor, DCsetargs, @@ -545,6 +546,11 @@ class DebuggerDriver : public QProcess */ virtual QString parseInfoTarget(const char* output) = 0; + /** + * Parse the output of the DCinfosources command. + */ + virtual QString parseInfoSources(const char* output) = 0; + /** * Parses the ouput of the DCdisassemble command. */ diff --git a/kdbg/dbgmainwnd.cpp b/kdbg/dbgmainwnd.cpp index 75b8bc9..2b3fa98 100644 --- a/kdbg/dbgmainwnd.cpp +++ b/kdbg/dbgmainwnd.cpp @@ -40,6 +40,7 @@ #include "memwindow.h" #include "ttywnd.h" #include "watchwindow.h" +#include "srcfileswindow.h" #include "procattach.h" #include "prefdebugger.h" #include "prefmisc.h" @@ -100,6 +101,9 @@ DebuggerMainWnd::DebuggerMainWnd() : QDockWidget* dw8 = createDockWidget("Memory", i18n("Memory")); m_memoryWindow = new MemoryWindow(dw8); dw8->setWidget(m_memoryWindow); + QDockWidget* dw9 = createDockWidget("SrcFiles", i18n("Source Files")); + m_srcFiles = new SrcFilesWindow(dw9); + dw9->setWidget(m_srcFiles); m_debugger = new KDebugger(this, m_localVariables, m_watches->watchVariables(), m_btWindow); @@ -178,6 +182,12 @@ DebuggerMainWnd::DebuggerMainWnd() : connect(m_localVariables, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotLocalsPopup(const QPoint&))); + // connect src files to src files window + connect(m_debugger, SIGNAL(sourceFiles(QString&)), + m_srcFiles, SLOT(sourceFiles(QString&))); + connect(m_srcFiles, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)), + m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&))); + makeDefaultLayout(); setupGUI(KXmlGuiWindow::Default, "kdbgui.rc"); restoreSettings(KSharedConfig::openConfig()); @@ -206,6 +216,7 @@ DebuggerMainWnd::~DebuggerMainWnd() delete m_localVariables; delete m_btWindow; delete m_filesWindow; + delete m_srcFiles; delete m_outputTermProc; } @@ -286,7 +297,8 @@ void DebuggerMainWnd::initKAction() { m_bpTable, "view_breakpoints", &m_bpTableAction }, { m_threads, "view_threads", &m_threadsAction }, { m_ttyWindow, "view_output", &m_ttyWindowAction }, - { m_memoryWindow, "view_memory", &m_memoryWindowAction } + { m_memoryWindow, "view_memory", &m_memoryWindowAction }, + { m_srcFiles, "view_srcfiles", &m_srcFilesAction}, }; for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) { QDockWidget* d = dockParent(dw[i].w); @@ -628,6 +640,7 @@ void DebuggerMainWnd::makeDefaultLayout() tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow)); tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow)); tabifyDockWidget(dockParent(m_threads), dockParent(m_watches)); + tabifyDockWidget(dockParent(m_watches), dockParent(m_srcFiles)); dockParent(m_localVariables)->setVisible(true); dockParent(m_ttyWindow)->setVisible(true); dockParent(m_watches)->setVisible(true); diff --git a/kdbg/dbgmainwnd.h b/kdbg/dbgmainwnd.h index a1e4d6f..6512ad8 100644 --- a/kdbg/dbgmainwnd.h +++ b/kdbg/dbgmainwnd.h @@ -27,6 +27,7 @@ class ThreadList; class MemoryWindow; class TTYWindow; class WatchWindow; +class SrcFilesWindow; class KDebugger; class DebuggerDriver; struct DbgAddr; @@ -76,6 +77,7 @@ class DebuggerMainWnd : public KXmlGuiWindow TTYWindow* m_ttyWindow; ThreadList* m_threads; MemoryWindow* m_memoryWindow; + SrcFilesWindow* m_srcFiles; QTimer m_backTimer; @@ -95,6 +97,7 @@ class DebuggerMainWnd : public KXmlGuiWindow QAction* m_ttyWindowAction; QAction* m_threadsAction; QAction* m_memoryWindowAction; + QAction* m_srcFilesAction; QAction* m_runAction; QAction* m_stepIntoAction; QAction* m_stepOverAction; diff --git a/kdbg/debugger.cpp b/kdbg/debugger.cpp index 74d6715..fae0fc2 100644 --- a/kdbg/debugger.cpp +++ b/kdbg/debugger.cpp @@ -180,6 +180,7 @@ bool KDebugger::debugProgram(const QString& name, m_executable = name; m_d->executeCmd(DCinfotarget); + m_d->executeCmd(DCinfosources); // set remote target if (!m_remoteDevice.isEmpty()) { @@ -1124,6 +1125,9 @@ void KDebugger::parse(CmdQueueItem* cmd, const char* output) case DCinfotarget: handleInfoTarget(output); break; + case DCinfosources: + handleInfoSources(output); + break; case DCdisassemble: handleDisassemble(cmd, output); break; @@ -2190,6 +2194,12 @@ void KDebugger::handleInfoTarget(const char* output) m_cpuTarget = m_d->parseInfoTarget(output); } +void KDebugger::handleInfoSources(const char* output) +{ + QString s = m_d->parseInfoSources(output); + emit sourceFiles(s); +} + void KDebugger::handleDisassemble(CmdQueueItem* cmd, const char* output) { emit disassembled(cmd->m_fileName, cmd->m_lineNo, diff --git a/kdbg/debugger.h b/kdbg/debugger.h index 80b6f71..4dd98fa 100644 --- a/kdbg/debugger.h +++ b/kdbg/debugger.h @@ -406,6 +406,7 @@ protected slots: void handleMemoryDump(const char* output); void handleInfoLine(CmdQueueItem* cmd, const char* output); void handleInfoTarget(const char* output); + void handleInfoSources(const char* output); void handleDisassemble(CmdQueueItem* cmd, const char* output); void handleThreadList(const char* output); void handleSetPC(const char* output); @@ -590,6 +591,11 @@ public slots: */ void restoreProgramSpecific(KConfigBase* config); + /** + * Gives source files to tree view + */ + void sourceFiles(QString &); + protected: ExprWnd& m_localVariables; ExprWnd& m_watchVariables; diff --git a/kdbg/gdbdriver.cpp b/kdbg/gdbdriver.cpp index f43fb27..aa71f82 100644 --- a/kdbg/gdbdriver.cpp +++ b/kdbg/gdbdriver.cpp @@ -80,6 +80,7 @@ static GdbCmdInfo cmds[] = { { DCexamine, "x %s %s\n", GdbCmdInfo::argString2 }, { DCinfoline, "info line %s:%d\n", GdbCmdInfo::argStringNum }, { DCinfotarget, "info target\n", GdbCmdInfo::argNone}, + { DCinfosources, "info sources\n", GdbCmdInfo::argNone}, { DCdisassemble, "disassemble %s %s\n", GdbCmdInfo::argString2 }, { DCsetdisassflavor, "set disassembly-flavor %s\n", GdbCmdInfo::argString}, { DCsetargs, "set args %s\n", GdbCmdInfo::argString }, @@ -2593,6 +2594,21 @@ QString GdbDriver::parseInfoTarget(const char* output) return {}; } +QString GdbDriver::parseInfoSources(const char* output) +{ + auto p1 = strstr(output, "read in:\n"); + if (p1) { + p1 += 9; + auto p2 = strstr(p1, "\nSource files"); + if (p2) { + auto p3 = strstr(p1, "on demand:\n"); + p3 += 11; + return QString::fromUtf8(p1, p2 - p1) + QString(p3); + } + } + return {}; +} + std::list GdbDriver::parseDisassemble(const char* output) { std::list code; diff --git a/kdbg/gdbdriver.h b/kdbg/gdbdriver.h index 4beb1ba..909e1e2 100644 --- a/kdbg/gdbdriver.h +++ b/kdbg/gdbdriver.h @@ -50,6 +50,7 @@ class GdbDriver : public DebuggerDriver bool parseInfoLine(const char* output, QString& addrFrom, QString& addrTo) override; QString parseInfoTarget(const char* output) override; + QString parseInfoSources(const char* output) override; std::list parseDisassemble(const char* output) override; QString parseMemoryDump(const char* output, std::list& memdump) override; QString parseSetVariable(const char* output) override; diff --git a/kdbg/kdbgui.rc b/kdbg/kdbgui.rc index 2f2dd4b..5d61e62 100644 --- a/kdbg/kdbgui.rc +++ b/kdbg/kdbgui.rc @@ -21,6 +21,7 @@ + E&xecution diff --git a/kdbg/srcfileswindow.cpp b/kdbg/srcfileswindow.cpp new file mode 100644 index 0000000..9a9cc39 --- /dev/null +++ b/kdbg/srcfileswindow.cpp @@ -0,0 +1,199 @@ +/* + * Copyright Johannes Sixt + * This file is licensed under the GNU General Public License Version 2. + * See the file COPYING in the toplevel directory of the source directory. + */ +#include + +#include "srcfileswindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debugger.h" + +const int COL_ADDR = 0; +const int COL_DUMP_ASCII = 9; +const int MAX_COL = 10; + +QMimeDatabase mime_database; + +class SrcFilesSortProxyModel : public QSortFilterProxyModel { +public: + SrcFilesSortProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {} + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override { + QVariant leftData = sourceModel()->data(left); + QVariant rightData = sourceModel()->data(right); + + // Custom sorting logic + QString leftText = leftData.toString(); + QString rightText = rightData.toString(); + + // Example: Sort directories first + bool leftIsDir = left.model()->hasChildren(left); + bool rightIsDir = right.model()->hasChildren(right); + + if (leftIsDir != rightIsDir) { + return leftIsDir < rightIsDir; // Directories first + } + + return leftText.toLower() > rightText.toLower(); // Default alphabetic sorting + } +}; + +QString getCommonPath(const QStringList &paths) { + if (paths.isEmpty()) { + return QString(); + } + + QString common = QFileInfo(paths[0]).absolutePath(); + for (const QString &path : paths) { + QFileInfo fileInfo(path); + QString absolutePath = fileInfo.absolutePath(); + while (!absolutePath.startsWith(common)) { + common = QFileInfo(common).absolutePath(); + common = common.left(common.lastIndexOf(QDir::separator())); + if (common.isEmpty()) { + return QString(); + } + } + } + return common; +} + +QIcon iconForFilename(const QString &filename) +{ + QIcon icon; + QList mime_types = mime_database.mimeTypesForFileName(filename); + for (int i=0; i < mime_types.count() && icon.isNull(); i++) + icon = QIcon::fromTheme(mime_types[i].iconName()); + + if (icon.isNull()) { + icon = QIcon::fromTheme("file"); + } + return icon; +} + +void SrcFilesWindow::fileClicked(const QModelIndex &index) +{ + QModelIndex sourceIndex = dynamic_cast(m_srctree.model())->mapToSource(index); + QStandardItem *item = m_srcfiles_model.itemFromIndex(sourceIndex); + if (!item->data().isNull()) { + qDebug() << item->data(); + emit activateFileLine(item->data().toString(), 0, DbgAddr()); + } +} + +SrcFilesWindow::SrcFilesWindow(QWidget* parent) : + QWidget(parent), + m_srcfiles_model(this), + m_srctree(this), + m_layout(QBoxLayout::TopToBottom, this) +{ + SrcFilesSortProxyModel *proxyModel = new SrcFilesSortProxyModel(this); + m_srctree.setHeaderHidden(true); + proxyModel->setSourceModel(&m_srcfiles_model); + m_srctree.setModel(proxyModel); + m_srctree.setSortingEnabled(true); + connect(&m_srctree, &QTreeView::clicked, this, &SrcFilesWindow::fileClicked); + m_layout.addWidget(&m_srctree, 0); + m_layout.activate(); +} + +SrcFilesWindow::~SrcFilesWindow() +{ +} + + +void SrcFilesWindow::sourceFiles(QString &files) { + QStandardItem *item; + QStringList filenamesRaw = files.split(","); + QStringList fileNames; + + m_srcfiles_model.clear(); + + /* Sanitize filenames and skip system libraries */ + for (const auto& f: filenamesRaw) { + QString filename = f.trimmed(); + if (filename.endsWith("", Qt::CaseSensitive) || + filename.startsWith("/usr/", Qt::CaseSensitive) || + filename.startsWith("/lib/", Qt::CaseSensitive) || + filename.contains("/.cargo/", Qt::CaseSensitive) || + filename.startsWith("/rustc/", Qt::CaseSensitive) || + filename.startsWith("/cargo/", Qt::CaseSensitive)) { + continue; + } + fileNames.append(filename); + } + + /* Get common path to remove it */ + QString commonPath = getCommonPath(fileNames); + + + QMap mapTree; + for (const auto& f: fileNames) { + QFileInfo fi(f); + QStringList pathList = fi.path().remove(commonPath).split(QDir::separator()); + QStandardItem *parentItem = m_srcfiles_model.invisibleRootItem(); + + /* Create folder and files tree from file paths a/b/c/f/f1.c a/b/e/f2.c + * |---a/ + * |---b/ + * |---c/ + * |---f/ + * |---f1.c + * |---e/ + * |---f2.c + * + * Removing common path + * + * |---b/ + * |---c/ + * |---f/ + * |---f1.c + * |---e/ + * |---f2.c + */ + + /* TODO check if file exists in file system???? should exists... */ + for (int i = 0; i < pathList.size(); i++) { + auto p = fi.path().section(QDir::separator(), 0, i); + auto k_it = mapTree.find(p); + if (p == "" || p == ".") { + continue; + } + if (k_it != mapTree.end() ) { + /* Directory already exists in tree */ + parentItem = k_it.value(); + } else { + /* Directory not exists create it */ + item = new QStandardItem(QIcon::fromTheme("folder"), pathList.at(i)); + item->setEditable(false); + /* Save path of directory created */ + parentItem->insertRow(0, item); + parentItem->sortChildren(0); + parentItem = item; + mapTree.insert(p, item); + } + } + + item = new QStandardItem(iconForFilename(fi.fileName()), fi.fileName()); + item->setEditable(false); + item->setData(QVariant(fi.filePath())); + parentItem->appendRow(item); + mapTree.insert(fi.filePath(), item); + } + +} + + diff --git a/kdbg/srcfileswindow.h b/kdbg/srcfileswindow.h new file mode 100644 index 0000000..0fd2c40 --- /dev/null +++ b/kdbg/srcfileswindow.h @@ -0,0 +1,38 @@ +/* + * Copyright Johannes Sixt + * This file is licensed under the GNU General Public License Version 2. + * See the file COPYING in the toplevel directory of the source directory. + */ + +#ifndef SRCFILESWINDOW_H +#define SRCFILESWINDOW_H + +#include +#include +#include +#include +#include "dbgdriver.h" + +class KDebugger; +class KConfigBase; + +class SrcFilesWindow : public QWidget +{ + Q_OBJECT +public: + SrcFilesWindow(QWidget* parent); + ~SrcFilesWindow(); +signals: + void activateFileLine(const QString& file, int lineNo, const DbgAddr& address); + +protected: + QStandardItemModel m_srcfiles_model; + QTreeView m_srctree; + QBoxLayout m_layout; + + void fileClicked(const QModelIndex &index); +public slots: + void sourceFiles(QString&); +}; + +#endif // SRCFILESWINDOW_H diff --git a/kdbg/xsldbgdriver.cpp b/kdbg/xsldbgdriver.cpp index b63385f..e464bd6 100644 --- a/kdbg/xsldbgdriver.cpp +++ b/kdbg/xsldbgdriver.cpp @@ -1303,6 +1303,12 @@ XsldbgDriver::parseInfoTarget(const char* /*output*/) return QString(); } +QString +XsldbgDriver::parseInfoSources(const char* /*output*/) +{ + return QString(); +} + std::list XsldbgDriver::parseDisassemble(const char */*output*/) { diff --git a/kdbg/xsldbgdriver.h b/kdbg/xsldbgdriver.h index 748d19f..79fd3d2 100644 --- a/kdbg/xsldbgdriver.h +++ b/kdbg/xsldbgdriver.h @@ -58,6 +58,7 @@ class XsldbgDriver:public DebuggerDriver { bool parseInfoLine(const char *output, QString & addrFrom, QString & addrTo) override; QString parseInfoTarget(const char* output) override; + QString parseInfoSources(const char* output) override; std::list parseDisassemble(const char *output) override; QString parseMemoryDump(const char *output, std::list < MemoryDump > &memdump) override;