Linux perf does not record function in libstdc++.so correctly

71 Views Asked by At

I am using perf to profile a C++ program. The code is like:

std::string generateRandomString() {
    // Initialize a random number generator
    std::random_device rd;
    std::mt19937 gen(rd());

    // Define a distribution for the length of the string (1 to 6)
    std::uniform_int_distribution<> lenDistribution(1, 6);
    int length = lenDistribution(gen);

    // Define characters that can be part of the string
    const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    // Generate the random string
    std::uniform_int_distribution<> charDistribution(0, characters.size() - 1);
    std::string result;
    result.reserve(length);

    for (int i = 0; i < length; ++i) {
        result += characters[charDistribution(gen)];
    }
    return result;
}


void test_trie(){
    for (...) generateRandomString();
}

void test(){
    // do something
}

int main(){
    test_trie();
    test();
}

Then

  1. I compile it with g++ -g -O2 main.cpp -o secmaster_test (-O0 is also tried)
  2. profile it with perf record -F 999 -g -- ./secmaster_test
  3. draw a flamegraph. like this enter image description here

But I think the result should be like follow:

                                           | --- some random -----|
                  | ...something...        | generateRandomString | -- --|
                  | ----------test-------- | --------------test_trie-----|
                  | ---------------------main----------------------------|
|--startup code-- | -------------------secmaster_test------------------- | -- cleanup -- |

Seems that perf somehow don't know functions about random is called in main() ?

BTW, I tried:

  1. using perf record --call-graph dwarf
  2. using perf record --call-graph fp

and failed

2

There are 2 best solutions below

0
54138qqqq On BEST ANSWER

Solved with the help of comments and answers.

Default compilation of stdlibc++ does not contain all frame pointers, so perf captures incorrect frame pointer. The solution is to recompile gcc (for c,c++, to get glibc and libstdc++)

Hereby is the steps for me:

  1. download gcc 11.4
  2. in gcc root dir, run ./contrib/download_prerequisites to download prerequisites
  3. run mkdir build && cd build && ../configure CXXFLAGS="-g -O2 -D_GNU_SOURCE -fno-omit-frame-pointer" --enable-languages=c,c++ --disable-multilib to configure gcc, maybe add more options like --prefix=/path/to/install
  4. compile and install make -j16 && make install

Then compile program with new gcc/g++, will give correct FlameGraph.

2
Useless On

From the this answer:

Always compile with frame pointers. [or] ... a workaround for missing frame pointers in user-level stacks: libunwind, which uses dwarf.

So, either build with

  • -fno-omit-frame-pointer or
  • -g dwarf

The frame pointer is the usual way perf (and other tools) "read" the call stack, and without it even a non-inlined function can be hard to identify.

DWARF debugging symbols are another way, but you have to ask the compiler to generate them.