How to correctly use PerfJITEventListener?


I'm struggling to get PerfJITEventListener to work. It was already fun
to find out that I need to enable LLVM_USE_PERF in cmake during
compilation, and that the jitdump files are stored in ~/.debug/jit/ by
default. But it's still not working.

The steps I do:

1. Get event listener with JITEventListener::createPerfJITEventListener
2. In NotifyLoadedFunction passed to orc::RTDyldObjectLinkingLayer ctor
   pass object and info to event listener's NotifyObjectEmitted

Then, I profile the application with:
perf record -k 1 ./app

This generates a jit-*.dump file in ~/.debug/jit/llvm-IR-jit-*/.

Inject the events into the perf data
perf inject -j -i -o

This creates jitted-*.so files in ~/.debug/jit/llvm-IR-jit-*/ for each
JIT compiled function.

Finally, I open the profile report:
perf report -i

But here is the problem. The JIT compiled functions are not "resolved".
I cannot "zoom in" to the code and annotate the instructions with
profile information.

Am I missing something here? Is there a small example somewhere which
uses PerfJITEventListener with ORC?

Best regards,

   I encountered the same problem recently. Finally I found it's a bug of linux kernel or linux-perf. LLVM uses mmap() and mprotect() to generate code. However, mprotect() will merge adjacent VMAs (virtual memory area) and report a event of merged VMA, causing pref think prior allocated memory has gone. I have reported the bug to Linux Perf Users mailing list.
   As a quick & super dirty workaround, I modified SectionMemoryManager::allocateSection (in llvm/lib/ExecutionEngine/SectionMemoryManager.cpp), let it alloc two pieces of memory for each memory request, and use only one of them:

--- a/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp
+++ b/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp
@@ -101,6 +101,9 @@ uint8_t *SectionMemoryManager::allocateSection(
    // FIXME: Initialize the Near member for each memory group to avoid
    // interleaving.
    std::error_code ec;
+ if (1) MMapper.allocateMappedMemory(
+ Purpose, RequiredSize, &MemGroup.Near,
+ sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec); // XXX: SUPER DIRTY
    sys::MemoryBlock MB = MMapper.allocateMappedMemory(
        Purpose, RequiredSize, &MemGroup.Near,
        sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);

   Of course this can't be merged into LLVM, but I think this code may help some people who encountered the same problem.

Zhang Boyang