forked from qt-creator/qt-creator
Debugger: Add option to attach to last core (Linux)
Fixes: QTCREATORBUG-29256 Change-Id: I52e77eb6c5fe3b4c959d906a62495c4bb7344d88 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -80,6 +80,7 @@ add_qtc_plugin(Debugger
|
||||
qml/qmlv8debuggerclientconstants.h
|
||||
registerhandler.cpp registerhandler.h
|
||||
shared/cdbsymbolpathlisteditor.cpp shared/cdbsymbolpathlisteditor.h
|
||||
shared/coredumputils.cpp shared/coredumputils.h
|
||||
shared/hostutils.cpp shared/hostutils.h
|
||||
shared/peutils.cpp shared/peutils.h
|
||||
shared/symbolpathsdialog.cpp shared/symbolpathsdialog.h
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "unstartedappwatcherdialog.h"
|
||||
#include "loadcoredialog.h"
|
||||
#include "sourceutils.h"
|
||||
#include "shared/coredumputils.h"
|
||||
#include "shared/hostutils.h"
|
||||
#include "console/console.h"
|
||||
|
||||
@@ -109,6 +110,7 @@
|
||||
#include <QJsonObject>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QScopeGuard>
|
||||
@@ -634,6 +636,7 @@ public:
|
||||
void attachToQmlPort();
|
||||
void runScheduled();
|
||||
void attachCore();
|
||||
void attachToLastCore();
|
||||
void reloadDebuggingHelpers();
|
||||
|
||||
void remoteCommand(const QStringList &options);
|
||||
@@ -671,6 +674,7 @@ public:
|
||||
QAction m_attachToRemoteServerAction{Tr::tr("Attach to Running Debug Server...")};
|
||||
QAction m_startRemoteCdbAction{Tr::tr("Attach to Remote CDB Session...")};
|
||||
QAction m_attachToCoreAction{Tr::tr("Load Core File...")};
|
||||
QAction m_attachToLastCoreAction{Tr::tr("Load Last Core File")};
|
||||
|
||||
// In the Debug menu.
|
||||
QAction m_startAndBreakOnMain{Tr::tr("Start and Break on Main")};
|
||||
@@ -872,6 +876,9 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments)
|
||||
connect(&m_attachToCoreAction, &QAction::triggered,
|
||||
this, &DebuggerPluginPrivate::attachCore);
|
||||
|
||||
connect(&m_attachToLastCoreAction, &QAction::triggered,
|
||||
this, &DebuggerPluginPrivate::attachToLastCore);
|
||||
|
||||
connect(&m_attachToRemoteServerAction, &QAction::triggered,
|
||||
this, &StartApplicationDialog::attachToRemoteServer);
|
||||
|
||||
@@ -947,6 +954,11 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments)
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
mstart->addAction(cmd, MENU_GROUP_GENERAL);
|
||||
|
||||
cmd = ActionManager::registerAction(&m_attachToLastCoreAction,
|
||||
"Debugger.AttachLastCore");
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
mstart->addAction(cmd, MENU_GROUP_GENERAL);
|
||||
|
||||
cmd = ActionManager::registerAction(&m_attachToRemoteServerAction,
|
||||
"Debugger.AttachToRemoteServer");
|
||||
cmd->setAttribute(Command::CA_Hide);
|
||||
@@ -1571,6 +1583,8 @@ void DebuggerPluginPrivate::updatePresetState()
|
||||
m_startAndDebugApplicationAction.setEnabled(true);
|
||||
m_attachToQmlPortAction.setEnabled(true);
|
||||
m_attachToCoreAction.setEnabled(true);
|
||||
m_attachToLastCoreAction.setEnabled(Utils::HostOsInfo::isLinuxHost());
|
||||
|
||||
m_attachToRemoteServerAction.setEnabled(true);
|
||||
m_attachToRunningApplication.setEnabled(true);
|
||||
m_attachToUnstartedApplication.setEnabled(true);
|
||||
@@ -1635,6 +1649,29 @@ void DebuggerPluginPrivate::attachCore()
|
||||
debugger->startRunControl();
|
||||
}
|
||||
|
||||
void DebuggerPluginPrivate::attachToLastCore()
|
||||
{
|
||||
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
LastCore lastCore = getLastCore();
|
||||
QGuiApplication::restoreOverrideCursor();
|
||||
if (!lastCore) {
|
||||
AsynchronousMessageBox::warning(Tr::tr("Warning"),
|
||||
Tr::tr("coredumpctl did not find any cores created by systemd-coredump"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
|
||||
runControl->setKit(KitManager::defaultKit());
|
||||
runControl->setDisplayName(Tr::tr("Last Core file \"%1\"").arg(lastCore.coreFile.toString()));
|
||||
auto debugger = new DebuggerRunTool(runControl);
|
||||
|
||||
debugger->setInferiorExecutable(lastCore.binary);
|
||||
debugger->setCoreFilePath(lastCore.coreFile);
|
||||
debugger->setStartMode(AttachToCore);
|
||||
debugger->setCloseMode(DetachAtClose);
|
||||
debugger->startRunControl();
|
||||
}
|
||||
|
||||
void DebuggerPluginPrivate::reloadDebuggingHelpers()
|
||||
{
|
||||
if (DebuggerEngine *engine = EngineManager::currentEngine())
|
||||
|
87
src/plugins/debugger/shared/coredumputils.cpp
Normal file
87
src/plugins/debugger/shared/coredumputils.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "coredumputils.h"
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace Debugger::Internal {
|
||||
|
||||
const char COREDUMPCTL[] = "coredumpctl";
|
||||
const char UNZSTD[] = "unzstd";
|
||||
|
||||
static LastCore findLastCore()
|
||||
{
|
||||
static const auto coredumpctlBinary = QStandardPaths::findExecutable(COREDUMPCTL);
|
||||
if (coredumpctlBinary.isEmpty()) {
|
||||
qWarning("%s was not found", COREDUMPCTL);
|
||||
return {};
|
||||
}
|
||||
Utils::Process coredumpctl;
|
||||
coredumpctl.setCommand({Utils::FilePath::fromString(coredumpctlBinary), {"info"}});
|
||||
coredumpctl.runBlocking();
|
||||
if (coredumpctl.exitStatus() != QProcess::NormalExit
|
||||
|| coredumpctl.exitCode() != 0) {
|
||||
qWarning("%s failed: %s", COREDUMPCTL, qPrintable(coredumpctl.errorString()));
|
||||
return {};
|
||||
}
|
||||
|
||||
LastCore result;
|
||||
const QString output = coredumpctl.readAllStandardOutput();
|
||||
const auto lines = QStringView{output}.split('\n');
|
||||
for (const QStringView &line : lines) {
|
||||
if (line.startsWith(u" Executable: ")) {
|
||||
const QStringView binary = line.sliced(line.indexOf(':') + 1).trimmed();
|
||||
result.binary = Utils::FilePath::fromString(binary.toString());
|
||||
} else if (line.startsWith(u" Storage: ") && line.endsWith(u" (present)")) {
|
||||
auto pos = line.indexOf(':') + 1;
|
||||
const auto len = line.size() - 10 - pos;
|
||||
const QStringView coreFile = line.sliced(pos, len).trimmed();
|
||||
result.coreFile = Utils::FilePath::fromString(coreFile.toString());
|
||||
}
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LastCore getLastCore()
|
||||
{
|
||||
auto lastCore = findLastCore();
|
||||
if (!lastCore || !lastCore.coreFile.endsWith(".zst")) {
|
||||
qWarning("No core was found");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Copy core to /tmp and uncompress (unless it already exists
|
||||
// on 2nd invocation).
|
||||
QString tmpCorePath = QDir::tempPath() + '/' + lastCore.coreFile.fileName();
|
||||
const auto tmpCore = Utils::FilePath::fromString(tmpCorePath);
|
||||
if (!tmpCore.exists() && !QFile::copy(lastCore.coreFile.toString(), tmpCorePath))
|
||||
return {};
|
||||
|
||||
const QString uncompressedCorePath = tmpCorePath.sliced(0, tmpCorePath.size() - 4);
|
||||
const auto uncompressedCore = Utils::FilePath::fromString(uncompressedCorePath);
|
||||
|
||||
if (!uncompressedCore.exists()) {
|
||||
Utils::Process uncompress;
|
||||
uncompress.setCommand({Utils::FilePath::fromString(UNZSTD),
|
||||
{"-f", tmpCorePath}});
|
||||
uncompress.runBlocking(std::chrono::seconds(20));
|
||||
if (uncompress.exitStatus() != QProcess::NormalExit
|
||||
|| uncompress.exitCode() != 0) {
|
||||
qWarning("%s failed: %s", UNZSTD, qPrintable(uncompress.errorString()));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
lastCore.coreFile = uncompressedCore;
|
||||
return lastCore;
|
||||
}
|
||||
|
||||
} // namespace Debugger::Internal {
|
21
src/plugins/debugger/shared/coredumputils.h
Normal file
21
src/plugins/debugger/shared/coredumputils.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
/* Helper functions to get the last core from systemd/coredumpctl */
|
||||
namespace Debugger::Internal {
|
||||
|
||||
struct LastCore
|
||||
{
|
||||
operator bool() const { return !binary.isEmpty() && !coreFile.isEmpty(); }
|
||||
|
||||
Utils::FilePath binary;
|
||||
Utils::FilePath coreFile;
|
||||
};
|
||||
|
||||
LastCore getLastCore();
|
||||
|
||||
} // namespace Debugger::Internal
|
Reference in New Issue
Block a user