// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "loadcoredialog.h" #include "debuggerkitinformation.h" #include "debuggertr.h" #include "gdb/gdbengine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Core; using namespace ProjectExplorer; using namespace Tasking; using namespace Utils; namespace Debugger::Internal { /////////////////////////////////////////////////////////////////////// // // AttachCoreDialog // /////////////////////////////////////////////////////////////////////// class AttachCoreDialogPrivate { public: KitChooser *kitChooser; PathChooser *symbolFileName; PathChooser *coreFileName; PathChooser *overrideStartScriptFileName; PathChooser *sysRootDirectory; FilePath debuggerPath; QDialogButtonBox *buttonBox; ProgressIndicator *progressIndicator; QLabel *progressLabel; TaskTree taskTree; expected_str coreFileResult; expected_str symbolFileResult; struct State { bool isValid() const { return validKit && validSymbolFilename && validCoreFilename; } bool validKit; bool validSymbolFilename; bool validCoreFilename; }; State getDialogState() const { State st; st.validKit = (kitChooser->currentKit() != nullptr); st.validSymbolFilename = symbolFileName->isValid(); st.validCoreFilename = coreFileName->isValid(); return st; } }; AttachCoreDialog::AttachCoreDialog(QWidget *parent) : QDialog(parent), d(new AttachCoreDialogPrivate) { setWindowTitle(Tr::tr("Load Core File")); d->buttonBox = new QDialogButtonBox(this); d->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); d->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); d->kitChooser = new KitChooser(this); d->kitChooser->setShowIcons(true); d->kitChooser->populate(); d->coreFileName = new PathChooser(this); d->coreFileName->setHistoryCompleter("Debugger.CoreFile.History"); d->coreFileName->setExpectedKind(PathChooser::File); d->coreFileName->setPromptDialogTitle(Tr::tr("Select Core File")); d->coreFileName->setAllowPathFromDevice(true); d->symbolFileName = new PathChooser(this); d->symbolFileName->setHistoryCompleter("Executable"); d->symbolFileName->setExpectedKind(PathChooser::File); d->symbolFileName->setPromptDialogTitle(Tr::tr("Select Executable or Symbol File")); d->symbolFileName->setAllowPathFromDevice(true); d->symbolFileName->setToolTip( Tr::tr("Select a file containing debug information corresponding to the core file. " "Typically, this is the executable or a *.debug file if the debug " "information is stored separately from the executable.")); d->overrideStartScriptFileName = new PathChooser(this); d->overrideStartScriptFileName->setHistoryCompleter("Debugger.StartupScript.History"); d->overrideStartScriptFileName->setExpectedKind(PathChooser::File); d->overrideStartScriptFileName->setPromptDialogTitle(Tr::tr("Select Startup Script")); d->sysRootDirectory = new PathChooser(this); d->sysRootDirectory->setHistoryCompleter("Debugger.SysRoot.History"); d->sysRootDirectory->setExpectedKind(PathChooser::Directory); d->sysRootDirectory->setPromptDialogTitle(Tr::tr("Select SysRoot Directory")); d->sysRootDirectory->setToolTip(Tr::tr( "This option can be used to override the kit's SysRoot setting")); d->progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Small, this); d->progressIndicator->setVisible(false); d->progressLabel = new QLabel(); d->progressLabel->setVisible(false); // clang-format off using namespace Layouting; Column { Form { Tr::tr("Kit:"), d->kitChooser, br, Tr::tr("Core file:"), d->coreFileName, br, Tr::tr("&Executable or symbol file:"), d->symbolFileName, br, Tr::tr("Override &start script:"), d->overrideStartScriptFileName, br, Tr::tr("Override S&ysRoot:"), d->sysRootDirectory, br, }, st, hr, Row { d->progressIndicator, d->progressLabel, d->buttonBox } }.attachTo(this); // clang-format on } AttachCoreDialog::~AttachCoreDialog() { delete d; } int AttachCoreDialog::exec() { connect(d->symbolFileName, &PathChooser::textChanged, this, &AttachCoreDialog::changed); connect(d->coreFileName, &PathChooser::textChanged, this, [this] { coreFileChanged(d->coreFileName->rawFilePath()); }); connect(d->kitChooser, &KitChooser::currentIndexChanged, this, &AttachCoreDialog::changed); connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(d->buttonBox, &QDialogButtonBox::accepted, this, &AttachCoreDialog::accepted); changed(); connect(&d->taskTree, &TaskTree::done, this, [&]() { setEnabled(true); d->progressIndicator->setVisible(false); d->progressLabel->setVisible(false); if (!d->coreFileResult) { QMessageBox::critical(this, Tr::tr("Error"), Tr::tr("Failed to copy core file to device: %1") .arg(d->coreFileResult.error())); return; } if (!d->symbolFileResult) { QMessageBox::critical(this, Tr::tr("Error"), Tr::tr("Failed to copy symbol file to device: %1") .arg(d->coreFileResult.error())); return; } accept(); }); connect(&d->taskTree, &TaskTree::progressValueChanged, this, [this](int value) { const QString text = Tr::tr("Copying files to device... %1/%2") .arg(value) .arg(d->taskTree.progressMaximum()); d->progressLabel->setText(text); }); AttachCoreDialogPrivate::State st = d->getDialogState(); if (!st.validKit) { d->kitChooser->setFocus(); } else if (!st.validCoreFilename) { d->coreFileName->setFocus(); } else if (!st.validSymbolFilename) { d->symbolFileName->setFocus(); } return QDialog::exec(); } void AttachCoreDialog::accepted() { const DebuggerItem *debuggerItem = Debugger::DebuggerKitAspect::debugger(kit()); const FilePath debuggerCommand = debuggerItem->command(); const auto copyFile = [debuggerCommand](const FilePath &srcPath) -> expected_str { if (!srcPath.isSameDevice(debuggerCommand)) { const expected_str tmpPath = debuggerCommand.tmpDir(); if (!tmpPath) return make_unexpected(tmpPath.error()); const FilePath pattern = (tmpPath.value() / (srcPath.fileName() + ".XXXXXXXXXXX")); const expected_str resultPath = pattern.createTempFile(); if (!resultPath) return make_unexpected(resultPath.error()); const expected_str result = srcPath.copyFile(resultPath.value()); if (!result) return make_unexpected(result.error()); return resultPath; } return srcPath; }; using ResultType = expected_str; const auto copyFileAsync = [=](QPromise &promise, const FilePath &srcPath) { promise.addResult(copyFile(srcPath)); }; const Group root = { parallel, AsyncTask{[=](auto &task) { task.setConcurrentCallData(copyFileAsync, this->coreFile()); }, [=](const auto &task) { d->coreFileResult = task.result(); }}, AsyncTask{[=](auto &task) { task.setConcurrentCallData(copyFileAsync, this->symbolFile()); }, [=](const auto &task) { d->symbolFileResult = task.result(); }}, }; d->taskTree.setupRoot(root); d->taskTree.start(); d->progressLabel->setText(Tr::tr("Copying files to device...")); setEnabled(false); d->progressIndicator->setVisible(true); d->progressLabel->setVisible(true); } void AttachCoreDialog::coreFileChanged(const FilePath &coreFile) { if (coreFile.osType() != OsType::OsTypeWindows && coreFile.exists()) { Kit *k = d->kitChooser->currentKit(); QTC_ASSERT(k, return); Runnable debugger = DebuggerKitAspect::runnable(k); CoreInfo cinfo = CoreInfo::readExecutableNameFromCore(debugger, coreFile); if (!cinfo.foundExecutableName.isEmpty()) d->symbolFileName->setFilePath(cinfo.foundExecutableName); else if (!d->symbolFileName->isValid() && !cinfo.rawStringFromCore.isEmpty()) d->symbolFileName->setFilePath(FilePath::fromString(cinfo.rawStringFromCore)); } changed(); } void AttachCoreDialog::changed() { AttachCoreDialogPrivate::State st = d->getDialogState(); d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(st.isValid()); } FilePath AttachCoreDialog::coreFile() const { return d->coreFileName->filePath(); } FilePath AttachCoreDialog::symbolFile() const { return d->symbolFileName->filePath(); } FilePath AttachCoreDialog::coreFileCopy() const { return d->coreFileResult.value_or(d->symbolFileName->filePath()); } FilePath AttachCoreDialog::symbolFileCopy() const { return d->symbolFileResult.value_or(d->symbolFileName->filePath()); } void AttachCoreDialog::setSymbolFile(const FilePath &symbolFilePath) { d->symbolFileName->setFilePath(symbolFilePath); } void AttachCoreDialog::setCoreFile(const FilePath &coreFilePath) { d->coreFileName->setFilePath(coreFilePath); } void AttachCoreDialog::setKitId(Id id) { d->kitChooser->setCurrentKitId(id); } Kit *AttachCoreDialog::kit() const { return d->kitChooser->currentKit(); } FilePath AttachCoreDialog::overrideStartScript() const { return d->overrideStartScriptFileName->filePath(); } void AttachCoreDialog::setOverrideStartScript(const FilePath &scriptName) { d->overrideStartScriptFileName->setFilePath(scriptName); } FilePath AttachCoreDialog::sysRoot() const { return d->sysRootDirectory->filePath(); } void AttachCoreDialog::setSysRoot(const FilePath &sysRoot) { d->sysRootDirectory->setFilePath(sysRoot); } } // Debugger::Internal