How to break when a specific exception type is thrown in GDB?

后端 未结 9 1210
迷失自我
迷失自我 2020-12-02 07:40

According to the documentation I can break on specific exception type by using conditional breakpoints. However the syntax for the condition isn\'t very clear to me:

9条回答
  •  广开言路
    2020-12-02 08:01

    Let's assume you have the following code.cpp with a thread that throws an exception:

    #include 
    #include 
    
    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;
    }
    

    Compile it with using the following 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)
    

    Now you can play with, running it will give you an abort because of bad_alloc. Before going on, it's better if you install libstd debug symbols, sudo apt-get install libstdc++6-5-dbg or whatever version you have.

    Debug compilation

    If you are compiling in Debug you can follow this answer https://stackoverflow.com/a/12434170/5639395 because constructors are usually defined.

    Release compilation

    If you are compiling in DebWithRelInfo you may not be able to find a proper constructor where to put your breakpoint because of the compiler optimization. In this case, you have some other options. Let's continue.

    Source code change solution

    If you can change the source code easily, this will work https://stackoverflow.com/a/9363680/5639395

    Gdb catch throw easy solution

    If you don't want to change the code, you can try to see if catch throw bad_alloc or in general catch throw exception_name works.

    Gdb catch throw workaround

    I will build on top of this answer https://stackoverflow.com/a/6849989/5639395 We will add a breakpoint in gdb in the function __cxxabiv1::__cxa_throw . This function takes a parameter called tinfo that has the information we need to conditionally check for the exception we care about.

    We want something like catch throw if exception==bad_alloc, so how to find the proper comparison? It turns out that tinfo is a pointer to a structure that has a variable called __name inside. This variable has a string with the mangled name of the exception type.

    So we can do something like: catch throw if tinfo->__name == mangled_exception_name

    We are almost there!

    We need a way to do string comparison, and it turns out gdb has a built-in function $_streq(str1,str2) that does exactly what we need. The mangled name of the exception is a little harder to find, but you can try to guess it or check the Appendix of this answer. Let's assume for now it is "St9bad_alloc".

    The final instruction is:

    catch throw if $_streq(tinfo->__name , "St9bad_alloc")

    or equivalent

    break __cxxabiv1::__cxa_throw if $_streq(tinfo->__name , "St9bad_alloc")

    How to find the name of your exception

    You have two options

    Look for the symbol in the library

    Assuming that you installed the libstd debug symbols, you can find the library name like this:

    apt search libstd | grep dbg | grep installed

    The name is something like this libstdc++6-5-dbg

    Now check the files installed:

    dpkg -L libstdc++6-5-dbg

    Look for something that has a debug in the path, and a .so extension. In my pc I have /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21. Finally, look for the exception you want in there.

    nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i bad_alloc

    Or nm /usr/lib/x86_64-linux-gnu/debug/libstdc++.so.6.0.21 | grep -i runtime_error etc.

    In my case I found something like 00000000003a4b20 V _ZTISt9bad_alloc which suggested me to use "St9bad_alloc" as the name.

    Throw it in gdb and inspect the name in there

    This is easy, just start gdb, catch throw everything and run the small executable I wrote before. When you are inside gdb, you can issue a p *tinfo and look for the __name description from gdb.

    gdb -ex 'file test_exceptions' -ex 'catch throw' -ex 'run'

    (gdb) p *tinfo $1 = {_vptr.type_info = 0x406260 , __name = 0x7ffff7b8ae78 "St9bad_alloc"}

提交回复
热议问题