Skip to content

Commit

Permalink
Improve support for applications that rely on fork for parallelism
Browse files Browse the repository at this point in the history
A forked process inherits the elfmap of the parent, thus we need to
copy that data when we encounter a fork event. This is complicated by
the fact that the task events and mmap events need to be sorted first.
Thus, we need to find the fork event when we deplete the buffers after
sorting and then initialize the child process elf map with the one
from the parent process.

Furthermore, pass the ppid through the ThreadStart event, to allow
client applications to inherit the comm for the newly created process.

Fixes: KDAB/hotspot#241
Change-Id: I5de13644e12def6704c5f622428a815fd87d2af4
  • Loading branch information
milianw committed Jun 13, 2020
1 parent c3be6e3 commit d56a7bb
Show file tree
Hide file tree
Showing 12 changed files with 1,418 additions and 6 deletions.
2 changes: 2 additions & 0 deletions app/perfdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,8 @@ class PerfRecordFork : public PerfRecord
bool sampleIdAll = false);
qint32 childTid() const { return m_tid; }
qint32 childPid() const { return m_pid; }
qint32 parentTid() const { return m_ptid; }
qint32 parentPid() const { return m_ppid; }
private:
qint32 m_pid, m_ppid;
qint32 m_tid, m_ptid;
Expand Down
6 changes: 6 additions & 0 deletions app/perfelfmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ class PerfElfMap : public QObject

bool isAddressInRange(quint64 addr) const;

void copyDataFrom(const PerfElfMap *parent)
{
m_elfs = parent->m_elfs;
m_lastBase = parent->m_lastBase;
}

signals:
void aboutToInvalidate(const ElfInfo &elf);

Expand Down
10 changes: 9 additions & 1 deletion app/perfsymboltable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,7 @@ PerfSymbolTable::ElfAndFile &PerfSymbolTable::ElfAndFile::operator=(
clear();
m_elf = other.m_elf;
m_file = other.m_file;
m_fullPath = std::move(other.m_fullPath);
other.m_elf = nullptr;
other.m_file = -1;
}
Expand All @@ -1156,6 +1157,7 @@ void PerfSymbolTable::ElfAndFile::clear()
}

PerfSymbolTable::ElfAndFile::ElfAndFile(const QFileInfo &fullPath)
: m_fullPath(fullPath)
{
m_file = eu_compat_open(fullPath.absoluteFilePath().toLocal8Bit().constData(),
O_RDONLY | O_BINARY);
Expand All @@ -1170,8 +1172,14 @@ PerfSymbolTable::ElfAndFile::ElfAndFile(const QFileInfo &fullPath)
}

PerfSymbolTable::ElfAndFile::ElfAndFile(PerfSymbolTable::ElfAndFile &&other)
: m_elf(other.m_elf), m_file(other.m_file)
: m_elf(other.m_elf), m_file(other.m_file), m_fullPath(std::move(other.m_fullPath))
{
other.m_elf = nullptr;
other.m_file = -1;
}

void PerfSymbolTable::initAfterFork(const PerfSymbolTable* parent)
{
m_elfs.copyDataFrom(&parent->m_elfs);
m_firstElf = ElfAndFile(parent->m_firstElf.fullPath());
}
6 changes: 5 additions & 1 deletion app/perfsymboltable.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class PerfSymbolTable
void clearCache();
bool cacheIsDirty() const { return m_cacheIsDirty; }

void initAfterFork(const PerfSymbolTable *parent);

private:
// Report an mmap to dwfl and parse it for symbols and inlines, or simply return it if dwfl has
// it already
Expand All @@ -84,20 +86,22 @@ class PerfSymbolTable
class ElfAndFile {
public:
ElfAndFile() {}
ElfAndFile(const QFileInfo &fullPath);
explicit ElfAndFile(const QFileInfo &fullPath);
ElfAndFile(ElfAndFile &&other);
ElfAndFile &operator=(ElfAndFile &&other);
ElfAndFile(const ElfAndFile &other) = delete;
ElfAndFile &operator=(const ElfAndFile &other) = delete;
~ElfAndFile();

Elf *elf() const { return m_elf; }
QFileInfo fullPath() const { return m_fullPath; }

private:
void clear();

Elf *m_elf = nullptr;
int m_file = -1;
QFileInfo m_fullPath;
};

QFile m_perfMapFile;
Expand Down
16 changes: 12 additions & 4 deletions app/perfunwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ void PerfUnwind::analyze(const PerfRecordSample &sample)
void PerfUnwind::fork(const PerfRecordFork &sample)
{
bufferEvent(TaskEvent{sample.childPid(), sample.childTid(), sample.time(), sample.cpu(),
0, ThreadStart},
sample.parentPid(), ThreadStart},
&m_taskEventsBuffer, &m_stats.numTaskEventsInRound);
}

Expand Down Expand Up @@ -948,16 +948,24 @@ void PerfUnwind::flushEventBuffer(uint desiredBufferSize)
m_lastFlushMaxTime = timestamp;
}

forwardMmapBuffer(mmapIt, mmapEnd, timestamp);

for (; taskEventIt != taskEventEnd && taskEventIt->time() <= sampleIt->time();
++taskEventIt) {
if (!m_stats.enabled) {
// flush the mmap buffer on fork events to allow initialization with the correct state
if (taskEventIt->m_type == ThreadStart && taskEventIt->m_pid != taskEventIt->m_payload) {
forwardMmapBuffer(mmapIt, mmapEnd, taskEventIt->time());
const auto childPid = taskEventIt->m_pid;
const auto parentPid = taskEventIt->m_payload;
symbolTable(childPid)->initAfterFork(symbolTable(parentPid));
}

sendTaskEvent(*taskEventIt);
}
m_eventBufferSize -= taskEventIt->size();
}

forwardMmapBuffer(mmapIt, mmapEnd, timestamp);

analyze(*sampleIt);
m_eventBufferSize -= sampleIt->size();
}
Expand Down Expand Up @@ -1034,7 +1042,7 @@ void PerfUnwind::sendTaskEvent(const TaskEvent& taskEvent)

if (taskEvent.m_type == ContextSwitchDefinition)
stream << static_cast<bool>(taskEvent.m_payload);
else if (taskEvent.m_type == Command)
else if (taskEvent.m_type == Command || taskEvent.m_type == ThreadStart)
stream << taskEvent.m_payload;

sendBuffer(buffer);
Expand Down
Binary file added tests/auto/perfdata/fork_static_gcc/fork
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit d56a7bb

Please sign in to comment.