diff --git a/core/adapters/dbgengttdadapter.cpp b/core/adapters/dbgengttdadapter.cpp index f68e1690..ef87bcdf 100644 --- a/core/adapters/dbgengttdadapter.cpp +++ b/core/adapters/dbgengttdadapter.cpp @@ -27,6 +27,30 @@ bool DbgEngTTDAdapter::ExecuteWithArgsInternal(const std::string& path, const st scope = SettingsResourceScope; auto inputFile = adapterSettings->Get("common.inputFile", data, &scope); + // A trace is not optional for replay, but a missing one only surfaces later as an opaque OpenDumpFile + // HRESULT, so check it up front. The UI blocks this in the adapter settings dialog; this covers the + // headless and scripted paths, plus the launches where that dialog is skipped. + std::error_code ec; + if (tracePath.empty() || !std::filesystem::exists(tracePath, ec)) + { + DebuggerEvent event; + event.type = LaunchFailureEventType; + if (tracePath.empty()) + { + event.data.errorData.error = "No TTD trace specified. Set the trace path in the debug adapter " + "settings, or record a new trace first."; + event.data.errorData.shortError = "No TTD trace specified"; + } + else + { + event.data.errorData.error = fmt::format("The TTD trace \"{}\" does not exist. Set the trace path in " + "the debug adapter settings.", tracePath); + event.data.errorData.shortError = "TTD trace not found"; + } + PostDebuggerEvent(event); + return false; + } + if (this->m_dbgengInitialized) { this->Reset(); } diff --git a/ui/adaptersettings.cpp b/ui/adaptersettings.cpp index fc79d6a6..38b1dab0 100644 --- a/ui/adaptersettings.cpp +++ b/ui/adaptersettings.cpp @@ -18,13 +18,15 @@ limitations under the License. #include "uicontext.h" #include "qfiledialog.h" #include "settingsview.h" +#include +#include using namespace BinaryNinjaDebuggerAPI; using namespace BinaryNinja; using namespace std; AdapterSettingsDialog::AdapterSettingsDialog(QWidget* parent, DbgRef controller, const std::string& highlightGroup) : - QDialog(), m_controller(controller) + QDialog(), m_controller(controller), m_highlightGroup(highlightGroup) { setWindowTitle("Debug Adapter Settings"); setAttribute(Qt::WA_DeleteOnClose); @@ -158,8 +160,46 @@ QWidget* AdapterSettingsDialog::getWidgetForAdapter(const QString& adapter) } +// The settings view writes each value as it is edited, so the current values can be read straight back from the +// adapter settings here. Only the settings whose absence is guaranteed to fail the operation are checked, so that +// the dialog does not get in the way of anything the adapter itself is willing to accept. +bool AdapterSettingsDialog::validateSettings() +{ + if ((m_highlightGroup != "launch") || (m_adapterEntry->currentText() != "DBGENG_TTD")) + return true; + + auto adapterSettings = m_controller->GetAdapterSettings(); + if (!adapterSettings) + return true; + + BNSettingsScope scope = SettingsResourceScope; + auto tracePath = adapterSettings->Get("launch.trace_path", m_controller->GetData(), &scope); + if (tracePath.empty()) + { + QMessageBox::warning(this, "No Trace Specified", + "The TTD adapter replays a previously recorded trace, so a trace path is required.\n\n" + "Set \"Trace Path\" in the launch settings, or record a new trace first."); + return false; + } + + std::error_code ec; + if (!std::filesystem::exists(tracePath, ec)) + { + QMessageBox::warning(this, "Trace Not Found", + QString("The trace file\n\n%1\n\ndoes not exist. Set \"Trace Path\" in the launch settings to an " + "existing trace.").arg(QString::fromStdString(tracePath))); + return false; + } + + return true; +} + + void AdapterSettingsDialog::apply() { + if (!validateSettings()) + return; + if (m_useSameSettingsCheckbox) m_controller->SetShowAdapterSettingsNextTime(!m_useSameSettingsCheckbox->isChecked()); accept(); diff --git a/ui/adaptersettings.h b/ui/adaptersettings.h index 0aa74d9f..ff5085c6 100644 --- a/ui/adaptersettings.h +++ b/ui/adaptersettings.h @@ -43,8 +43,10 @@ class AdapterSettingsDialog : public QDialog QMap m_viewMap; QLabel* m_noSettingsLabel; QCheckBox* m_useSameSettingsCheckbox = nullptr; + std::string m_highlightGroup; QWidget* getWidgetForAdapter(const QString& adapter); + bool validateSettings(); public: AdapterSettingsDialog(QWidget* parent, DbgRef controller, const std::string& highlightGroup = "");