Получение трассировки стека в системах Unix, автоматически

Какие существуют способы автоматического получения трассировки стека в системах Unix? Я имею в виду не просто получение основного файла или интерактивное присоединение с помощью GDB, но наличие обработчика SIGSEGV, который выводит обратную трассировку в текстовый файл.

Бонусные баллы за следующие дополнительные функции:

  • Сбор дополнительной информации во время сбоя (например, файлы конфигурации).
  • Отправить пакет с информацией о сбоях разработчикам.
  • Возможность добавить это в dlopenобщую библиотеку ed
  • Не требуется графический интерфейс
20.08.2008 15:50:52
4 ОТВЕТА
РЕШЕНИЕ

Если вы работаете в системах с backtraceдоступной функциональностью BSD (конечно, Linux, OSX 1.5, BSD), вы можете сделать это программно в своем обработчике сигналов.

Например ( backtraceкод, полученный из примера IBM ):

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void sig_handler(int sig)
{
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);

    for (int i = 0; i < nSize; i++)
    {
        puts(symbols[i]);;
    }

    free(symbols);

    signal(sig, &sig_handler);
}

void h()
{
    kill(0, SIGSEGV);
}

void g()
{
    h();
}

void f()
{
    g();
}

int main(int argc, char ** argv)
{
    signal(SIGSEGV, &sig_handler);
    f();
}

Вывод:

0   a.out                               0x00001f2d sig_handler + 35
1   libSystem.B.dylib                   0x95f8f09b _sigtramp + 43
2   ???                                 0xffffffff 0x0 + 4294967295
3   a.out                               0x00001fb1 h + 26
4   a.out                               0x00001fbe g + 11
5   a.out                               0x00001fcb f + 11
6   a.out                               0x00001ff5 main + 40
7   a.out                               0x00001ede start + 54

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

6
13.04.2016 17:11:09
Это работает как игрушечный пример (например, когда вы вызываете SIGSEGV вручную), но, вероятно, не будет работать, когда вам это нужно больше всего. Смотрите ответ @Gilad Ben-Yossef.
Andreas Magnusson 18.12.2019 22:23:58

Решение Дерекс, вероятно, лучшее, но в любом случае вот альтернатива:

Последние версии ядра Linux позволяют передавать дампы ядра в скрипт или программу. Вы можете написать скрипт, чтобы перехватить дамп ядра, собрать любую дополнительную информацию, которая вам нужна, и отправить все обратно. Это глобальная настройка, поэтому она применима к любой сбойной программе в системе. Для установки также потребуются права root. Его можно настроить через файл / proc / sys / kernel / core_pattern. Установите что-то вроде '| / Главная / MyUser / бен / мой-ядро-обработчик-скрипт.

Люди Ubuntu также используют эту функцию.

2
23.08.2008 06:31:11

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

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <cxxabi.h>

void sig_handler(int sig)
{
    std::stringstream stream;
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);
    for (unsigned int i = 0; i < size; i++) {
        int status;
        char *realname;
        std::string current = symbols[i];
        size_t start = current.find("(");
        size_t end = current.find("+");
        realname = NULL;
        if (start != std::string::npos && end != std::string::npos) {
            std::string symbol = current.substr(start+1, end-start-1);
            realname = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
        }
        if (realname != NULL)
            stream << realname << std::endl;
        else
            stream << symbols[i] << std::endl;
        free(realname);
    }
    free(symbols);
    std::cerr << stream.str();
    std::ofstream file("/tmp/error.log");
    if (file.is_open()) {
        if (file.good())
            file << stream.str();
        file.close();
    }
    signal(sig, &sig_handler);
}
3
13.04.2016 17:10:29
использование C ++ в обработчиках сигналов - ошибка в геноме.
vitaly.v.ch 5.01.2010 08:54:40
@ vitaly.v.ch Ошибка заключается не столько в использовании C ++, сколько в использовании множества небезопасных функций. Все, что выделяет память, в значительной степени гарантированно не будет работать, когда вы больше всего этого хотите. Конечно, это будет выглядеть хорошо, если вы, kill -11 <pid>но если это то, что разрушает ваш код, просто прекратите это делать ...
Andreas Magnusson 18.12.2019 22:10:47

FYI,

предлагаемое решение (использование backtrace_symbols в обработчике сигналов) опасно нарушено. НЕ ИСПОЛЬЗУЙТЕ ЕГО -

Да, backtrace и backtrace_symbols создадут backtrace и переведут его на символические имена:

  1. backtrace_symbols распределяет память, используя malloc, и вы используете free для ее освобождения - если вы зависаете из-за повреждения памяти, ваша арена malloc, скорее всего, будет повреждена и приведет к двойной ошибке.

  2. malloc и free защищают арену malloc с помощью внутреннего замка. Возможно, вы ошиблись в середине malloc / free с полученной блокировкой, что вызовет эти функции или все, что вызывает их к блокировке.

  3. Вы используете put, который использует стандартный поток, который также защищен блокировкой. Если вы ошиблись в середине печати, вы снова оказались в тупике.

  4. На 32-битных платформах (например, на вашем обычном ПК 2 года назад) ядро ​​установит обратный адрес внутренней функции glibc вместо вашей ошибочной функции в вашем стеке, поэтому наиболее важная часть информации, которая вас интересует, - которая Функция сделала ошибку программы, будет фактически повреждена на этих платформах.

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

Кстати, заинтересованы в этом правильно? проверить это .

Ура, Гилад.

16
20.11.2008 13:36:48
Я не очень разбираюсь в этой области. Но если вы разветвляетесь и выполняете обратный путь в дочернем процессе, с которым вы можете обойтись, нет?
LiquidityC 26.02.2018 21:40:10
@LiquidityC Если вы находитесь fork()внутри своего обработчика сигналов, ваш дочерний процесс будет в той же ситуации, что и процесс, который вы только что разветвили. fork()в обработчике сигналов, вероятно, наиболее полезно, если за ним следует execv()/, execve()иначе вы просто разветвляете что-то.
Andreas Magnusson 18.12.2019 22:21:14
Как ни странно, ответы должны содержать ответ на заданный вопрос. Не объяснять, никогда не применять другие ответы, которые работают в 99% случаев, или размещать неработающие ссылки. Поэтому, пожалуйста, включите ваше решение здесь.
calandoa 15.01.2020 10:44:58