Отчет о переполнении буфера записи Perf без обратной трассировки

Я использую perf для профилирования сервера узла. Проблема в том, что после того, как я останавливаю сервер, perf фиксирует переполнение буфера и не записывает никаких данных.

Команда

perf record -e cycles:u -g -- npm run start

Результат

[ perf record: Woken up 96 times to write data ]
*** buffer overflow detected ***: terminated    

Я попытался использовать memcheck valgrind для поиска переполнения буфера, но он ничего не сообщает, что означает, что это может быть переполнение статического буфера. Есть ли способ для perf сообщить, где происходит переполнение буфера?

Редактировать

Результат выполнения perf под gdb с точкой останова на __stack_chk_fail

Thread 1 "perf" received signal SIGABRT, Aborted.                                                                 
__pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0)              
    at ./nptl/pthread_kill.c:44                                                                                   
44      ./nptl/pthread_kill.c: No such file or directory.                                                         
(gdb) bt                                                                                                          
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0)          
    at ./nptl/pthread_kill.c:44                                                                                   
#1  0x00007ffff72d4d2f in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78 
#2  0x00007ffff7285ef2 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26                             
#3  0x00007ffff7270472 in __GI_abort () at ./stdlib/abort.c:79                                                    
#4  0x00007ffff72c92d0 in __libc_message (action=action@entry=do_abort,                                           
    fmt=fmt@entry=0x7ffff73e3210 "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:155                 
#5  0x00007ffff7361e82 in __GI___fortify_fail (msg=msg@entry=0x7ffff73e31b6 "buffer overflow detected")           
    at ./debug/fortify_fail.c:26                                                                                  
#6  0x00007ffff7360990 in __GI___chk_fail () at ./debug/chk_fail.c:28                                             
#7  0x00005555557e7ddd in memcpy (__len=40, __src=0x555556a28b38, __dest=0x7fffffff843c)                          
    at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:29                                                   
#8  write_buildid (fd=0x7fffffff8590, misc=<optimized out>, pid=-1, bid=0x555556a28b38,                           
    name_len=<optimized out>, name=0x555556a28c0c "/opt/pylon/lib/libpylonbase-6.1.1.so") at util/build-id.c:312  
#9  machine__write_buildid_table (machine=machine@entry=0x555555d9bef0, fd=fd@entry=0x7fffffff8590)               
    at util/build-id.c:361                                                                                        
#10 0x00005555557e865e in perf_session__write_buildid_table (session=session@entry=0x555555d9bd00,                
    fd=fd@entry=0x7fffffff8590) at util/build-id.c:374                                                            
#11 0x000055555581c4b9 in write_build_id (ff=ff@entry=0x7fffffff8590, evlist=evlist@entry=0x555555d96d60)         
    at util/header.c:320                                                                                          
#12 0x0000555555824fa3 in do_write_feat (evlist=0x555555d96d60, p=<synthetic pointer>, type=2, ff=0x7fffffff8590) 
    at util/header.c:3224                                                                                         
#13 perf_header__adds_write (fd=3, evlist=0x555555d96d60, header=<optimized out>) at util/header.c:3269           
#14 perf_session__write_header (session=<optimized out>, evlist=0x555555d96d60, fd=3, at_exit=at_exit@entry=true) 
    at util/header.c:3353                                                                                         
#15 0x0000555555760777 in record__finish_output (rec=0x555555b9bb40 <record>) at builtin-record.c:1236            
#16 0x0000555555763560 in __cmd_record (rec=0x555555b9bb40 <record>, argv=<optimized out>, argc=<optimized out>)  
    at builtin-record.c:2026                                                                                      
#17 cmd_record (argc=<optimized out>, argv=<optimized out>) at builtin-record.c:2835                              
#18 0x00005555557dc8a3 in run_builtin (p=p@entry=0x555555ba6cb8 <commands+216>, argc=argc@entry=8,                
    argv=argv@entry=0x7fffffffdb90) at perf.c:312                                                                 
#19 0x000055555574af48 in handle_internal_command (argv=0x7fffffffdb90, argc=8) at perf.c:364                     
#20 run_argv (argv=<synthetic pointer>, argcp=<synthetic pointer>) at perf.c:408                                  
#21 main (argc=8, argv=0x7fffffffdb90) at perf.c:538                                                              

                                                                              

Что интересно, я получаю ошибку переполнения буфера только тогда, когда я включаю конкретную надстройку C++, которая загружает пару дополнительных общих библиотек. Если я запускаю сервер без этой надстройки C++, то perf не сообщает о переполнении буфера.

