Получить номер строки исключения C [дублировать]

Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я их добавлю:

Случай с краем # 1: одновременный доступ к словарю

Родовые словари в .NET не являются потокобезопасными, а иногда могут бросать NullReference или даже (чаще) a KeyNotFoundException при попытке получить доступ к ключу из двух параллельных потоков. Исключение в этом случае является довольно ошибочным.

Случай с краем # 2: небезопасный код

Если код NullReferenceException задан кодом unsafe, вы можете посмотреть на переменные указателя , и проверьте их на IntPtr.Zero или что-то в этом роде. Это одно и то же («исключение нулевого указателя»), но в небезопасном коде переменные часто переводятся в типы значений / массивы и т. Д., И вы ударяете головой о стену, задаваясь вопросом, как тип значения может исключение.

(Еще одна причина для небезопасного использования небезопасного кода, если вам это нужно)

149
задан Peter Mortensen 6 January 2010 в 23:50
поделиться

14 ответов

Это зависит от какой платформы.

В GCC это довольно тривиально, см. этот пост для более подробной информации.

В MSVC вы можете использовать библиотеку StackWalker который обрабатывает все базовые вызовы API, необходимые для Windows.

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

67
ответ дан Community 3 September 2018 в 09:29
поделиться
  • 1
    сообщение, на которое вы ссылаетесь, в основном указывает на создание трассировки из segfault, но спрашивающий конкретно упоминает исключения, которые являются совсем другим зверем. – Shep 13 March 2014 в 16:29
  • 2
    Я согласен с @Shep - этот ответ на самом деле не помогает получить трассировку стека кода бросания на GCC. См. Мой ответ для возможного решения. – Thomas Tempelmann 12 November 2014 в 10:17
  • 3
    Этот ответ вводит в заблуждение. Ссылка указывает на ответ, характерный для Linux, а не gcc. – fjardon 2 May 2018 в 09:21

в linux с g ++ проверить этот lib

https://sourceforge.net/projects/libcsdbg

он делает всю работу за вас

3
ответ дан Alexis Wilke 3 September 2018 в 09:29
поделиться

Поскольку стек уже размотан при входе в блок catch, решение в моем случае состояло в том, чтобы не улавливать некоторые исключения, которые затем приводят к SIGABRT. В обработчике сигнала для SIGABRT я затем fork () и execl () либо gdb (в отладочных сборках), либо Google breakpads stackwalk (в версиях сборки). Кроме того, я пытаюсь использовать только безопасные функции обработчика сигналов.

GDB:

static const char BACKTRACE_START[] = "<2>--- backtrace of entire stack ---\n";
static const char BACKTRACE_STOP[] = "<2>--- backtrace finished ---\n";

static char *ltrim(char *s)
{
    while (' ' == *s) {
        s++;
    }
    return s;
}

void Backtracer::print()
{
    int child_pid = ::fork();
    if (child_pid == 0) {
        // redirect stdout to stderr
        ::dup2(2, 1);

        // create buffer for parent pid (2+16+1 spaces to allow up to a 64 bit hex parent pid)
        char pid_buf[32];
        const char* stem = "                   ";
        const char* s = stem;
        char* d = &pid_buf[0];
        while (static_cast<bool>(*s))
        {
            *d++ = *s++;
        }
        *d-- = '\0';
        char* hexppid = d;

        // write parent pid to buffer and prefix with 0x
        int ppid = getppid();
        while (ppid != 0) {
            *hexppid = ((ppid & 0xF) + '0');
            if(*hexppid > '9') {
                *hexppid += 'a' - '0' - 10;
            }
            --hexppid;
            ppid >>= 4;
        }
        *hexppid-- = 'x';
        *hexppid = '0';

        // invoke GDB
        char name_buf[512];
        name_buf[::readlink("/proc/self/exe", &name_buf[0], 511)] = 0;
        ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_START[0], sizeof(BACKTRACE_START));
        (void)r;
        ::execl("/usr/bin/gdb",
                "/usr/bin/gdb", "--batch", "-n", "-ex", "thread apply all bt full", "-ex", "quit",
                &name_buf[0], ltrim(&pid_buf[0]), nullptr);
        ::exit(1); // if GDB failed to start
    } else if (child_pid == -1) {
        ::exit(1); // if forking failed
    } else {
        // make it work for non root users
        if (0 != getuid()) {
            ::prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
        }
        ::waitpid(child_pid, nullptr, 0);
        ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_STOP[0], sizeof(BACKTRACE_STOP));
        (void)r;
    }
}

