Skip to content

Commit 845ff48

Browse files
committed
Fix race condition when setting thread names in perfparser
We must not write to any data member from the background thread. Instead, use a signal to serialize the write operation into the context of the main thread. This fixes the data race and potential issues when running this code.
1 parent 6b32d74 commit 845ff48

File tree

3 files changed

+25
-13
lines changed

3 files changed

+25
-13
lines changed

src/models/data.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,11 @@ struct Summary
821821
QStringList errors;
822822
};
823823

824+
struct ThreadNames
825+
{
826+
QHash<qint32, QHash<qint32, QString>> names;
827+
};
828+
824829
struct EventResults
825830
{
826831
QVector<ThreadEvents> threads;
@@ -951,6 +956,9 @@ Q_DECLARE_TYPEINFO(Data::Summary, Q_MOVABLE_TYPE);
951956
Q_DECLARE_METATYPE(Data::CostSummary)
952957
Q_DECLARE_TYPEINFO(Data::CostSummary, Q_MOVABLE_TYPE);
953958

959+
Q_DECLARE_METATYPE(Data::ThreadNames)
960+
Q_DECLARE_TYPEINFO(Data::ThreadNames, Q_MOVABLE_TYPE);
961+
954962
Q_DECLARE_METATYPE(Data::EventResults)
955963
Q_DECLARE_TYPEINFO(Data::EventResults, Q_MOVABLE_TYPE);
956964

src/parsers/perf/perfparser.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -555,20 +555,20 @@ void addCallerCalleeEvent(const Data::Symbol& symbol, const Data::Location& loca
555555

556556
template<typename FrameCallback>
557557
void addBottomUpResult(Data::BottomUpResults* bottomUpResult, Settings::CostAggregation costAggregation,
558-
const QHash<qint32, QHash<qint32, QString>>& commands, int type, quint64 cost, qint32 pid,
559-
qint32 tid, quint32 cpu, const QVector<qint32>& frames, const FrameCallback& frameCallback)
558+
const Data::ThreadNames& commands, int type, quint64 cost, qint32 pid, qint32 tid, quint32 cpu,
559+
const QVector<qint32>& frames, const FrameCallback& frameCallback)
560560
{
561561
switch (costAggregation) {
562562
case Settings::CostAggregation::BySymbol:
563563
bottomUpResult->addEvent(type, cost, frames, frameCallback);
564564
break;
565565
case Settings::CostAggregation::ByThread: {
566-
auto thread = commands.value(pid).value(tid);
566+
auto thread = commands.names.value(pid).value(tid);
567567
bottomUpResult->addEvent(thread.isEmpty() ? QString::number(tid) : thread, type, cost, frames, frameCallback);
568568
break;
569569
}
570570
case Settings::CostAggregation::ByProcess: {
571-
auto process = commands.value(pid).value(pid);
571+
auto process = commands.names.value(pid).value(pid);
572572
bottomUpResult->addEvent(process.isEmpty() ? QString::number(pid) : process, type, cost, frames, frameCallback);
573573
break;
574574
}
@@ -747,8 +747,8 @@ class PerfParserPrivate : public QObject
747747
auto thread = addThread(threadStart);
748748
thread->time.start = threadStart.time;
749749
if (threadStart.ppid != threadStart.pid) {
750-
const auto parentComm = commands.value(threadStart.ppid).value(threadStart.ppid);
751-
commands[threadStart.pid][threadStart.pid] = parentComm;
750+
const auto parentComm = commands.names.value(threadStart.ppid).value(threadStart.ppid);
751+
commands.names[threadStart.pid][threadStart.pid] = parentComm;
752752
thread->name = parentComm;
753753
}
754754
break;
@@ -960,9 +960,9 @@ class PerfParserPrivate : public QObject
960960
// we started the application, otherwise we override the start time when
961961
// we encounter a ThreadStart event
962962
thread.time.start = applicationTime.start;
963-
thread.name = commands.value(thread.pid).value(thread.tid);
963+
thread.name = commands.names.value(thread.pid).value(thread.tid);
964964
if (thread.name.isEmpty() && thread.pid != thread.tid)
965-
thread.name = commands.value(thread.pid).value(thread.pid);
965+
thread.name = commands.names.value(thread.pid).value(thread.pid);
966966
eventResult.threads.push_back(thread);
967967
return &eventResult.threads.last();
968968
}
@@ -984,7 +984,7 @@ class PerfParserPrivate : public QObject
984984
thread->name = comm;
985985
}
986986
// and remember the command, maybe a future ThreadStart event references it
987-
commands[command.pid][command.tid] = comm;
987+
commands.names[command.pid][command.tid] = comm;
988988
}
989989

