Как автоматически генерировать трассировку стека при сбое моей программы - PullRequest
531 голосов
/ 17 сентября 2008

Я работаю в Linux с компилятором GCC. Когда моя программа на C ++ падает, я бы хотел, чтобы она автоматически генерировала трассировку стека.

Моя программа запускается многими разными пользователями, а также работает в Linux, Windows и Macintosh (все версии скомпилированы с использованием gcc).

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

Ответы [ 29 ]

3 голосов
/ 31 декабря 2016

Новый король в городе прибыл https://github.com/bombela/backward-cpp

1 заголовок для размещения в вашем коде и 1 библиотека для установки.

Лично я называю это с помощью этой функции

#include "backward.hpp"
void stacker() {

using namespace backward;
StackTrace st;


st.load_here(99); //Limit the number of trace depth to 99
st.skip_n_firsts(3);//This will skip some backward internal function from the trace

Printer p;
p.snippet = true;
p.object = true;
p.color = true;
p.address = true;
p.print(st, stderr);
}
3 голосов
/ 13 декабря 2013

Я видел много ответов здесь, выполняющих обработчик сигнала и затем выходящих. Это путь, но помните очень важный факт: если вы хотите получить дамп ядра для сгенерированной ошибки, вы не можете вызвать exit(status). Звоните abort() вместо!

3 голосов
/ 17 сентября 2008

Я бы использовал код, который генерирует трассировку стека для утечки памяти в Визуальный детектор утечки . Это работает только на Win32.

2 голосов
/ 01 марта 2018

В качестве решения только для Windows вы можете получить эквивалент трассировки стека (с гораздо большей информацией), используя Windows Error Reporting . Имея всего несколько записей в реестре, можно настроить до сбор дампов в режиме пользователя :

Начиная с Windows Server 2008 и Windows Vista с пакетом обновления 1 (SP1), отчеты об ошибках Windows (WER) можно настроить так, чтобы полные дампы пользовательского режима собирались и сохранялись локально после сбоя приложения пользовательского режима. [...]

Эта функция не включена по умолчанию. Включение этой функции требует прав администратора. Чтобы включить и настроить эту функцию, используйте следующие параметры реестра в разделе HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ Windows Error Reporting \ LocalDumps .

Вы можете установить записи реестра из вашего установщика, который обладает необходимыми привилегиями.

Создание дампа в режиме пользователя имеет следующие преимущества по сравнению с генерацией трассировки стека на клиенте:

  • Это уже реализовано в системе. Вы можете использовать WER, как описано выше, или вызывать MiniDumpWriteDump самостоятельно, если вам нужен более детальный контроль над объемом информации для выгрузки. (Обязательно вызовите его из другого процесса.)
  • Способ более полный, чем трассировка стека. Среди прочего он может содержать локальные переменные, аргументы функций, стеки для других потоков, загруженные модули и так далее. Объем данных (и, следовательно, размер) очень настраиваемый.
  • Нет необходимости отправлять отладочные символы. Это существенно уменьшает размер вашего развертывания, а также усложняет обратную разработку приложения.
  • Во многом не зависит от используемого компилятора. Использование WER даже не требует никакого кода. В любом случае, возможность получить базу данных символов (PDB) очень полезна для автономного анализа. Я считаю, что GCC может либо генерировать PDB, либо существуют инструменты для преобразования базы данных символов в формат PDB.

Обратите внимание, что WER может быть вызван только аварийным завершением приложения (т. Е. Системой, завершающей процесс из-за необработанного исключения). MiniDumpWriteDump можно вызвать в любое время. Это может быть полезно, если вам нужно вывести текущее состояние для диагностики других проблем, кроме сбоя.

Обязательное чтение, если вы хотите оценить применимость мини-дампов:

2 голосов
/ 08 мая 2013

