Отладка повреждения лямбда-памяти || Автоматически наблюдать указатель объекта в GDB

TL; DR: как автоматически добавить часы в gdb при вызове функции, чтобы я мог отладить некоторые повреждения памяти?

В настоящее время я имею дело с некоторым повреждением памяти в C ++
Я в основном наблюдаю 4-5 типов восстанавливающих сбоев — все они не имеют особого смысла, поэтому я предполагаю, что это должно быть связано с повреждением памяти.

Эти сбои происходят только на рабочем сервере, примерно каждые 2-5 часов.
Большинство из них состоят из доступа или передачи нулевого указателя, где он, возможно, вообще не существовал.
Одним из таких мест является лямбда-захват этого. (увидеть ниже)

Очевидно, посмотрел на дампы ядра и даже подключил GDB во время сбоя
Вальгринд: Я часами смотрел на несколько экземпляров Вальгринда, но безуспешно.
Включена защита стека gccs (-fstack-protector-all)
Я попытался просмотреть код & изменения, но мне было невозможно что-либо найти (всего 100 тыс. строк кода: «На главном компьютере изменилось 10 437 файлов, и было сделано 3 352 600 добавлений и 85 495 удалений». с момента последнего выпуска на производственном сервере). Возможно, я просто что-то пропустил или не посмотрел в правильные места — не могу сказать.
Использовал cppcheck, чтобы увидеть, было ли что-то явно не так с кодом

Если есть более простой / более прямой метод определения места коррупции, не стесняйтесь предлагать это тоже.

Давайте посмотрим на упрощенный код.
У меня есть класс, Socket, который управляет клиентским подключением.
Он построен примерно так

Listener::OnAccept(fd){
Socket* s = new Socket();
if (s->Setup(fd)){
// push into a vector and do some other things
}
}

Socket :: Setup вызывает (виртуальный) OnConnect класса Socket, который затем создает событие ping, используя лямбду:

Socket::OnConnect(){
m_pingEvent = new Event([this](Event* e){
if (!this->GotPong()){
// close connection
}else{
this->Ping();
}
}, 30 /*seconds*/, true /* loop */);
}

Событие принимает функцию std :: function в качестве обратного вызова
m_pingEvent удаляется в деструкторе (если он установлен), который отменяет событие, если оно запущено.

Что случается (редко), так это то, что лямбда вызывает Ping для nullptr, который вызывает m_pingPacket-> Send () для this = 0x1f8, что приводит к segfault.

Мой вопрос — или, скорее, мое предлагаемое решение — будет наблюдать за записанным этим указателем для записи, что определенно не должно произойти.
Есть только одна маленькая проблема с этим ..

Как бы я вообще наблюдал за таким большим количеством указателей, не добавляя каждый из них вручную? (около 400 одновременных соединений с большим количеством соединений)

Что касается захваченных данных, я обнаружил, что это в объекте __closure:

(gdb) frame 2
#2  0x081b9d63 in operator() (e=0x9b2a748, __closure=0xb5a8318)
at net/socket/Client.cpp:151
151     net/socket/Client.cpp: No such file or directory.
(gdb) ptype __closure
type = const struct {
net::socket::Client * const __this;
} * const

Что я могу получить при создании лямбды, просто переместив лямбду в «auto callback =», который будет иметь тип:

(gdb) info locals
callback = {__this = 0xb4dd0948}
(gdb) ptype callback
type = struct {
net::socket::Client * const __this;
}
(gdb) print callback
$1 = {__this = 0xb4dd0948}

(Это gcc версии 4.7.2 (Debian 4.7.2-5) для справки, может отличаться от других компиляторов / версий)
Незадолго до публикации я понял, что структура, вероятно, изменит адрес после перемещения в std :: function (это правильно?)
Я копался в «функциональном» заголовке gnu, но я пока не смог ничего найти, я буду продолжать искать (и обновлять это)

Еще одно примечание: я публикую это полное описание со всеми подробностями, включенными в случае, если у кого-то есть более простое решение для меня. (Проблема XY)

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

(gdb) print *(void**)m_pingEvent->m_callback._M_functor._M_unused._M_object
$8 = (void *) 0xb4dd56d8
(gdb) print this
$4 = (net::socket::Client * const) 0xb4dd56d8

Нашел это 🙂

Edit2:

break net/socket/Client.cpp:158
commands
silent
watch -l m_pingEvent->m_callback._M_functor._M_unused._M_object
continue
end

Это имеет два недостатка: вы можете смотреть только 4 адреса одновременно & нет способа удалить часы, как только объект будет освобожден.
Су это непригодно.

Изменить 3:
Я разобрался, как сделать наблюдение, используя этот скрипт на python, который я написал (связывая его внешне, так как он довольно длинный): https://gist.github.com/imermcmaps/4a6d8a1577118645acf3

Следующая проблема имеет смысл вывода ..

Added watch 7 -> 0x10eb2200
Hardware watchpoint 7: -location m_pingEvent->m_callback._M_functor._M_unused._M_obj

Old value = (void *) 0x10eba4b0
New value = (void *) 0x10eba400
net::Packet::Packet (this=0x10eb1088) at ../shared/net/Packet.cpp:13

Как будто он говорит, что он изменился со старого значения, которое даже не должно быть исходным, так как я проверяю, совпадают ли указатель this и значение указателя, что они и делают.

Изменить 4 (yay):
Оказывается, часы не работают так, как я хочу.
Ручной захват адреса, а затем просмотр этого адреса, кажется, работает

1

Решение

Как автоматически добавить часы в GDB, когда функция вызывается так
Я могу отладить некоторые повреждения памяти?

Повреждение памяти часто обнаруживается после того, как некоторые модули загружены в ваш процесс. Поэтому ручная отладка может быть не очень полезна для реальных сложных проектов. Потому что любые сторонние модули / библиотеки, загруженные в ваш процесс, также могут привести к этой проблеме. Из вашего поста видно, что эта проблема не всегда воспроизводима, что указывает на то, что это может быть связано с проблемой многопоточности / синхронизации, которая приводит к некоторому повреждению памяти. Исходя из моего опыта, я настоятельно рекомендую вам сосредоточиться на воспроизведении проблемы в динамических инструментах (Valgrind/Helgrind).

Однако, как вы упомянули в своем вопросе, вы можете прикрепить свою программу с помощью Valgrind. Поэтому вы можете прикрепить свою программу (a.out) на тот случай, если вы этого не сделали.

$ valgrind --tool=memcheck --db-attach=yes ./a.out

Таким образом, Вальгринд автоматически прикрепите вашу программу в отладчике при обнаружении первой ошибки памяти, чтобы вы могли выполнить оперативную отладку (GDB). Похоже, что это лучший способ выяснить причину вашей проблемы.

Однако я думаю, что может быть какой-то сценарий гонки данных, который приводит к повреждению памяти. Так что вы можете использовать Helgrind проверить / найти проблему скачек / потоков данных, которая может привести к этой проблеме.

Для получения дополнительной информации об этом, вы можете обратиться к следующему сообщению:

https://stackoverflow.com/a/22658693/2724703

https://stackoverflow.com/a/22617989/2724703

0

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector