forked from qt-creator/qt-creator
m.txt
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
**************************************************************************/
|
**************************************************************************/
|
||||||
|
|
||||||
#include "consoleprocess.h"
|
#include "consoleprocess.h"
|
||||||
|
#include "winutils.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
@@ -122,7 +123,7 @@ bool ConsoleProcess::start(const QString &program, const QStringList &args)
|
|||||||
delete m_tempFile;
|
delete m_tempFile;
|
||||||
m_tempFile = 0;
|
m_tempFile = 0;
|
||||||
stubServerShutdown();
|
stubServerShutdown();
|
||||||
emit processError(tr("The process could not be started!"));
|
emit processError(tr("The process '%1' could not be started: %2").arg(cmdLine, winErrorMessage(GetLastError())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,18 +174,6 @@ void ConsoleProcess::stubConnectionAvailable()
|
|||||||
connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
|
connect(m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString errorMsg(int code)
|
|
||||||
{
|
|
||||||
LPVOID lpMsgBuf;
|
|
||||||
|
|
||||||
int len = FormatMessage(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
NULL, (DWORD)code, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
|
|
||||||
QString ret = QString::fromUtf16((ushort *)lpMsgBuf, len);
|
|
||||||
LocalFree(lpMsgBuf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleProcess::readStubOutput()
|
void ConsoleProcess::readStubOutput()
|
||||||
{
|
{
|
||||||
while (m_stubSocket->canReadLine()) {
|
while (m_stubSocket->canReadLine()) {
|
||||||
@@ -192,10 +181,10 @@ void ConsoleProcess::readStubOutput()
|
|||||||
out.chop(2); // \r\n
|
out.chop(2); // \r\n
|
||||||
if (out.startsWith("err:chdir ")) {
|
if (out.startsWith("err:chdir ")) {
|
||||||
emit processError(tr("Cannot change to working directory %1: %2")
|
emit processError(tr("Cannot change to working directory %1: %2")
|
||||||
.arg(workingDirectory(), errorMsg(out.mid(10).toInt())));
|
.arg(workingDirectory(), winErrorMessage(out.mid(10).toInt())));
|
||||||
} else if (out.startsWith("err:exec ")) {
|
} else if (out.startsWith("err:exec ")) {
|
||||||
emit processError(tr("Cannot execute %1: %2")
|
emit processError(tr("Cannot execute %1: %2")
|
||||||
.arg(m_executable, errorMsg(out.mid(9).toInt())));
|
.arg(m_executable, winErrorMessage(out.mid(9).toInt())));
|
||||||
} else if (out.startsWith("pid ")) {
|
} else if (out.startsWith("pid ")) {
|
||||||
// Will not need it any more
|
// Will not need it any more
|
||||||
delete m_tempFile;
|
delete m_tempFile;
|
||||||
@@ -207,7 +196,7 @@ void ConsoleProcess::readStubOutput()
|
|||||||
FALSE, m_appPid);
|
FALSE, m_appPid);
|
||||||
if (m_hInferior == NULL) {
|
if (m_hInferior == NULL) {
|
||||||
emit processError(tr("Cannot obtain a handle to the inferior: %1")
|
emit processError(tr("Cannot obtain a handle to the inferior: %1")
|
||||||
.arg(errorMsg(GetLastError())));
|
.arg(winErrorMessage(GetLastError())));
|
||||||
// Uhm, and now what?
|
// Uhm, and now what?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -237,7 +226,7 @@ void ConsoleProcess::inferiorExited()
|
|||||||
|
|
||||||
if (!GetExitCodeProcess(m_hInferior, &chldStatus))
|
if (!GetExitCodeProcess(m_hInferior, &chldStatus))
|
||||||
emit processError(tr("Cannot obtain exit status from inferior: %1")
|
emit processError(tr("Cannot obtain exit status from inferior: %1")
|
||||||
.arg(errorMsg(GetLastError())));
|
.arg(winErrorMessage(GetLastError())));
|
||||||
cleanupInferior();
|
cleanupInferior();
|
||||||
m_appStatus = QProcess::NormalExit;
|
m_appStatus = QProcess::NormalExit;
|
||||||
m_appCode = chldStatus;
|
m_appCode = chldStatus;
|
||||||
|
|||||||
@@ -26,8 +26,13 @@ SOURCES += \
|
|||||||
submiteditorwidget.cpp \
|
submiteditorwidget.cpp \
|
||||||
synchronousprocess.cpp
|
synchronousprocess.cpp
|
||||||
|
|
||||||
win32:SOURCES += consoleprocess_win.cpp
|
win32 {
|
||||||
else:SOURCES += consoleprocess_unix.cpp
|
SOURCES += consoleprocess_win.cpp \
|
||||||
|
winutils.cpp
|
||||||
|
HEADERS += winutils.h
|
||||||
|
} else {
|
||||||
|
SOURCES += consoleprocess_unix.cpp
|
||||||
|
}
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
utils_global.h \
|
utils_global.h \
|
||||||
|
|||||||
56
src/libs/utils/winutils.cpp
Normal file
56
src/libs/utils/winutils.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "winutils.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
QWORKBENCH_UTILS_EXPORT QString winErrorMessage(unsigned long error)
|
||||||
|
{
|
||||||
|
QString rc = QString::fromLatin1("#%1: ").arg(error);
|
||||||
|
ushort *lpMsgBuf;
|
||||||
|
|
||||||
|
const int len = FormatMessage(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||||
|
if (len) {
|
||||||
|
rc = QString::fromUtf16(lpMsgBuf, len);
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
|
} else {
|
||||||
|
rc += QString::fromLatin1("<unknown error>");
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Utils
|
||||||
|
} // namespace Core
|
||||||
48
src/libs/utils/winutils.h
Normal file
48
src/libs/utils/winutils.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef WINUTILS_H
|
||||||
|
#define WINUTILS_H
|
||||||
|
|
||||||
|
#include "utils_global.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QString;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
// Helper to format a Windows error message, taking the
|
||||||
|
// code as returned by the GetLastError()-API.
|
||||||
|
QWORKBENCH_UTILS_EXPORT QString winErrorMessage(unsigned long error);
|
||||||
|
|
||||||
|
} // namespace Utils
|
||||||
|
} // namespace Core
|
||||||
|
#endif // WINUTILS_H
|
||||||
@@ -198,7 +198,7 @@ bool CdbDebugEngine::startDebugger()
|
|||||||
m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
|
m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
|
||||||
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
|
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
|
||||||
|
|
||||||
if (m_d->m_debuggerManager->startMode() == DebuggerManager::AttachExternal) {
|
if (m_d->m_debuggerManager->startMode() == AttachExternal) {
|
||||||
qWarning("CdbDebugEngine: attach to process not yet implemented!");
|
qWarning("CdbDebugEngine: attach to process not yet implemented!");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -86,4 +86,7 @@ HEADERS += $$PWD/modeltest.h
|
|||||||
DEFINES += USE_MODEL_TEST=1
|
DEFINES += USE_MODEL_TEST=1
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG(cdbdebugger):include(cdb\cdb.pri)
|
win32 {
|
||||||
|
include(win/win.pri)
|
||||||
|
CONFIG(cdbdebugger):include(cdb\cdb.pri)
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ const char * const GDBRUNNING = "Gdb.Running";
|
|||||||
|
|
||||||
const char * const PROPERTY_REGISTER_FORMAT = "Debugger.Property.RegisterFormat";
|
const char * const PROPERTY_REGISTER_FORMAT = "Debugger.Property.RegisterFormat";
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
enum { debug = 0 };
|
||||||
|
}
|
||||||
} // namespace Constants
|
} // namespace Constants
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,9 @@
|
|||||||
#include "watchhandler.h"
|
#include "watchhandler.h"
|
||||||
|
|
||||||
#include "debuggerdialogs.h"
|
#include "debuggerdialogs.h"
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
# include "peutils.h"
|
||||||
|
#endif
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
@@ -432,6 +434,8 @@ void DebuggerManager::init()
|
|||||||
winEngine = createWinEngine(this);
|
winEngine = createWinEngine(this);
|
||||||
scriptEngine = createScriptEngine(this);
|
scriptEngine = createScriptEngine(this);
|
||||||
setDebuggerType(GdbDebugger);
|
setDebuggerType(GdbDebugger);
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << gdbEngine << winEngine << scriptEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerManager::setDebuggerType(DebuggerType type)
|
void DebuggerManager::setDebuggerType(DebuggerType type)
|
||||||
@@ -549,7 +553,8 @@ void DebuggerManager::clearStatusMessage()
|
|||||||
void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
|
void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
|
||||||
{
|
{
|
||||||
Q_UNUSED(timeout)
|
Q_UNUSED(timeout)
|
||||||
//qDebug() << "STATUS: " << msg;
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << "STATUS MSG: " << msg;
|
||||||
showDebuggerOutput("status:", msg);
|
showDebuggerOutput("status:", msg);
|
||||||
m_statusLabel->setText(" " + msg);
|
m_statusLabel->setText(" " + msg);
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
@@ -594,8 +599,9 @@ void DebuggerManager::notifyInferiorExited()
|
|||||||
|
|
||||||
void DebuggerManager::notifyInferiorPidChanged(int pid)
|
void DebuggerManager::notifyInferiorPidChanged(int pid)
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << pid;
|
||||||
//QMessageBox::warning(0, "PID", "PID: " + QString::number(pid));
|
//QMessageBox::warning(0, "PID", "PID: " + QString::number(pid));
|
||||||
//qDebug() << "PID: " << pid;
|
|
||||||
emit inferiorPidChanged(pid);
|
emit inferiorPidChanged(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,11 +612,11 @@ void DebuggerManager::showApplicationOutput(const QString &str)
|
|||||||
|
|
||||||
void DebuggerManager::shutdown()
|
void DebuggerManager::shutdown()
|
||||||
{
|
{
|
||||||
//qDebug() << "DEBUGGER_MANAGER SHUTDOWN START";
|
if (Debugger::Constants::Internal::debug)
|
||||||
if (m_engine) {
|
qDebug() << Q_FUNC_INFO << m_engine;
|
||||||
//qDebug() << "SHUTTING DOWN ENGINE" << m_engine;
|
|
||||||
|
if (m_engine)
|
||||||
m_engine->shutdown();
|
m_engine->shutdown();
|
||||||
}
|
|
||||||
m_engine = 0;
|
m_engine = 0;
|
||||||
|
|
||||||
delete scriptEngine;
|
delete scriptEngine;
|
||||||
@@ -673,6 +679,9 @@ void DebuggerManager::toggleBreakpoint()
|
|||||||
|
|
||||||
void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
|
void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||||
|
|
||||||
QTC_ASSERT(m_engine, return);
|
QTC_ASSERT(m_engine, return);
|
||||||
QTC_ASSERT(m_breakHandler, return);
|
QTC_ASSERT(m_breakHandler, return);
|
||||||
if (status() != DebuggerInferiorRunning
|
if (status() != DebuggerInferiorRunning
|
||||||
@@ -779,8 +788,43 @@ void DebuggerManager::attachCore()
|
|||||||
emit debuggingFinished();
|
emit debuggingFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Figure out the debugger type of an exexcutable
|
||||||
|
static bool determineDebuggerType(const QString &executable,
|
||||||
|
DebuggerManager::DebuggerType *dt,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
if (executable.endsWith(QLatin1String(".js"))) {
|
||||||
|
*dt = DebuggerManager::ScriptDebugger;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifndef Q_WS_WIN
|
||||||
|
*dt = DebuggerManager::GdbDebugger;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// If a file has PDB files, it has been compiled by VS.
|
||||||
|
QStringList pdbFiles;
|
||||||
|
if (!getPDBFiles(executable, &pdbFiles, errorMessage))
|
||||||
|
return false;
|
||||||
|
if (pdbFiles.empty()) {
|
||||||
|
*dt = DebuggerManager::GdbDebugger;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We need the CDB debugger in order to be able to debug VS
|
||||||
|
// executables
|
||||||
|
if (!winEngine) {
|
||||||
|
*errorMessage = DebuggerManager::tr("Debugging VS executables is not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*dt = DebuggerManager::WinDebugger;
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool DebuggerManager::startNewDebugger(DebuggerStartMode mode)
|
bool DebuggerManager::startNewDebugger(DebuggerStartMode mode)
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << mode;
|
||||||
|
|
||||||
m_startMode = mode;
|
m_startMode = mode;
|
||||||
// FIXME: Clean up
|
// FIXME: Clean up
|
||||||
|
|
||||||
@@ -858,11 +902,18 @@ bool DebuggerManager::startNewDebugger(DebuggerStartMode mode)
|
|||||||
|
|
||||||
emit debugModeRequested();
|
emit debugModeRequested();
|
||||||
|
|
||||||
if (m_executable.endsWith(".js"))
|
DebuggerType type;
|
||||||
setDebuggerType(ScriptDebugger);
|
QString errorMessage;
|
||||||
else
|
if (!determineDebuggerType(m_executable, &type, &errorMessage)) {
|
||||||
setDebuggerType(GdbDebugger);
|
QMessageBox::warning(mainWindow(), tr("Warning"),
|
||||||
|
tr("Cannot debug '%1': %2").arg(m_executable, errorMessage));
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << m_executable << type;
|
||||||
|
|
||||||
|
setDebuggerType(type);
|
||||||
setStatus(DebuggerProcessStartingUp);
|
setStatus(DebuggerProcessStartingUp);
|
||||||
if (!m_engine->startDebugger()) {
|
if (!m_engine->startDebugger()) {
|
||||||
setStatus(DebuggerProcessNotReady);
|
setStatus(DebuggerProcessNotReady);
|
||||||
@@ -886,7 +937,9 @@ void DebuggerManager::cleanupViews()
|
|||||||
|
|
||||||
void DebuggerManager::exitDebugger()
|
void DebuggerManager::exitDebugger()
|
||||||
{
|
{
|
||||||
//qDebug() << "DebuggerManager::exitDebugger";
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
if (m_engine)
|
if (m_engine)
|
||||||
m_engine->exitDebugger();
|
m_engine->exitDebugger();
|
||||||
cleanupViews();
|
cleanupViews();
|
||||||
@@ -962,6 +1015,9 @@ void DebuggerManager::nextIExec()
|
|||||||
|
|
||||||
void DebuggerManager::executeDebuggerCommand(const QString &command)
|
void DebuggerManager::executeDebuggerCommand(const QString &command)
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO <<command;
|
||||||
|
|
||||||
QTC_ASSERT(m_engine, return);
|
QTC_ASSERT(m_engine, return);
|
||||||
m_engine->executeDebuggerCommand(command);
|
m_engine->executeDebuggerCommand(command);
|
||||||
}
|
}
|
||||||
@@ -1030,6 +1086,9 @@ void DebuggerManager::watchExpression(const QString &expression)
|
|||||||
|
|
||||||
void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber)
|
void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber)
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||||
|
|
||||||
QTC_ASSERT(m_breakHandler, return);
|
QTC_ASSERT(m_breakHandler, return);
|
||||||
QTC_ASSERT(m_engine, return);
|
QTC_ASSERT(m_engine, return);
|
||||||
m_breakHandler->setBreakpoint(fileName, lineNumber);
|
m_breakHandler->setBreakpoint(fileName, lineNumber);
|
||||||
@@ -1062,7 +1121,8 @@ void DebuggerManager::breakAtMain()
|
|||||||
|
|
||||||
void DebuggerManager::setStatus(int status)
|
void DebuggerManager::setStatus(int status)
|
||||||
{
|
{
|
||||||
//qDebug() << "STATUS CHANGE: from" << m_status << "to" << status;
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << "STATUS CHANGE: from" << m_status << "to" << status;
|
||||||
|
|
||||||
if (status == m_status)
|
if (status == m_status)
|
||||||
return;
|
return;
|
||||||
@@ -1179,8 +1239,9 @@ void DebuggerManager::continueExec()
|
|||||||
|
|
||||||
void DebuggerManager::interruptDebuggingRequest()
|
void DebuggerManager::interruptDebuggingRequest()
|
||||||
{
|
{
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << status();
|
||||||
QTC_ASSERT(m_engine, return);
|
QTC_ASSERT(m_engine, return);
|
||||||
//qDebug() << "INTERRUPTING AT" << status();
|
|
||||||
bool interruptIsExit = (status() != DebuggerInferiorRunning);
|
bool interruptIsExit = (status() != DebuggerInferiorRunning);
|
||||||
if (interruptIsExit)
|
if (interruptIsExit)
|
||||||
exitDebugger();
|
exitDebugger();
|
||||||
@@ -1197,8 +1258,11 @@ void DebuggerManager::runToLineExec()
|
|||||||
QString fileName;
|
QString fileName;
|
||||||
int lineNumber = -1;
|
int lineNumber = -1;
|
||||||
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
||||||
if (!fileName.isEmpty())
|
if (!fileName.isEmpty()) {
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||||
m_engine->runToLineExec(fileName, lineNumber);
|
m_engine->runToLineExec(fileName, lineNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerManager::runToFunctionExec()
|
void DebuggerManager::runToFunctionExec()
|
||||||
@@ -1228,7 +1292,9 @@ void DebuggerManager::runToFunctionExec()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//qDebug() << "RUN TO FUNCTION " << functionName;
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << functionName;
|
||||||
|
|
||||||
if (!functionName.isEmpty())
|
if (!functionName.isEmpty())
|
||||||
m_engine->runToFunctionExec(functionName);
|
m_engine->runToFunctionExec(functionName);
|
||||||
}
|
}
|
||||||
@@ -1238,8 +1304,11 @@ void DebuggerManager::jumpToLineExec()
|
|||||||
QString fileName;
|
QString fileName;
|
||||||
int lineNumber = -1;
|
int lineNumber = -1;
|
||||||
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
|
||||||
if (!fileName.isEmpty())
|
if (!fileName.isEmpty()) {
|
||||||
|
if (Debugger::Constants::Internal::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << fileName << lineNumber;
|
||||||
m_engine->jumpToLineExec(fileName, lineNumber);
|
m_engine->jumpToLineExec(fileName, lineNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerManager::resetLocation()
|
void DebuggerManager::resetLocation()
|
||||||
|
|||||||
279
src/plugins/debugger/win/peutils.cpp
Normal file
279
src/plugins/debugger/win/peutils.cpp
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "peutils.h"
|
||||||
|
|
||||||
|
#include <utils/winutils.h>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
using Core::Utils::winErrorMessage;
|
||||||
|
|
||||||
|
// Create a pointer from base and offset when rummaging around in
|
||||||
|
// a memory mapped file
|
||||||
|
|
||||||
|
template <class Ptr>
|
||||||
|
inline Ptr *makePtr(void *base, ptrdiff_t offset)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Ptr*>(static_cast<char*>(base) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeView header
|
||||||
|
struct CV_HEADER
|
||||||
|
{
|
||||||
|
DWORD CvSignature; // NBxx
|
||||||
|
LONG Offset; // Always 0 for NB10
|
||||||
|
};
|
||||||
|
|
||||||
|
// CodeView NB10 debug information of a PDB 2.00 file (VS 6)
|
||||||
|
struct CV_INFO_PDB20
|
||||||
|
{
|
||||||
|
CV_HEADER Header;
|
||||||
|
DWORD Signature;
|
||||||
|
DWORD Age;
|
||||||
|
BYTE PdbFileName[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// CodeView RSDS debug information of a PDB 7.00 file
|
||||||
|
struct CV_INFO_PDB70
|
||||||
|
{
|
||||||
|
DWORD CvSignature;
|
||||||
|
GUID Signature;
|
||||||
|
DWORD Age;
|
||||||
|
BYTE PdbFileName[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the NT image header of an executable via the legacy DOS header.
|
||||||
|
static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
|
||||||
|
{
|
||||||
|
IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
|
||||||
|
// Check DOS header consistency
|
||||||
|
if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
|
||||||
|
|| dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
|
*errorMessage = QString::fromLatin1("DOS header check failed.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Retrieve NT header
|
||||||
|
IMAGE_NT_HEADERS *ntHeaders = makePtr<IMAGE_NT_HEADERS>(dosHeader, dosHeader->e_lfanew);
|
||||||
|
// check NT header consistency
|
||||||
|
if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
|
||||||
|
|| ntHeaders->Signature != IMAGE_NT_SIGNATURE
|
||||||
|
|| IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
|
||||||
|
*errorMessage = QString::fromLatin1("NT header check failed.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Check magic
|
||||||
|
const WORD magic = ntHeaders->OptionalHeader.Magic;
|
||||||
|
#ifdef __GNUC__ // MinGW does not have complete 64bit definitions.
|
||||||
|
if (magic != 0x10b) {
|
||||||
|
*errorMessage = QString::fromLatin1("NT header check failed; magic %1 is not that of a 32-bit executable.").
|
||||||
|
arg(magic);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||||
|
*errorMessage = QString::fromLatin1("NT header check failed; magic %1 is none of %2, %3.").
|
||||||
|
arg(magic).arg(IMAGE_NT_OPTIONAL_HDR32_MAGIC).arg(IMAGE_NT_OPTIONAL_HDR64_MAGIC);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Check section headers
|
||||||
|
IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
|
||||||
|
if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
|
||||||
|
*errorMessage = QString::fromLatin1("NT header section header check failed.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ntHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the COFF section an RVA belongs to and convert to file offset
|
||||||
|
static bool getFileOffsetFromRVA(IMAGE_NT_HEADERS *ntHeaders, DWORD rva, DWORD* fileOffset)
|
||||||
|
{
|
||||||
|
IMAGE_SECTION_HEADER *sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
|
||||||
|
for( int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++ ) {
|
||||||
|
const DWORD sectionSize = sectionHeader->Misc.VirtualSize ?
|
||||||
|
sectionHeader->Misc.VirtualSize : sectionHeader->SizeOfRawData;
|
||||||
|
if ((rva >= sectionHeader->VirtualAddress) && (rva < sectionHeader->VirtualAddress + sectionSize)) {
|
||||||
|
const DWORD diff = sectionHeader->VirtualAddress - sectionHeader->PointerToRawData;
|
||||||
|
*fileOffset = rva - diff;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve debug directory and number of entries
|
||||||
|
static bool getDebugDirectory(IMAGE_NT_HEADERS *ntHeaders,
|
||||||
|
void *fileMemory,
|
||||||
|
IMAGE_DEBUG_DIRECTORY **debugDir,
|
||||||
|
int *count,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
DWORD debugDirRva = 0;
|
||||||
|
DWORD debugDirSize;
|
||||||
|
*debugDir = 0;
|
||||||
|
*count = 0;
|
||||||
|
#ifdef __GNUC__ // MinGW does not have complete 64bit definitions.
|
||||||
|
typedef IMAGE_OPTIONAL_HEADER IMAGE_OPTIONAL_HEADER32;
|
||||||
|
#else
|
||||||
|
// Find the virtual address
|
||||||
|
const bool is64Bit = ntHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
|
||||||
|
if (is64Bit) {
|
||||||
|
IMAGE_OPTIONAL_HEADER64 *optionalHeader64 = reinterpret_cast<IMAGE_OPTIONAL_HEADER64*>(&(ntHeaders->OptionalHeader));
|
||||||
|
debugDirRva = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
||||||
|
debugDirSize = optionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
IMAGE_OPTIONAL_HEADER32 *optionalHeader32 = reinterpret_cast<IMAGE_OPTIONAL_HEADER32*>(&(ntHeaders->OptionalHeader));
|
||||||
|
debugDirRva = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
||||||
|
debugDirSize = optionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
|
||||||
|
#ifndef __GNUC__
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Empty. This is the case for MinGW binaries
|
||||||
|
if (debugDirSize == 0)
|
||||||
|
return true;
|
||||||
|
// Look up in file
|
||||||
|
DWORD debugDirOffset;
|
||||||
|
if (!getFileOffsetFromRVA(ntHeaders, debugDirRva, &debugDirOffset)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Unable to locate debug dir RVA %1/%2.").arg(debugDirRva).arg(debugDirSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*debugDir = makePtr<IMAGE_DEBUG_DIRECTORY>(fileMemory, debugDirOffset);
|
||||||
|
// Check
|
||||||
|
if (IsBadReadPtr(*debugDir, debugDirSize) || debugDirSize < sizeof(IMAGE_DEBUG_DIRECTORY)) {
|
||||||
|
*errorMessage = QString::fromLatin1("Debug directory corrupted.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*count = debugDirSize / sizeof(IMAGE_DEBUG_DIRECTORY);
|
||||||
|
return debugDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the PDB file of a Code View debug section
|
||||||
|
static QString getPDBFileOfCodeViewSection(void *debugInfo, DWORD size)
|
||||||
|
{
|
||||||
|
static const DWORD CV_SIGNATURE_NB10 = 0x3031424e; // '01BN';
|
||||||
|
static const DWORD CV_SIGNATURE_RSDS = 0x53445352; // 'SDSR';
|
||||||
|
if (IsBadReadPtr(debugInfo, size) || size < sizeof(DWORD))
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
const DWORD cvSignature = *static_cast<DWORD*>(debugInfo);
|
||||||
|
if (cvSignature == CV_SIGNATURE_NB10) {
|
||||||
|
CV_INFO_PDB20* cvInfo = static_cast<CV_INFO_PDB20*>(debugInfo);
|
||||||
|
if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB20)))
|
||||||
|
return QString();
|
||||||
|
CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
|
||||||
|
if (IsBadStringPtrA(pdbFileName, UINT_MAX))
|
||||||
|
return QString();
|
||||||
|
return QString::fromLocal8Bit(pdbFileName);
|
||||||
|
}
|
||||||
|
if (cvSignature == CV_SIGNATURE_RSDS) {
|
||||||
|
CV_INFO_PDB70* cvInfo = static_cast<CV_INFO_PDB70*>(debugInfo);
|
||||||
|
if (IsBadReadPtr(debugInfo, sizeof(CV_INFO_PDB70)))
|
||||||
|
return QString();
|
||||||
|
CHAR* pdbFileName = reinterpret_cast<CHAR*>(cvInfo->PdbFileName);
|
||||||
|
if (IsBadStringPtrA(pdbFileName, UINT_MAX))
|
||||||
|
return QString();
|
||||||
|
return QString::fromLocal8Bit(pdbFileName);
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all PDB files of all debug sections
|
||||||
|
static void collectPDBfiles(void *fileMemory, IMAGE_DEBUG_DIRECTORY *directoryBase, int count, QStringList *pdbFiles)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++, directoryBase++)
|
||||||
|
if (directoryBase->Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||||
|
const QString pdb = getPDBFileOfCodeViewSection(static_cast<char*>(fileMemory) + directoryBase->PointerToRawData, directoryBase->SizeOfData);
|
||||||
|
if (!pdb.isEmpty())
|
||||||
|
pdbFiles->push_back(pdb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
bool getPDBFiles(const QString &peExecutableFileName, QStringList *rc, QString *errorMessage)
|
||||||
|
{
|
||||||
|
HANDLE hFile = NULL;
|
||||||
|
HANDLE hFileMap = NULL;
|
||||||
|
void *fileMemory = 0;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
rc->clear();
|
||||||
|
do {
|
||||||
|
// Create a memory mapping of the file
|
||||||
|
hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
|
||||||
|
*errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||||
|
if (hFileMap == NULL) {
|
||||||
|
*errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
|
||||||
|
if(!fileMemory) {
|
||||||
|
*errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
|
||||||
|
if (!ntHeaders)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int debugSectionCount;
|
||||||
|
IMAGE_DEBUG_DIRECTORY *debugDir;
|
||||||
|
if (!getDebugDirectory(ntHeaders, fileMemory, &debugDir, &debugSectionCount, errorMessage))
|
||||||
|
return false;
|
||||||
|
if (debugSectionCount)
|
||||||
|
collectPDBfiles(fileMemory, debugDir, debugSectionCount, rc);
|
||||||
|
success = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
if (fileMemory)
|
||||||
|
UnmapViewOfFile(fileMemory);
|
||||||
|
|
||||||
|
if (hFileMap != NULL)
|
||||||
|
CloseHandle(hFileMap);
|
||||||
|
|
||||||
|
if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(hFile);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/plugins/debugger/win/peutils.h
Normal file
51
src/plugins/debugger/win/peutils.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at qt-sales@nokia.com.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PEUTILS_H
|
||||||
|
#define PEUTILS_H
|
||||||
|
|
||||||
|
#include <QtCore/qnamespace.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QStringList;
|
||||||
|
class QString;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
/* Helper functions to extract information from PE Win32 executable
|
||||||
|
* files (cf dumpbin utility). */
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
// Return a list of Program-Database (*.pdb) files a PE executable refers to. */
|
||||||
|
bool getPDBFiles(const QString &peExecutableFileName, QStringList *rc, QString *errorMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PEUTILS_H
|
||||||
3
src/plugins/debugger/win/win.pri
Normal file
3
src/plugins/debugger/win/win.pri
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
INCLUDEPATH+=$$PWD
|
||||||
|
SOURCES += $$PWD/peutils.cpp
|
||||||
|
HEADERS += $$PWD/peutils.h
|
||||||
Reference in New Issue
Block a user