forked from qt-creator/qt-creator
Debugger: Support setting substitute path by regexp
This is useful when there are multiple build machines with different path, and the user would like to match anything up to some known directory to his local project (variable support will also be useful - will try to add that later). Syntax: (/home/.*)/KnownSubdir -> /home/my/project Capture group will be replaced by the value. In this example the substitute path will be (in case a source string found such as /home/SomeUser/SomeProject/KnownSubdir/foo.cpp): /home/SomeUser/SomeProject -> /home/my/project Change-Id: I19d03c9388161d8456a86676086dcb06dc3d7370 Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
committed by
Orgad Shaneh
parent
63da3cb9e0
commit
f0e2708d3e
@@ -101,12 +101,9 @@ static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfDa
|
|||||||
sh->memsz = getWord(s, context);
|
sh->memsz = getWord(s, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ElfMapper
|
ElfMapper::ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
|
||||||
{
|
|
||||||
public:
|
|
||||||
ElfMapper(const ElfReader *reader) : file(reader->m_binary) {}
|
|
||||||
|
|
||||||
bool map()
|
bool ElfMapper::map()
|
||||||
{
|
{
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
return false;
|
return false;
|
||||||
@@ -122,13 +119,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
QFile file;
|
|
||||||
QByteArray raw;
|
|
||||||
union { const char *start; const uchar *ustart; };
|
|
||||||
quint64 fdlen;
|
|
||||||
};
|
|
||||||
|
|
||||||
ElfReader::ElfReader(const QString &binary)
|
ElfReader::ElfReader(const QString &binary)
|
||||||
: m_binary(binary)
|
: m_binary(binary)
|
||||||
{
|
{
|
||||||
@@ -298,19 +288,22 @@ ElfReader::Result ElfReader::readIt()
|
|||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ElfReader::readSection(const QByteArray &name)
|
QSharedPointer<ElfMapper> ElfReader::readSection(const QByteArray &name)
|
||||||
{
|
{
|
||||||
|
QSharedPointer<ElfMapper> mapper;
|
||||||
readIt();
|
readIt();
|
||||||
int i = m_elfData.indexOf(name);
|
int i = m_elfData.indexOf(name);
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
return QByteArray();
|
return mapper;
|
||||||
|
|
||||||
ElfMapper mapper(this);
|
mapper.reset(new ElfMapper(this));
|
||||||
if (!mapper.map())
|
if (!mapper->map())
|
||||||
return QByteArray();
|
return mapper;
|
||||||
|
|
||||||
const ElfSectionHeader §ion = m_elfData.sectionHeaders.at(i);
|
const ElfSectionHeader §ion = m_elfData.sectionHeaders.at(i);
|
||||||
return QByteArray(mapper.start + section.offset, section.size);
|
mapper->start += section.offset;
|
||||||
|
mapper->fdlen = section.size;
|
||||||
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QByteArray cutout(const char *s)
|
static QByteArray cutout(const char *s)
|
||||||
|
@@ -32,12 +32,17 @@
|
|||||||
|
|
||||||
#include "utils_global.h"
|
#include "utils_global.h"
|
||||||
|
|
||||||
|
#include <qbytearray.h>
|
||||||
#include <qendian.h>
|
#include <qendian.h>
|
||||||
|
#include <qfile.h>
|
||||||
#include <qvector.h>
|
#include <qvector.h>
|
||||||
#include <qcoreapplication.h>
|
#include <qcoreapplication.h>
|
||||||
|
#include <qsharedpointer.h>
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
|
class ElfMapper;
|
||||||
|
|
||||||
enum ElfProgramHeaderType
|
enum ElfProgramHeaderType
|
||||||
{
|
{
|
||||||
Elf_PT_NULL = 0,
|
Elf_PT_NULL = 0,
|
||||||
@@ -161,7 +166,7 @@ public:
|
|||||||
enum Result { Ok, NotElf, Corrupt };
|
enum Result { Ok, NotElf, Corrupt };
|
||||||
|
|
||||||
ElfData readHeaders();
|
ElfData readHeaders();
|
||||||
QByteArray readSection(const QByteArray §ionName);
|
QSharedPointer<ElfMapper> readSection(const QByteArray §ionName);
|
||||||
QString errorString() const { return m_errorString; }
|
QString errorString() const { return m_errorString; }
|
||||||
QByteArray readCoreName(bool *isCore);
|
QByteArray readCoreName(bool *isCore);
|
||||||
|
|
||||||
@@ -174,6 +179,19 @@ private:
|
|||||||
ElfData m_elfData;
|
ElfData m_elfData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QTCREATOR_UTILS_EXPORT ElfMapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ElfMapper(const ElfReader *reader);
|
||||||
|
bool map();
|
||||||
|
|
||||||
|
public:
|
||||||
|
QFile file;
|
||||||
|
QByteArray raw;
|
||||||
|
union { const char *start; const uchar *ustart; };
|
||||||
|
quint64 fdlen;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
||||||
#endif // UTILS_ELFREADER_H
|
#endif // UTILS_ELFREADER_H
|
||||||
|
@@ -230,13 +230,24 @@ CommonOptionsPageWidget::CommonOptionsPageWidget
|
|||||||
GlobalDebuggerOptions CommonOptionsPageWidget::globalOptions() const
|
GlobalDebuggerOptions CommonOptionsPageWidget::globalOptions() const
|
||||||
{
|
{
|
||||||
GlobalDebuggerOptions o;
|
GlobalDebuggerOptions o;
|
||||||
o.sourcePathMap = sourcesMappingWidget->sourcePathMap();
|
SourcePathMap allPathMap = sourcesMappingWidget->sourcePathMap();
|
||||||
|
for (auto it = allPathMap.begin(), end = allPathMap.end(); it != end; ++it) {
|
||||||
|
const QString key = it.key();
|
||||||
|
if (key.startsWith(QLatin1Char('(')))
|
||||||
|
o.sourcePathRegExpMap.append(qMakePair(QRegExp(key), it.value()));
|
||||||
|
else
|
||||||
|
o.sourcePathMap.insert(key, it.value());
|
||||||
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommonOptionsPageWidget::setGlobalOptions(const GlobalDebuggerOptions &go)
|
void CommonOptionsPageWidget::setGlobalOptions(const GlobalDebuggerOptions &go)
|
||||||
{
|
{
|
||||||
sourcesMappingWidget->setSourcePathMap(go.sourcePathMap);
|
SourcePathMap allPathMap = go.sourcePathMap;
|
||||||
|
foreach (auto regExpMap, go.sourcePathRegExpMap)
|
||||||
|
allPathMap.insert(regExpMap.first.pattern(), regExpMap.second);
|
||||||
|
|
||||||
|
sourcesMappingWidget->setSourcePathMap(allPathMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
@@ -55,7 +55,7 @@ void GlobalDebuggerOptions::toSettings() const
|
|||||||
{
|
{
|
||||||
QSettings *s = Core::ICore::settings();
|
QSettings *s = Core::ICore::settings();
|
||||||
s->beginWriteArray(QLatin1String(sourcePathMappingArrayNameC));
|
s->beginWriteArray(QLatin1String(sourcePathMappingArrayNameC));
|
||||||
if (!sourcePathMap.isEmpty()) {
|
if (!sourcePathMap.isEmpty() || !sourcePathRegExpMap.isEmpty()) {
|
||||||
const QString sourcePathMappingSourceKey = QLatin1String(sourcePathMappingSourceKeyC);
|
const QString sourcePathMappingSourceKey = QLatin1String(sourcePathMappingSourceKeyC);
|
||||||
const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC);
|
const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -66,6 +66,13 @@ void GlobalDebuggerOptions::toSettings() const
|
|||||||
s->setValue(sourcePathMappingSourceKey, it.key());
|
s->setValue(sourcePathMappingSourceKey, it.key());
|
||||||
s->setValue(sourcePathMappingTargetKey, it.value());
|
s->setValue(sourcePathMappingTargetKey, it.value());
|
||||||
}
|
}
|
||||||
|
for (auto it = sourcePathRegExpMap.constBegin(), cend = sourcePathRegExpMap.constEnd();
|
||||||
|
it != cend;
|
||||||
|
++it, ++i) {
|
||||||
|
s->setArrayIndex(i);
|
||||||
|
s->setValue(sourcePathMappingSourceKey, it->first.pattern());
|
||||||
|
s->setValue(sourcePathMappingTargetKey, it->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s->endArray();
|
s->endArray();
|
||||||
}
|
}
|
||||||
@@ -79,8 +86,12 @@ void GlobalDebuggerOptions::fromSettings()
|
|||||||
const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC);
|
const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
s->setArrayIndex(i);
|
s->setArrayIndex(i);
|
||||||
sourcePathMap.insert(s->value(sourcePathMappingSourceKey).toString(),
|
const QString key = s->value(sourcePathMappingSourceKey).toString();
|
||||||
s->value(sourcePathMappingTargetKey).toString());
|
const QString value = s->value(sourcePathMappingTargetKey).toString();
|
||||||
|
if (key.startsWith(QLatin1Char('(')))
|
||||||
|
sourcePathRegExpMap.append(qMakePair(QRegExp(key), value));
|
||||||
|
else
|
||||||
|
sourcePathMap.insert(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s->endArray();
|
s->endArray();
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QRegExp>
|
||||||
|
|
||||||
namespace Utils { class SavedAction; }
|
namespace Utils { class SavedAction; }
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ namespace Debugger {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
typedef QMap<QString, QString> SourcePathMap;
|
typedef QMap<QString, QString> SourcePathMap;
|
||||||
|
typedef QVector<QPair<QRegExp, QString> > SourcePathRegExpMap;
|
||||||
|
|
||||||
// Global debugger options that are not stored as saved action.
|
// Global debugger options that are not stored as saved action.
|
||||||
class GlobalDebuggerOptions
|
class GlobalDebuggerOptions
|
||||||
@@ -48,11 +50,17 @@ public:
|
|||||||
void toSettings() const;
|
void toSettings() const;
|
||||||
void fromSettings();
|
void fromSettings();
|
||||||
bool operator==(const GlobalDebuggerOptions &rhs) const
|
bool operator==(const GlobalDebuggerOptions &rhs) const
|
||||||
{ return sourcePathMap == rhs.sourcePathMap; }
|
{
|
||||||
|
return sourcePathMap == rhs.sourcePathMap
|
||||||
|
&& sourcePathRegExpMap == rhs.sourcePathRegExpMap;
|
||||||
|
}
|
||||||
bool operator!=(const GlobalDebuggerOptions &rhs) const
|
bool operator!=(const GlobalDebuggerOptions &rhs) const
|
||||||
{ return sourcePathMap != rhs.sourcePathMap; }
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
SourcePathMap sourcePathMap;
|
SourcePathMap sourcePathMap;
|
||||||
|
SourcePathRegExpMap sourcePathRegExpMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebuggerSettings : public QObject
|
class DebuggerSettings : public QObject
|
||||||
|
@@ -705,7 +705,7 @@ void DebuggerEnginePrivate::doSetupEngine()
|
|||||||
{
|
{
|
||||||
m_engine->showMessage(_("CALL: SETUP ENGINE"));
|
m_engine->showMessage(_("CALL: SETUP ENGINE"));
|
||||||
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << m_engine << state());
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << m_engine << state());
|
||||||
m_engine->checkForReleaseBuild(m_startParameters);
|
m_engine->validateExecutable(&m_startParameters);
|
||||||
m_engine->setupEngine();
|
m_engine->setupEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1749,18 +1749,19 @@ void DebuggerEngine::setStateDebugging(bool on)
|
|||||||
d->m_isStateDebugging = on;
|
d->m_isStateDebugging = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerEngine::checkForReleaseBuild(const DebuggerStartParameters &sp)
|
void DebuggerEngine::validateExecutable(DebuggerStartParameters *sp)
|
||||||
{
|
{
|
||||||
if (!boolSetting(WarnOnReleaseBuilds) || !(sp.languages & CppLanguage))
|
if (sp->languages == QmlLanguage)
|
||||||
return;
|
return;
|
||||||
QString binary = sp.executable;
|
QString binary = sp->executable;
|
||||||
if (binary.isEmpty())
|
if (binary.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds);
|
||||||
QString detailedWarning;
|
QString detailedWarning;
|
||||||
switch (sp.toolChainAbi.binaryFormat()) {
|
switch (sp->toolChainAbi.binaryFormat()) {
|
||||||
case ProjectExplorer::Abi::PEFormat: {
|
case ProjectExplorer::Abi::PEFormat: {
|
||||||
if (sp.masterEngineType != CdbEngineType)
|
if (!warnOnRelease || (sp->masterEngineType != CdbEngineType))
|
||||||
return;
|
return;
|
||||||
if (!binary.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive))
|
if (!binary.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive))
|
||||||
binary.append(QLatin1String(".exe"));
|
binary.append(QLatin1String(".exe"));
|
||||||
@@ -1822,6 +1823,36 @@ void DebuggerEngine::checkForReleaseBuild(const DebuggerStartParameters &sp)
|
|||||||
// bool hasBuildId = elfData.indexOf(".note.gnu.build-id") >= 0;
|
// bool hasBuildId = elfData.indexOf(".note.gnu.build-id") >= 0;
|
||||||
bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0;
|
bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0;
|
||||||
bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0;
|
bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0;
|
||||||
|
if (hasEmbeddedInfo) {
|
||||||
|
if (QSharedPointer<Utils::ElfMapper> mapper = reader.readSection(".debug_str")) {
|
||||||
|
QSharedPointer<GlobalDebuggerOptions> options = debuggerCore()->globalDebuggerOptions();
|
||||||
|
SourcePathRegExpMap globalRegExpSourceMap = options->sourcePathRegExpMap;
|
||||||
|
const char *str = mapper->start;
|
||||||
|
const char *limit = str + mapper->fdlen;
|
||||||
|
while (str < limit) {
|
||||||
|
QString string = QString::fromUtf8(str);
|
||||||
|
auto itExp = globalRegExpSourceMap.begin();
|
||||||
|
auto itEnd = globalRegExpSourceMap.end();
|
||||||
|
while (itExp != itEnd) {
|
||||||
|
QRegExp exp = itExp->first;
|
||||||
|
int index = exp.indexIn(string);
|
||||||
|
if (index != -1) {
|
||||||
|
sp->sourcePathMap.insert(string.left(index) + exp.cap(1), itExp->second);
|
||||||
|
itExp = globalRegExpSourceMap.erase(itExp);
|
||||||
|
} else {
|
||||||
|
++itExp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (globalRegExpSourceMap.isEmpty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
const int len = strlen(str);
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
str += len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (hasEmbeddedInfo || hasLink)
|
if (hasEmbeddedInfo || hasLink)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1834,11 +1865,13 @@ void DebuggerEngine::checkForReleaseBuild(const DebuggerStartParameters &sp)
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (warnOnRelease) {
|
||||||
showMessageBox(QMessageBox::Information, tr("Warning"),
|
showMessageBox(QMessageBox::Information, tr("Warning"),
|
||||||
tr("This does not seem to be a \"Debug\" build.\n"
|
tr("This does not seem to be a \"Debug\" build.\n"
|
||||||
"Setting breakpoints by file name and line number may fail.")
|
"Setting breakpoints by file name and line number may fail.")
|
||||||
+ QLatin1Char('\n') + detailedWarning);
|
+ QLatin1Char('\n') + detailedWarning);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
@@ -382,7 +382,7 @@ protected:
|
|||||||
bool isStateDebugging() const;
|
bool isStateDebugging() const;
|
||||||
void setStateDebugging(bool on);
|
void setStateDebugging(bool on);
|
||||||
|
|
||||||
static void checkForReleaseBuild(const DebuggerStartParameters& sp);
|
static void validateExecutable(DebuggerStartParameters *sp);
|
||||||
|
|
||||||
virtual void setupSlaveInferior();
|
virtual void setupSlaveInferior();
|
||||||
virtual void setupSlaveEngine();
|
virtual void setupSlaveEngine();
|
||||||
|
@@ -211,7 +211,13 @@ DebuggerSourcePathMappingWidget::DebuggerSourcePathMappingWidget(QWidget *parent
|
|||||||
"<p>This is useful when using a copy of the source tree "
|
"<p>This is useful when using a copy of the source tree "
|
||||||
"at a location different from the one "
|
"at a location different from the one "
|
||||||
"at which the modules where built, for example, while "
|
"at which the modules where built, for example, while "
|
||||||
"doing remote debugging.</body></html>"));
|
"doing remote debugging.</p>"
|
||||||
|
"<p>If source is specified as a regular expression by starting it with an "
|
||||||
|
"open parenthesis, Qt Creator matches the paths in the ELF with the "
|
||||||
|
"regular expression to automatically determine the source path.</p>"
|
||||||
|
"<p>Example: <b>(/home/.*/Project)/KnownSubDir -> D:\\Project</b> will "
|
||||||
|
"substitute ELF built by any user to your local project directory.</p>"
|
||||||
|
"</body></html>"));
|
||||||
// Top list/left part.
|
// Top list/left part.
|
||||||
m_treeView->setRootIsDecorated(false);
|
m_treeView->setRootIsDecorated(false);
|
||||||
m_treeView->setUniformRowHeights(true);
|
m_treeView->setUniformRowHeights(true);
|
||||||
|
Reference in New Issue
Block a user