Files
qt-creator/src/plugins/debugger/loadcoredialog.cpp
Marcus Tillmanns 96ebe93ecb Debugger: Rebuild AttachCoreDialog
The old dialog was based on local vs. remote file paths.
These days we can simply treat them the same.

If the selected files are not on the same device as the
debuggger they will be copied first.

Change-Id: If5ec93d1e1e49c51b3032fec8384682d2a5e35b4
Reviewed-by: hjk <hjk@qt.io>
2023-02-07 12:31:40 +00:00

354 lines
11 KiB
C++

// 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 <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/asynctask.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/processinterface.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
#include <utils/tasktree.h>
#include <utils/temporaryfile.h>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QTextBrowser>
#include <QTreeView>
using namespace Core;
using namespace ProjectExplorer;
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<FilePath> coreFileResult;
expected_str<FilePath> 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();
using namespace Tasking;
const auto copyFile = [debuggerCommand](const FilePath &srcPath) -> expected_str<FilePath> {
if (!srcPath.isSameDevice(debuggerCommand)) {
const expected_str<FilePath> tmpPath = debuggerCommand.tmpDir();
if (!tmpPath)
return make_unexpected(tmpPath.error());
const FilePath pattern = (tmpPath.value()
/ (srcPath.fileName() + ".XXXXXXXXXXX"));
const expected_str<FilePath> resultPath = pattern.createTempFile();
if (!resultPath)
return make_unexpected(resultPath.error());
const expected_str<void> result = srcPath.copyFile(resultPath.value());
if (!result)
return make_unexpected(result.error());
return resultPath;
}
return srcPath;
};
using ResultType = expected_str<FilePath>;
const auto copyFileAsync = [=](QFutureInterface<ResultType> &fi, const FilePath &srcPath) {
fi.reportResult(copyFile(srcPath));
};
const Group root = {
parallel,
Async<ResultType>{[=](auto &task) {
task.setAsyncCallData(copyFileAsync, this->coreFile());
},
[=](const auto &task) { d->coreFileResult = task.result(); }},
Async<ResultType>{[=](auto &task) {
task.setAsyncCallData(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