990990
void addLocation(const LocationDefinition& location)
@@ -1124,7 +1124,7 @@ class PerfParserPrivate : public QObject
11241124
void addSampleToBottomUp(const Sample& sample, SampleCost sampleCost)
11251125
{
11261126
if (perfScriptOutput) {
1127-
*perfScriptOutput << commands.value(sample.pid).value(sample.pid) << '\t' << sample.pid << '\t'
1127+
*perfScriptOutput << commands.names.value(sample.pid).value(sample.pid) << '\t' << sample.pid << '\t'
11281128
<< sample.time / 1000000000 << '.' << qSetFieldWidth(9) << qSetPadChar(QLatin1Char('0'))
11291129
<< sample.time % 1000000000 << qSetFieldWidth(0) << ":\t" << sampleCost.cost << ' '
11301130
<< strings.value(attributes.value(sampleCost.attributeId).name.id) << '\n';
@@ -1376,7 +1376,7 @@ class PerfParserPrivate : public QObject
13761376
Data::EventResults eventResult;
13771377
Data::TracepointResults tracepointResult;
13781378
Data::FrequencyResults frequencyResult;
1379-
QHash<qint32, QHash<qint32, QString>> commands;
1379+
Data::ThreadNames commands;
13801380
std::unique_ptr<QTextStream> perfScriptOutput;
13811381
QHash<qint32, SymbolCount> numSymbolsByModule;
13821382
QSet<QString> encounteredErrors;
@@ -1420,6 +1420,7 @@ PerfParser::PerfParser(QObject* parent)
14201420
qRegisterMetaType<Data::PerLibraryResults>();
14211421
qRegisterMetaType<Data::TracepointResults>();
14221422
qRegisterMetaType<Data::FrequencyResults>();
1423+
qRegisterMetaType<Data::ThreadNames>();
14231424

14241425
// set data via signal/slot connection to ensure we don't introduce a data race
14251426
connect(this, &PerfParser::bottomUpDataAvailable, this, [this](const Data::BottomUpResults& data) {
@@ -1447,6 +1448,8 @@ PerfParser::PerfParser(QObject* parent)
14471448
m_tracepointResults = data;
14481449
}
14491450
});
1451+
connect(this, &PerfParser::threadNamesAvailable, this,
1452+
[this](const Data::ThreadNames& threadNames) { m_threadNames = threadNames; });
14501453
connect(this, &PerfParser::parsingStarted, this, [this]() {
14511454
m_isParsing = true;
14521455
m_stopRequested = false;
@@ -1562,7 +1565,7 @@ void PerfParser::startParseFile(const QString& path)
15621565
emit frequencyDataAvailable(d.frequencyResult);
15631566
emit parsingFinished();
15641567

1565-
m_threadNames = d.commands;
1568+
emit threadNamesAvailable(d.commands);
15661569

15671570
if (d.m_numSamplesWithMoreThanOneFrame == 0) {
15681571
emit parserWarning(tr("Samples contained no call stack frames. Consider passing <code>--call-graph "

src/parsers/perf/perfparser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class PerfParser : public QObject
5858
void tracepointDataAvailable(const Data::TracepointResults& data);
5959
void frequencyDataAvailable(const Data::FrequencyResults& data);
6060
void eventsAvailable(const Data::EventResults& events);
61+
void threadNamesAvailable(const Data::ThreadNames& threadNames);
6162
void parsingFinished();
6263
void parsingFailed(const QString& errorMessage);
6364
void exportFailed(const QString& errorMessage);
@@ -86,5 +87,5 @@ class PerfParser : public QObject
8687
std::atomic<bool> m_stopRequested;
8788
std::atomic<bool> m_costAggregationChanged;
8889
std::unique_ptr<QTemporaryFile> m_decompressed;
89-
QHash<qint32, QHash<qint32, QString>> m_threadNames;
90+
Data::ThreadNames m_threadNames;
9091
};

0 commit comments

Comments
 (0)