From 94da1a23e421a4320edaf565fd81f90565046371 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Mon, 13 Jul 2026 23:05:01 -0400 Subject: [PATCH 01/20] this is a bit of a brute force approach but works! now bookmarks are saved/restored in all modules (heap/stack is kinda a wild card, but not too much we can do about that). Only real annoyance is minor, and that is that they aren't restored upon _start, but only once ld has loaded the modules they reside in. Might be able to hack around that though... --- include/IPlugin.h | 4 ++ plugins/Bookmarks/Bookmarks.cpp | 81 ++++++++++++++++++++++++++++++--- plugins/Bookmarks/Bookmarks.h | 14 ++++++ src/Debugger.cpp | 15 +++++- 4 files changed, 105 insertions(+), 9 deletions(-) diff --git a/include/IPlugin.h b/include/IPlugin.h index cae32a12c..83e6a37c8 100644 --- a/include/IPlugin.h +++ b/include/IPlugin.h @@ -13,6 +13,7 @@ class QMenu; class QAction; +class Module; class IPlugin { public: @@ -40,6 +41,9 @@ class IPlugin { // optional, overload this to add a page to the options dialog [[nodiscard]] virtual QWidget *optionsPage() { return nullptr; } + // optional, overload this to get notified when a library is loaded or unloaded + virtual void libraryEvent(const Module & /*module*/, bool /*loaded*/) {} + public: [[nodiscard]] virtual QVariantMap saveState() const { return {}; } virtual void restoreState(const QVariantMap &) {} diff --git a/plugins/Bookmarks/Bookmarks.cpp b/plugins/Bookmarks/Bookmarks.cpp index fb54e9385..01a355124 100644 --- a/plugins/Bookmarks/Bookmarks.cpp +++ b/plugins/Bookmarks/Bookmarks.cpp @@ -6,7 +6,11 @@ #include "Bookmarks.h" #include "BookmarkWidget.h" +#include "IDebugger.h" +#include "IProcess.h" +#include "Module.h" #include "edb.h" + #include #include #include @@ -108,12 +112,32 @@ void Bookmarks::addBookmarkMenu() { QVariantMap Bookmarks::saveState() const { QVariantMap state; QVariantList bookmarks; + + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + for (auto &bookmark : bookmarkWidget_->entries()) { + edb::address_t module_address = -1; + QString module_name; + + // Find which module whose base address is closest to the bookmark address + for (const auto &module : modules) { + if (module.baseAddress <= bookmark.address) { + if (module_address == -1 || module.baseAddress > module_address) { + module_address = module.baseAddress; + module_name = module.name; + } + } + } + QVariantMap entry; - entry[QStringLiteral("address")] = bookmark.address.toHexString(); entry[QStringLiteral("type")] = BookmarksModel::bookmarkTypeToString(bookmark.type); entry[QStringLiteral("comment")] = bookmark.comment; + entry[QStringLiteral("module")] = module_name; + entry[QStringLiteral("offset")] = (module_address != -1) ? (bookmark.address - module_address).toHexString() : QString(); bookmarks.push_back(entry); } @@ -129,17 +153,60 @@ QVariantMap Bookmarks::saveState() const { */ void Bookmarks::restoreState(const QVariantMap &state) { + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + QVariantList bookmarks = state[QStringLiteral("bookmarks")].toList(); for (auto &entry : bookmarks) { auto bookmark = entry.value(); - auto address = edb::address_t::fromHexString(bookmark[QStringLiteral("address")].toString()); - QString type = bookmark[QStringLiteral("type")].toString(); - QString comment = bookmark[QStringLiteral("comment")].toString(); - - qDebug() << "Restoring bookmark with address: " << address.toHexString(); + QString module_name = bookmark[QStringLiteral("module")].toString(); + QString offset_str = bookmark[QStringLiteral("offset")].toString(); + QString type = bookmark[QStringLiteral("type")].toString(); + QString comment = bookmark[QStringLiteral("comment")].toString(); + + edb::address_t offset = edb::address_t::fromHexString(offset_str); + + auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { + return module.name == module_name; + }); + + if (it != modules.end()) { + edb::address_t address = offset + it->baseAddress; + bookmarkWidget_->addAddress(address, type, comment); + continue; + } else { + BookmarkEntry entry; + entry.type = type; + entry.comment = comment; + entry.module = module_name; + entry.offset = offset_str; + + bookmarkEntries_.push_back(entry); + } + } +} - bookmarkWidget_->addAddress(address, type, comment); +/** + * @brief Handles library load/unload events to restore bookmarks for newly loaded modules. + * + * @param module The module that was loaded or unloaded. + * @param loaded True if the module was loaded, false if it was unloaded. + */ +void Bookmarks::libraryEvent(const Module &module, bool loaded) { + if (loaded) { + auto it = std::remove_if(bookmarkEntries_.begin(), bookmarkEntries_.end(), [&module, this](const BookmarkEntry &entry) { + if (entry.module == module.name) { + edb::address_t offset = edb::address_t::fromHexString(entry.offset); + edb::address_t address = offset + module.baseAddress; + bookmarkWidget_->addAddress(address, entry.type, entry.comment); + return true; + } + return false; + }); + bookmarkEntries_.erase(it, bookmarkEntries_.end()); } } diff --git a/plugins/Bookmarks/Bookmarks.h b/plugins/Bookmarks/Bookmarks.h index b90b3ea3d..639cb6083 100644 --- a/plugins/Bookmarks/Bookmarks.h +++ b/plugins/Bookmarks/Bookmarks.h @@ -23,6 +23,14 @@ class Bookmarks : public QObject, public IPlugin { Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") +private: + struct BookmarkEntry { + QString type; + QString comment; + QString module; + QString offset; + }; + public: explicit Bookmarks(QObject *parent = nullptr); @@ -34,12 +42,18 @@ class Bookmarks : public QObject, public IPlugin { [[nodiscard]] QVariantMap saveState() const override; void restoreState(const QVariantMap &) override; +public: + void libraryEvent(const Module &module, bool loaded) override; + private: void addBookmarkMenu(); private: QMenu *menu_ = nullptr; BookmarkWidget *bookmarkWidget_ = nullptr; + + // These are the ones not restored yet, but will be restored when the modules are loaded + std::vector bookmarkEntries_; }; } diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 9310a9411..9d5f8ad60 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -3633,6 +3633,7 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add edb::linux_struct::r_debug dynamic_info; const bool ok = (process->readBytes(debug_pointer, &dynamic_info, sizeof(dynamic_info)) == sizeof(dynamic_info)); if (ok) { + switch (dynamic_info.r_state) { case edb::linux_struct::r_debug::RT_CONSISTENT: break; @@ -3645,10 +3646,15 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add qDebug() << "Added modules:"; for (const Module &module : added_modules) { qDebug() << " " << module.name << "@" << edb::v1::format_pointer(module.baseAddress); + + for (QObject *plugin : edb::v1::plugin_list()) { + if (auto p = qobject_cast(plugin)) { + p->libraryEvent(module, true); + } + } } loadedModules_ = modules; - break; } case edb::linux_struct::r_debug::RT_DELETE: { @@ -3660,10 +3666,15 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add qDebug() << "Removed modules:"; for (const Module &module : removed_modules) { qDebug() << " " << module.name << "@" << edb::v1::format_pointer(module.baseAddress); + + for (QObject *plugin : edb::v1::plugin_list()) { + if (auto p = qobject_cast(plugin)) { + p->libraryEvent(module, false); + } + } } loadedModules_ = modules; - break; } } From 0c0ef940fc81b473e5324ff7bf39c9c48c1c898f Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Mon, 13 Jul 2026 23:40:08 -0400 Subject: [PATCH 02/20] Some minor cleanup with regards to "" literals and QString(), it's more efficient to just default construct a QString also added preliminary label support (Not module aware yet) --- include/ISymbolManager.h | 2 +- plugins/InstructionInspector/Plugin.cpp | 8 ++-- .../arch/arm-generic/armGroups.cpp | 4 +- src/Debugger.cpp | 6 +-- src/ExpressionDialog.cpp | 4 +- src/SymbolManager.cpp | 24 +++++----- src/SymbolManager.h | 6 +-- src/arch/arm-generic/ArchProcessor.cpp | 2 +- src/graph/GraphWidget.cpp | 2 +- src/session/SessionManager.cpp | 47 +++++++++++++++++-- src/session/SessionManager.h | 3 ++ src/widgets/QDisassemblyView.cpp | 4 +- 12 files changed, 78 insertions(+), 34 deletions(-) diff --git a/include/ISymbolManager.h b/include/ISymbolManager.h index f0b19cfe2..625b9d2b0 100644 --- a/include/ISymbolManager.h +++ b/include/ISymbolManager.h @@ -21,7 +21,7 @@ class ISymbolManager { virtual ~ISymbolManager() = default; public: - [[nodiscard]] virtual QHash labels() const = 0; + [[nodiscard]] virtual QMap labels() const = 0; [[nodiscard]] virtual QString findAddressName(edb::address_t address, bool prefixed = true) = 0; [[nodiscard]] virtual QStringList files() const = 0; [[nodiscard]] virtual std::optional find(const QString &name) const = 0; diff --git a/plugins/InstructionInspector/Plugin.cpp b/plugins/InstructionInspector/Plugin.cpp index da4580d46..57aaac683 100644 --- a/plugins/InstructionInspector/Plugin.cpp +++ b/plugins/InstructionInspector/Plugin.cpp @@ -824,7 +824,7 @@ std::string runOBJDUMP(const std::vector &bytes, edb::address_t ad } const auto output = QString::fromUtf8(process.readAllStandardOutput()).split(QLatin1Char('\n')); - const auto addrStr = address.toHexString().toLower().replace(QRegularExpression(QStringLiteral("^0+")), QStringLiteral("")); + const auto addrStr = address.toHexString().toLower().replace(QRegularExpression(QStringLiteral("^0+")), QString()); QString result; for (auto &line : output) { @@ -975,7 +975,7 @@ std::pair normalizeOBJCONV(const QString &t const auto disasm = expectedMatch.captured(1).trimmed().replace(QRegularExpression(QStringLiteral(" +")), QStringLiteral(" ")); const auto result = addr + QStringLiteral(" ") + bytes + QStringLiteral(" ") + disasm; - bytes.replace(QRegularExpression(QStringLiteral("[^0-9a-fA-F]")), QStringLiteral("")); + bytes.replace(QRegularExpression(QStringLiteral("[^0-9a-fA-F]")), QString()); const std::size_t insnLength = bytes.length() / 2; return std::make_pair(result, insnLength); } @@ -1197,8 +1197,8 @@ std::string runOBJCONV(std::vector bytes, edb::address_t address) Instruction, } mode = LookingFor::FunctionBegin; - const QString addrFormatted = address.toHexString().toUpper().replace(QRegularExpression(QStringLiteral("^0+")), QStringLiteral("")); - const QString addrTruncatedFormatted = (address & UINT32_MAX).toHexString().toUpper().replace(QRegularExpression(QStringLiteral("^0+")), QStringLiteral("")); + const QString addrFormatted = address.toHexString().toUpper().replace(QRegularExpression(QStringLiteral("^0+")), QString()); + const QString addrTruncatedFormatted = (address & UINT32_MAX).toHexString().toUpper().replace(QRegularExpression(QStringLiteral("^0+")), QString()); for (const QByteArray &byteString : lines) { const auto line = QString::fromUtf8(byteString); diff --git a/plugins/ODbgRegisterView/arch/arm-generic/armGroups.cpp b/plugins/ODbgRegisterView/arch/arm-generic/armGroups.cpp index e524d1869..bc36a8791 100644 --- a/plugins/ODbgRegisterView/arch/arm-generic/armGroups.cpp +++ b/plugins/ODbgRegisterView/arch/arm-generic/armGroups.cpp @@ -53,8 +53,8 @@ const BitFieldDescription fpscrSTRDescription = { }, { tr("Set stride to 1"), - QStringLiteral(""), - QStringLiteral(""), + QString(), + QString(), tr("Set stride to 2"), }, }; diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 9d5f8ad60..63ec39a3a 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -1721,8 +1721,8 @@ void Debugger::on_actionApplication_Working_Directory_triggered() { */ void Debugger::mnuStackPush() { Register value(edb::v1::debuggeeIs32Bit() - ? make_Register(QStringLiteral(""), edb::value32(0), Register::TYPE_GPR) - : make_Register(QStringLiteral(""), edb::value64(0), Register::TYPE_GPR)); + ? make_Register(QString(), edb::value32(0), Register::TYPE_GPR) + : make_Register(QString(), edb::value64(0), Register::TYPE_GPR)); if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { @@ -3605,7 +3605,7 @@ void Debugger::on_action_Reset_UI_triggered() { QSettings settings; settings.beginGroup(QStringLiteral("Window")); - settings.remove(QStringLiteral("")); + settings.remove(QString()); settings.endGroup(); ui_reset_ = true; } diff --git a/src/ExpressionDialog.cpp b/src/ExpressionDialog.cpp index 6519a0220..67bef7d7b 100644 --- a/src/ExpressionDialog.cpp +++ b/src/ExpressionDialog.cpp @@ -57,8 +57,8 @@ ExpressionDialog::ExpressionDialog(const QString &title, const QString &prompt, } void ExpressionDialog::on_text_changed(const QString &text) { - QHash labels = edb::v1::symbol_manager().labels(); - edb::address_t resAddr = labels.key(text); + QMap labels = edb::v1::symbol_manager().labels(); + edb::address_t resAddr = labels.key(text); bool retval = false; diff --git a/src/SymbolManager.cpp b/src/SymbolManager.cpp index 4181274e4..8df672b9a 100644 --- a/src/SymbolManager.cpp +++ b/src/SymbolManager.cpp @@ -314,19 +314,19 @@ void SymbolManager::setLabel(edb::address_t address, const QString &label) { if (label.isEmpty()) { labelsByName_.remove(labels_[address]); labels_.remove(address); - } else { - - if (labelsByName_.contains(label) && labelsByName_[label] != address) { - QMessageBox::warning( - edb::v1::debugger_ui, - tr("Duplicate Label"), - tr("You are attempting to give two separate addresses the same label, this is not supported.")); - return; - } + return; + } - labels_[address] = label; - labelsByName_[label] = address; + if (labelsByName_.contains(label) && labelsByName_[label] != address) { + QMessageBox::warning( + edb::v1::debugger_ui, + tr("Duplicate Label"), + tr("You are attempting to give two separate addresses the same label, this is not supported.")); + return; } + + labels_[address] = label; + labelsByName_[label] = address; } /** @@ -354,7 +354,7 @@ QString SymbolManager::findAddressName(edb::address_t address, bool prefixed) { * * @return The labels for all addresses. */ -QHash SymbolManager::labels() const { +QMap SymbolManager::labels() const { return labels_; } diff --git a/src/SymbolManager.h b/src/SymbolManager.h index baf71d658..8a550902d 100644 --- a/src/SymbolManager.h +++ b/src/SymbolManager.h @@ -23,7 +23,7 @@ class SymbolManager final : public ISymbolManager { SymbolManager() = default; public: - [[nodiscard]] QHash labels() const override; + [[nodiscard]] QMap labels() const override; [[nodiscard]] QString findAddressName(edb::address_t address, bool prefixed = true) override; [[nodiscard]] QStringList files() const override; [[nodiscard]] std::optional find(const QString &name) const override; @@ -46,8 +46,8 @@ class SymbolManager final : public ISymbolManager { QMap symbolsByAddress_; QHash> symbolsByFile_; QHash symbolsByName_; - QHash labels_; - QHash labelsByName_; + QMap labels_; + QMap labelsByName_; ISymbolGenerator *symbolGenerator_ = nullptr; bool showPathNotice_ = true; }; diff --git a/src/arch/arm-generic/ArchProcessor.cpp b/src/arch/arm-generic/ArchProcessor.cpp index b38d8a7e3..93825ce57 100644 --- a/src/arch/arm-generic/ArchProcessor.cpp +++ b/src/arch/arm-generic/ArchProcessor.cpp @@ -380,7 +380,7 @@ QString fpscrComment(edb::reg_t fpscr) { case 8: return QStringLiteral("(LT)"); default: - return QStringLiteral(""); + return QString(); } } diff --git a/src/graph/GraphWidget.cpp b/src/graph/GraphWidget.cpp index 0fc99e7b0..1942d2b78 100644 --- a/src/graph/GraphWidget.cpp +++ b/src/graph/GraphWidget.cpp @@ -89,7 +89,7 @@ GraphWidget::GraphWidget(QWidget *parent) // Set default attributes for the future nodes setNodeAttribute(QStringLiteral("fixedsize"), QStringLiteral("false")); - setNodeAttribute(QStringLiteral("label"), QStringLiteral("")); + setNodeAttribute(QStringLiteral("label"), QString()); setNodeAttribute(QStringLiteral("regular"), QStringLiteral("true")); // Divide the wanted width by the DPI to get the value in points diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 4869ed448..dd499d6c4 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -6,6 +6,7 @@ #include "SessionManager.h" #include "IPlugin.h" +#include "SymbolManager.h" #include "edb.h" #include @@ -82,9 +83,12 @@ Result SessionManager::loadSession(const QString &filename) QJsonObject object = doc.object(); sessionData_ = object.toVariantMap(); - QString id = sessionData_[QStringLiteral("id")].toString(); - QString ts = sessionData_[QStringLiteral("timestamp")].toString(); - int version = sessionData_[QStringLiteral("version")].toInt(); + QString id = sessionData_[QStringLiteral("id")].toString(); + QString ts = sessionData_[QStringLiteral("timestamp")].toString(); + int version = sessionData_[QStringLiteral("version")].toInt(); + QVariantMap labels = sessionData_[QStringLiteral("labels")].toMap(); + + loadLabels(labels); Q_UNUSED(ts) @@ -128,6 +132,7 @@ void SessionManager::saveSession(const QString &filename) { sessionData_[QStringLiteral("id")] = SessionFileIdString; // just so we can sanity check things sessionData_[QStringLiteral("timestamp")] = QDateTime::currentDateTimeUtc(); sessionData_[QStringLiteral("plugin-data")] = plugin_data; + sessionData_[QStringLiteral("labels")] = saveLabels(); auto object = QJsonObject::fromVariantMap(sessionData_); QJsonDocument doc(object); @@ -222,3 +227,39 @@ void SessionManager::removeComment(edb::address_t address) { sessionData_[QStringLiteral("comments")] = comments_data; } + +/** + * @brief Saves the labels to a QVariantMap for session persistence. + * + * @return A QVariantMap containing the labels. + */ +QVariantMap SessionManager::saveLabels() const { + QMap labels = edb::v1::symbol_manager().labels(); + QVariantMap labels_data; + for (auto it = labels.begin(); it != labels.end(); ++it) { + + qDebug() << "Saving label for address" << it.key().toHexString() << ":" << it.value(); + + labels_data[it.key().toHexString()] = it.value(); + } + return labels_data; +} + +/** + * @brief Loads the labels from a QVariantMap for session restoration. + * + * @param labels A QVariantMap containing the labels to load. + */ +void SessionManager::loadLabels(const QVariantMap &labels) { + + qDebug("Loading labels"); + + for (auto it = labels.begin(); it != labels.end(); ++it) { + edb::address_t address = edb::address_t::fromHexString(it.key()); + QString label = it.value().toString(); + + qDebug() << "Loading label for address" << address.toHexString() << ":" << label; + + edb::v1::symbol_manager().setLabel(address, label); + } +} diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 550eecf89..104f63ceb 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -14,6 +14,7 @@ #include #include #include +#include class SessionManager { Q_DECLARE_TR_FUNCTIONS(SessionManager) @@ -37,6 +38,8 @@ class SessionManager { private: void loadPluginData(); + QVariantMap saveLabels() const; + void loadLabels(const QVariantMap &labels); private: QVariantMap sessionData_; diff --git a/src/widgets/QDisassemblyView.cpp b/src/widgets/QDisassemblyView.cpp index 377c4405a..fbf3576c5 100644 --- a/src/widgets/QDisassemblyView.cpp +++ b/src/widgets/QDisassemblyView.cpp @@ -1228,7 +1228,7 @@ void QDisassemblyView::drawComments(QPainter &painter, const DrawingContext *ctx painter.setPen(palette().color(ctx->group, QPalette::Text)); } - QString annotation = comments_.value(address, QStringLiteral("")); + QString annotation = comments_.value(address, QString()); auto &&inst = instructions_[line]; if (annotation.isEmpty() && inst && !is_jump(inst) && !is_call(inst)) { // draw ascii representations of immediate constants @@ -2215,7 +2215,7 @@ int QDisassemblyView::removeComment(edb::address_t address) { * @return The comment string associated with the address, or an empty string if no comment exists for that address. */ QString QDisassemblyView::getComment(edb::address_t address) const { - return comments_.value(address, QStringLiteral("")); + return comments_.value(address, QString()); } /** From 31156dac2e8f89710d97057a1b621dc3aaee43f3 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 00:03:09 -0400 Subject: [PATCH 03/20] some minor naming improvments NOTE: the current approach has one fatal flaw, and that is that the modules are no longer loaded after the application exits, so we lose the information and cannot properly save the bookmarks/labels/etc. I think we need to store the module when we add the bookmark itself so we know it immediately. --- plugins/Bookmarks/Bookmarks.cpp | 10 ++++++---- plugins/Bookmarks/Bookmarks.h | 2 +- plugins/DebuggerCore/unix/linux/PlatformProcess.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/Bookmarks/Bookmarks.cpp b/plugins/Bookmarks/Bookmarks.cpp index 01a355124..9cbc15118 100644 --- a/plugins/Bookmarks/Bookmarks.cpp +++ b/plugins/Bookmarks/Bookmarks.cpp @@ -169,6 +169,7 @@ void Bookmarks::restoreState(const QVariantMap &state) { edb::address_t offset = edb::address_t::fromHexString(offset_str); + // Figure out which module this bookmark belongs to and add it if the module is loaded auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { return module.name == module_name; }); @@ -178,13 +179,14 @@ void Bookmarks::restoreState(const QVariantMap &state) { bookmarkWidget_->addAddress(address, type, comment); continue; } else { + + // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded BookmarkEntry entry; entry.type = type; entry.comment = comment; entry.module = module_name; entry.offset = offset_str; - - bookmarkEntries_.push_back(entry); + deferredBookmarks_.push_back(entry); } } } @@ -197,7 +199,7 @@ void Bookmarks::restoreState(const QVariantMap &state) { */ void Bookmarks::libraryEvent(const Module &module, bool loaded) { if (loaded) { - auto it = std::remove_if(bookmarkEntries_.begin(), bookmarkEntries_.end(), [&module, this](const BookmarkEntry &entry) { + auto it = std::remove_if(deferredBookmarks_.begin(), deferredBookmarks_.end(), [&module, this](const BookmarkEntry &entry) { if (entry.module == module.name) { edb::address_t offset = edb::address_t::fromHexString(entry.offset); edb::address_t address = offset + module.baseAddress; @@ -206,7 +208,7 @@ void Bookmarks::libraryEvent(const Module &module, bool loaded) { } return false; }); - bookmarkEntries_.erase(it, bookmarkEntries_.end()); + deferredBookmarks_.erase(it, deferredBookmarks_.end()); } } diff --git a/plugins/Bookmarks/Bookmarks.h b/plugins/Bookmarks/Bookmarks.h index 639cb6083..45fc9ef92 100644 --- a/plugins/Bookmarks/Bookmarks.h +++ b/plugins/Bookmarks/Bookmarks.h @@ -53,7 +53,7 @@ class Bookmarks : public QObject, public IPlugin { BookmarkWidget *bookmarkWidget_ = nullptr; // These are the ones not restored yet, but will be restored when the modules are loaded - std::vector bookmarkEntries_; + std::vector deferredBookmarks_; }; } diff --git a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp index 6685a7327..f7bf4fd99 100644 --- a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp +++ b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp @@ -187,7 +187,7 @@ QSet get_loaded_modules(const IProcess *process) { } } - // fallback + // fallback, unfortunately due to symlink shenanigans, this won't quite match the link_map results, but it's better than nothing if (ret.isEmpty()) { const QList> r = edb::v1::memory_regions().regions(); QSet found_modules; From 419f919b634ba9fb777e9715a4e5c6be0892f4d0 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 00:06:06 -0400 Subject: [PATCH 04/20] giving myself a reminder of where to implement some details --- plugins/Bookmarks/BookmarkWidget.cpp | 2 ++ plugins/Bookmarks/BookmarksModel.cpp | 4 ++-- plugins/Bookmarks/BookmarksModel.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/Bookmarks/BookmarkWidget.cpp b/plugins/Bookmarks/BookmarkWidget.cpp index a315b62dc..23becb862 100644 --- a/plugins/Bookmarks/BookmarkWidget.cpp +++ b/plugins/Bookmarks/BookmarkWidget.cpp @@ -177,6 +177,8 @@ void BookmarkWidget::addAddress(edb::address_t address, const QString &type, con comment, }; + // TODO(eteran): figure out the module and store it here so that it can be restored later if the module is unloaded and reloaded + model_->addBookmark(bookmark); } } diff --git a/plugins/Bookmarks/BookmarksModel.cpp b/plugins/Bookmarks/BookmarksModel.cpp index 9c53fb306..21eb49359 100644 --- a/plugins/Bookmarks/BookmarksModel.cpp +++ b/plugins/Bookmarks/BookmarksModel.cpp @@ -128,9 +128,9 @@ QVariant BookmarksModel::data(const QModelIndex &index, int role) const { * * @param r */ -void BookmarksModel::addBookmark(const Bookmark &r) { +void BookmarksModel::addBookmark(const Bookmark &bookmark) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); - bookmarks_.push_back(r); + bookmarks_.push_back(bookmark); endInsertRows(); } diff --git a/plugins/Bookmarks/BookmarksModel.h b/plugins/Bookmarks/BookmarksModel.h index 8c3d840bc..849eaecbd 100644 --- a/plugins/Bookmarks/BookmarksModel.h +++ b/plugins/Bookmarks/BookmarksModel.h @@ -72,7 +72,7 @@ class BookmarksModel final : public QAbstractItemModel { [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; public Q_SLOTS: - void addBookmark(const Bookmark &r); + void addBookmark(const Bookmark &bookmark); void clearBookmarks(); void deleteBookmark(const QModelIndex &index); void setComment(const QModelIndex &index, const QString &comment); From 0997efa46d7415d5767fd207d644f8093501831e Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 00:22:03 -0400 Subject: [PATCH 05/20] added a note on implementation improvements --- plugins/Bookmarks/Bookmarks.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/Bookmarks/Bookmarks.cpp b/plugins/Bookmarks/Bookmarks.cpp index 9cbc15118..04527985a 100644 --- a/plugins/Bookmarks/Bookmarks.cpp +++ b/plugins/Bookmarks/Bookmarks.cpp @@ -167,6 +167,9 @@ void Bookmarks::restoreState(const QVariantMap &state) { QString type = bookmark[QStringLiteral("type")].toString(); QString comment = bookmark[QStringLiteral("comment")].toString(); + // TODO(eteran): don't compare modules by name! Instead, we should dereference any symlinks, and then compare the dev/ino of the file to the dev/ino of the loaded module. + // This will allow us to handle module name inconsistencies, such as when a module is loaded from a different path or with a different name. + edb::address_t offset = edb::address_t::fromHexString(offset_str); // Figure out which module this bookmark belongs to and add it if the module is loaded From 9f19fb06f4a76c1c8617285c56a080640f412e78 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 10:17:13 -0400 Subject: [PATCH 06/20] working out saving/restore with respect to modules, really close to being perfect :-) --- include/edb.h | 5 ++ plugins/Bookmarks/BookmarkWidget.cpp | 8 +- plugins/Bookmarks/BookmarkWidget.h | 1 + plugins/Bookmarks/Bookmarks.cpp | 30 ++------ plugins/Bookmarks/BookmarksModel.h | 7 ++ .../DialogProcessProperties.cpp | 2 +- src/edb.cpp | 76 ++++++++++++++++++- 7 files changed, 96 insertions(+), 33 deletions(-) diff --git a/include/edb.h b/include/edb.h index 331172e5e..b03c781eb 100644 --- a/include/edb.h +++ b/include/edb.h @@ -9,12 +9,15 @@ #include "API.h" #include "IBinary.h" +#include "Module.h" #include "Status.h" #include "Types.h" + #include #include #include #include + #include #include @@ -53,6 +56,8 @@ namespace v2 { EDB_EXPORT std::optional get_expression_from_user(const QString &title, const QString &prompt); EDB_EXPORT std::optional eval_expression(const QString &expression); EDB_EXPORT QString format_bytes(const void *buffer, size_t count); +EDB_EXPORT std::optional module_for_address(edb::address_t address); +EDB_EXPORT bool compare_module_names(const QString &name1, const QString &name2); } diff --git a/plugins/Bookmarks/BookmarkWidget.cpp b/plugins/Bookmarks/BookmarkWidget.cpp index 23becb862..b860815fd 100644 --- a/plugins/Bookmarks/BookmarkWidget.cpp +++ b/plugins/Bookmarks/BookmarkWidget.cpp @@ -8,7 +8,10 @@ #include "BookmarksModel.h" #include "Expression.h" #include "IBreakpoint.h" +#include "IDebugger.h" +#include "IProcess.h" #include "edb.h" + #include #include #include @@ -175,10 +178,9 @@ void BookmarkWidget::addAddress(edb::address_t address, const QString &type, con address, BookmarksModel::bookmarkStringToType(type), comment, + edb::v2::module_for_address(address), }; - // TODO(eteran): figure out the module and store it here so that it can be restored later if the module is unloaded and reloaded - model_->addBookmark(bookmark); } } @@ -279,7 +281,7 @@ void BookmarkWidget::on_tableView_customContextMenuRequested(const QPoint &pos) /** * @brief Returns a copy of the current bookmark list. * - * @return + * @return A QList of BookmarksModel::Bookmark entries. */ QList BookmarkWidget::entries() const { const QVector &bookmarks = model_->bookmarks(); diff --git a/plugins/Bookmarks/BookmarkWidget.h b/plugins/Bookmarks/BookmarkWidget.h index 49d4523e1..cc64141aa 100644 --- a/plugins/Bookmarks/BookmarkWidget.h +++ b/plugins/Bookmarks/BookmarkWidget.h @@ -10,6 +10,7 @@ #include "BookmarksModel.h" #include "Types.h" #include "ui_BookmarkWidget.h" + #include class QModelIndex; diff --git a/plugins/Bookmarks/Bookmarks.cpp b/plugins/Bookmarks/Bookmarks.cpp index 04527985a..20ef3c46a 100644 --- a/plugins/Bookmarks/Bookmarks.cpp +++ b/plugins/Bookmarks/Bookmarks.cpp @@ -113,31 +113,13 @@ QVariantMap Bookmarks::saveState() const { QVariantMap state; QVariantList bookmarks; - IProcess *process = edb::v1::debugger_core->process(); - Q_ASSERT(process); - - QSet modules = process->loadedModules(); - for (auto &bookmark : bookmarkWidget_->entries()) { - edb::address_t module_address = -1; - QString module_name; - - // Find which module whose base address is closest to the bookmark address - for (const auto &module : modules) { - if (module.baseAddress <= bookmark.address) { - if (module_address == -1 || module.baseAddress > module_address) { - module_address = module.baseAddress; - module_name = module.name; - } - } - } - QVariantMap entry; entry[QStringLiteral("type")] = BookmarksModel::bookmarkTypeToString(bookmark.type); entry[QStringLiteral("comment")] = bookmark.comment; - entry[QStringLiteral("module")] = module_name; - entry[QStringLiteral("offset")] = (module_address != -1) ? (bookmark.address - module_address).toHexString() : QString(); + entry[QStringLiteral("module")] = bookmark.module ? bookmark.module->name : QString(); + entry[QStringLiteral("offset")] = (bookmark.module) ? (bookmark.address - bookmark.module->baseAddress).toHexString() : QString(); bookmarks.push_back(entry); } @@ -167,14 +149,11 @@ void Bookmarks::restoreState(const QVariantMap &state) { QString type = bookmark[QStringLiteral("type")].toString(); QString comment = bookmark[QStringLiteral("comment")].toString(); - // TODO(eteran): don't compare modules by name! Instead, we should dereference any symlinks, and then compare the dev/ino of the file to the dev/ino of the loaded module. - // This will allow us to handle module name inconsistencies, such as when a module is loaded from a different path or with a different name. - edb::address_t offset = edb::address_t::fromHexString(offset_str); // Figure out which module this bookmark belongs to and add it if the module is loaded auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { - return module.name == module_name; + return edb::v2::compare_module_names(module.name, module_name); }); if (it != modules.end()) { @@ -203,12 +182,13 @@ void Bookmarks::restoreState(const QVariantMap &state) { void Bookmarks::libraryEvent(const Module &module, bool loaded) { if (loaded) { auto it = std::remove_if(deferredBookmarks_.begin(), deferredBookmarks_.end(), [&module, this](const BookmarkEntry &entry) { - if (entry.module == module.name) { + if (edb::v2::compare_module_names(entry.module, module.name)) { edb::address_t offset = edb::address_t::fromHexString(entry.offset); edb::address_t address = offset + module.baseAddress; bookmarkWidget_->addAddress(address, entry.type, entry.comment); return true; } + return false; }); deferredBookmarks_.erase(it, deferredBookmarks_.end()); diff --git a/plugins/Bookmarks/BookmarksModel.h b/plugins/Bookmarks/BookmarksModel.h index 849eaecbd..1d4dcb2a5 100644 --- a/plugins/Bookmarks/BookmarksModel.h +++ b/plugins/Bookmarks/BookmarksModel.h @@ -7,11 +7,15 @@ #ifndef BOOKMARKS_MODEL_H_20170103_ #define BOOKMARKS_MODEL_H_20170103_ +#include "Module.h" #include "Types.h" + #include #include #include +#include + namespace BookmarksPlugin { class BookmarksModel final : public QAbstractItemModel { @@ -28,6 +32,9 @@ class BookmarksModel final : public QAbstractItemModel { edb::address_t address; Type type; QString comment; + + // internal use only, used to store the module that the bookmark belongs to, if any + std::optional module; }; static QString bookmarkTypeToString(Bookmark::Type type) { diff --git a/plugins/ProcessProperties/DialogProcessProperties.cpp b/plugins/ProcessProperties/DialogProcessProperties.cpp index a0fbca534..2728875c0 100644 --- a/plugins/ProcessProperties/DialogProcessProperties.cpp +++ b/plugins/ProcessProperties/DialogProcessProperties.cpp @@ -494,7 +494,7 @@ void DialogProcessProperties::updateHandles() { const QFileInfoList entries = dir.entryInfoList(QStringList() << QStringLiteral("[0-9]*")); for (const QFileInfo &info : entries) { if (info.isSymLink()) { - QString symlink(info.symLinkTarget()); + QString symlink = info.symLinkTarget(); const QString type(file_type(symlink)); if (type == tr("Socket")) { diff --git a/src/edb.cpp b/src/edb.cpp index 375d75666..ecd2e1d54 100644 --- a/src/edb.cpp +++ b/src/edb.cpp @@ -23,6 +23,7 @@ #include "IRegion.h" #include "IThread.h" #include "MemoryRegions.h" +#include "Module.h" #include "Prototype.h" #include "QHexView" #include "QtHelper.h" @@ -42,8 +43,10 @@ #include #include #include +#include #include +#include IDebugger *edb::v1::debugger_core = nullptr; QWidget *edb::v1::debugger_ui = nullptr; @@ -923,10 +926,6 @@ std::unique_ptr get_binary_info(const std::shared_ptr ®ion) } } -#if 0 - qDebug() << "Failed to find any binary parser for region" - << QString::number(region->start(), 16); -#endif return nullptr; } @@ -1646,5 +1645,74 @@ QString format_bytes(const void *buffer, size_t count) { return bytes; } +/** + * @brief Finds the module whose base address is closest to the given address, if any. + * + * @param address The address to find the module for. + * @return An optional Module object if a module is found, or std::nullopt if not. + */ +std::optional module_for_address(edb::address_t address) { + + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + + std::optional best_module; + + // Find which module whose base address is closest to the bookmark address + for (const auto &module : modules) { + if (module.baseAddress <= address) { + if (!best_module || module.baseAddress > best_module->baseAddress) { + best_module = module; + } + } + } + + return best_module; +} + +/** + * @brief Compares two module names for equality, taking into account symbolic links and canonical paths. + * + * @param name1 The first module name to compare. + * @param name2 The second module name to compare. + * @return true if the module names refer to the same file, false otherwise. + */ +bool compare_module_names(const QString &name1, const QString &name2) { + + // TODO(eteran): this works great except for the case where one of the names is the empty string + // and the other is not, this happens because ld.so gives the "primary" module the name "" (empty string) + // and the other modules have their full path names. But... our fallback mechanism for finding modules is + // to look in /proc//maps which gives the full path name. We need to figure out a good plan for this. + + // Convert both names into canonical paths to ensure consistent comparison + // and then resolve any symbolic links or shortcuts to their target paths. + + QString canonicalName1 = QFileInfo(name1).canonicalFilePath(); + QString canonicalName2 = QFileInfo(name2).canonicalFilePath(); + + if (QFileInfo(canonicalName1).isSymLink()) { + canonicalName1 = QFileInfo(canonicalName1).symLinkTarget(); + } + + if (QFileInfo(canonicalName2).isSymLink()) { + canonicalName2 = QFileInfo(canonicalName2).symLinkTarget(); + } + + QT_STATBUF statbuf1; + if (QT_STAT(canonicalName1.toUtf8().data(), &statbuf1) != 0) { + return false; + } + + QT_STATBUF statbuf2; + if (QT_STAT(canonicalName2.toUtf8().data(), &statbuf2) != 0) { + return false; + } + + return statbuf1.st_dev == statbuf2.st_dev && statbuf1.st_ino == statbuf2.st_ino; +} + } + } From a442be73348008079ee9aaac45e1f018297e9a96 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 16:50:50 -0400 Subject: [PATCH 07/20] normalized the main module name so save/restore works better! --- plugins/DebuggerCore/unix/linux/PlatformProcess.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp index f7bf4fd99..ad54243e9 100644 --- a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp +++ b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp @@ -172,7 +172,8 @@ QSet get_loaded_modules(const IProcess *process) { if (map.l_addr) { Module module; - module.name = QString::fromLocal8Bit(path); + // NOTE(eteran): we use the path[0] check here because ld.so gives the "primary" module the name "" (empty string) + module.name = path[0] ? QString::fromLocal8Bit(path) : process->executable(); module.baseAddress = map.l_addr; ret.insert(std::move(module)); } From 524f003bf7a5179214b6fdcf2f452f4cd0b6bda7 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 20:15:02 -0400 Subject: [PATCH 08/20] now labels can be saved/restored properly --- include/ISymbolManager.h | 13 +++++ plugins/Bookmarks/BookmarkWidget.h | 2 +- src/Debugger.cpp | 9 +++ src/SymbolManager.cpp | 37 ++++++++++--- src/SymbolManager.h | 8 ++- src/edb.cpp | 5 -- src/session/SessionManager.cpp | 88 ++++++++++++++++++++++++------ src/session/SessionManager.h | 17 +++++- 8 files changed, 144 insertions(+), 35 deletions(-) diff --git a/include/ISymbolManager.h b/include/ISymbolManager.h index 625b9d2b0..0dc0a7dc3 100644 --- a/include/ISymbolManager.h +++ b/include/ISymbolManager.h @@ -7,8 +7,12 @@ #ifndef ISYMBOL_MANAGER_H_20110307_ #define ISYMBOL_MANAGER_H_20110307_ +#include "Module.h" #include "Types.h" + #include +#include + #include #include @@ -17,10 +21,19 @@ class Symbol; class ISymbolGenerator; class ISymbolManager { +public: + struct LabelEntry { + QString name; + edb::address_t address; + std::optional module; + + }; + public: virtual ~ISymbolManager() = default; public: + [[nodiscard]] virtual QVector labelData() const = 0; [[nodiscard]] virtual QMap labels() const = 0; [[nodiscard]] virtual QString findAddressName(edb::address_t address, bool prefixed = true) = 0; [[nodiscard]] virtual QStringList files() const = 0; diff --git a/plugins/Bookmarks/BookmarkWidget.h b/plugins/Bookmarks/BookmarkWidget.h index cc64141aa..c6452ffa5 100644 --- a/plugins/Bookmarks/BookmarkWidget.h +++ b/plugins/Bookmarks/BookmarkWidget.h @@ -33,7 +33,7 @@ public Q_SLOTS: public: void shortcut(int index); void addAddress(edb::address_t address, const QString &type = QString(), const QString &comment = QString()); - [[nodiscard]] QList entries() const; + [[nodiscard]] QVector entries() const; private: void buttonAddClicked(); diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 63ec39a3a..41985aadc 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -3647,11 +3647,18 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add for (const Module &module : added_modules) { qDebug() << " " << module.name << "@" << edb::v1::format_pointer(module.baseAddress); + // Notify all plugins who care about library load/unload events for (QObject *plugin : edb::v1::plugin_list()) { if (auto p = qobject_cast(plugin)) { p->libraryEvent(module, true); } } + + // Notify the session manager about the library load event + // NOTE(eteran): this is a bit "out of place", because we are using it to restore labels/comments, + // but it would be better if we informed the symbol manager more directly. But that's a larger refactor, + // so for now, this is fine. + SessionManager::instance().libraryEvent(module, true); } loadedModules_ = modules; @@ -3672,6 +3679,8 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add p->libraryEvent(module, false); } } + + SessionManager::instance().libraryEvent(module, false); } loadedModules_ = modules; diff --git a/src/SymbolManager.cpp b/src/SymbolManager.cpp index 8df672b9a..3abc6f30a 100644 --- a/src/SymbolManager.cpp +++ b/src/SymbolManager.cpp @@ -30,7 +30,7 @@ void SymbolManager::clear() { symbolsByFile_.clear(); symbolsByName_.clear(); labels_.clear(); - labelsByName_.clear(); + labelNames_.clear(); } /** @@ -312,12 +312,12 @@ void SymbolManager::setSymbolGenerator(ISymbolGenerator *generator) { */ void SymbolManager::setLabel(edb::address_t address, const QString &label) { if (label.isEmpty()) { - labelsByName_.remove(labels_[address]); + labelNames_.remove(labels_[address].name); labels_.remove(address); return; } - if (labelsByName_.contains(label) && labelsByName_[label] != address) { + if (labelNames_.contains(label) && labels_[address].name != label) { QMessageBox::warning( edb::v1::debugger_ui, tr("Duplicate Label"), @@ -325,8 +325,14 @@ void SymbolManager::setLabel(edb::address_t address, const QString &label) { return; } - labels_[address] = label; - labelsByName_[label] = address; + LabelEntry newLabel = { + label, + address, + edb::v2::module_for_address(address), + }; + + labels_[address] = newLabel; + labelNames_.insert(label); } /** @@ -339,7 +345,7 @@ void SymbolManager::setLabel(edb::address_t address, const QString &label) { QString SymbolManager::findAddressName(edb::address_t address, bool prefixed) { auto it = labels_.find(address); if (it != labels_.end()) { - return it.value(); + return it.value().name; } if (const std::optional sym = find(address)) { @@ -355,7 +361,11 @@ QString SymbolManager::findAddressName(edb::address_t address, bool prefixed) { * @return The labels for all addresses. */ QMap SymbolManager::labels() const { - return labels_; + QMap result; + for (auto it = labels_.begin(); it != labels_.end(); ++it) { + result[it.key()] = it.value().name; + } + return result; } /** @@ -366,3 +376,16 @@ QMap SymbolManager::labels() const { QStringList SymbolManager::files() const { return symbolsByFile_.keys(); } + +/** + * @brief Gets the label data for all addresses. + * + * @return A QVector containing the label data for all addresses. + */ +QVector SymbolManager::labelData() const { + QVector result; + for (auto it = labels_.begin(); it != labels_.end(); ++it) { + result.push_back(it.value()); + } + return result; +} diff --git a/src/SymbolManager.h b/src/SymbolManager.h index 8a550902d..681e40b72 100644 --- a/src/SymbolManager.h +++ b/src/SymbolManager.h @@ -8,12 +8,15 @@ #define SYMBOL_MANAGER_H_20060814_ #include "ISymbolManager.h" +#include "Module.h" #include #include #include #include +#include + class QString; class SymbolManager final : public ISymbolManager { @@ -23,6 +26,7 @@ class SymbolManager final : public ISymbolManager { SymbolManager() = default; public: + [[nodiscard]] QVector labelData() const override; [[nodiscard]] QMap labels() const override; [[nodiscard]] QString findAddressName(edb::address_t address, bool prefixed = true) override; [[nodiscard]] QStringList files() const override; @@ -46,8 +50,8 @@ class SymbolManager final : public ISymbolManager { QMap symbolsByAddress_; QHash> symbolsByFile_; QHash symbolsByName_; - QMap labels_; - QMap labelsByName_; + QMap labels_; + QSet labelNames_; ISymbolGenerator *symbolGenerator_ = nullptr; bool showPathNotice_ = true; }; diff --git a/src/edb.cpp b/src/edb.cpp index ecd2e1d54..41c4db119 100644 --- a/src/edb.cpp +++ b/src/edb.cpp @@ -1681,11 +1681,6 @@ std::optional module_for_address(edb::address_t address) { */ bool compare_module_names(const QString &name1, const QString &name2) { - // TODO(eteran): this works great except for the case where one of the names is the empty string - // and the other is not, this happens because ld.so gives the "primary" module the name "" (empty string) - // and the other modules have their full path names. But... our fallback mechanism for finding modules is - // to look in /proc//maps which gives the full path name. We need to figure out a good plan for this. - // Convert both names into canonical paths to ensure consistent comparison // and then resolve any symbolic links or shortcuts to their target paths. diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index dd499d6c4..d78f80b27 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -5,7 +5,10 @@ */ #include "SessionManager.h" +#include "IDebugger.h" #include "IPlugin.h" +#include "IProcess.h" +#include "Module.h" #include "SymbolManager.h" #include "edb.h" @@ -83,10 +86,10 @@ Result SessionManager::loadSession(const QString &filename) QJsonObject object = doc.object(); sessionData_ = object.toVariantMap(); - QString id = sessionData_[QStringLiteral("id")].toString(); - QString ts = sessionData_[QStringLiteral("timestamp")].toString(); - int version = sessionData_[QStringLiteral("version")].toInt(); - QVariantMap labels = sessionData_[QStringLiteral("labels")].toMap(); + QString id = sessionData_[QStringLiteral("id")].toString(); + QString ts = sessionData_[QStringLiteral("timestamp")].toString(); + int version = sessionData_[QStringLiteral("version")].toInt(); + QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); loadLabels(labels); @@ -233,33 +236,82 @@ void SessionManager::removeComment(edb::address_t address) { * * @return A QVariantMap containing the labels. */ -QVariantMap SessionManager::saveLabels() const { - QMap labels = edb::v1::symbol_manager().labels(); - QVariantMap labels_data; - for (auto it = labels.begin(); it != labels.end(); ++it) { +QVariantList SessionManager::saveLabels() const { + QVector labels = edb::v1::symbol_manager().labelData(); - qDebug() << "Saving label for address" << it.key().toHexString() << ":" << it.value(); + QVariantList label_data; - labels_data[it.key().toHexString()] = it.value(); + for (const auto &label : labels) { + + edb::address_t address = label.module ? label.address - label.module->baseAddress : edb::address_t(); + QString name = label.name; + + QVariantMap entry; + entry[QStringLiteral("module")] = label.module ? label.module->name : QString(); + entry[QStringLiteral("offset")] = address.toHexString(); + entry[QStringLiteral("name")] = name; + label_data.push_back(entry); } - return labels_data; + + return label_data; } /** * @brief Loads the labels from a QVariantMap for session restoration. * - * @param labels A QVariantMap containing the labels to load. + * @param labels A QVariantList containing the labels to load. */ -void SessionManager::loadLabels(const QVariantMap &labels) { +void SessionManager::loadLabels(const QVariantList &labels) { qDebug("Loading labels"); - for (auto it = labels.begin(); it != labels.end(); ++it) { - edb::address_t address = edb::address_t::fromHexString(it.key()); - QString label = it.value().toString(); + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + + for (auto &entry : labels) { + auto label = entry.value(); + + QString module_name = label[QStringLiteral("module")].toString(); + QString offset_str = label[QStringLiteral("offset")].toString(); + QString name = label[QStringLiteral("name")].toString(); + + edb::address_t offset = edb::address_t::fromHexString(offset_str); - qDebug() << "Loading label for address" << address.toHexString() << ":" << label; + // Figure out which module this bookmark belongs to and add it if the module is loaded + auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { + return edb::v2::compare_module_names(module.name, module_name); + }); + + if (it != modules.end()) { + edb::address_t address = offset + it->baseAddress; + edb::v1::symbol_manager().setLabel(address, name); + continue; + } else { + + // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded + LabelEntry entry; + entry.name = name; + entry.module = module_name; + entry.offset = offset_str; + deferredLabels_.push_back(entry); + } + } +} + +void SessionManager::libraryEvent(const Module &module, bool loaded) { + if (loaded) { + auto it = std::remove_if(deferredLabels_.begin(), deferredLabels_.end(), [&module, this](const LabelEntry &entry) { + if (edb::v2::compare_module_names(entry.module, module.name)) { + edb::address_t offset = edb::address_t::fromHexString(entry.offset); + edb::address_t address = offset + module.baseAddress; + edb::v1::symbol_manager().setLabel(address, entry.name); + return true; + } - edb::v1::symbol_manager().setLabel(address, label); + return false; + }); + deferredLabels_.erase(it, deferredLabels_.end()); } } diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 104f63ceb..a3fc0fecc 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -16,9 +16,18 @@ #include #include +class Module; + class SessionManager { Q_DECLARE_TR_FUNCTIONS(SessionManager) +private: + struct LabelEntry { + QString name; + QString module; + QString offset; + }; + private: SessionManager() = default; @@ -36,13 +45,17 @@ class SessionManager { void addComment(const Comment &c); void removeComment(edb::address_t address); +public: + void libraryEvent(const Module &module, bool loaded); + private: void loadPluginData(); - QVariantMap saveLabels() const; - void loadLabels(const QVariantMap &labels); + QVariantList saveLabels() const; + void loadLabels(const QVariantList &labels); private: QVariantMap sessionData_; + std::vector deferredLabels_; }; #endif From 6eaccfa9adeb1c5e39f3192d2d09b178f6f0e488 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 20:42:21 -0400 Subject: [PATCH 09/20] had to untangle a bit of a mess, but comments are now properly persisted too! --- include/Comment.h | 18 +++ include/ISymbolManager.h | 1 - include/Types.h | 7 +- src/Debugger.cpp | 6 +- src/session/SessionManager.cpp | 189 +++++++++++++++++++------------ src/session/SessionManager.h | 6 + src/widgets/QDisassemblyView.cpp | 43 ++++--- src/widgets/QDisassemblyView.h | 8 +- 8 files changed, 177 insertions(+), 101 deletions(-) create mode 100644 include/Comment.h diff --git a/include/Comment.h b/include/Comment.h new file mode 100644 index 000000000..2d77b18bc --- /dev/null +++ b/include/Comment.h @@ -0,0 +1,18 @@ + +#ifndef COMMENT_H_ +#define COMMENT_H_ + +#include "Types.h" +#include "Module.h" + +#include + +#include + +struct Comment { + edb::address_t address; + QString comment; + std::optional module; +}; + +#endif diff --git a/include/ISymbolManager.h b/include/ISymbolManager.h index 0dc0a7dc3..6b840511f 100644 --- a/include/ISymbolManager.h +++ b/include/ISymbolManager.h @@ -26,7 +26,6 @@ class ISymbolManager { QString name; edb::address_t address; std::optional module; - }; public: diff --git a/include/Types.h b/include/Types.h index ca42e5153..3cb13a1b6 100644 --- a/include/Types.h +++ b/include/Types.h @@ -8,6 +8,7 @@ #define TYPES_H_20071127_ #include "Value.h" + #include namespace edb { @@ -23,11 +24,5 @@ enum EventStatus { } -/* Comment Type */ -struct Comment { - edb::address_t address; - QString comment; -}; - #include "ArchTypes.h" #endif diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 41985aadc..806100d43 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -2973,10 +2973,8 @@ void Debugger::setInitialDebuggerState() { SessionManager &session_manager = SessionManager::instance(); - if (Result session_error = session_manager.loadSession(filename)) { - QVariantList comments_data = session_manager.comments(); - cpuView_->restoreComments(comments_data); - } else { + Result session_error = session_manager.loadSession(filename); + if (!session_error) { QMessageBox::warning( this, tr("Error Loading Session"), diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index d78f80b27..0dc438b38 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -9,6 +9,7 @@ #include "IPlugin.h" #include "IProcess.h" #include "Module.h" +#include "QDisassemblyView.h" #include "SymbolManager.h" #include "edb.h" @@ -86,12 +87,14 @@ Result SessionManager::loadSession(const QString &filename) QJsonObject object = doc.object(); sessionData_ = object.toVariantMap(); - QString id = sessionData_[QStringLiteral("id")].toString(); - QString ts = sessionData_[QStringLiteral("timestamp")].toString(); - int version = sessionData_[QStringLiteral("version")].toInt(); - QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); + QString id = sessionData_[QStringLiteral("id")].toString(); + QString ts = sessionData_[QStringLiteral("timestamp")].toString(); + int version = sessionData_[QStringLiteral("version")].toInt(); + QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); + QVariantList comments = sessionData_[QStringLiteral("comments")].toList(); loadLabels(labels); + loadComments(comments); Q_UNUSED(ts) @@ -136,6 +139,7 @@ void SessionManager::saveSession(const QString &filename) { sessionData_[QStringLiteral("timestamp")] = QDateTime::currentDateTimeUtc(); sessionData_[QStringLiteral("plugin-data")] = plugin_data; sessionData_[QStringLiteral("labels")] = saveLabels(); + sessionData_[QStringLiteral("comments")] = saveComments(); auto object = QJsonObject::fromVariantMap(sessionData_); QJsonDocument doc(object); @@ -174,86 +178,59 @@ void SessionManager::loadPluginData() { } /** - * @brief Returns all comments in the session - * - * @return A list of all comments in the session. - */ -QVariantList SessionManager::comments() const { - return sessionData_[QStringLiteral("comments")].toList(); -} - -/** - * @brief Adds a comment to the session + * @brief Saves the labels to a QVariantMap for session persistence. * - * @param c The comment to add. + * @return A QVariantMap containing the labels. */ -void SessionManager::addComment(const Comment &c) { +QVariantList SessionManager::saveLabels() const { + QVector labels = edb::v1::symbol_manager().labelData(); - QVariantList comments_data = sessionData_[QStringLiteral("comments")].toList(); + QVariantList label_data; - QVariantMap comment; - comment[QStringLiteral("address")] = c.address.toHexString(); - comment[QStringLiteral("comment")] = c.comment; + for (const auto &label : labels) { - // Check if we already have an entry with the same address and overwrite it - auto it = std::find_if(comments_data.begin(), comments_data.end(), [&comment](QVariant entry) { - QVariantMap data = entry.toMap(); - return data[QStringLiteral("address")] == comment[QStringLiteral("address")]; - }); + edb::address_t address = label.module ? label.address - label.module->baseAddress : edb::address_t(); + QString name = label.name; - if (it != comments_data.end()) { - *it = comment; - } else { - comments_data.push_back(comment); + QVariantMap entry; + entry[QStringLiteral("module")] = label.module ? label.module->name : QString(); + entry[QStringLiteral("offset")] = address.toHexString(); + entry[QStringLiteral("name")] = name; + label_data.push_back(entry); } - sessionData_[QStringLiteral("comments")] = comments_data; + return label_data; } /** - * @brief Removes a comment from the session_data + * @brief Saves the comments to a QVariantMap for session persistence. * - * @param address The address of the comment to remove. + * @return A QVariantMap containing the comments. */ -void SessionManager::removeComment(edb::address_t address) { - QString hexAddressString = address.toHexString(); - QVariantList comments_data = sessionData_[QStringLiteral("comments")].toList(); +QVariantList SessionManager::saveComments() const { - auto it = std::find_if(comments_data.begin(), comments_data.end(), [&hexAddressString](QVariant entry) { - QVariantMap data = entry.toMap(); - return data[QStringLiteral("address")] == hexAddressString; - }); - - if (it != comments_data.end()) { - comments_data.erase(it); + auto cpuView = qobject_cast(edb::v1::disassembly_widget()); + if (!cpuView) { + qDebug() << "Failed to get disassembly widget"; + return {}; } + QVector comments = cpuView->commentData(); - sessionData_[QStringLiteral("comments")] = comments_data; -} + QVariantList comment_data; -/** - * @brief Saves the labels to a QVariantMap for session persistence. - * - * @return A QVariantMap containing the labels. - */ -QVariantList SessionManager::saveLabels() const { - QVector labels = edb::v1::symbol_manager().labelData(); + for (const auto &comment : comments) { - QVariantList label_data; - - for (const auto &label : labels) { - - edb::address_t address = label.module ? label.address - label.module->baseAddress : edb::address_t(); - QString name = label.name; + edb::address_t address = comment.module ? comment.address - comment.module->baseAddress : edb::address_t(); + QString comment_text = comment.comment; QVariantMap entry; - entry[QStringLiteral("module")] = label.module ? label.module->name : QString(); + entry[QStringLiteral("module")] = comment.module ? comment.module->name : QString(); entry[QStringLiteral("offset")] = address.toHexString(); - entry[QStringLiteral("name")] = name; - label_data.push_back(entry); + entry[QStringLiteral("name")] = comment_text; + comment_data.push_back(entry); } - return label_data; + return comment_data; } /** @@ -300,18 +277,90 @@ void SessionManager::loadLabels(const QVariantList &labels) { } } +/** + * @brief Loads the comments from a QVariantMap for session restoration. + * + * @param comments A QVariantList containing the comments to load. + */ +void SessionManager::loadComments(const QVariantList &comments) { + qDebug("Loading comments"); + + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + + auto cpuView = qobject_cast(edb::v1::disassembly_widget()); + if (!cpuView) { + qDebug() << "Failed to get disassembly widget"; + return; + } + + for (auto &entry : comments) { + auto comment = entry.value(); + + QString module_name = comment[QStringLiteral("module")].toString(); + QString offset_str = comment[QStringLiteral("offset")].toString(); + QString name = comment[QStringLiteral("name")].toString(); + + edb::address_t offset = edb::address_t::fromHexString(offset_str); + + // Figure out which module this bookmark belongs to and add it if the module is loaded + auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { + return edb::v2::compare_module_names(module.name, module_name); + }); + + if (it != modules.end()) { + edb::address_t address = offset + it->baseAddress; + cpuView->addComment(address, name); + continue; + } else { + + // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded + Comment entry; + entry.comment = name; + entry.module = edb::v2::module_for_address(offset); + entry.address = offset; + deferredComments_.push_back(entry); + } + } +} + void SessionManager::libraryEvent(const Module &module, bool loaded) { if (loaded) { - auto it = std::remove_if(deferredLabels_.begin(), deferredLabels_.end(), [&module, this](const LabelEntry &entry) { - if (edb::v2::compare_module_names(entry.module, module.name)) { - edb::address_t offset = edb::address_t::fromHexString(entry.offset); - edb::address_t address = offset + module.baseAddress; - edb::v1::symbol_manager().setLabel(address, entry.name); - return true; + // Load labels for the module that was just loaded + { + auto it = std::remove_if(deferredLabels_.begin(), deferredLabels_.end(), [&module, this](const LabelEntry &entry) { + if (edb::v2::compare_module_names(entry.module, module.name)) { + edb::address_t offset = edb::address_t::fromHexString(entry.offset); + edb::address_t address = offset + module.baseAddress; + edb::v1::symbol_manager().setLabel(address, entry.name); + return true; + } + + return false; + }); + deferredLabels_.erase(it, deferredLabels_.end()); + } + + // Load comments for the module that was just loaded + { + auto cpuView = qobject_cast(edb::v1::disassembly_widget()); + if (!cpuView) { + qDebug() << "Failed to get disassembly widget"; + return; } - return false; - }); - deferredLabels_.erase(it, deferredLabels_.end()); + auto it = std::remove_if(deferredComments_.begin(), deferredComments_.end(), [&module, cpuView, this](const Comment &entry) { + if (entry.module && edb::v2::compare_module_names(entry.module->name, module.name)) { + edb::address_t address = entry.address + module.baseAddress; + cpuView->addComment(address, entry.comment); + return true; + } + + return false; + }); + deferredComments_.erase(it, deferredComments_.end()); + } } } diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index a3fc0fecc..d76cfb437 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -7,6 +7,7 @@ #ifndef SESSION_MANAGER_H_20170928_ #define SESSION_MANAGER_H_20170928_ +#include "Comment.h" #include "SessionError.h" #include "Status.h" #include "Types.h" @@ -50,12 +51,17 @@ class SessionManager { private: void loadPluginData(); + QVariantList saveLabels() const; void loadLabels(const QVariantList &labels); + QVariantList saveComments() const; + void loadComments(const QVariantList &comments); + private: QVariantMap sessionData_; std::vector deferredLabels_; + std::vector deferredComments_; }; #endif diff --git a/src/widgets/QDisassemblyView.cpp b/src/widgets/QDisassemblyView.cpp index fbf3576c5..e6d32821c 100644 --- a/src/widgets/QDisassemblyView.cpp +++ b/src/widgets/QDisassemblyView.cpp @@ -16,7 +16,6 @@ #include "IThread.h" #include "Instruction.h" #include "MemoryRegions.h" -#include "SessionManager.h" #include "State.h" #include "SyntaxHighlighter.h" #include "Theme.h" @@ -1228,8 +1227,10 @@ void QDisassemblyView::drawComments(QPainter &painter, const DrawingContext *ctx painter.setPen(palette().color(ctx->group, QPalette::Text)); } - QString annotation = comments_.value(address, QString()); - auto &&inst = instructions_[line]; + const Comment &comment_entry = comments_.value(address, Comment()); + QString annotation = comment_entry.comment; + + auto &&inst = instructions_[line]; if (annotation.isEmpty() && inst && !is_jump(inst) && !is_call(inst)) { // draw ascii representations of immediate constants size_t op_count = inst.operandCount(); @@ -2184,7 +2185,7 @@ std::shared_ptr QDisassemblyView::region() const { } /** - * @brief Adds a comment to the comment hash and persists it in the session manager. + * @brief Adds a comment to the comment hash. * * @param address The address to associate the comment with. * @param comment The comment text to add. @@ -2192,19 +2193,19 @@ std::shared_ptr QDisassemblyView::region() const { void QDisassemblyView::addComment(edb::address_t address, QString comment) { Comment temp_comment = { address, - comment}; - SessionManager::instance().addComment(temp_comment); - comments_.insert(address, comment); + comment, + edb::v2::module_for_address(address), + }; + comments_.insert(address, temp_comment); } /** - * @brief Removes a comment associated with the given address from the comment hash and the session manager. + * @brief Removes a comment associated with the given address from the comment hash. * * @param address The address of the comment to remove. * @return The number of comments removed (0 if no comment was found for the address, 1 if a comment was successfully removed). */ int QDisassemblyView::removeComment(edb::address_t address) { - SessionManager::instance().removeComment(address); return comments_.remove(address); } @@ -2215,7 +2216,9 @@ int QDisassemblyView::removeComment(edb::address_t address) { * @return The comment string associated with the address, or an empty string if no comment exists for that address. */ QString QDisassemblyView::getComment(edb::address_t address) const { - return comments_.value(address, QString()); + + const Comment &comment_entry = comments_.value(address, Comment()); + return comment_entry.comment; } /** @@ -2275,16 +2278,20 @@ void QDisassemblyView::restoreState(const QByteArray &stateBuffer) { line4_ = state.line4; } } + /** - * @brief Restores comments from a QVariantList containing comment data, inserting them into the comment hash based on their associated addresses. + * @brief Retrieves all comments stored in the disassembly view. * - * @param comments_data A QVariantList containing comment data, where each entry is a QVariantMap with "address" and "comment" keys. + * @return All comments, where each comment is represented as a Comment object with its associated address and text. */ -void QDisassemblyView::restoreComments(QVariantList &comments_data) { - for (const QVariant &entry : comments_data) { - QVariantMap data = entry.toMap(); - if (const Result addr = edb::v1::string_to_address(data[QStringLiteral("address")].toString())) { - comments_.insert(*addr, data[QStringLiteral("comment")].toString()); - } +QVector QDisassemblyView::commentData() const { + + QVector comment_vector; + comment_vector.reserve(comments_.size()); + + for (const auto &comment_entry : comments_) { + comment_vector.push_back(comment_entry); } + + return comment_vector; } diff --git a/src/widgets/QDisassemblyView.h b/src/widgets/QDisassemblyView.h index 9423df1e9..d8c3bbfcb 100644 --- a/src/widgets/QDisassemblyView.h +++ b/src/widgets/QDisassemblyView.h @@ -7,6 +7,7 @@ #ifndef QDISASSEMBLY_VIEW_H_20061101_ #define QDISASSEMBLY_VIEW_H_20061101_ +#include "Comment.h" #include "NavigationHistory.h" #include "Types.h" @@ -69,10 +70,13 @@ class QDisassemblyView final : public QAbstractScrollArea { [[nodiscard]] QByteArray saveState() const; [[nodiscard]] QString getComment(edb::address_t address) const; [[nodiscard]] std::shared_ptr region() const; + int removeComment(edb::address_t address); void addComment(edb::address_t address, QString comment); void clearComments(); - void restoreComments(QVariantList &); + + [[nodiscard]] QVector commentData() const; + void restoreState(const QByteArray &stateBuffer); void setSelectedAddress(edb::address_t address); @@ -162,7 +166,7 @@ public Q_SLOTS: std::vector instructions_; SyntaxHighlighter *highlighter_; bool showAddressSeparator_; - QHash comments_; + QHash comments_; NavigationHistory history_; QSvgRenderer breakpointRenderer_; QSvgRenderer currentRenderer_; From ffb5cb66d69e02b5040b940de2352b4c4d154272 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 22:14:51 -0400 Subject: [PATCH 10/20] Fix Qt5 build --- plugins/Bookmarks/BookmarkWidget.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/Bookmarks/BookmarkWidget.cpp b/plugins/Bookmarks/BookmarkWidget.cpp index b860815fd..6551d7b50 100644 --- a/plugins/Bookmarks/BookmarkWidget.cpp +++ b/plugins/Bookmarks/BookmarkWidget.cpp @@ -281,11 +281,10 @@ void BookmarkWidget::on_tableView_customContextMenuRequested(const QPoint &pos) /** * @brief Returns a copy of the current bookmark list. * - * @return A QList of BookmarksModel::Bookmark entries. + * @return A QVector of BookmarksModel::Bookmark entries. */ -QList BookmarkWidget::entries() const { - const QVector &bookmarks = model_->bookmarks(); - return bookmarks.toList(); +QVector BookmarkWidget::entries() const { + return model_->bookmarks(); } // This is copied from Debugger::createAction, so really there should either be a class that implements From 13998ba60dc9cec1f0a8732eeb08eaceb605bd76 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 22:26:23 -0400 Subject: [PATCH 11/20] and finally, laying the groundwork for save/restore of breakpoints --- src/session/SessionManager.cpp | 42 ++++++++++++++++++++++++++++++---- src/session/SessionManager.h | 13 ++++++++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 0dc438b38..43bfc6173 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -87,14 +87,16 @@ Result SessionManager::loadSession(const QString &filename) QJsonObject object = doc.object(); sessionData_ = object.toVariantMap(); - QString id = sessionData_[QStringLiteral("id")].toString(); - QString ts = sessionData_[QStringLiteral("timestamp")].toString(); - int version = sessionData_[QStringLiteral("version")].toInt(); - QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); - QVariantList comments = sessionData_[QStringLiteral("comments")].toList(); + QString id = sessionData_[QStringLiteral("id")].toString(); + QString ts = sessionData_[QStringLiteral("timestamp")].toString(); + int version = sessionData_[QStringLiteral("version")].toInt(); + QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); + QVariantList comments = sessionData_[QStringLiteral("comments")].toList(); + QVariantList breakpoints = sessionData_[QStringLiteral("breakpoints")].toList(); loadLabels(labels); loadComments(comments); + loadBreakpoints(breakpoints); Q_UNUSED(ts) @@ -140,6 +142,7 @@ void SessionManager::saveSession(const QString &filename) { sessionData_[QStringLiteral("plugin-data")] = plugin_data; sessionData_[QStringLiteral("labels")] = saveLabels(); sessionData_[QStringLiteral("comments")] = saveComments(); + sessionData_[QStringLiteral("breakpoints")] = saveBreakpoints(); auto object = QJsonObject::fromVariantMap(sessionData_); QJsonDocument doc(object); @@ -364,3 +367,32 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { } } } + +QVariantList SessionManager::saveBreakpoints() const { + QVariantList breakpoints; + + const IDebugger::BreakpointList breakpoint_state = edb::v1::debugger_core->backupBreakpoints(); + for (const std::shared_ptr &bp : breakpoint_state) { + if (bp->internal()) { + continue; + } + + // TODO(eteran): make relative to the module + + const edb::address_t address = bp->address(); + const QString condition = bp->condition; + const bool onetime = bp->oneTime(); + + QVariantMap entry; + entry[QStringLiteral("module")] = QString(); + entry[QStringLiteral("offset")] = address.toHexString(); + entry[QStringLiteral("condition")] = condition; + entry[QStringLiteral("one_time")] = onetime; + breakpoints.push_back(entry); + } + + return breakpoints; +} + +void SessionManager::loadBreakpoints(const QVariantList &breakpoints) { +} diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index d76cfb437..769f610c1 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -29,6 +29,13 @@ class SessionManager { QString offset; }; + struct BreakpointEntry { + QString module; + QString offset; + QString condition; + QString type; + }; + private: SessionManager() = default; @@ -42,9 +49,6 @@ class SessionManager { public: Result loadSession(const QString &filename); void saveSession(const QString &filename); - [[nodiscard]] QVariantList comments() const; - void addComment(const Comment &c); - void removeComment(edb::address_t address); public: void libraryEvent(const Module &module, bool loaded); @@ -58,6 +62,9 @@ class SessionManager { QVariantList saveComments() const; void loadComments(const QVariantList &comments); + QVariantList saveBreakpoints() const; + void loadBreakpoints(const QVariantList &breakpoints); + private: QVariantMap sessionData_; std::vector deferredLabels_; From fae13d1133774da83d4472d7816162bca2489914 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Tue, 14 Jul 2026 22:27:56 -0400 Subject: [PATCH 12/20] this seems approximately right for dynamic restoration of bps... but there's still a bunch to go --- src/session/SessionManager.cpp | 17 +++++++++++++++++ src/session/SessionManager.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 43bfc6173..0c8936a31 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -365,6 +365,23 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { }); deferredComments_.erase(it, deferredComments_.end()); } + + // Load breakpoints for the module that was just loaded + { + auto it = std::remove_if(deferredBreakpoints_.begin(), deferredBreakpoints_.end(), [&module, this](const BreakpointEntry &entry) { + if (edb::v2::compare_module_names(entry.module, module.name)) { + edb::address_t offset = edb::address_t::fromHexString(entry.offset); + edb::address_t address = offset + module.baseAddress; + std::shared_ptr bp = edb::v1::debugger_core->addBreakpoint(address); + if (bp) { + bp->condition = entry.condition; + } + return true; + } + return false; + }); + deferredBreakpoints_.erase(it, deferredBreakpoints_.end()); + } } } diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 769f610c1..2d0a184e5 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -69,6 +69,7 @@ class SessionManager { QVariantMap sessionData_; std::vector deferredLabels_; std::vector deferredComments_; + std::vector deferredBreakpoints_; }; #endif From 4934286444767db28d6e6cb4de68cd6fd55fd023 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 09:34:33 -0400 Subject: [PATCH 13/20] saving oneTime detail --- src/session/SessionManager.cpp | 1 + src/session/SessionManager.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 0c8936a31..3708577c9 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -375,6 +375,7 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { std::shared_ptr bp = edb::v1::debugger_core->addBreakpoint(address); if (bp) { bp->condition = entry.condition; + bp->setOneTime(entry.oneTime); } return true; } diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 2d0a184e5..4c100507a 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -34,6 +34,7 @@ class SessionManager { QString offset; QString condition; QString type; + bool oneTime = false; }; private: From 5339a9b63bf3b8934962ebb3c0cc4b2aab3338fb Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 09:54:14 -0400 Subject: [PATCH 14/20] got breakpoint restoration working too --- include/IBreakpoint.h | 3 ++ .../arch/arm-generic/Breakpoint.cpp | 4 +- .../arch/arm-generic/Breakpoint.h | 2 + .../arch/x86-generic/Breakpoint.cpp | 2 + .../arch/x86-generic/Breakpoint.h | 3 ++ src/session/SessionManager.cpp | 45 ++++++++++++++++++- src/session/SessionManager.h | 1 - 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/include/IBreakpoint.h b/include/IBreakpoint.h index 6ee675c59..4ad076a69 100644 --- a/include/IBreakpoint.h +++ b/include/IBreakpoint.h @@ -8,9 +8,11 @@ #define IBREAKPOINT_H_20060720_ #include "Types.h" +#include "Module.h" #include #include +#include class QByteArray; @@ -45,6 +47,7 @@ class IBreakpoint { [[nodiscard]] virtual const uint8_t *originalBytes() const = 0; [[nodiscard]] virtual size_t size() const = 0; [[nodiscard]] virtual TypeId type() const = 0; + [[nodiscard]] virtual std::optional module() const = 0; public: virtual bool enable() = 0; diff --git a/plugins/DebuggerCore/arch/arm-generic/Breakpoint.cpp b/plugins/DebuggerCore/arch/arm-generic/Breakpoint.cpp index af62ce131..88db5f6d0 100644 --- a/plugins/DebuggerCore/arch/arm-generic/Breakpoint.cpp +++ b/plugins/DebuggerCore/arch/arm-generic/Breakpoint.cpp @@ -19,7 +19,7 @@ const std::vector BreakpointInstructionThumb_LE = {0x01, 0xde}; // correctly use it see GDB's thumb_get_next_pcs_raw function and comments // around arm_linux_thumb2_le_breakpoint array. const std::vector BreakpointInstructionThumb2_LE = {0xf0, 0xf7, 0x00, 0xa0}; // udf.w #0 -// This one generates SIGILL both in ARM32 and Thumb mode. In ARM23 mode it's decoded as UDF 0xDDE0, while +// This one generates SIGILL both in ARM32 and Thumb mode. In ARM32 mode it's decoded as UDF 0xDDE0, while // in Thumb it's a sequence `1: UDF 0xF0; B 1b`, which does stop the process even if it lands in the // middle (on the second half-word), although the signal still occurs at the first half-word. const std::vector BreakpointInstructionUniversalThumbARM_LE = {0xf0, 0xde, 0xfd, 0xe7}; @@ -38,6 +38,8 @@ Breakpoint::Breakpoint(edb::address_t address) if (!enable()) { throw BreakpointCreationError(); } + + module_ = edb::v2::module_for_address(address); } /** diff --git a/plugins/DebuggerCore/arch/arm-generic/Breakpoint.h b/plugins/DebuggerCore/arch/arm-generic/Breakpoint.h index 691a52b26..c6aadceae 100644 --- a/plugins/DebuggerCore/arch/arm-generic/Breakpoint.h +++ b/plugins/DebuggerCore/arch/arm-generic/Breakpoint.h @@ -43,6 +43,7 @@ class Breakpoint final : public IBreakpoint { [[nodiscard]] size_t size() const override { return originalBytes_.size(); } [[nodiscard]] const uint8_t *originalBytes() const override { return &originalBytes_[0]; } [[nodiscard]] IBreakpoint::TypeId type() const override { return type_; } + [[nodiscard]] std::optional module() const override { return module_; } [[nodiscard]] static std::vector supportedTypes(); [[nodiscard]] static std::vector possibleRewindSizes(); @@ -63,6 +64,7 @@ class Breakpoint final : public IBreakpoint { bool oneTime_ = false; bool internal_ = false; Type type_; + std::optional module_; }; } diff --git a/plugins/DebuggerCore/arch/x86-generic/Breakpoint.cpp b/plugins/DebuggerCore/arch/x86-generic/Breakpoint.cpp index accf6055a..3174bf668 100644 --- a/plugins/DebuggerCore/arch/x86-generic/Breakpoint.cpp +++ b/plugins/DebuggerCore/arch/x86-generic/Breakpoint.cpp @@ -38,6 +38,8 @@ Breakpoint::Breakpoint(edb::address_t address) if (!this->enable()) { throw BreakpointCreationError(); } + + module_ = edb::v2::module_for_address(address); } /** diff --git a/plugins/DebuggerCore/arch/x86-generic/Breakpoint.h b/plugins/DebuggerCore/arch/x86-generic/Breakpoint.h index 209ad83eb..a97d0a009 100644 --- a/plugins/DebuggerCore/arch/x86-generic/Breakpoint.h +++ b/plugins/DebuggerCore/arch/x86-generic/Breakpoint.h @@ -9,6 +9,7 @@ #include "IBreakpoint.h" #include "Util.h" + #include #include #include @@ -50,6 +51,7 @@ class Breakpoint final : public IBreakpoint { [[nodiscard]] size_t size() const override { return originalBytes_.size(); } [[nodiscard]] const uint8_t *originalBytes() const override { return originalBytes_.data(); } [[nodiscard]] IBreakpoint::TypeId type() const override { return type_; } + [[nodiscard]] std::optional module() const override { return module_; } [[nodiscard]] static std::vector supportedTypes(); [[nodiscard]] static std::vector possibleRewindSizes(); @@ -70,6 +72,7 @@ class Breakpoint final : public IBreakpoint { bool oneTime_ = false; bool internal_ = false; Type type_; + std::optional module_; }; } diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 3708577c9..cbbe987a4 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -400,10 +400,11 @@ QVariantList SessionManager::saveBreakpoints() const { const edb::address_t address = bp->address(); const QString condition = bp->condition; const bool onetime = bp->oneTime(); + const edb::address_t offset = bp->module() ? address - bp->module()->baseAddress : edb::address_t(); QVariantMap entry; - entry[QStringLiteral("module")] = QString(); - entry[QStringLiteral("offset")] = address.toHexString(); + entry[QStringLiteral("module")] = bp->module() ? bp->module()->name : QString(); + entry[QStringLiteral("offset")] = offset.toHexString(); entry[QStringLiteral("condition")] = condition; entry[QStringLiteral("one_time")] = onetime; breakpoints.push_back(entry); @@ -413,4 +414,44 @@ QVariantList SessionManager::saveBreakpoints() const { } void SessionManager::loadBreakpoints(const QVariantList &breakpoints) { + qDebug("Loading breakpoints"); + + IProcess *process = edb::v1::debugger_core->process(); + Q_ASSERT(process); + + QSet modules = process->loadedModules(); + + for (auto &entry : breakpoints) { + auto breakpoint = entry.value(); + + QString module_name = breakpoint[QStringLiteral("module")].toString(); + QString offset_str = breakpoint[QStringLiteral("offset")].toString(); + QString condition = breakpoint[QStringLiteral("condition")].toString(); + bool one_time = breakpoint[QStringLiteral("one_time")].toBool(); + + edb::address_t offset = edb::address_t::fromHexString(offset_str); + + // Figure out which module this bookmark belongs to and add it if the module is loaded + auto it = std::find_if(modules.begin(), modules.end(), [&module_name](const Module &module) { + return edb::v2::compare_module_names(module.name, module_name); + }); + + if (it != modules.end()) { + edb::address_t address = offset + it->baseAddress; + std::shared_ptr bp = edb::v1::debugger_core->addBreakpoint(address); + if (bp) { + bp->condition = condition; + bp->setOneTime(one_time); + } + continue; + } else { + + // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded + BreakpointEntry entry; + entry.condition = condition; + entry.module = module_name; + entry.offset = offset_str; + deferredBreakpoints_.push_back(entry); + } + } } diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 4c100507a..80e431333 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -33,7 +33,6 @@ class SessionManager { QString module; QString offset; QString condition; - QString type; bool oneTime = false; }; From 9092332fcccd06cdddcdcc90ba6d407b01c4f0c5 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 10:15:28 -0400 Subject: [PATCH 15/20] normalize some logging --- plugins/Analyzer/Analyzer.cpp | 3 +- plugins/Analyzer/AnalyzerWidget.cpp | 4 +-- plugins/BinaryInfo/ELFXX.cpp | 2 +- plugins/BinaryInfo/symbols.cpp | 2 +- plugins/CheckVersion/CheckVersion.cpp | 2 +- plugins/DebuggerCore/DebuggerCoreBase.cpp | 2 +- .../DebuggerCore/unix/linux/DebuggerCore.cpp | 4 +-- .../unix/linux/PlatformProcess.cpp | 2 +- plugins/HeapAnalyzer/DialogHeap.cpp | 12 +++---- src/Debugger.cpp | 30 ++++++++-------- src/SymbolManager.cpp | 2 +- src/edb.cpp | 2 +- src/graph/GraphWidget.cpp | 4 +-- src/session/SessionManager.cpp | 36 +++++++++++++------ src/widgets/QDisassemblyView.cpp | 5 +-- 15 files changed, 65 insertions(+), 47 deletions(-) diff --git a/plugins/Analyzer/Analyzer.cpp b/plugins/Analyzer/Analyzer.cpp index dc9955245..7f995e90a 100644 --- a/plugins/Analyzer/Analyzer.cpp +++ b/plugins/Analyzer/Analyzer.cpp @@ -114,7 +114,8 @@ void set_function_types(IAnalyzer::FunctionMap *results) { Function &function = it.value(); if (function.empty()) { - qDebug() << "HERE:" << it.key().toString(); + qDebug() << "Function at " << function.entryAddress().toHexString() << " is empty, skipping type classification"; + continue; } Q_ASSERT(!function.empty()); diff --git a/plugins/Analyzer/AnalyzerWidget.cpp b/plugins/Analyzer/AnalyzerWidget.cpp index e0562dc2d..60b26cbbb 100644 --- a/plugins/Analyzer/AnalyzerWidget.cpp +++ b/plugins/Analyzer/AnalyzerWidget.cpp @@ -133,14 +133,14 @@ void AnalyzerWidget::paintEvent(QPaintEvent * /*event*/) { const int64_t renderTime = timer.elapsed(); if (renderTime > 8) { - qDebug() << "AnalyzerWidget: Painting took longer than desired: " << renderTime << "ms"; + qDebug() << "[AnalyzerWidget]: Painting took longer than desired: " << renderTime << "ms"; } } /** * @brief Handles a click on the overview bar by jumping the disassembly view to the corresponding address. * - * @param event + * @param event The mouse press event that triggered this function. */ void AnalyzerWidget::mousePressEvent(QMouseEvent *event) { diff --git a/plugins/BinaryInfo/ELFXX.cpp b/plugins/BinaryInfo/ELFXX.cpp index 8726355d6..1bd87b208 100644 --- a/plugins/BinaryInfo/ELFXX.cpp +++ b/plugins/BinaryInfo/ELFXX.cpp @@ -106,7 +106,7 @@ ELFXX::ELFXX(const std::shared_ptr ®ion) for (uint16_t entry = 0; entry < header_.e_phnum; ++entry) { if (!process->readBytes(phdr_base + (phdr_size * entry), &phdr, sizeof(phdr_type))) { - qDebug() << "Failed to read program header"; + qDebug("Failed to read program header"); break; } diff --git a/plugins/BinaryInfo/symbols.cpp b/plugins/BinaryInfo/symbols.cpp index 12fcaedd0..ba49548bd 100644 --- a/plugins/BinaryInfo/symbols.cpp +++ b/plugins/BinaryInfo/symbols.cpp @@ -477,7 +477,7 @@ bool generate_symbols_internal(QFile &file, std::shared_ptr &debugFile, s return true; } - qDebug() << "unknown file type"; + qDebug("unknown file type"); } return false; diff --git a/plugins/CheckVersion/CheckVersion.cpp b/plugins/CheckVersion/CheckVersion.cpp index 1f0956f89..e21077f11 100644 --- a/plugins/CheckVersion/CheckVersion.cpp +++ b/plugins/CheckVersion/CheckVersion.cpp @@ -169,7 +169,7 @@ void CheckVersion::requestFinished(QNetworkReply *reply) { return; } - qDebug("comparing versions: [%d] [%d]", edb::v1::int_version(version), edb::v1::edb_version()); + qDebug("[CheckVersion] comparing versions: [%d] [%d]", edb::v1::int_version(version), edb::v1::edb_version()); if (edb::v1::int_version(version) > edb::v1::edb_version()) { QMessageBox msg; diff --git a/plugins/DebuggerCore/DebuggerCoreBase.cpp b/plugins/DebuggerCore/DebuggerCoreBase.cpp index eb0d8d455..9e532ca5d 100644 --- a/plugins/DebuggerCore/DebuggerCoreBase.cpp +++ b/plugins/DebuggerCore/DebuggerCoreBase.cpp @@ -42,7 +42,7 @@ std::shared_ptr DebuggerCoreBase::addBreakpoint(edb::address_t addr return nullptr; } catch (const BreakpointCreationError &) { - qDebug() << "Failed to create breakpoint"; + qDebug("Failed to create breakpoint"); return nullptr; } } diff --git a/plugins/DebuggerCore/unix/linux/DebuggerCore.cpp b/plugins/DebuggerCore/unix/linux/DebuggerCore.cpp index b17a2570f..fe6b61e96 100644 --- a/plugins/DebuggerCore/unix/linux/DebuggerCore.cpp +++ b/plugins/DebuggerCore/unix/linux/DebuggerCore.cpp @@ -829,7 +829,7 @@ void DebuggerCore::detectCpuMode() { if (!errno) { if (cs == userCodeSegment32_) { if (pointerSize_ == sizeof(uint64_t)) { - qDebug() << "Debuggee is now 32 bit"; + qDebug("Debuggee is now 32 bit"); cpuMode_ = CpuMode::x86_32; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_X86); } @@ -839,7 +839,7 @@ void DebuggerCore::detectCpuMode() { if (cs == userCodeSegment64_) { if (pointerSize_ == sizeof(uint32_t)) { - qDebug() << "Debuggee is now 64 bit"; + qDebug("Debuggee is now 64 bit"); cpuMode_ = CpuMode::x86_64; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_AMD64); } diff --git a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp index ad54243e9..f47a071f3 100644 --- a/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp +++ b/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp @@ -1007,7 +1007,7 @@ edb::address_t get_debug_pointer(const IProcess *process, edb::address_t phdr_me if (process->readBytes(phdr_memaddr + i * sizeof(elf_phdr), &phdr, sizeof(elf_phdr))) { if (phdr.p_type == PT_DYNAMIC) { if (phdr.p_memsz > 0x100000) { - qDebug() << "[get_debug_pointer] p_memsz is too large, skipping"; + qDebug("[get_debug_pointer] p_memsz is too large, skipping"); return 0; } diff --git a/plugins/HeapAnalyzer/DialogHeap.cpp b/plugins/HeapAnalyzer/DialogHeap.cpp index e6d10b028..02ec0189c 100644 --- a/plugins/HeapAnalyzer/DialogHeap.cpp +++ b/plugins/HeapAnalyzer/DialogHeap.cpp @@ -345,11 +345,11 @@ void DialogHeap::processPotentialPointers(const QHash targets; - qDebug() << "[Heap Analyzer] collecting possible targets addresses"; + qDebug("[Heap Analyzer] collecting possible targets addresses"); for (int row = 0; row < model_->rowCount(); ++row) { QModelIndex index = model_->index(row, 0); if (auto result = static_cast(index.internalPointer())) { @@ -362,7 +362,7 @@ void DialogHeap::detectPointers() { } } - qDebug() << "[Heap Analyzer] linking blocks to target addresses"; + qDebug("[Heap Analyzer] linking blocks to target addresses"); for (int row = 0; row < model_->rowCount(); ++row) { QModelIndex index = model_->index(row, 0); processPotentialPointers(targets, index); @@ -553,13 +553,13 @@ void DialogHeap::doFind() { if (heap_symbol_start != 0) { process->readBytes(heap_symbol_start, &start_address, edb::v1::pointer_size()); } else { - qDebug() << "[Heap Analyzer] __curbrk symbol not found in ld, falling back on heuristic! This may or may not work."; + qDebug("[Heap Analyzer] __curbrk symbol not found in ld, falling back on heuristic! This may or may not work."); } if (heap_symbol_end != 0) { process->readBytes(heap_symbol_end, &end_address, edb::v1::pointer_size()); } else { - qDebug() << "[Heap Analyzer] __curbrk symbol not found in libc, falling back on heuristic! This may or may not work."; + qDebug("[Heap Analyzer] __curbrk symbol not found in libc, falling back on heuristic! This may or may not work."); } if (start_address != 0 && end_address != 0) { @@ -575,7 +575,7 @@ void DialogHeap::doFind() { }); if (it != regions.end()) { - qDebug() << "Found a memory region named '[heap]', assuming that it provides sane bounds"; + qDebug("Found a memory region named '[heap]', assuming that it provides sane bounds"); if (start_address == 0) { start_address = (*it)->start(); diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 806100d43..0196f6b47 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -192,11 +192,11 @@ class RunUntilRet : public IDebugEventHandler { * then finally for the syscall bug. */ if (trap_reason == IDebugEvent::TRAP_BREAKPOINT) { - qDebug() << "Trap breakpoint"; + qDebug("Trap breakpoint"); // Take care of exit/terminated conditions; address == 0 may suffice to catch all, but not 100% sure. if (reason == IDebugEvent::EVENT_EXITED || reason == IDebugEvent::EVENT_TERMINATED || address == 0) { - qDebug() << "The process is no longer running."; + qDebug("The process is no longer running."); return pass_back_to_debugger(); } @@ -218,10 +218,10 @@ class RunUntilRet : public IDebugEventHandler { // If it wasn't internal, it was a user breakpoint. Pass back to Debugger. if (!bp->internal()) { - qDebug() << "Previous was not an internal breakpoint."; + qDebug("Previous was not an internal breakpoint."); return pass_back_to_debugger(); } - qDebug() << "Previous was an internal breakpoint."; + qDebug("Previous was an internal breakpoint."); bp->disable(); edb::v1::debugger_core->removeBreakpoint(bp->address()); } else { @@ -232,14 +232,14 @@ class RunUntilRet : public IDebugEventHandler { // If we are on our ret (or the instr after?), then ret. if (address == returnAddress_) { - qDebug() << QStringLiteral("On our terminator at 0x%1").arg(address, 0, 16); + qDebug() << "On our terminator at " << address.toHexString(); if (is_instruction_ret(address)) { - qDebug() << "Found ret; passing back to debugger"; + qDebug("Found ret; passing back to debugger"); return pass_back_to_debugger(); } // If not a ret, then step so we can find the next block terminator. - qDebug() << "Not ret. Single-stepping"; + qDebug("Not ret. Single-stepping"); return edb::DEBUG_CONTINUE_STEP; } @@ -259,14 +259,14 @@ class RunUntilRet : public IDebugEventHandler { qDebug() << QStringLiteral("Found terminator %1 at 0x%2").arg(QString::fromStdString(inst.mnemonic())).arg(address, 0, 16); // If we already had a breakpoint there, then just continue. if (std::shared_ptr bp = edb::v1::debugger_core->findBreakpoint(address)) { - qDebug() << QStringLiteral("Already a breakpoint at terminator 0x%1").arg(address, 0, 16); + qDebug() << "Already a breakpoint at terminator: " << address.toHexString(); return edb::DEBUG_CONTINUE; } // Otherwise, attempt to set a breakpoint there and continue. if (std::shared_ptr bp = edb::v1::debugger_core->addBreakpoint(address)) { ownBreakpoints_.emplace_back(address, bp); - qDebug() << QStringLiteral("Setting breakpoint at terminator 0x%1").arg(address, 0, 16); + qDebug() << "Setting breakpoint at terminator: " << address.toHexString(); bp->setInternal(true); bp->setOneTime(true); // If the 0xcc get's rm'd on next event, then // don't set it one time; we'll handle it manually @@ -297,7 +297,7 @@ class RunUntilRet : public IDebugEventHandler { return pass_back_to_debugger(); } - qDebug() << "The process is no longer running."; + qDebug("The process is no longer running."); return pass_back_to_debugger(); } @@ -637,10 +637,10 @@ QString Debugger::createTty() { const int rv = select(fd + 1, &set, nullptr, nullptr, &timeout); switch (rv) { case -1: - qDebug() << "An error occurred while attempting to get the TTY of the terminal sub-process"; + qDebug("An error occurred while attempting to get the TTY of the terminal sub-process"); break; case 0: - qDebug() << "A Timeout occurred while attempting to get the TTY of the terminal sub-process"; + qDebug("A Timeout occurred while attempting to get the TTY of the terminal sub-process"); break; default: if (read(fd, buf, sizeof(buf)) != -1) { @@ -2878,7 +2878,7 @@ QString Debugger::sessionFilename() const { QString session_path = edb::v1::config().session_path; if (session_path.isEmpty()) { if (show_path_notice) { - qDebug() << "No session path specified. Please set it in the preferences to enable sessions."; + qDebug("No session path specified. Please set it in the preferences to enable sessions."); show_path_notice = false; } return QString(); @@ -3641,7 +3641,7 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add QSet modules = process->loadedModules(); QSet added_modules = modules - loadedModules_; - qDebug() << "Added modules:"; + qDebug("Added modules:"); for (const Module &module : added_modules) { qDebug() << " " << module.name << "@" << edb::v1::format_pointer(module.baseAddress); @@ -3668,7 +3668,7 @@ void Debugger::handle_library_event(IProcess *process, [[maybe_unused]] edb::add QSet modules = process->loadedModules(); QSet removed_modules = loadedModules_ - modules; - qDebug() << "Removed modules:"; + qDebug("Removed modules:"); for (const Module &module : removed_modules) { qDebug() << " " << module.name << "@" << edb::v1::format_pointer(module.baseAddress); diff --git a/src/SymbolManager.cpp b/src/SymbolManager.cpp index 3abc6f30a..9dd29d659 100644 --- a/src/SymbolManager.cpp +++ b/src/SymbolManager.cpp @@ -45,7 +45,7 @@ void SymbolManager::loadSymbolFile(const QString &filename, edb::address_t base) if (symbol_directory.isEmpty()) { if (showPathNotice_) { - qDebug() << "No symbol path specified. Please set it in the preferences to enable symbols."; + qDebug("No symbol path specified. Please set it in the preferences to enable symbols."); showPathNotice_ = false; } return; diff --git a/src/edb.cpp b/src/edb.cpp index 41c4db119..f13a6cca8 100644 --- a/src/edb.cpp +++ b/src/edb.cpp @@ -1018,7 +1018,7 @@ std::shared_ptr primary_data_region() { } } - qDebug() << "primary data region not found!"; + qDebug("primary data region not found!"); return nullptr; } diff --git a/src/graph/GraphWidget.cpp b/src/graph/GraphWidget.cpp index 1942d2b78..7347f8938 100644 --- a/src/graph/GraphWidget.cpp +++ b/src/graph/GraphWidget.cpp @@ -173,7 +173,7 @@ void GraphWidget::layout() { inLayout_ = true; - qDebug() << "Starting Layout Engine"; + qDebug("Starting Layout Engine"); gvFreeLayout(context_, graph_); gvLayout(context_, graph_, "dot"); @@ -194,7 +194,7 @@ void GraphWidget::layout() { } } - qDebug() << "Layout Complete"; + qDebug("Layout Complete"); // make the scene HUGE so it feels like you can just scroll forever scene()->setSceneRect(sceneRect().adjusted(-ScenePadding, -ScenePadding, +ScenePadding, +ScenePadding)); diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index cbbe987a4..10e29b2a6 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -23,7 +23,7 @@ namespace { -constexpr int SessionFileVersion = 1; +constexpr int SessionFileVersion = 2; const auto SessionFileIdString = QStringLiteral("edb-session"); } @@ -181,9 +181,9 @@ void SessionManager::loadPluginData() { } /** - * @brief Saves the labels to a QVariantMap for session persistence. + * @brief Saves the labels for session persistence. * - * @return A QVariantMap containing the labels. + * @return A QVariantList containing the labels. */ QVariantList SessionManager::saveLabels() const { QVector labels = edb::v1::symbol_manager().labelData(); @@ -206,15 +206,15 @@ QVariantList SessionManager::saveLabels() const { } /** - * @brief Saves the comments to a QVariantMap for session persistence. + * @brief Saves the comments for session persistence. * - * @return A QVariantMap containing the comments. + * @return A QVariantList containing the comments. */ QVariantList SessionManager::saveComments() const { auto cpuView = qobject_cast(edb::v1::disassembly_widget()); if (!cpuView) { - qDebug() << "Failed to get disassembly widget"; + qDebug("Failed to get disassembly widget"); return {}; } QVector comments = cpuView->commentData(); @@ -237,7 +237,7 @@ QVariantList SessionManager::saveComments() const { } /** - * @brief Loads the labels from a QVariantMap for session restoration. + * @brief Loads the labels for session restoration. * * @param labels A QVariantList containing the labels to load. */ @@ -281,7 +281,7 @@ void SessionManager::loadLabels(const QVariantList &labels) { } /** - * @brief Loads the comments from a QVariantMap for session restoration. + * @brief Loads the comments for session restoration. * * @param comments A QVariantList containing the comments to load. */ @@ -295,7 +295,7 @@ void SessionManager::loadComments(const QVariantList &comments) { auto cpuView = qobject_cast(edb::v1::disassembly_widget()); if (!cpuView) { - qDebug() << "Failed to get disassembly widget"; + qDebug("Failed to get disassembly widget"); return; } @@ -329,6 +329,12 @@ void SessionManager::loadComments(const QVariantList &comments) { } } +/** + * @brief Handles library events for loading and unloading modules. + * + * @param module The module that was loaded or unloaded. + * @param loaded True if the module was loaded, false if it was unloaded. + */ void SessionManager::libraryEvent(const Module &module, bool loaded) { if (loaded) { // Load labels for the module that was just loaded @@ -350,7 +356,7 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { { auto cpuView = qobject_cast(edb::v1::disassembly_widget()); if (!cpuView) { - qDebug() << "Failed to get disassembly widget"; + qDebug("Failed to get disassembly widget"); return; } @@ -386,6 +392,11 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { } } +/** + * @brief Saves the breakpoints for session persistence. + * + * @return A QVariantList containing the breakpoints. + */ QVariantList SessionManager::saveBreakpoints() const { QVariantList breakpoints; @@ -413,6 +424,11 @@ QVariantList SessionManager::saveBreakpoints() const { return breakpoints; } +/** + * @brief Loads the breakpoints for session restoration. + * + * @param breakpoints A QVariantList containing the breakpoints to load. + */ void SessionManager::loadBreakpoints(const QVariantList &breakpoints) { qDebug("Loading breakpoints"); diff --git a/src/widgets/QDisassemblyView.cpp b/src/widgets/QDisassemblyView.cpp index e6d32821c..73da55db9 100644 --- a/src/widgets/QDisassemblyView.cpp +++ b/src/widgets/QDisassemblyView.cpp @@ -778,7 +778,7 @@ int QDisassemblyView::updateDisassembly(int lines_to_render) { const edb::address_t start_address = addressOffset_ + verticalScrollBar()->value(); if (!edb::v1::get_instruction_bytes(start_address, inst_buf, &bufsize)) { - qDebug() << "Failed to read" << bufsize << "bytes from" << QString::number(start_address, 16); + qDebug() << "[QDisassemblyView] Failed to read" << bufsize << "bytes from" << start_address.toHexString(); lines_to_render = 0; } @@ -806,6 +806,7 @@ int QDisassemblyView::updateDisassembly(int lines_to_render) { } line++; } + Q_ASSERT(line <= lines_to_render); if (lines_to_render != line) { partialLastLine_ = false; @@ -1685,7 +1686,7 @@ void QDisassemblyView::paintEvent(QPaintEvent * /*event*/) { const int64_t renderTime = timer.elapsed(); if (renderTime > 50) { - qDebug() << "Painting took longer than desired: " << renderTime << "ms"; + qDebug() << "[QDisassemblyView] Painting took longer than desired: " << renderTime << "ms"; } } From 79a49179d160fedc76430de655e6357b13a3c0f8 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 10:49:14 -0400 Subject: [PATCH 16/20] a little less back and forth between JSON objects and variants --- src/session/SessionManager.cpp | 84 +++++++++++++++++----------------- src/session/SessionManager.h | 17 +++---- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 10e29b2a6..7020c2453 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -84,19 +84,15 @@ Result SessionManager::loadSession(const QString &filename) return make_unexpected(session_error); } - QJsonObject object = doc.object(); - sessionData_ = object.toVariantMap(); + QJsonObject sessionData = doc.object(); - QString id = sessionData_[QStringLiteral("id")].toString(); - QString ts = sessionData_[QStringLiteral("timestamp")].toString(); - int version = sessionData_[QStringLiteral("version")].toInt(); - QVariantList labels = sessionData_[QStringLiteral("labels")].toList(); - QVariantList comments = sessionData_[QStringLiteral("comments")].toList(); - QVariantList breakpoints = sessionData_[QStringLiteral("breakpoints")].toList(); - - loadLabels(labels); - loadComments(comments); - loadBreakpoints(breakpoints); + const auto id = sessionData[QStringLiteral("id")].toString(); + const auto ts = sessionData[QStringLiteral("timestamp")].toString(); + const auto version = sessionData[QStringLiteral("version")].toInt(); + const auto labels = sessionData[QStringLiteral("labels")].toArray(); + const auto comments = sessionData[QStringLiteral("comments")].toArray(); + const auto breakpoints = sessionData[QStringLiteral("breakpoints")].toArray(); + const auto plugin_data = sessionData[QStringLiteral("plugin-data")].toObject(); Q_UNUSED(ts) @@ -108,7 +104,10 @@ Result SessionManager::loadSession(const QString &filename) } qDebug("Loading session file"); - loadPluginData(); // First, load the plugin-data + loadLabels(labels); + loadComments(comments); + loadBreakpoints(breakpoints); + loadPluginData(plugin_data); // First, load the plugin-data return {}; } @@ -136,16 +135,16 @@ void SessionManager::saveSession(const QString &filename) { } } - sessionData_[QStringLiteral("version")] = SessionFileVersion; - sessionData_[QStringLiteral("id")] = SessionFileIdString; // just so we can sanity check things - sessionData_[QStringLiteral("timestamp")] = QDateTime::currentDateTimeUtc(); - sessionData_[QStringLiteral("plugin-data")] = plugin_data; - sessionData_[QStringLiteral("labels")] = saveLabels(); - sessionData_[QStringLiteral("comments")] = saveComments(); - sessionData_[QStringLiteral("breakpoints")] = saveBreakpoints(); + QJsonObject sessionData; + sessionData[QStringLiteral("version")] = SessionFileVersion; + sessionData[QStringLiteral("id")] = SessionFileIdString; // just so we can sanity check things + sessionData[QStringLiteral("timestamp")] = QDateTime::currentDateTimeUtc().toString(); + sessionData[QStringLiteral("plugin-data")] = QJsonObject::fromVariantMap(plugin_data); + sessionData[QStringLiteral("labels")] = saveLabels(); + sessionData[QStringLiteral("comments")] = saveComments(); + sessionData[QStringLiteral("breakpoints")] = saveBreakpoints(); - auto object = QJsonObject::fromVariantMap(sessionData_); - QJsonDocument doc(object); + QJsonDocument doc(sessionData); QByteArray json = doc.toJson(); QFile file(filename); @@ -158,17 +157,16 @@ void SessionManager::saveSession(const QString &filename) { /** * @brief Loads the plugin data from the session. */ -void SessionManager::loadPluginData() { +void SessionManager::loadPluginData(const QJsonObject &plugin_data) { qDebug("Loading plugin-data"); - QVariantMap plugin_data = sessionData_[QStringLiteral("plugin-data")].toMap(); for (auto it = plugin_data.begin(); it != plugin_data.end(); ++it) { for (QObject *plugin : edb::v1::plugin_list()) { if (auto p = qobject_cast(plugin)) { if (const QMetaObject *const meta = plugin->metaObject()) { auto name = QString::fromLocal8Bit(meta->className()); - QVariantMap data = it.value().toMap(); + QVariantMap data = it.value().toObject().toVariantMap(); if (name == it.key()) { p->restoreState(data); @@ -185,17 +183,17 @@ void SessionManager::loadPluginData() { * * @return A QVariantList containing the labels. */ -QVariantList SessionManager::saveLabels() const { +QJsonArray SessionManager::saveLabels() const { QVector labels = edb::v1::symbol_manager().labelData(); - QVariantList label_data; + QJsonArray label_data; for (const auto &label : labels) { edb::address_t address = label.module ? label.address - label.module->baseAddress : edb::address_t(); QString name = label.name; - QVariantMap entry; + QJsonObject entry; entry[QStringLiteral("module")] = label.module ? label.module->name : QString(); entry[QStringLiteral("offset")] = address.toHexString(); entry[QStringLiteral("name")] = name; @@ -210,7 +208,7 @@ QVariantList SessionManager::saveLabels() const { * * @return A QVariantList containing the comments. */ -QVariantList SessionManager::saveComments() const { +QJsonArray SessionManager::saveComments() const { auto cpuView = qobject_cast(edb::v1::disassembly_widget()); if (!cpuView) { @@ -219,14 +217,14 @@ QVariantList SessionManager::saveComments() const { } QVector comments = cpuView->commentData(); - QVariantList comment_data; + QJsonArray comment_data; for (const auto &comment : comments) { edb::address_t address = comment.module ? comment.address - comment.module->baseAddress : edb::address_t(); QString comment_text = comment.comment; - QVariantMap entry; + QJsonObject entry; entry[QStringLiteral("module")] = comment.module ? comment.module->name : QString(); entry[QStringLiteral("offset")] = address.toHexString(); entry[QStringLiteral("name")] = comment_text; @@ -239,9 +237,9 @@ QVariantList SessionManager::saveComments() const { /** * @brief Loads the labels for session restoration. * - * @param labels A QVariantList containing the labels to load. + * @param labels A QJsonArray containing the labels to load. */ -void SessionManager::loadLabels(const QVariantList &labels) { +void SessionManager::loadLabels(const QJsonArray &labels) { qDebug("Loading labels"); @@ -251,7 +249,7 @@ void SessionManager::loadLabels(const QVariantList &labels) { QSet modules = process->loadedModules(); for (auto &entry : labels) { - auto label = entry.value(); + auto label = entry.toObject(); QString module_name = label[QStringLiteral("module")].toString(); QString offset_str = label[QStringLiteral("offset")].toString(); @@ -283,9 +281,9 @@ void SessionManager::loadLabels(const QVariantList &labels) { /** * @brief Loads the comments for session restoration. * - * @param comments A QVariantList containing the comments to load. + * @param comments A QJsonArray containing the comments to load. */ -void SessionManager::loadComments(const QVariantList &comments) { +void SessionManager::loadComments(const QJsonArray &comments) { qDebug("Loading comments"); IProcess *process = edb::v1::debugger_core->process(); @@ -300,7 +298,7 @@ void SessionManager::loadComments(const QVariantList &comments) { } for (auto &entry : comments) { - auto comment = entry.value(); + auto comment = entry.toObject(); QString module_name = comment[QStringLiteral("module")].toString(); QString offset_str = comment[QStringLiteral("offset")].toString(); @@ -397,8 +395,8 @@ void SessionManager::libraryEvent(const Module &module, bool loaded) { * * @return A QVariantList containing the breakpoints. */ -QVariantList SessionManager::saveBreakpoints() const { - QVariantList breakpoints; +QJsonArray SessionManager::saveBreakpoints() const { + QJsonArray breakpoints; const IDebugger::BreakpointList breakpoint_state = edb::v1::debugger_core->backupBreakpoints(); for (const std::shared_ptr &bp : breakpoint_state) { @@ -413,7 +411,7 @@ QVariantList SessionManager::saveBreakpoints() const { const bool onetime = bp->oneTime(); const edb::address_t offset = bp->module() ? address - bp->module()->baseAddress : edb::address_t(); - QVariantMap entry; + QJsonObject entry; entry[QStringLiteral("module")] = bp->module() ? bp->module()->name : QString(); entry[QStringLiteral("offset")] = offset.toHexString(); entry[QStringLiteral("condition")] = condition; @@ -427,9 +425,9 @@ QVariantList SessionManager::saveBreakpoints() const { /** * @brief Loads the breakpoints for session restoration. * - * @param breakpoints A QVariantList containing the breakpoints to load. + * @param breakpoints A QJsonArray containing the breakpoints to load. */ -void SessionManager::loadBreakpoints(const QVariantList &breakpoints) { +void SessionManager::loadBreakpoints(const QJsonArray &breakpoints) { qDebug("Loading breakpoints"); IProcess *process = edb::v1::debugger_core->process(); @@ -438,7 +436,7 @@ void SessionManager::loadBreakpoints(const QVariantList &breakpoints) { QSet modules = process->loadedModules(); for (auto &entry : breakpoints) { - auto breakpoint = entry.value(); + auto breakpoint = entry.toObject(); QString module_name = breakpoint[QStringLiteral("module")].toString(); QString offset_str = breakpoint[QStringLiteral("offset")].toString(); diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index 80e431333..ee74c78aa 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -13,6 +13,8 @@ #include "Types.h" #include +#include +#include #include #include #include @@ -54,19 +56,18 @@ class SessionManager { void libraryEvent(const Module &module, bool loaded); private: - void loadPluginData(); + void loadPluginData(const QJsonObject &plugin_data); - QVariantList saveLabels() const; - void loadLabels(const QVariantList &labels); + QJsonArray saveLabels() const; + void loadLabels(const QJsonArray &labels); - QVariantList saveComments() const; - void loadComments(const QVariantList &comments); + QJsonArray saveComments() const; + void loadComments(const QJsonArray &comments); - QVariantList saveBreakpoints() const; - void loadBreakpoints(const QVariantList &breakpoints); + QJsonArray saveBreakpoints() const; + void loadBreakpoints(const QJsonArray &breakpoints); private: - QVariantMap sessionData_; std::vector deferredLabels_; std::vector deferredComments_; std::vector deferredBreakpoints_; From a632036715e6fdee5577fe62136b1c6d34206172 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 11:09:24 -0400 Subject: [PATCH 17/20] a little bit of minor refactoring --- include/Comment.h | 2 +- src/session/SessionManager.cpp | 35 +++++++++++++++++-------------- src/widgets/NavigationHistory.cpp | 28 +++++++++++++++++++++---- src/widgets/NavigationHistory.h | 6 ++++-- src/widgets/QDisassemblyView.cpp | 14 ++++++------- 5 files changed, 55 insertions(+), 30 deletions(-) diff --git a/include/Comment.h b/include/Comment.h index 2d77b18bc..693340a07 100644 --- a/include/Comment.h +++ b/include/Comment.h @@ -10,8 +10,8 @@ #include struct Comment { - edb::address_t address; QString comment; + edb::address_t address; std::optional module; }; diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 7020c2453..ca1d36389 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -104,10 +104,10 @@ Result SessionManager::loadSession(const QString &filename) } qDebug("Loading session file"); + loadPluginData(plugin_data); loadLabels(labels); loadComments(comments); loadBreakpoints(breakpoints); - loadPluginData(plugin_data); // First, load the plugin-data return {}; } @@ -156,6 +156,8 @@ void SessionManager::saveSession(const QString &filename) { /** * @brief Loads the plugin data from the session. + * + * @param plugin_data The QJsonObject containing the plugin data to load. */ void SessionManager::loadPluginData(const QJsonObject &plugin_data) { @@ -269,11 +271,11 @@ void SessionManager::loadLabels(const QJsonArray &labels) { } else { // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded - LabelEntry entry; - entry.name = name; - entry.module = module_name; - entry.offset = offset_str; - deferredLabels_.push_back(entry); + deferredLabels_.push_back(LabelEntry{ + name, + module_name, + offset_str, + }); } } } @@ -318,11 +320,11 @@ void SessionManager::loadComments(const QJsonArray &comments) { } else { // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded - Comment entry; - entry.comment = name; - entry.module = edb::v2::module_for_address(offset); - entry.address = offset; - deferredComments_.push_back(entry); + deferredComments_.push_back(Comment{ + name, + offset, + edb::v2::module_for_address(offset), + }); } } } @@ -461,11 +463,12 @@ void SessionManager::loadBreakpoints(const QJsonArray &breakpoints) { } else { // If the module is not loaded, store the bookmark entry for later restoration when the module is loaded - BreakpointEntry entry; - entry.condition = condition; - entry.module = module_name; - entry.offset = offset_str; - deferredBreakpoints_.push_back(entry); + deferredBreakpoints_.push_back(BreakpointEntry{ + module_name, + offset_str, + condition, + one_time, + }); } } } diff --git a/src/widgets/NavigationHistory.cpp b/src/widgets/NavigationHistory.cpp index 4a1104403..5e1086021 100644 --- a/src/widgets/NavigationHistory.cpp +++ b/src/widgets/NavigationHistory.cpp @@ -6,10 +6,20 @@ #include "NavigationHistory.h" +/** + * @brief Constructs a NavigationHistory object with the specified maximum count. + * + * @param count The maximum number of addresses to store in the navigation history. + */ NavigationHistory::NavigationHistory(int count) : maxCount_(count) { } +/** + * @brief Adds an address to the navigation history. + * + * @param address The address to add to the history. + */ void NavigationHistory::add(edb::address_t address) { if (list_.size() == maxCount_) { list_.removeFirst(); @@ -37,9 +47,14 @@ void NavigationHistory::add(edb::address_t address) { lastOp_ = LastOp::None; } -edb::address_t NavigationHistory::getNext() { +/** + * @brief Retrieves the next address in the navigation history. + * + * @return The next address in the history, or std::nullopt if there is no next address. + */ +std::optional NavigationHistory::getNext() { if (list_.isEmpty()) { - return edb::address_t(0); + return std::nullopt; } if (pos_ != (list_.size() - 1)) { @@ -50,9 +65,14 @@ edb::address_t NavigationHistory::getNext() { return list_.at(pos_); } -edb::address_t NavigationHistory::getPrev() { +/** + * @brief Retrieves the previous address in the navigation history. + * + * @return The previous address in the history, or std::nullopt if there is no previous address. + */ +std::optional NavigationHistory::getPrev() { if (list_.isEmpty()) { - return edb::address_t(0); + return std::nullopt; } if (pos_ != 0) { diff --git a/src/widgets/NavigationHistory.h b/src/widgets/NavigationHistory.h index 11ced1886..64fd005f0 100644 --- a/src/widgets/NavigationHistory.h +++ b/src/widgets/NavigationHistory.h @@ -11,6 +11,8 @@ #include +#include + class NavigationHistory { enum class LastOp { None = 0, @@ -21,8 +23,8 @@ class NavigationHistory { public: explicit NavigationHistory(int count = 100); void add(edb::address_t address); - [[nodiscard]] edb::address_t getNext(); - [[nodiscard]] edb::address_t getPrev(); + [[nodiscard]] std::optional getNext(); + [[nodiscard]] std::optional getPrev(); private: QList list_; diff --git a/src/widgets/QDisassemblyView.cpp b/src/widgets/QDisassemblyView.cpp index 73da55db9..c4aa398df 100644 --- a/src/widgets/QDisassemblyView.cpp +++ b/src/widgets/QDisassemblyView.cpp @@ -266,14 +266,14 @@ void QDisassemblyView::keyPressEvent(QKeyEvent *event) { setSelectedAddress(showAddresses_[selectedLine]); } } else if (event->key() == Qt::Key_Minus) { - edb::address_t prev_addr = history_.getPrev(); - if (prev_addr != 0) { - edb::v1::jump_to_address(prev_addr); + std::optional prev_addr = history_.getPrev(); + if (prev_addr) { + edb::v1::jump_to_address(*prev_addr); } } else if (event->key() == Qt::Key_Plus) { - edb::address_t next_addr = history_.getNext(); - if (next_addr != 0) { - edb::v1::jump_to_address(next_addr); + std::optional next_addr = history_.getNext(); + if (next_addr) { + edb::v1::jump_to_address(*next_addr); } } else if (event->key() == Qt::Key_Down && (event->modifiers() & Qt::ControlModifier)) { const int address = verticalScrollBar()->value(); @@ -2193,8 +2193,8 @@ std::shared_ptr QDisassemblyView::region() const { */ void QDisassemblyView::addComment(edb::address_t address, QString comment) { Comment temp_comment = { - address, comment, + address, edb::v2::module_for_address(address), }; comments_.insert(address, temp_comment); From 5238e033d7831ff9c25ba03a465c6806811f13e1 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 11:43:17 -0400 Subject: [PATCH 18/20] minor simplifications --- src/session/SessionManager.cpp | 1 + src/session/SessionManager.h | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index ca1d36389..ab2497b75 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/src/session/SessionManager.h b/src/session/SessionManager.h index ee74c78aa..04bab6fac 100644 --- a/src/session/SessionManager.h +++ b/src/session/SessionManager.h @@ -13,13 +13,13 @@ #include "Types.h" #include -#include -#include #include -#include -#include + +#include class Module; +class QJsonArray; +class QJsonObject; class SessionManager { Q_DECLARE_TR_FUNCTIONS(SessionManager) @@ -56,16 +56,13 @@ class SessionManager { void libraryEvent(const Module &module, bool loaded); private: - void loadPluginData(const QJsonObject &plugin_data); - - QJsonArray saveLabels() const; - void loadLabels(const QJsonArray &labels); - - QJsonArray saveComments() const; - void loadComments(const QJsonArray &comments); - QJsonArray saveBreakpoints() const; + QJsonArray saveComments() const; + QJsonArray saveLabels() const; void loadBreakpoints(const QJsonArray &breakpoints); + void loadComments(const QJsonArray &comments); + void loadLabels(const QJsonArray &labels); + void loadPluginData(const QJsonObject &plugin_data); private: std::vector deferredLabels_; From e391aaab1751ce01ce7ecdeae877112ea6aa11fa Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 11:47:40 -0400 Subject: [PATCH 19/20] fixing Qt5 --- src/session/SessionManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index ab2497b75..a4d4df649 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -251,7 +251,7 @@ void SessionManager::loadLabels(const QJsonArray &labels) { QSet modules = process->loadedModules(); - for (auto &entry : labels) { + for (const QJsonValue &entry : labels) { auto label = entry.toObject(); QString module_name = label[QStringLiteral("module")].toString(); @@ -300,7 +300,7 @@ void SessionManager::loadComments(const QJsonArray &comments) { return; } - for (auto &entry : comments) { + for (const QJsonValue &entry : comments) { auto comment = entry.toObject(); QString module_name = comment[QStringLiteral("module")].toString(); @@ -438,7 +438,7 @@ void SessionManager::loadBreakpoints(const QJsonArray &breakpoints) { QSet modules = process->loadedModules(); - for (auto &entry : breakpoints) { + for (const QJsonValue &entry : breakpoints) { auto breakpoint = entry.toObject(); QString module_name = breakpoint[QStringLiteral("module")].toString(); From 95fb3db1f30280753693be061f10c3589c2bafe6 Mon Sep 17 00:00:00 2001 From: Evan Teran Date: Wed, 15 Jul 2026 11:51:48 -0400 Subject: [PATCH 20/20] zero is just fine here --- src/capstone-edb/include/Instruction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/capstone-edb/include/Instruction.h b/src/capstone-edb/include/Instruction.h index 0048c9deb..11eedc7e9 100644 --- a/src/capstone-edb/include/Instruction.h +++ b/src/capstone-edb/include/Instruction.h @@ -67,7 +67,7 @@ class EDB_EXPORT Instruction { #elif defined(EDB_ARM32) || defined(EDB_ARM64) return insn_ ? insn_->detail->arm.op_count : 0; #else -#error "What to return here?" + return 0; #endif } [[nodiscard]] std::size_t byteSize() const { return insn_ ? insn_->size : 1; }