Предположим, у вас есть следующий code.cpp с потоком, который генерирует исключение:
#include <iostream>
#include <thread>
void thr()
{
while (true) {
new int[1000000000000ul];
}
}
int main(int argc, char* argv[]) {
std::thread t(thr);
t.join();
std::cout << "Hello, World!" << std::endl;
return 0;
}
Скомпилируйте его с помощью следующего CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(tutorial)
set(CMAKE_CXX_STANDARD 11)
add_executable(test_exceptions main.cpp)
target_link_libraries(test stdc++ pthread)
Теперь вы можете поиграть, его запуск приведет к прерыванию из-за bad_alloc.
Прежде чем продолжить, лучше установить символы отладки libstd, sudo apt-get install libstdc++6-5-dbg
или любую другую версию.
Отладка компиляции
Если вы компилируете в Debug
, вы можете следовать этому ответу https://stackoverflow.com/a/12434170/5639395, потому что обычно определяются конструкторы.
Сборник релизов
Если вы компилируете в DebWithRelInfo
, возможно, вам не удастся найти правильный конструктор, куда поместить точку останова из-за оптимизации компилятора. В этом случае у вас есть другие варианты. Давайте продолжим.
Решение для изменения исходного кода
Если вы можете легко изменить исходный код, это будет работать https://stackoverflow.com/a/9363680/5639395
Простое решение для броска GDB
Если вы не хотите изменять код, вы можете попробовать проверить, работает ли catch throw bad_alloc
или вообще catch throw exception_name
.
Обходной путь GDB catch 10 * *
Я буду строить поверх этого ответа https://stackoverflow.com/a/6849989/5639395
Мы добавим точку останова в gdb в функцию __cxxabiv1::__cxa_throw
. Эта функция принимает параметр с именем tinfo
, который содержит информацию, необходимую для условной проверки исключительной ситуации, о которой мы заботимся.
Мы хотим что-то вроде catch throw, если исключение == bad_alloc , так как найти правильное сравнение?
Оказывается, tinfo
- это указатель на структуру, в которой есть переменная с именем __name
. Эта переменная содержит строку с искаженным именем типа исключения.
Итак, мы можем сделать что-то вроде: catch throw, если tinfo -> __name == mangled_exception_name
Мы почти у цели!
Нам нужен способ сравнения строк, и оказывается, что в gdb есть встроенная функция $ _streq (str1, str2), которая делает именно то, что нам нужно.
Изуродованное имя исключения найти немного сложнее, но вы можете попытаться угадать его или проверить Приложение этого ответа. Давайте предположим, что сейчас это "St9bad_alloc".
Последняя инструкция:
catch throw if $_streq(tinfo->__name , "St9bad_alloc")
или эквивалент
break __cxxabiv1::__cxa_throw if $_streq(tinfo->__name , "St9bad_alloc")
Как найти название вашего исключения
У вас есть два варианта
Ищите символ в библиотеке
Предполагая, что вы установили символы отладки libstd, вы можете найти имя библиотеки следующим образом:
apt search libstd | grep dbg | grep installed
Название примерно такое libstdc++6-5-dbg
Теперь проверьте установленные файлы:
dpkg -L libstdc++6-5-dbg
Ищите что-то с отладкой в пути и расширением .so. В моем компьютере у меня есть /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21
.
Наконец, найдите там исключение, которое вы хотите.
nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i bad_alloc
Или
nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i runtime_error
и т.д.
В моем случае я нашел что-то вроде 00000000003a4b20 V _ZTISt9bad_alloc
, в котором предлагалось использовать «St9bad_alloc» в качестве имени.
Брось его в gdb и проверь там имя
Это просто, просто запустите gdb, catch throw
все и запустите небольшой исполняемый файл, который я написал ранее. Когда вы находитесь внутри GDB, вы можете выдать p *tinfo
и найти описание __name
в GDB.
gdb -ex 'file test_exceptions' -ex 'catch throw' -ex 'run'
(gdb) p *tinfo
$1 = {_vptr.type_info = 0x406260 <vtable for __cxxabiv1::__si_class_type_info+16>,
__name = 0x7ffff7b8ae78 <typeinfo name for std::bad_alloc> "St9bad_alloc"}