Как сломать, когда определенный тип исключения выбрасывается в GDB? - PullRequest
49 голосов
/ 26 июля 2011

В соответствии с документацией, я могу разбить конкретный тип исключения, используя условные контрольные точки.Однако синтаксис для условия не очень понятен для меня:

condition bnum <expression>

Глядя на синтаксис выражения, я думаю, что это шаблон, который мне нужен:

{type} addr

Однако я не знаю, что я должен передать за аргумент addr.Я попробовал следующее:

(gdb) catch throw
(gdb) condition 1 boost::bad_function_call *

Но это не работает (разрывы GDB для всех типов исключений).

Кто-нибудь может помочь?

Обновление

Я также попробовал предложение @ Адама, но оно приводит к сообщению об ошибке:
(gdb) catch throw boost::bad_function_call
Junk at end of arguments.

Без boost:: пространства имен:

(gdb) catch throw bad_function_call
Junk at end of arguments.

Обходной путь

Взлом в конструкторе bad_function_call Работы.

Ответы [ 7 ]

22 голосов
/ 27 июля 2011

РЕДАКТИРОВАТЬ

Документация предполагает, что catch throw <exceptname> может использоваться для прерывания всякий раз, когда выбрасывается исключение типа <exceptname>;Однако на практике это не работает.

(gdb) help catch
Set catchpoints to catch events.
Raised signals may be caught:
        catch signal              - all signals
        catch signal <signame>    - a particular signal
Raised exceptions may be caught:
        catch throw               - all exceptions, when thrown
        catch throw <exceptname>  - a particular exception, when thrown
        catch catch               - all exceptions, when caught
        catch catch <exceptname>  - a particular exception, when caught
Thread or process events may be caught:
        catch thread_start        - any threads, just after creation
        catch thread_exit         - any threads, just before expiration
        catch thread_join         - any threads, just after joins
Process events may be caught:
        catch start               - any processes, just after creation
        catch exit                - any processes, just before expiration
        catch fork                - calls to fork()
        catch vfork               - calls to vfork()
        catch exec                - calls to exec()
Dynamically-linked library events may be caught:
        catch load                - loads of any library
        catch load <libname>      - loads of a particular library
        catch unload              - unloads of any library
        catch unload <libname>    - unloads of a particular library
The act of your program's execution stopping may also be caught:
        catch stop

C++ exceptions may be caught:
        catch throw               - all exceptions, when thrown
        catch catch               - all exceptions, when caught
Ada exceptions may be caught:
        catch exception           - all exceptions, when raised
        catch exception <name>    - a particular exception, when raised
        catch exception unhandled - all unhandled exceptions, when raised
        catch assert              - all failed assertions, when raised

Do "help set follow-fork-mode" for info on debugging your program
after a fork or vfork is caught.

Do "help breakpoints" for info on other commands dealing with breakpoints.
16 голосов
/ 20 февраля 2012

Когда команда GDB 'catch throw' не выполняется, попробуйте этот обходной путь:
(протестировано с Linux g ++ 4.4.5 / gdb 6.6)
1 / Добавьте этот код в любом месте программы для отладки:

#include <stdexcept>
#include <exception>
#include <typeinfo>

struct __cxa_exception {
    std::type_info *inf;
};
struct __cxa_eh_globals {
    __cxa_exception *exc;
};
extern "C" __cxa_eh_globals* __cxa_get_globals();
const char* what_exc() {
    __cxa_eh_globals* eh = __cxa_get_globals();
    if (eh && eh->exc && eh->exc->inf)
        return eh->exc->inf->name();
    return NULL;
}

2 / В GDB вы сможете фильтровать исключения с помощью:

(gdb) break __cxa_begin_catch  
(gdb) cond N (what_exc()?strstr(what_exc(),"exception_name"):0!=0)  

где N - номер точки останова, а имя_ исключения - это имя исключения, для которого мы хотим разбить.

13 голосов
/ 15 сентября 2012

Из того, что я понял из приведенного здесь вопроса, вы хотите прерваться, когда в ваше приложение будет добавлено конкретное исключение boost::bad_function_call.

$> gdb /path/to/binary
(gdb) break boost::bad_function_call::bad_function_call()
(gdb) run --some-cli-options

То есть, когда временный объект boost::bad_function_call создается вподготовка к throw;GDB вырвется!

Я проверил это, и это работает.Если вы точно знаете, как создается объект исключения, то вы можете установить точку останова на конкретном конструкторе, в противном случае, как показано в примере ниже, вы можете опустить список прототипов аргументов, а gdb установит точки останова для всех разных вариантов.конструктора.