minidump_stackwalk:

static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
    int child_pid = ::fork();
    if (child_pid == 0) {
        ::dup2(open("/dev/null", O_WRONLY), 2); // ignore verbose output on stderr
        ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_START[0], sizeof(MINIDUMP_STACKWALK_START));
        (void)r;
        ::execl("/usr/bin/minidump_stackwalk", "/usr/bin/minidump_stackwalk", descriptor.path(), "/usr/share/breakpad-syms", nullptr);
        ::exit(1); // if minidump_stackwalk failed to start
    } else if (child_pid == -1) {
        ::exit(1); // if forking failed
    } else {
        ::waitpid(child_pid, nullptr, 0);
        ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_STOP[0], sizeof(MINIDUMP_STACKWALK_STOP));
        (void)r;
    }
    ::remove(descriptor.path()); // this is not signal safe anymore but should still work
    return succeeded;
}

Изменить: заставить работать для перемычки Я также должен был добавить это:

std::set_terminate([]()
{
    ssize_t r = ::write(STDERR_FILENO, EXCEPTION, sizeof(EXCEPTION));
    (void)r;
    google_breakpad::ExceptionHandler::WriteMinidump(std::string("/tmp"), dumpCallback, NULL);
    exit(1); // avoid creating a second dump by not calling std::abort
});

Источник: Как получить трассировку стека для C ++ с использованием gcc с информацией о номере линии? и Можно ли подключить gdb к процессу с разбивкой (например, отладка «точно в момент»)

2
ответ дан Bl00dh0und 3 September 2018 в 09:29
поделиться
12
ответ дан bobobobo 3 September 2018 в 09:29
поделиться

Cpp-tool ex_diag - легкий, многоплатформенный, минимальный ресурс, простой и гибкий в трассировке.

1
ответ дан Boris 3 September 2018 в 09:29
поделиться

Я рекомендую http://stacktrace.sourceforge.net/ проект. Он поддерживает Windows, Mac OS, а также Linux

4
ответ дан cahit beyaz 3 September 2018 в 09:29
поделиться
  • 1
    На своей домашней странице я вижу throw stack_runtime_error. Правильно ли я понимаю, что эта библиотека работает только для исключений, полученных из этого класса, а не для std::exception или исключений из сторонних библиотек? – Thomas 15 November 2017 в 08:32
  • 2
    Вы правы. – Marc J. Schmidt 2 June 2018 в 12:58

Я хотел бы добавить стандартную библиотечную опцию (то есть кросс-платформу), как создавать исключения, которые стали доступны с C ++ 11:

Использовать std::nested_exception и std::throw_with_nested

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

Поскольку вы можете сделать это с любым производным классом исключений, вы можете добавить много информации в такую ​​обратную линию! Вы также можете взглянуть на мой MWE на GitHub , где обратная линия будет выглядеть примерно так:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
3
ответ дан GPMueller 3 September 2018 в 09:29
поделиться
  • 1
    Вероятно, это намного лучше, если вы готовы выполнить дополнительную работу, чем обычная трассировка стека. – Clearer 14 May 2018 в 07:44

В Windows проверьте BugTrap . Он не длиннее в исходной ссылке, но все еще доступен для CodeProject.

3
ответ дан jww 3 September 2018 в 09:29
поделиться

Следующий код останавливает выполнение сразу после генерирования исключения. Вам нужно установить команду windows_exception_handler вместе с обработчиком завершения. Я тестировал это в MinGW 32bits.

void beforeCrash(void);

static const bool SET_TERMINATE = std::set_terminate(beforeCrash);

void beforeCrash() {
    __asm("int3");
}

int main(int argc, char *argv[])
{
SetUnhandledExceptionFilter(windows_exception_handler);
...
}

Проверьте следующий код для функции windows_exception_handler: http://www.codedisqus.com/0ziVPgVPUk/exception-handling-and-stacktrace-under-windows-mingwgcc.html

1
ответ дан Marcos Fuentes 3 September 2018 в 09:29
поделиться

AFAIK libunwind довольно портативен, и до сих пор я не нашел ничего более легкого в использовании.

4
ответ дан Nico Brailovsky 3 September 2018 в 09:29
поделиться

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

2
ответ дан Orlin Georgiev 3 September 2018 в 09:29
поделиться

