Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Программирование под Unix/Linux > отлов ошибок с памятью с помощью valgrind |
Автор: Zerstroer 6.1.2016, 09:45 |
Здравствуйте. Отлаживаю свой проект. Написан на C. Сам по себе проект представляет демон, который постоянно висит в памяти. Поведение в общих чертах у него следующее: использует статическую библиотеку (мою), регулярно делает вызовы её функций, так же, периодически работает со сторонней библиотекой libconfig, кроме этого, периодично с помощью fork и execl делает запуск стороннего бинарника и с помощью pipe обменивается с ним данными. Кроме этого, демон так же регулярно пишет с syslog. Ранее, ошибки, связанные с памятью я отслеживал наблюдая за объемом памяти, которое занимает приложение и обычным прогоном через gdb в IDE. Сейчас столкнулся с проблемой, что через некоторое время работы (порядка 12-24 часов) моя программа падает без каких либо видимых на то причин. При этом, в самом первом падении в лог записалась ошибка вида: glibc detected: double free or corruption (!prev). Более, при падении демон в лог писать ничего не пишет. Сейчас хочу продиагностировать программу с помощью valgrind. Я изменил код таким образом, что бы она завершалась после прохода одной итерации цикла. Перекомпилировал бинарник и свою библиотеку с ключами -g и -O0. После чего, запустил valgrind с такими ключами: valgrind --leak-check=full --leak-resolution=high --trace-children=yes --show-reachable=yes --thack-origins=yes --log-file=/home/user/valgrind.output Кое-какой вывод я разобрал и код исправил соответствующим образом (неинициализированные переменные). Но с кое, чем у меня возникли вопросы. Прилагаю лог valgrind. Вопросы следующие: 1. Верные ли вообще ключи у valgrind? Можно ли с ними проверить приложение на double free or corruption (!prev)? 2. Что означает длиннющщая строка из знаков "дельта" на 82-90 строках лога? 3. Может ли valgrind проверить мою статическую библиотеку, которую демон вызывает? Спасибо. |
Автор: fish9370 6.1.2016, 11:03 |
мне valgrind ни разу не помог (возможно я не умею его готовить). Им вообще можно дебажить многопоточные приложения? Мне пришлось встроить в приложение инструменты отладки. Обычно это делают так: переопределяются все стандартные вызовы на свои (malloc, calloc ...). Все данные заносятся в таблицу, где ведется учет обо всех выделениях/освобождениях. Плюс блоки памяти обносятся забором, чтобы отслеживать переполнения буферов. У меня есть встроенный CLI и я когда тестирую вызываю команду, чтобы посмотреть, что у меня с памятью. Сразу становится видно, кто что навыделял. Ну и если где-то возникает переполнение буфера это тоже сразу попадает в лог Valgrind в принципе делает тоже самое, только динамически перехватывая функции, но у меня с ним не сраслось Добавлено через 5 минут и 12 секунд Ну и исходя из твоего лога вижу, что у тебя 7 блоков потеряно. Адреса строк указаны. И да coredump тебе в помощь - неоценимая вещь! |
Автор: Zerstroer 6.1.2016, 13:00 |
fish9370, с этими семью блоками все просто - это одноразово выделяемая при запуске демона память. В режиме демона, она как бы и не подразумевается к освобождению, а так как я подправил код на прогон только одной итерации тем, что в теряется память именно в этих семи блоках - можно пренебречь. Большое спасибо за совет по coredump. Гуглю, как им пользоваться. Жду еще чьих-нибудь советов/ответов. |
Автор: fish9370 6.1.2016, 13:30 | ||||
Я делаю так: В /etc/sysctl.conf прописываем в конце:
sysctl -p В своей программе:
прога должна запускаться из под рута как отлаживать потом это с помощью gdb думаю разберешься |
Автор: Zerstroer 6.1.2016, 16:28 |
fish9370, подскажите, пожалуйста, что делает код, который необходимо встроить в свою программу и куда его нужно встроить? В начало? Я понял, что он настраивает лимиты ресурсов, но как это работает? Снимается ограничение на размер дампа программы? |
Автор: Zerstroer 7.1.2016, 13:45 |
fish9370, т.е. это принудительный краш с дампом? |
Автор: fish9370 7.1.2016, 14:42 |
нет, это дамп памяти процесса, в момент краша, со всеми стеками |
Автор: Zerstroer 8.1.2016, 13:32 |
fish9370, пробую воспользоваться этой штукой. Тему пока оставляю открытой, на случай возникновения вопросов. |
Автор: Zerstroer 3.2.2016, 19:24 | ||||
fish9370, ну вот, прошел почти месяц, как я запустил бинарник с реализованым функционалам сброса дампа. Почти через месяц мой демон таки вывалился. Дамп на руках есть. Бинарник, который запускался есть. Команда gdb бинарник /путь_к_дампу/дамп на моей машине выдает следующее:
Помогите, пожалуйста, интерпретировать результаты. Есть еще два вопроса. 1. Как я могу сделать вывод более информативным? 2. Данные не собрались в дамп ядра или gdb не может их интерпретировать? upd. Почесал макушку и сделал bt. Получил следующий вывод от gdb:
А в main.c на строчке 817 у меня как раз есть free()... |
Автор: xvr 4.2.2016, 13:40 |
Похоже что на машине, где вы пытаетесь запустит gdb и на машине, где получен core dump разные версии ОС (по крайней мере библиотек) |
Автор: fish9370 5.2.2016, 00:27 | ||
Для начала: нужно собирать с ключем -ggdb Второе: вместо голого gdb лучше использовать cgdb (https://cgdb.github.io) - это консольный отладчик на базе gdb Отлаживать надо так: - определяем список потоков: info threads - переключаемся в нужный поток: thread <номер> - делаем вывод стека вызовов: backtrace - идем по кадрам снизу вверх, рассматривая параметры: frame <номер стекового кадра> чтобы отлаживать требуется: - чтобы система была такой же - на вход нужно передать исполнимый файл (cgdb gconv_db) - в консоли gdb указываешь core-file /tmp/core.6953.e.11
Лезть в ядро нет ни какого смысла, если мы пишем в usermode. В помощь отладчику можно установить доп пакет, и он будет показывать, что вызывается в ядре, но это не нужно PS: можешь выслать сордамп и бинарник, если можешь вышли исходник, попробуем определить проблему |
Автор: fish9370 5.2.2016, 00:44 |
ну значит, двойное освобождение тут я бы посоветовал обнулять указатели после освобождения и делать проверку указателя на NULL |
Автор: Zerstroer 5.2.2016, 09:05 |
fish9370, обнуления указателей у меня как раз таки и не было. Присвоение NULL после free() сделал. Хотя ситуация очень странная, перепроверил код, не нашел ничего, что могло бы приводить к двойному освобождению. Я правильно понял, что проверку на NULL нужно выполнять перед вызовом free()? Пока опять же запустил бинарник в работу. Coredump и бинарник вышлю, если опять произойдет вывал. Исходники не маленькие и им явно требуется сторонняя ревизия так как пишу я в одиночку и могу заблуждаться насчет читабельности и адекватности своего кода. ![]() |
Автор: fish9370 6.2.2016, 12:59 | ||
да, правильно. Но такая ситуация, скорее всего, свидетельствует о том, что в логике присутствует ошибка, так как такого быть не должно. И возможно тут следует поставить assert подождем ![]() Добавлено через 4 минуты и 15 секунд честно сказать, интересно было бы взглянуть. Интересно сравнить со своими поделками ![]() |