Я написал небольшую тестовую программу на C++, которую хочу профилировать с помощью perf на руке. Запуск и профилирование программы на моем x86 WSL дает ожидаемые результаты. Однако когда я профилирую программу в системе Arm, отчет о производительности не содержит стеков вызовов и показывает другие методы по сравнению с выходными данными x86. Я покажу свою программу, производительность x86 и производительность руки.
В моей программе есть функция main, которая непрерывно зацикливает короткий и длинный метод, оба из которых вызывают run.
#include <map>
int run(int loop, std::map<int,int> m)
{
int x = 0;
for(int i = 0; i < loop; i++)
{
x += i + i * x;
m.insert({i,x});
}
return x;
}
int short_method(std::map<int,int> m)
{
return run(100, m);
}
int long_method(std::map<int,int> m)
{
return run(10000, m);
}
int main()
{
while(true)
{
std::map<int,int> m;
short_method(m);
long_method(m);
}
return 0;
}
Сначала посмотрите на ожидаемое поведение под x86 WSL. Компиляция и профилирование:
g++ -g -O0 main.cpp
perf record -g -F 1000 -p$(pgrep -d, a.out) sleep 5
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 0.637 MB perf.data (5002 samples) ]
perf report -g
Выход:
+ 97.84% 0.00% a.out [unknown] [.] 0x41fd89415541f689 ▒
+ 97.84% 0.00% a.out libc-2.28.so [.] __libc_start_main ▒
- 97.84% 0.02% a.out a.out [.] main ▒
- 97.82% main ▒
- 97.14% long_method ▒
+ 90.31% run ▒
+ 6.62% std::map<int, int, std::less<int>, std::allocator<std::pair<int const, int> > >::~map ▒
- 0.64% short_method ◆
+ 0.54% run ▒
+ 97.14% 0.00% a.out a.out [.] long_method ▒
+ 90.85% 0.44% a.out a.out [.] run ▒
+ 89.59% 0.30% a.out a.out [.] std::map<int, int, std::less<int>, std::allocator<std::pair<int const, int> > >::in▒
Вывод мне пригоден, так как основной метод явно находится сверху, и я могу открыть стек вызовов, чтобы идентифицировать методы, вызываемые внутри основного.
Теперь компиляция и профилирование на системе Arm:
arm-__-linux-gnueabi-g++ -mthumb -mfpu=neon-vfpv4 -mfloat-abi=hard -mcpu=cortex-a7 -O0 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/sdk/sysroots/cortexa7t2hf-neon-vfpv4-__-linux-gnueabi -g -fno-omit-frame-pointer main.cpp -o a_arm.out
perf record -g -F 1000 -p 405 sleep 5
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.346 MB perf.data (5005 samples) ]
perf report -g
Выход:
- 21.35% 21.12% a_arm.out a_arm.out [.] std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_get_insert_unique_pos a
std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_get_insert_unique_pos a
- 12.46% 12.40% a_arm.out a_arm.out [.] std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_S_key a
std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_S_key a
- 11.15% 11.07% a_arm.out a_arm.out [.] __gnu_cxx::__aligned_membuf<std::pair<int const, int> >::_M_ptr a
__gnu_cxx::__aligned_membuf<std::pair<int const, int> >::_M_ptr a
- 7.31% 7.31% a_arm.out a_arm.out [.] std::_Rb_tree_node<std::pair<int const, int> >::_M_valptr a
std::_Rb_tree_node<std::pair<int const, int> >::_M_valptr a
- 4.86% 4.84% a_arm.out a_arm.out [.] __gnu_cxx::__aligned_membuf<std::pair<int const, int> >::_M_addr a
__gnu_cxx::__aligned_membuf<std::pair<int const, int> >::_M_addr a
- 3.05% 3.03% a_arm.out a_arm.out [.] std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_insert_<std::pair<int a
std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >,
Различные вызовы находятся в верхней части времени выполнения, не ясно видно, что большая часть времени тратится на основную и на открытие любой из самых верхних функций, я не вижу правильного стека вызовов, сообщающего мне, кто вызывает функции или какие функции звонят. (в зависимости от варианта вызывающего/вызывающего абонента)
Интересно, почему у меня нет подходящего стека вызовов для моей программы Arm, есть идеи?
редактировать Кажется, у этого 12-летнего вопроса та же проблема: Как заставить профилирование графа вызовов работать с скомпилированным кодом gcc и целевым объектом ARM Cortex A8?
обновление: 04.07.24 та же проблема появляется здесь: https://lore.kernel.org/all/[email protected]/T/
Я сам попробовал свой пример программы на малине с ядром Linux 6.6.31 и версией perf 6.6.31. Та же проблема - плоские стеки вызовов. Кажется, проблема возникает в первую очередь на архитектурах ARM.
аналогично с 2016 года





Добавьте флаги -marm -fno-omit-frame-pointer -mapcs-frame в компиляцию, чтобы генерировать кадры стека, которые работают с perf.
Судя по этой ошибке llvm
В ARM для этого требуются кадры APCS (-fno-omit-frame-pointer -mapcs-frame), чтобы гарантировать, что указатели кадров хранятся в предсказуемых местах.
Добавление -mapcs-frame решило эту проблему для меня, так что я мог видеть стеки вызовов с опцией --call-graph fp. Опция dwarf по-прежнему не работает.
Кроме того, необходимо отключить режим большого пальца, поэтому я удалил флаг -mthumb и заменил его на -marm, чтобы принудительно использовать набор инструкций руки.
См. Символика графа вызовов Perf на большом пальце проблемы в Google, начиная с 2020 года и закрываясь в 2023 году, поскольку это не будет исправлено. Судя по всему, это связано с выпуском Arm 32 и больше не присутствует в Arm64.
Добро пожаловать в StackOverflow! Пожалуйста, посетите тур , чтобы узнать, как работает этот сайт, и прочитайте « Как спросить ». Затем вернитесь и отредактируйте свой вопрос, чтобы предоставить соответствующие части отчетов о производительности в виде текста. Используйте скриншоты только для нетекстового контента.