$ gdb /path/to/binary

(gdb) break boost::bad_function_call::bad_function_call
Breakpoint 1 at 0x850f7bf: boost::bad_function_call::bad_function_call. (4 locations)

(gdb) info breakpoints
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   <MULTIPLE>
1.1                         y     0x0850f7bf in boost::bad_function_call::bad_function_call() at /usr/include/boost/function/function_base.hpp:742
1.2                         y     0x0850fdd5 in boost::bad_function_call::bad_function_call(boost::bad_function_call const&) at /usr/include/boost/function/function_base.hpp:739
1.3                         y     0x0863b7d2 <boost::bad_function_call::bad_function_call()+4>
1.4                         y     0x086490ee <boost::bad_function_call::bad_function_call(boost::bad_function_call const&)+6>
3 голосов
/ 11 декабря 2015

Другой подход заключается в использовании аргумента tinfo, доступного при срабатывании точки захвата, который является указателем на объект, возвращаемый typeid(type).

Так что, если я хочу перехватить исключение std::bad_alloc будучи брошенным, я мог бы просто сделать:

> p &typeid(std::bad_alloc)
> $1 = (__cxxabiv1::__si_class_type_info *) 0x8c6db60 <typeinfo for std::bad_alloc>
> catch throw if tinfo == 0x8c6db60
2 голосов
/ 02 ноября 2018

Предположим, у вас есть следующий 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"}

2 голосов
/ 27 июля 2011

Как уже упоминалось, эта функциональность на практике не работает.Но в качестве обходного пути вы можете поставить условие на catch throw.Когда выдается исключение, мы приходим к функции __cxa_throw.У него есть несколько параметров, указывающих на класс исключений, поэтому мы можем установить условие для одного из них.В приведенном ниже примере сеанса GDB я поставил условие для параметра dest, равного __cxa_throw.Единственная проблема заключается в том, что значение dest (в данном случае 0x80486ec) заранее неизвестно.Это может быть известно, например, при первом запуске gdb без условия на точке останова.

[root@localhost ~]#
[root@localhost ~]# gdb ./a.out
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/a.out...done.
(gdb) catch throw
Catchpoint 1 (throw)
(gdb) condition 1 dest==0x80486ec
No symbol "dest" in current context.
(gdb) r
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
warning: failed to reevaluate condition for breakpoint 1: No symbol "dest" in current context.
Catchpoint 1 (exception thrown), __cxxabiv1::__cxa_throw (obj=0x804a080, tinfo=0x8049ca0, dest=0x80486ec <_ZNSt13runtime_errorD1Ev@plt>) at ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc:68
68      ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc: No such file or directory.
        in ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc
(gdb) bt
#0  __cxxabiv1::__cxa_throw (obj=0x804a080, tinfo=0x8049ca0, dest=0x80486ec <_ZNSt13runtime_errorD1Ev@plt>) at ../../../../gcc-4.4.3/libstdc++-v3/libsupc++/eh_throw.cc:68
#1  0x08048940 in main () at test.cpp:14
(gdb) i b
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x008d9ddb exception throw
        stop only if dest==0x80486ec
        breakpoint already hit 1 time
(gdb)

Обновление

Вы также должны загрузить отладочную информацию для libstdc ++ для этого временного решенияна работу.

1 голос
/ 27 июля 2011

Я думаю, что могу ответить на часть о настройке условных разрывов. Я не буду отвечать на вопрос об исключениях, так как __raise_exception, по-видимому, не существует в g ​​++ 4.5.2 (?)

Давайте предположим, что у вас есть следующий код (я использую void, чтобы получить нечто похожее на __raise_exception из gdb doc)

void foo(void* x) {

}

int main() {
    foo((void*)1);
    foo((void*)2);
}

для прерывания в foo (2) вы используете следующие команды

(gdb) break foo
Breakpoint 1 at 0x804851c: file q.cpp, line 20.
(gdb) condition 1 x == 2

Если вы запускаете с

(gdb) r

вы увидите, что он останавливается при втором вызове foo, но не при первом

Я думаю, что они имели в виду в документах, что вы устанавливаете перерыв на функцию __raise_exception (очень зависит от реализации)

 /* addr is where the exception identifier is stored
    id is the exception identifier.  */
    void __raise_exception (void **addr, void *id);

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

К сожалению

 (gdb) break __raise_exception

Результаты с (g ++ 4.5.2)

 Function "__raise_exception" not defined.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...