From 694ba6385b378eee61afa5681f16663e2037766d Mon Sep 17 00:00:00 2001 From: Nav Date: Wed, 12 Apr 2023 22:47:11 +0100 Subject: [PATCH] Implemented restore snapshot function --- src/Insight/CMakeLists.txt | 1 + .../SnapshotManager/SnapshotManager.cpp | 152 +++++++++++++++++- .../SnapshotManager/SnapshotManager.hpp | 13 ++ .../TargetMemoryInspectionPane.cpp | 10 ++ .../TargetMemoryInspectionPane.hpp | 1 + 5 files changed, 176 insertions(+), 1 deletion(-) diff --git a/src/Insight/CMakeLists.txt b/src/Insight/CMakeLists.txt index f0d76b88..f42b8184 100755 --- a/src/Insight/CMakeLists.txt +++ b/src/Insight/CMakeLists.txt @@ -97,6 +97,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.cpp diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp index bfc20218..888a5ed2 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp @@ -1,11 +1,15 @@ #include "SnapshotManager.hpp" #include +#include #include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" -#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ConfirmationDialog.hpp" +#include "src/Insight/InsightSignals.hpp" #include "src/Insight/InsightWorker/Tasks/RetrieveMemorySnapshots.hpp" +#include "src/Insight/InsightWorker/Tasks/CaptureMemorySnapshot.hpp" +#include "src/Insight/InsightWorker/Tasks/WriteTargetMemory.hpp" #include "src/Insight/InsightWorker/InsightWorker.hpp" #include "src/Services/PathService.hpp" @@ -96,6 +100,34 @@ namespace Bloom::Widgets } ); + + QObject::connect( + this->snapshotListScene, + &ListScene::itemContextMenu, + this, + &SnapshotManager::onSnapshotItemContextMenu + ); + + QObject::connect( + this->restoreSnapshotAction, + &QAction::triggered, + this, + [this] { + if (this->contextMenuSnapshotItem != nullptr) { + this->restoreSnapshot(this->contextMenuSnapshotItem->memorySnapshot.id, true); + } + } + ); + + auto* insightSignals = InsightSignals::instance(); + + QObject::connect( + insightSignals, + &InsightSignals::targetStateUpdated, + this, + &SnapshotManager::onTargetStateChanged + ); + const auto retrieveSnapshotsTask = QSharedPointer( new RetrieveMemorySnapshots(this->memoryDescriptor.type), &QObject::deleteLater @@ -190,4 +222,122 @@ namespace Bloom::Widgets this->selectedItem = item; } + + void SnapshotManager::restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) { + const auto& snapshotIt = this->snapshotsById.find(snapshotId); + assert(snapshotIt != this->snapshotsById.end()); + const auto& snapshot = snapshotIt.value(); + + if (confirmationPromptEnabled) { + auto* confirmationDialog = new ConfirmationDialog( + "Restore snapshot", + "This operation will overwrite the entire address range of the target's " + + QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM") + + " with the contents of the selected snapshot.