У меня подобная проблема, и хотя мне нравится переносимость, мне нужна только поддержка gcc. В gcc доступны execinfo.h и вызовы backtrace . Для демпфирования имен функций г-н Бинманн имеет хороший фрагмент кода. Чтобы сбросить обратную трассировку при исключении, я создаю исключение, которое печатает backtrace в конструкторе. Если бы я ожидал, что это сработает с исключением, созданным в библиотеке, это может потребовать пересоединения / связывания, чтобы использовать исключение обратного трассировки.

/******************************************
#Makefile with flags for printing backtrace with function names
# compile with symbols for backtrace
CXXFLAGS=-g
# add symbols to dynamic symbol table for backtrace
LDFLAGS=-rdynamic
turducken: turducken.cc
******************************************/

#include <cstdio>
#include <stdexcept>
#include <execinfo.h>
#include "stacktrace.h" /* https://panthema.net/2008/0901-stacktrace-demangled/ */

// simple exception that prints backtrace when constructed
class btoverflow_error: public std::overflow_error
{
    public:
    btoverflow_error( const std::string& arg ) :
        std::overflow_error( arg )
    {
        print_stacktrace();
    };
};


void chicken(void)
{
    throw btoverflow_error( "too big" );
}

void duck(void)
{
    chicken();
}

void turkey(void)
{
    duck();
}

int main( int argc, char *argv[])
{
    try
    {
        turkey();
    }
    catch( btoverflow_error e)
    {
        printf( "caught exception: %s\n", e.what() );
    }
}

Компиляция и запуск этого с помощью gcc 4.8.4 дает backtrace с красиво неперепутанными именами функций C ++:

stack trace:
 ./turducken : btoverflow_error::btoverflow_error(std::string const&)+0x43
 ./turducken : chicken()+0x48
 ./turducken : duck()+0x9
 ./turducken : turkey()+0x9
 ./turducken : main()+0x15
 /lib/x86_64-linux-gnu/libc.so.6 : __libc_start_main()+0xf5
 ./turducken() [0x401629]
2
ответ дан Thomas 3 September 2018 в 09:29
поделиться

Ответ Эндрю Гранта not помогает получить трассировку стека функции throwing , по крайней мере, не с GCC, потому что оператор throw не сохраняет текущую трассировку стека на его собственный, а обработчик уловов больше не будет иметь доступ к трассировке стека в этой точке.

Единственный способ - с помощью GCC - решить эту задачу - убедиться, что вы создаете трассировку стека на point команды throw и сохранить это с объектом исключения.

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

Обновление 11 июля 2017 г. : Для получения некоторого полезного кода взгляните на ответ cahit beyaz, который указывает на http://stacktrace.sourceforge.net - я еще не использовал его но это выглядит многообещающе.

40
ответ дан Thomas Tempelmann 3 September 2018 в 09:29
поделиться
  • 1
    К сожалению, ссылка мертва. Не могли бы вы предоставить некоторые другие? – warran 20 May 2015 в 14:29
  • 2
    И archive.org тоже этого не знает. Черт. Ну, процедура должна быть ясной: бросить объект пользовательского класса, который записывает трассировку стека во время броска. – Thomas Tempelmann 20 May 2015 в 21:21
  • 3
    На домашней странице StackTrace я вижу throw stack_runtime_error. Правильно ли я понимаю, что эта библиотека работает только для исключений, полученных из этого класса, а не для std::exception или исключений из сторонних библиотек? – Thomas 15 November 2017 в 08:33
  • 4
    Так грустно, что ответ «Нет, вы не можете получить трассировку стека из исключения C ++», единственный вариант - бросить свой собственный класс, который генерирует трассировку стека при его построении. Если вы застряли, используя такие вещи, как, скажем, любую часть библиотеки std :: C ++, вам не повезло. Извините, отстой, быть вам. – Code Abominator 28 November 2017 в 22:19

Если вы используете Boost 1.65 или выше, вы можете использовать boost :: stacktrace :

#include <boost/stacktrace.hpp>

// ... somewhere inside the bar(int) function that is called recursively:
std::cout << boost::stacktrace::stacktrace();
23
ответ дан vasek 3 September 2018 в 09:29
поделиться
  • 1
    Документы boost объясняют не только захват трассировки стека, но и как сделать это для исключений и утверждений. Качественный товар. – moodboom 14 January 2018 в 19:45
  • 2
    Дики, Вашку. В jsem přesně potřeboval :) – Machta 21 February 2018 в 22:00
Другие вопросы по тегам:

Похожие вопросы: