Анализ дампа с целью поиска segfault
От: Кузнец Россия  
Дата: 08.08.17 10:53
Оценка:
Есть многопоточное приложение, написанное на c++. При определённых условиях оно слетает, слёт плавающий, вероятнее всего этот segfault. При запуске из Qt под отладчиком слёт пока поймать не удалось. Есть возможность получить дамп памяти в момент слёта (в линуксе поставил unlimited размер дампов, смоделировал слёт — разыменовал нулевой указатель, дамп получился). Можно как-то по дампу понять причины слёта? Может можно скомпилировать приложение в debug-режиме, и эта информация сделает дамп более понятным?

Погуглил по этой теме, простых методов анализа пока найти не удалось. Дамп вообще может как-то указать на код, наподобие отладчика, или по нему можно лазить только как по слепку памяти, пытаясь понять, какой hex код за что там отвечает?

Посоветуйте какие-нибудь хорошие ресурсы по отладке через дампы, буду разбираться.

Вообще эта тема походу сложная, так что я не жду лёгких ответов, подскажите в какую сторону копать

PS. Если тему создал не там — прошу перенести.
Re: Анализ дампа с целью поиска segfault
От: niXman Ниоткуда https://github.com/niXman
Дата: 08.08.17 11:16
Оценка: 2 (1)
google: linux core files, gdb, core files.

ну и google: gcc, thread sanitizer
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: Анализ дампа с целью поиска segfault
От: niXman Ниоткуда https://github.com/niXman
Дата: 08.08.17 11:17
Оценка:
Здравствуйте, Кузнец, Вы писали:

К>... вероятнее всего этот segfault

что значит "вероятнее всего"? при падении сигнал всегда известен.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: Анализ дампа с целью поиска segfault
От: dead0k  
Дата: 08.08.17 12:11
Оценка: 2 (1) +1
Здравствуйте, Кузнец, Вы писали:

К>Вообще эта тема походу сложная, так что я не жду лёгких ответов, подскажите в какую сторону копать


1. Собираешь бинарники с символами, не стрипаешь их. (по умолчанию gcc собирает с символами -g все-таки надо, а еще лучше -ggdb)
2. Запускаешь приложуху, падаешь с коркой.
3. Запускаешь:
gdb [executable] [corefile]

4. После того, как gdb загрузится, пишешь bt и 90% вопросов решаются. В оставшихся 10% изучаешь как работать с gdb

В принципе можно попробовать 3 и 4 без 1 и 2, если текущие бинарники не стрипнутые.
Отредактировано 08.08.2017 12:33 dead0k . Предыдущая версия . Еще …
Отредактировано 08.08.2017 12:14 dead0k . Предыдущая версия .
Re: Анализ дампа с целью поиска segfault
От: kov_serg Россия  
Дата: 08.08.17 12:27
Оценка: +1
Здравствуйте, Кузнец, Вы писали:

К>Есть многопоточное приложение, написанное на c++. При определённых условиях оно слетает, слёт плавающий, вероятнее всего этот segfault. При запуске из Qt под отладчиком слёт пока поймать не удалось. Есть возможность получить дамп памяти в момент слёта (в линуксе поставил unlimited размер дампов, смоделировал слёт — разыменовал нулевой указатель, дамп получился). Можно как-то по дампу понять причины слёта? Может можно скомпилировать приложение в debug-режиме, и эта информация сделает дамп более понятным?


К>Погуглил по этой теме, простых методов анализа пока найти не удалось. Дамп вообще может как-то указать на код, наподобие отладчика, или по нему можно лазить


Обычно как-то так
#include <stdio.h>

int main(int argc,char** argv) {
    int *a=0;
    printf("a=%d\n",*a);
    return 0;
}

gcc -g a.cpp
ulimit -c unlimited
./a.out
gdb a.out core

Потом жмём <ctrl+x> и <1>
   ┌──a.cpp───────────────────────────────────────────────────────┐
   │1       #include <stdio.h>                                    │
   │2                                                             │
   │3       int main(int argc,char** argv) {                      │
   │4               int *a=0;                                     │
  >│5               printf("a=%d\n",*a);                          │
   │6               return 0;                                     │
   │7       }                                                     │
   │8                                                             │
   │9                                                             │
   │10                                                            │
   │11                                                            │
   │12                                                            │
   │13                                                            │
   └──────────────────────────────────────────────────────────────┘
core LWP 1710 In: main                     Line: 5    PC: 0x400548 
(gdb)

