Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я их добавлю:
Родовые словари в .NET не являются потокобезопасными, а иногда могут бросать NullReference
или даже (чаще) a KeyNotFoundException
при попытке получить доступ к ключу из двух параллельных потоков. Исключение в этом случае является довольно ошибочным.
Если код NullReferenceException
задан кодом unsafe
, вы можете посмотреть на переменные указателя , и проверьте их на IntPtr.Zero
или что-то в этом роде. Это одно и то же («исключение нулевого указателя»), но в небезопасном коде переменные часто переводятся в типы значений / массивы и т. Д., И вы ударяете головой о стену, задаваясь вопросом, как тип значения может исключение.
(Еще одна причина для небезопасного использования небезопасного кода, если вам это нужно)
Это зависит от какой платформы.
В GCC это довольно тривиально, см. этот пост для более подробной информации.
В MSVC вы можете использовать библиотеку StackWalker который обрабатывает все базовые вызовы API, необходимые для Windows.
Вам нужно будет найти лучший способ интегрировать эту функциональность в ваше приложение, но количество кода, которое вам нужно написать, должно быть минимальным.
в linux с g ++ проверить этот lib
https://sourceforge.net/projects/libcsdbg
он делает всю работу за вас
Поскольку стек уже размотан при входе в блок 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 к процессу с разбивкой (например, отладка «точно в момент»)
Cpp-tool ex_diag - легкий, многоплатформенный, минимальный ресурс, простой и гибкий в трассировке.
Я рекомендую http://stacktrace.sourceforge.net/ проект. Он поддерживает Windows, Mac OS, а также Linux
throw stack_runtime_error
. Правильно ли я понимаю, что эта библиотека работает только для исключений, полученных из этого класса, а не для std::exception
или исключений из сторонних библиотек?
– Thomas
15 November 2017 в 08:32
Я хотел бы добавить стандартную библиотечную опцию (то есть кросс-платформу), как создавать исключения, которые стали доступны с 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"
В Windows проверьте BugTrap . Он не длиннее в исходной ссылке, но все еще доступен для CodeProject.
Следующий код останавливает выполнение сразу после генерирования исключения. Вам нужно установить команду 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
AFAIK libunwind довольно портативен, и до сих пор я не нашел ничего более легкого в использовании.
Poppy может собирать не только трассировку стека, но и значения параметров, локальные переменные и т. д. - все, что приводит к сбою.
У меня подобная проблема, и хотя мне нравится переносимость, мне нужна только поддержка 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]
Ответ Эндрю Гранта not помогает получить трассировку стека функции throwing , по крайней мере, не с GCC, потому что оператор throw не сохраняет текущую трассировку стека на его собственный, а обработчик уловов больше не будет иметь доступ к трассировке стека в этой точке.
Единственный способ - с помощью GCC - решить эту задачу - убедиться, что вы создаете трассировку стека на point команды throw и сохранить это с объектом исключения.
Этот метод требует, конечно, чтобы каждый код, который генерирует исключение, использует этот конкретный класс исключения.
Обновление 11 июля 2017 г. : Для получения некоторого полезного кода взгляните на ответ cahit beyaz, который указывает на http://stacktrace.sourceforge.net - я еще не использовал его но это выглядит многообещающе.
throw stack_runtime_error
. Правильно ли я понимаю, что эта библиотека работает только для исключений, полученных из этого класса, а не для std::exception
или исключений из сторонних библиотек?
– Thomas
15 November 2017 в 08:33
Если вы используете Boost 1.65 или выше, вы можете использовать boost :: stacktrace :
#include <boost/stacktrace.hpp>
// ... somewhere inside the bar(int) function that is called recursively:
std::cout << boost::stacktrace::stacktrace();
Linux
, а неgcc
. – fjardon 2 May 2018 в 09:21