/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "coregdbadapter.h" #include #include #include #include #include #include #include #include #include using namespace Utils; namespace Debugger { namespace Internal { #define CB(callback) [this](const DebuggerResponse &r) { callback(r); } #define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0) /////////////////////////////////////////////////////////////////////// // // CoreGdbAdapter // /////////////////////////////////////////////////////////////////////// GdbCoreEngine::GdbCoreEngine(const DebuggerRunParameters &startParameters) : GdbEngine(startParameters), m_coreUnpackProcess(0) {} GdbCoreEngine::~GdbCoreEngine() { if (m_coreUnpackProcess) { m_coreUnpackProcess->blockSignals(true); m_coreUnpackProcess->terminate(); m_coreUnpackProcess->deleteLater(); m_coreUnpackProcess = 0; if (m_tempCoreFile.isOpen()) m_tempCoreFile.close(); } if (!m_tempCoreName.isEmpty()) { QFile tmpFile(m_tempCoreName); tmpFile.remove(); } } void GdbCoreEngine::setupEngine() { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); showMessage(_("TRYING TO START ADAPTER")); const DebuggerRunParameters &rp = runParameters(); m_executable = rp.inferior.executable; QFileInfo fi(rp.coreFile); m_coreName = fi.absoluteFilePath(); unpackCoreIfNeeded(); } static QString findExecutableFromName(const QString &fileNameFromCore, const QString &coreFile) { if (fileNameFromCore.isEmpty()) return fileNameFromCore; QFileInfo fi(fileNameFromCore); if (fi.isFile()) return fileNameFromCore; // turn the filename into an absolute path, using the location of the core as a hint QString absPath; if (fi.isAbsolute()) { absPath = fileNameFromCore; } else { QFileInfo coreInfo(coreFile); QDir coreDir = coreInfo.dir(); absPath = FileUtils::resolvePath(coreDir.absolutePath(), fileNameFromCore); } if (QFileInfo(absPath).isFile() || absPath.isEmpty()) return absPath; // remove possible trailing arguments QLatin1Char sep(' '); QStringList pathFragments = absPath.split(sep); while (pathFragments.size() > 0) { QString joined_path = pathFragments.join(sep); if (QFileInfo(joined_path).isFile()) { return joined_path; } pathFragments.pop_back(); } return QString(); } GdbCoreEngine::CoreInfo GdbCoreEngine::readExecutableNameFromCore(const QString &debuggerCommand, const QString &coreFile) { CoreInfo cinfo; #if 0 ElfReader reader(coreFile); cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore)); cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); #else QStringList args; args.append(QLatin1String("-nx")); args.append(QLatin1String("-batch")); args.append(QLatin1String("-c")); args.append(coreFile); QProcess proc; QStringList envLang = QProcess::systemEnvironment(); envLang.replaceInStrings(QRegExp(QLatin1String("^LC_ALL=.*")), QLatin1String("LC_ALL=C")); proc.setEnvironment(envLang); proc.start(debuggerCommand, args); if (proc.waitForFinished()) { QByteArray ba = proc.readAllStandardOutput(); // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'. // Program terminated with signal 11, Segmentation fault. int pos1 = ba.indexOf("Core was generated by"); if (pos1 != -1) { pos1 += 23; int pos2 = ba.indexOf('\'', pos1); if (pos2 != -1) { cinfo.isCore = true; cinfo.rawStringFromCore = QString::fromLocal8Bit(ba.mid(pos1, pos2 - pos1)); cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); } } } #endif return cinfo; } void GdbCoreEngine::continueSetupEngine() { bool isCore = true; if (m_coreUnpackProcess) { isCore = m_coreUnpackProcess->exitCode() == 0; m_coreUnpackProcess->deleteLater(); m_coreUnpackProcess = 0; if (m_tempCoreFile.isOpen()) m_tempCoreFile.close(); } if (isCore && m_executable.isEmpty()) { GdbCoreEngine::CoreInfo cinfo = readExecutableNameFromCore( runParameters().debuggerCommand, coreFileName()); if (cinfo.isCore) { m_executable = cinfo.foundExecutableName; if (m_executable.isEmpty()) { Core::AsynchronousMessageBox::warning( tr("Error Loading Symbols"), tr("No executable to load symbols from specified core.")); notifyEngineSetupFailed(); return; } } } if (isCore) { startGdb(); } else { Core::AsynchronousMessageBox::warning( tr("Error Loading Core File"), tr("The specified file does not appear to be a core file.")); notifyEngineSetupFailed(); } } void GdbCoreEngine::writeCoreChunk() { m_tempCoreFile.write(m_coreUnpackProcess->readAll()); } void GdbCoreEngine::setupInferior() { CHECK_STATE(InferiorSetupRequested); // Do that first, otherwise no symbols are loaded. QFileInfo fi(m_executable); QByteArray path = fi.absoluteFilePath().toLocal8Bit(); runCommand({"-file-exec-and-symbols \"" + path + '"', NoFlags, CB(handleFileExecAndSymbols)}); } void GdbCoreEngine::handleFileExecAndSymbols(const DebuggerResponse &response) { CHECK_STATE(InferiorSetupRequested); QString core = coreFileName(); if (response.resultClass == ResultDone) { showMessage(tr("Symbols found."), StatusBar); handleInferiorPrepared(); } else { QString msg = tr("No symbols found in core file %1.").arg(core) + _(" ") + tr("This can be caused by a path length limitation " "in the core file.") + _(" ") + tr("Try to specify the binary using the " "Debug->Start Debugging->Attach to Core dialog."); notifyInferiorSetupFailed(msg); } } void GdbCoreEngine::runEngine() { CHECK_STATE(EngineRunRequested); runCommand({"target core " + coreFileName().toLocal8Bit(), NoFlags, CB(handleTargetCore)}); } void GdbCoreEngine::handleTargetCore(const DebuggerResponse &response) { CHECK_STATE(EngineRunRequested); notifyEngineRunOkAndInferiorUnrunnable(); showMessage(tr("Attached to core."), StatusBar); if (response.resultClass == ResultError) { // We'll accept any kind of error e.g. &"Cannot access memory at address 0x2abc2a24\n" // Even without the stack, the user can find interesting stuff by exploring // the memory, globals etc. showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile) + QLatin1Char('\n') + QString::fromLocal8Bit(response.data["msg"].data()) + QLatin1Char('\n') + tr("Continuing nevertheless.")); } // Due to the auto-solib-add off setting, we don't have any // symbols yet. Load them in order of importance. reloadStack(); reloadModulesInternal(); runCommand({"p 5", NoFlags, CB(handleRoundTrip)}); } void GdbCoreEngine::handleRoundTrip(const DebuggerResponse &response) { CHECK_STATE(InferiorUnrunnable); Q_UNUSED(response); loadSymbolsForStack(); handleStop2(); QTimer::singleShot(1000, this, SLOT(loadAllSymbols())); } void GdbCoreEngine::interruptInferior() { // A core never runs, so this cannot be called. QTC_CHECK(false); } void GdbCoreEngine::shutdownEngine() { notifyAdapterShutdownOk(); } static QString tempCoreFilename() { QString pattern = QDir::tempPath() + QLatin1String("/tmpcore-XXXXXX"); QTemporaryFile tmp(pattern); tmp.open(); return tmp.fileName(); } void GdbCoreEngine::unpackCoreIfNeeded() { QStringList arguments; const QString msg = _("Unpacking core file to %1"); if (m_coreName.endsWith(QLatin1String(".lzo"))) { m_tempCoreName = tempCoreFilename(); showMessage(msg.arg(m_tempCoreName)); arguments << QLatin1String("-o") << m_tempCoreName << QLatin1String("-x") << m_coreName; m_coreUnpackProcess = new QProcess(this); m_coreUnpackProcess->setWorkingDirectory(QDir::tempPath()); m_coreUnpackProcess->start(QLatin1String("lzop"), arguments); connect(m_coreUnpackProcess, static_cast(&QProcess::finished), this, &GdbCoreEngine::continueSetupEngine); } else if (m_coreName.endsWith(QLatin1String(".gz"))) { m_tempCoreName = tempCoreFilename(); showMessage(msg.arg(m_tempCoreName)); m_tempCoreFile.setFileName(m_tempCoreName); m_tempCoreFile.open(QFile::WriteOnly); arguments << QLatin1String("-c") << QLatin1String("-d") << m_coreName; m_coreUnpackProcess = new QProcess(this); m_coreUnpackProcess->setWorkingDirectory(QDir::tempPath()); m_coreUnpackProcess->start(QLatin1String("gzip"), arguments); connect(m_coreUnpackProcess, &QProcess::readyRead, this, &GdbCoreEngine::writeCoreChunk); connect(m_coreUnpackProcess, static_cast(&QProcess::finished), this, &GdbCoreEngine::continueSetupEngine); } else { continueSetupEngine(); } } QString GdbCoreEngine::coreFileName() const { return m_tempCoreName.isEmpty() ? m_coreName : m_tempCoreName; } } // namespace Internal } // namespace Debugger