https://sourceware.org/gdb/onlinedocs/gdb/TUI.html
Отредактировано 08.08.2017 12:28 kov_serg . Предыдущая версия .
Re: Анализ дампа с целью поиска segfault
От: Chorkov Россия  
Дата: 08.08.17 13:58
Оценка: 2 (1)
Здравствуйте, Кузнец, Вы писали:

К>Есть многопоточное приложение, написанное на c++. При определённых условиях оно слетает, слёт плавающий, вероятнее всего этот segfault. При запуске из Qt под отладчиком слёт пока поймать не удалось. Есть возможность получить дамп памяти в момент слёта (в линуксе поставил unlimited размер дампов, смоделировал слёт — разыменовал нулевой указатель, дамп получился). Можно как-то по дампу понять причины слёта? Может можно скомпилировать приложение в debug-режиме, и эта информация сделает дамп более понятным?


Если у тебя Qt, то среда по-видимому QtCreator. Он умеет загружать дампы: Debug \ Start Debgging \ Load Core File.
И далее как обычным отладчиком. Увидишь стек вызовов (на момент падения), переменные, потоки и т.д.
Разумеется отладочная информация к упавшей программе должна быть. (Также как и для обычной отладки.)
Re: Анализ дампа с целью поиска segfault
От: Кузнец Россия  
Дата: 09.08.17 07:14
Оценка: 8 (2)
Спасибо всем отозвавшимся. Каждый ответ оказался очень полезен. Дампы сделаны, разобраны, ошибка успешно найдена.

Чтобы подвести небольшой итог, сделаю ответ, собирающий все рекомендации, на случай если кто-то будет читать тему.

Итак, ситуация — есть слёт, который не выходит поймать в отладчике, но поймать очень хочется. Система linux debian, но думаю, это подойдёт для любого линукса.

Сначала нужно получить дамп. Для этого следуем инструкции по ссылке: . Там достаточно понятно описано, как включить сброс дампов при слётах процессов. Единственное, на что нужно обратить особое внимание — у меня почему-то максимальный размер дампа после простоя системы сбрасывался на 0, так что имеет смысл периодически мониторить его значение и при необходимости выставлять обратно в unlimited: -ulimit -c, ulimit -c unlimited .

Обратите внимание, что дамп создаётся не при любом слёте, есть список сигналов аварийного завершения, при некоторых из них дампа не будет. Если приложение слетает, а дамп ни в какую не появляется, возможно, это именно такой сигнал. Рекомендую попробовать написать небольшую програмку, которая при запуске слетает по segfault (разыменуйте нулевой указатель), и проверьте, что она сбрасывает дамп, если дампа и с ней не будет, значит проблемы с настройкой дампов, если она дамп даёт, а ваше приложение — нет, то приложение было завершено сигналом, который не делает дамп (SigTerm кажется один из таких, список можно найти гуглением).

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

Итак, по инструкции включаем дампы, затем запускаем приложение и добиваемся слёта. При слёте в выбранном каталоге появится файл core.Name.12345, здесь Name — название бинарника, 12345 — id процесса. Этот файл и есть наш дамп. Здесь нужно обратить внимание на один момент, приложение можно скомпилировать в релизной сборке, либо в отладочной сборке, лучше в отладочной, тогда анализ дампа сможет показать прямо строку в коде, в которой произошёл слёт, если отладочная сборка не падает, то можно ограничиться релизной, но тогда мы получим лишь название функции, в которой слетело приложение (при не слишком глубоком анализе).

Теперь у нас есть дамп, его нужно проанализировать. Это очень просто, тут поможет отладчик gdb, команда: gdb полный_путь_к_бинарнику полный_путь_к_дампу, отладчик покажет место слёта (функцию, или вообще строчку кода для отладочной версии бинарника). Дамп должен соответствовать бинарнику, иначе можно получить непонятную фигню (то есть бинарник должен быть именно тот, на котором был получен дамп).

Полезные приёмы при работе в gdb.
1) Команда bt покажет стек вызова функций.
2) Если нажать ctrl-X, затем 1, то будет показан красивый листинг с указанием места слёта.
3) Командой print <имя переменной> можно посмотреть значения переменных на момент слёта.
4) dead0k советует делать не просто отладочную сборку (флаг -g), а добавить флаг -ggdb, я пока не пробовал, но стоит обратить внимание на этот совет.

Также дамп можно открыть в среде QtCreator, для этого выбираем в меню Отладка -> Начать отладку -> Загрузить файл дампа (Debug -> Start debug -> Load dump file). Qt покажет слёт "красиво", как будто мы его словили в отладчике (но только для отладочной версии приложения, в противном случае будет дизассемблер).