В дополнение к приведенным выше ответам, здесь вы узнаете, как заставить ОС Debian Linux генерировать дамп ядра

  1. Создать папку «coredumps» в домашней папке пользователя
  2. Перейдите в /etc/security/limits.conf. Под строкой «» введите «soft core unlimited» и «root soft core unlimited», если разрешены дампы ядра для root, чтобы обеспечить неограниченное пространство для дампов ядра.
  3. ПРИМЕЧАНИЕ: «* soft core unlimited» не распространяется на root, поэтому root следует указывать в отдельной строке.
  4. Чтобы проверить эти значения, выйдите из системы, войдите снова и введите «ulimit -a». «Основной размер файла» должен быть неограниченным.
  5. Проверьте файлы .bashrc (пользователь и root, если применимо), чтобы убедиться, что ulimit там не установлен. В противном случае указанное выше значение будет перезаписано при запуске.
  6. Откройте /etc/sysctl.conf. Введите следующее внизу: «kernel.core_pattern = /home//coredumps/%e_%t.dump». (% e будет именем процесса, а% t будет системным временем)
  7. Выйдите и введите «sysctl -p», чтобы загрузить новую конфигурацию Проверьте / proc / sys / kernel / core_pattern и убедитесь, что это соответствует тому, что вы только что набрали.
  8. Дамп ядра можно проверить, запустив процесс в командной строке («&»), а затем убив его командой «kill -11». Если дамп ядра успешен, вы увидите «(дамп ядра)» после индикации ошибки сегментации.
1 голос
/ 17 сентября 2008

В Linux / unix / MacOSX используйте основные файлы (вы можете включить их с помощью ulimit или совместимого системного вызова ). В Windows используйте отчеты об ошибках Microsoft (вы можете стать партнером и получить доступ к данным о сбое вашего приложения).

0 голосов
/ 11 апреля 2019

Если вы все еще хотите сделать это в одиночку, как я, вы можете связать с bfd и избегать использования addr2line, как я сделал здесь:

https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c

Это производит вывод:

[E]        crash.linux.c:170  | crit_err_hdlr                  | ==== FATAL CRASH (a12-151-g28b12c85f4+1) ====
[E]        crash.linux.c:171  | crit_err_hdlr                  | signal 11 (Segmentation fault), address is (nil)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (0) /home/geoff/Projects/LookingGlass/client/src/main.c:936 (register_key_binds)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (1) /home/geoff/Projects/LookingGlass/client/src/main.c:1069 (run)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (2) /home/geoff/Projects/LookingGlass/client/src/main.c:1314 (main)
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (3) /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f8aa65f809b]
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (4) ./looking-glass-client(_start+0x2a) [0x55c70fc4aeca]
0 голосов
/ 29 января 2019

Похоже, что в одной из последних появившихся библиотек c ++ boost, предоставляющих именно то, что Вы хотите, возможно, код будет мультиплатформенным. Это boost :: stacktrace , который можно использовать как , как в примере Boost :

#include <filesystem>
#include <sstream>
#include <fstream>
#include <signal.h>     // ::signal, ::raise
#include <boost/stacktrace.hpp>

const char* backtraceFileName = "./backtraceFile.dump";

void signalHandler(int)
{
    ::signal(SIGSEGV, SIG_DFL);
    ::signal(SIGABRT, SIG_DFL);
    boost::stacktrace::safe_dump_to(backtraceFileName);
    ::raise(SIGABRT);
}

void sendReport()
{
    if (std::filesystem::exists(backtraceFileName))
    {
        std::ifstream file(backtraceFileName);

        auto st = boost::stacktrace::stacktrace::from_dump(file);
        std::ostringstream backtraceStream;
        backtraceStream << st << std::endl;

        // sending the code from st

        file.close();
        std::filesystem::remove(backtraceFileName);
    }
}

int main()
{
    ::signal(SIGSEGV, signalHandler);
    ::signal(SIGABRT, signalHandler);

    sendReport();
    // ... rest of code
}

В Linux Вы компилируете код выше:

g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace

Пример обратного следа, скопированного из форсированной документации :

0# bar(int) at /path/to/source/file.cpp:70
1# bar(int) at /path/to/source/file.cpp:70
2# bar(int) at /path/to/source/file.cpp:70
3# bar(int) at /path/to/source/file.cpp:70
4# main at /path/to/main.cpp:93
5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
6# _start
0 голосов
/ 17 сентября 2008

Я забыл о технологии GNOME "apport", но я мало что знаю об ее использовании. Он используется для генерации трассировки стека и другой диагностики для обработки и может автоматически регистрировать ошибки. Это, безусловно, стоит проверить.

...