perf может говорить о буферах сэмплов, написанных ядром. «Переполнение» может означать, что у ядра закончились буферы, предоставленные пространством пользователя, и ему пришлось отбросить данные, в отличие от уязвимости безопасности, связанной с переполнением буфера стека. Или, если это именно то сообщение, которое gcc -fstack-protector-strong использует, когда обнаруживает, что канарейка стека была перезаписана, то, вероятно, это так.
Peter Cordes 20.04.2023 02:38

В моей системе (Arch GNU/Linux с gcc 12.2.1, glibc 2.37-2) я получаю *** stack smashing detected ***: terminated из тестовой программы (godbolt.org/z/3v69o98or). Запуск на Godbolt дает то же самое сообщение. Таким образом, это то же самое форматирование, но другое сообщение. Возможно, просто более старая версия glibc. Так что, вероятно, ошибка производительности.

Peter Cordes 20.04.2023 02:45

Можете ли вы запустить perf record под GDB с точкой останова на __stack_chk_fail? (Тогда bt ака обратная трассировка должна работать, чтобы увидеть, по крайней мере, непосредственного вызывающего абонента. Может быть, не дальше по стеку вызовов, если переполнение буфера прошло мимо канарейки к адресу возврата!) Какую perf версию вы используете? Вы пытались воспроизвести это на более новой версии ядра/perf?

Peter Cordes 20.04.2023 02:49

Я запускаю это на Debian с версией ядра 5.10.0-20-amd64 с версией perf 5.10.158. Я попробую запустить perf под gdb и посмотреть, смогу ли я получить стек вызовов

WillOw 20.04.2023 15:07

Отладочная сборка самого perf, вероятно, позволит вам увидеть, частью какой функции является 0x00005555557c27bd. (Или отладочные символы для имеющейся у вас сборки, если Debian поставляет отладочные символы в отдельном пакете, который вы можете установить, или что-то в этом роде.) Если вы не хотите отлаживать perf самостоятельно, вы должны отправить отчет об ошибке разработчикам perf; им, вероятно, понадобится минимальный воспроизводимый пример кода, который вы профилируете, если вы не можете указать им правильное направление с помощью обратной трассировки с рабочими символами отладки и некоторого расследования GDB вызывающей стороны __GI___chk_fail. Адреса кажутся действительными для PIE

Peter Cordes 21.04.2023 09:22

Достаточно забавно, что я загрузил и построил perf с символами отладки, чтобы сделать именно это, и теперь он работает... Это тот же точный номер версии, но, может быть, есть разница в коммите? Это может быть проблема и с пакетом Debian. Или, что более вероятно, я построил его без опции защиты стека, поэтому переполнение буфера все еще происходит, но это не фатально.

WillOw 21.04.2023 16:15

По умолчанию Debian GCC, вероятно, включает -fstack-protector-strong. Другая возможность заключается в том, что это была ошибка в perf, которая с тех пор была обнаружена и исправлена, если вы загрузили последнюю версию. В таком случае отлично, не нужно ни о чем сообщать вышестоящим, просто наслаждайтесь использованием более нового, лучшего, менее глючного perf. Или, если это была просто удача другого макета, это может повториться в других случаях, но вы будете готовы к отладке сейчас.

Peter Cordes 21.04.2023 16:41

Восстановил производительность с помощью -fstack-protector-strong и по-прежнему никаких проблем. Я просмотрел журналы сборки Debian и подтвердил, что они используют -fstack-protector-strong наряду со многими другими параметрами. Это кажется странным пограничным случаем и, вероятно, даже не стоит отчета об ошибке. Спасибо за вашу помощь!

WillOw 21.04.2023 18:49

Вы собираете тот же исходный пакет, из которого была собрана сбойная версия, или более новую версию? Если вы собираете из того же исходного кода, что и исходный двоичный пакет, странно, что он не падает; -g не влияет на генерацию кода, только на метаданные. Я думаю, что даже статические буферы должны оказаться в одних и тех же позициях относительно друг друга, а также, конечно, пространство стека, если вы использовали те же параметры оптимизации.

Peter Cordes 21.04.2023 19:32

Это та же версия. Я действительно непреднамеренно воспроизвел проблему. Perf имеет ряд «функций», которые доступны только в том случае, если у вас установлены определенные библиотеки. Я установил некоторые библиотеки, и теперь perf снова падает! Теперь, когда у меня есть символы отладки, я отправлю отчет об ошибке.

WillOw 21.04.2023 20:54
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это ошибка в перформансе. Perf записывает только первые 20 символов идентификатора сборки с выделенным стеком массивом из 20 символов. У одной из загружаемых библиотек был идентификатор сборки, длина которого превышала 20 символов. Ошибка в ядре означала, что размер будет записан как более 20 символов, и когда мы попытались скопировать идентификатор сборки, мы разбили стек.

https://lkml.org/lkml/2023/4/26/1064

Другие вопросы по теме