Также для дополнительной информации гуглить: linux core files, gdb, core files, thread sanitizer (собственно, по этим ключевым словам, следуя совету niXman, я и получил массу полезной информации).
Отредактировано 09.08.2017 7:27 Кузнец . Предыдущая версия .
Re[2]: Анализ дампа с целью поиска segfault
От: Кузнец Россия  
Дата: 09.08.17 07:20
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, Кузнец, Вы писали:


К>>... вероятнее всего этот segfault

X>что значит "вероятнее всего"? при падении сигнал всегда известен.

А как можно посмотреть сигнал, с которым приложение упало? Мы запускались в фоновом режиме, падало оно при тестировании (когда тестили вообще другие фичи, функционал, не слёт ловили), так что ошибки в консоли увидеть нет возможности. Конечно, мы потом при поимке слёта запускались из консоли, и ловили в ней краш, было "Ошибка сегментирования", но в такой ситуации нельзя быть уверенным, что это именно тот слёт (он плавающий, бывало приходилось довольно долго гонять ПО, пока оно вылетало, а бывало буквально минуту поработало и накрывалось).
Re[3]: Анализ дампа с целью поиска segfault
От: dead0k  
Дата: 09.08.17 11:09
Оценка:
Здравствуйте, Кузнец, Вы писали:

К>А как можно посмотреть сигнал, с которым приложение упало?


Когда корку в gdb открываешь, он, емнип, пишет с чем оно упало.
Re[4]: Анализ дампа с целью поиска segfault
От: Кузнец Россия  
Дата: 09.08.17 11:27
Оценка:
Здравствуйте, dead0k, Вы писали:

D>Здравствуйте, Кузнец, Вы писали:


К>>А как можно посмотреть сигнал, с которым приложение упало?


D>Когда корку в gdb открываешь, он, емнип, пишет с чем оно упало.


Если дамп есть, то да, а если нету? Тогда мне кажется узнать скорее всего не получится. По умолчанию у нас на всех машинах ulimit -c 0 стоит, и дампов нету. Ну и плюс бывают сигналы, при которых дамп не скидывается...

Если дампа нет, то картина такова — работало приложение, и вдруг упало (процесс пропал). Так как работает ПО в фоновом режиме, консольного лога нету, логи программы тут бесполезны, потому что в них причины падения не пишутся (если только попытаться понять причины косвенно, по последним действиям программы).
Re[3]: Анализ дампа с целью поиска segfault
От: dead0k  
Дата: 09.08.17 12:49
Оценка:
Здравствуйте, Кузнец, Вы писали:


К>Если дамп есть, то да, а если нету?


man acct
требуется поддержка ядром
Отредактировано 09.08.2017 12:52 dead0k . Предыдущая версия .
Re[2]: Анализ дампа с целью поиска segfault
От: AleksandrN Россия  
Дата: 10.08.17 11:27
Оценка:
Здравствуйте, Кузнец, Вы писали:

К>Также для дополнительной информации гуглить: linux core files, gdb, core files, thread sanitizer (собственно, по этим ключевым словам, следуя совету niXman, я и получил массу полезной информации).


Подскажу ещё один хороший инструмент — valgrind. С его помощью можно провести динамический анализ выполнения и определить утечки памяти, выход за пределы массива и другие ошибки работы с памятью.
Re[3]: Анализ дампа с целью поиска segfault
От: alex_mah Россия www.elsy.ru
Дата: 16.08.17 20:55
Оценка:
Здравствуйте, Кузнец, Вы писали:

К>Здравствуйте, niXman, Вы писали:


X>>Здравствуйте, Кузнец, Вы писали:


К>>>... вероятнее всего этот segfault

X>>что значит "вероятнее всего"? при падении сигнал всегда известен.

К>А как можно посмотреть сигнал, с которым приложение упало? Мы запускались в фоновом режиме, падало оно при тестировании (когда тестили вообще другие фичи, функционал, не слёт ловили), так что ошибки в консоли увидеть нет возможности. Конечно, мы потом при поимке слёта запускались из консоли, и ловили в ней краш, было "Ошибка сегментирования", но в такой ситуации нельзя быть уверенным, что это именно тот слёт (он плавающий, бывало приходилось довольно долго гонять ПО, пока оно вылетало, а бывало буквально минуту поработало и накрывалось).


Если приложение "падает" то ИМХО это SIGSEGV.
Ксати, на сигналы можно натравить свои обработчики. В этом случае можно быть точно уверенным, какой сигнал получила программа. А по SIGSEGV можно еще и самоперезапуститься.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.