From f0e2708d3e516a2999818dfc85d0d4809b21171e Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Sun, 7 Sep 2014 23:24:10 +0300 Subject: [PATCH] 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 --- src/libs/utils/elfreader.cpp | 53 ++++++++----------- src/libs/utils/elfreader.h | 20 ++++++- src/plugins/debugger/commonoptionspage.cpp | 15 +++++- src/plugins/debugger/debuggeractions.cpp | 17 ++++-- src/plugins/debugger/debuggeractions.h | 12 ++++- src/plugins/debugger/debuggerengine.cpp | 53 +++++++++++++++---- src/plugins/debugger/debuggerengine.h | 2 +- .../debuggersourcepathmappingwidget.cpp | 8 ++- 8 files changed, 130 insertions(+), 50 deletions(-) diff --git a/src/libs/utils/elfreader.cpp b/src/libs/utils/elfreader.cpp index 25bb35cf5b2..b48ae2a7a5d 100644 --- a/src/libs/utils/elfreader.cpp +++ b/src/libs/utils/elfreader.cpp @@ -101,33 +101,23 @@ static void parseProgramHeader(const uchar *s, ElfProgramHeader *sh, const ElfDa sh->memsz = getWord(s, context); } -class ElfMapper +ElfMapper::ElfMapper(const ElfReader *reader) : file(reader->m_binary) {} + +bool ElfMapper::map() { -public: - ElfMapper(const ElfReader *reader) : file(reader->m_binary) {} + if (!file.open(QIODevice::ReadOnly)) + return false; - bool map() - { - if (!file.open(QIODevice::ReadOnly)) - return false; - - fdlen = file.size(); - ustart = file.map(0, fdlen); - if (ustart == 0) { - // Try reading the data into memory instead. - raw = file.readAll(); - start = raw.constData(); - fdlen = raw.size(); - } - return true; + fdlen = file.size(); + ustart = file.map(0, fdlen); + if (ustart == 0) { + // Try reading the data into memory instead. + raw = file.readAll(); + start = raw.constData(); + fdlen = raw.size(); } - -public: - QFile file; - QByteArray raw; - union { const char *start; const uchar *ustart; }; - quint64 fdlen; -}; + return true; +} ElfReader::ElfReader(const QString &binary) : m_binary(binary) @@ -298,19 +288,22 @@ ElfReader::Result ElfReader::readIt() return Ok; } -QByteArray ElfReader::readSection(const QByteArray &name) +QSharedPointer ElfReader::readSection(const QByteArray &name) { + QSharedPointer mapper; readIt(); int i = m_elfData.indexOf(name); if (i == -1) - return QByteArray(); + return mapper; - ElfMapper mapper(this); - if (!mapper.map()) - return QByteArray(); + mapper.reset(new ElfMapper(this)); + if (!mapper->map()) + return mapper; 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) diff --git a/src/libs/utils/elfreader.h b/src/libs/utils/elfreader.h index 4692f0384a5..67d0539de31 100644 --- a/src/libs/utils/elfreader.h +++ b/src/libs/utils/elfreader.h @@ -32,12 +32,17 @@ #include "utils_global.h" +#include #include +#include #include #include +#include namespace Utils { +class ElfMapper; + enum ElfProgramHeaderType { Elf_PT_NULL = 0, @@ -161,7 +166,7 @@ public: enum Result { Ok, NotElf, Corrupt }; ElfData readHeaders(); - QByteArray readSection(const QByteArray §ionName); + QSharedPointer readSection(const QByteArray §ionName); QString errorString() const { return m_errorString; } QByteArray readCoreName(bool *isCore); @@ -174,6 +179,19 @@ private: 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 #endif // UTILS_ELFREADER_H diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index 87f0229de19..34263c3e699 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -230,13 +230,24 @@ CommonOptionsPageWidget::CommonOptionsPageWidget GlobalDebuggerOptions CommonOptionsPageWidget::globalOptions() const { 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; } 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); } /////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index d39f3117113..190b014c28c 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -55,7 +55,7 @@ void GlobalDebuggerOptions::toSettings() const { QSettings *s = Core::ICore::settings(); s->beginWriteArray(QLatin1String(sourcePathMappingArrayNameC)); - if (!sourcePathMap.isEmpty()) { + if (!sourcePathMap.isEmpty() || !sourcePathRegExpMap.isEmpty()) { const QString sourcePathMappingSourceKey = QLatin1String(sourcePathMappingSourceKeyC); const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC); int i = 0; @@ -66,6 +66,13 @@ void GlobalDebuggerOptions::toSettings() const s->setValue(sourcePathMappingSourceKey, it.key()); 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(); } @@ -79,8 +86,12 @@ void GlobalDebuggerOptions::fromSettings() const QString sourcePathMappingTargetKey = QLatin1String(sourcePathMappingTargetKeyC); for (int i = 0; i < count; ++i) { s->setArrayIndex(i); - sourcePathMap.insert(s->value(sourcePathMappingSourceKey).toString(), - s->value(sourcePathMappingTargetKey).toString()); + const QString key = s->value(sourcePathMappingSourceKey).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(); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index 9a33116a36a..9998d73bf03 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace Utils { class SavedAction; } @@ -40,6 +41,7 @@ namespace Debugger { namespace Internal { typedef QMap SourcePathMap; +typedef QVector > SourcePathRegExpMap; // Global debugger options that are not stored as saved action. class GlobalDebuggerOptions @@ -48,11 +50,17 @@ public: void toSettings() const; void fromSettings(); bool operator==(const GlobalDebuggerOptions &rhs) const - { return sourcePathMap == rhs.sourcePathMap; } + { + return sourcePathMap == rhs.sourcePathMap + && sourcePathRegExpMap == rhs.sourcePathRegExpMap; + } bool operator!=(const GlobalDebuggerOptions &rhs) const - { return sourcePathMap != rhs.sourcePathMap; } + { + return !(*this == rhs); + } SourcePathMap sourcePathMap; + SourcePathRegExpMap sourcePathRegExpMap; }; class DebuggerSettings : public QObject diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index de3d8130a84..93ae9aeba1f 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -705,7 +705,7 @@ void DebuggerEnginePrivate::doSetupEngine() { m_engine->showMessage(_("CALL: SETUP ENGINE")); QTC_ASSERT(state() == EngineSetupRequested, qDebug() << m_engine << state()); - m_engine->checkForReleaseBuild(m_startParameters); + m_engine->validateExecutable(&m_startParameters); m_engine->setupEngine(); } @@ -1749,18 +1749,19 @@ void DebuggerEngine::setStateDebugging(bool 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; - QString binary = sp.executable; + QString binary = sp->executable; if (binary.isEmpty()) return; + const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds); QString detailedWarning; - switch (sp.toolChainAbi.binaryFormat()) { + switch (sp->toolChainAbi.binaryFormat()) { case ProjectExplorer::Abi::PEFormat: { - if (sp.masterEngineType != CdbEngineType) + if (!warnOnRelease || (sp->masterEngineType != CdbEngineType)) return; if (!binary.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) binary.append(QLatin1String(".exe")); @@ -1822,6 +1823,36 @@ void DebuggerEngine::checkForReleaseBuild(const DebuggerStartParameters &sp) // bool hasBuildId = elfData.indexOf(".note.gnu.build-id") >= 0; bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0; bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0; + if (hasEmbeddedInfo) { + if (QSharedPointer mapper = reader.readSection(".debug_str")) { + QSharedPointer 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) return; @@ -1834,10 +1865,12 @@ void DebuggerEngine::checkForReleaseBuild(const DebuggerStartParameters &sp) default: return; } - showMessageBox(QMessageBox::Information, tr("Warning"), - tr("This does not seem to be a \"Debug\" build.\n" - "Setting breakpoints by file name and line number may fail.") - + QLatin1Char('\n') + detailedWarning); + if (warnOnRelease) { + showMessageBox(QMessageBox::Information, tr("Warning"), + tr("This does not seem to be a \"Debug\" build.\n" + "Setting breakpoints by file name and line number may fail.") + + QLatin1Char('\n') + detailedWarning); + } } } // namespace Debugger diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 75f6716bc9a..ce299a09bbb 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -382,7 +382,7 @@ protected: bool isStateDebugging() const; void setStateDebugging(bool on); - static void checkForReleaseBuild(const DebuggerStartParameters& sp); + static void validateExecutable(DebuggerStartParameters *sp); virtual void setupSlaveInferior(); virtual void setupSlaveEngine(); diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index e2175cdb781..34b1ce7277a 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -211,7 +211,13 @@ DebuggerSourcePathMappingWidget::DebuggerSourcePathMappingWidget(QWidget *parent "

This is useful when using a copy of the source tree " "at a location different from the one " "at which the modules where built, for example, while " - "doing remote debugging.")); + "doing remote debugging.

" + "

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.

" + "

Example: (/home/.*/Project)/KnownSubDir -> D:\\Project will " + "substitute ELF built by any user to your local project directory.

" + "")); // Top list/left part. m_treeView->setRootIsDecorated(false); m_treeView->setUniformRowHeights(true);