Do you wish to proceed?", + "Proceed", + std::nullopt, + this + ); + + QObject::connect( + confirmationDialog, + &ConfirmationDialog::confirmed, + this, + [this, snapshotId] { + this->restoreSnapshot(snapshotId, false); + } + ); + + confirmationDialog->show(); + return; + } + + /* + * We don't restore any excluded regions from the snapshot, so we split the write operation into blocks of + * contiguous data, leaving out any address range that is part of an excluded region. + */ + auto writeBlocks = std::vector(); + + auto sortedExcludedRegions = std::map(); + std::transform( + snapshot.excludedRegions.begin(), + snapshot.excludedRegions.end(), + std::inserter(sortedExcludedRegions, sortedExcludedRegions.end()), + [] (const ExcludedMemoryRegion& excludedMemoryRegion) { + return std::pair(excludedMemoryRegion.addressRange.startAddress, &excludedMemoryRegion); + } + ); + + auto blockStartAddress = this->memoryDescriptor.addressRange.startAddress; + + for (const auto& [excludedRegionStartAddress, excludedRegion] : sortedExcludedRegions) { + assert(excludedRegionStartAddress >= this->memoryDescriptor.addressRange.startAddress); + assert(excludedRegion->addressRange.endAddress <= this->memoryDescriptor.addressRange.endAddress); + + const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress; + const auto dataEndOffset = excludedRegionStartAddress - this->memoryDescriptor.addressRange.startAddress; + + writeBlocks.emplace_back( + blockStartAddress, + Targets::TargetMemoryBuffer( + snapshot.data.begin() + dataBeginOffset, + snapshot.data.begin() + dataEndOffset + ) + ); + + blockStartAddress = excludedRegion->addressRange.endAddress + 1; + } + + if (blockStartAddress < this->memoryDescriptor.addressRange.endAddress) { + writeBlocks.emplace_back( + blockStartAddress, + Targets::TargetMemoryBuffer( + snapshot.data.begin() + (blockStartAddress - this->memoryDescriptor.addressRange.startAddress), + snapshot.data.end() + ) + ); + } + + const auto writeMemoryTask = QSharedPointer( + new WriteTargetMemory(this->memoryDescriptor, std::move(writeBlocks)), + &QObject::deleteLater + ); + + QObject::connect( + writeMemoryTask.get(), + &WriteTargetMemory::targetMemoryWritten, + this, + [this, snapshotId] () { + emit this->snapshotRestored(snapshotId); + } + ); + + emit this->insightWorkerTaskCreated(writeMemoryTask); + InsightWorker::queueTask(writeMemoryTask); + } + + void SnapshotManager::onSnapshotItemContextMenu(ListItem *item, QPoint sourcePosition) { + auto* snapshotItem = dynamic_cast(item); + + if (snapshotItem == nullptr) { + return; + } + + this->contextMenuSnapshotItem = snapshotItem; + + auto* menu = new QMenu(this); + menu->addAction(this->deleteSnapshotAction); + + menu->addSeparator(); + + menu->addAction(this->restoreSnapshotAction); + + this->restoreSnapshotAction->setEnabled(this->targetState == Targets::TargetState::STOPPED); + + menu->exec(sourcePosition); + } + + void SnapshotManager::onTargetStateChanged(Targets::TargetState newState) { + this->targetState = newState; + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp index e22237a5..8d0bdb1f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp @@ -6,11 +6,13 @@ #include #include #include +#include #include "src/Insight/UserInterfaces/InsightWindow/Widgets/PaneWidget.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListView.hpp" +#include "src/Targets/TargetState.hpp" #include "src/Targets/TargetMemory.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" @@ -43,6 +45,7 @@ namespace Bloom::Widgets signals: void insightWorkerTaskCreated(const QSharedPointer& task); + void snapshotRestored(const QString& snapshotId); protected: void resizeEvent(QResizeEvent* event) override; @@ -56,6 +59,8 @@ namespace Bloom::Widgets const std::vector& focusedMemoryRegions; const std::vector& excludedMemoryRegions; + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + QMap snapshotsById; QMap snapshotItemsById; @@ -68,6 +73,11 @@ namespace Bloom::Widgets ListView* snapshotListView = nullptr; ListScene* snapshotListScene = nullptr; + MemorySnapshotItem* contextMenuSnapshotItem = nullptr; + + QAction* deleteSnapshotAction = new QAction("Delete", this); + QAction* restoreSnapshotAction = new QAction("Restore", this); + void createSnapshot( const QString& name, const QString& description, @@ -76,5 +86,8 @@ namespace Bloom::Widgets ); void addSnapshot(MemorySnapshot&& snapshotTmp); void onSnapshotItemSelected(MemorySnapshotItem* item); + void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled); + void onSnapshotItemContextMenu(ListItem* item, QPoint sourcePosition); + void onTargetStateChanged(Targets::TargetState newState); }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp index 1dfb8fa6..0d163cf4 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp @@ -203,6 +203,12 @@ namespace Bloom::Widgets this, &TargetMemoryInspectionPane::onSubtaskCreated ); + + QObject::connect( + this->snapshotManager, + &SnapshotManager::snapshotRestored, + this, + &TargetMemoryInspectionPane::onSnapshotRestored ); QObject::connect( @@ -637,6 +643,10 @@ namespace Bloom::Widgets this->taskProgressIndicator->addTask(task); } + void TargetMemoryInspectionPane::onSnapshotRestored(const QString& snapshotId) { + this->refreshButton->click(); + } + void TargetMemoryInspectionPane::setStaleData(bool staleData) { this->staleData = staleData; this->staleDataLabelContainer->setVisible(this->staleData); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp index e5ffb235..86a779af 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp @@ -105,6 +105,7 @@ namespace Bloom::Widgets Targets::TargetMemoryAddressRange addressRange ); void onSubtaskCreated(const QSharedPointer& task); + void onSnapshotRestored(const QString& snapshotId); void setStaleData(bool staleData); }; }