根据文档,我可以通过使用条件断点来破解特定的异常类型。但是这个条件的语法对我来说不是很清楚:
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
的构造函数。
答案 0 :(得分:22)
修改强>
文档表明,只要抛出类型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.
答案 1 :(得分:15)
当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是断点号,exception_name是我们希望中断的异常的名称。
答案 2 :(得分:13)
根据我从这里的问题中理解,您希望在应用程序中抛出特定异常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 :(得分:3)
另一种方法是依赖触发捕获点时可用的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
答案 4 :(得分:2)
正如其他人已经提到过的,这种功能在实践中并不起作用。但是作为解决方法,您可以将条件置于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 ++的调试信息才能使此解决方法正常工作。
答案 5 :(得分:2)
让我们假设您具有以下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
如果您不想更改代码,可以尝试查看catch throw bad_alloc
或总体上catch throw exception_name
是否有效。
我将基于此答案https://stackoverflow.com/a/6849989/5639395
我们将在函数__cxxabiv1::__cxa_throw
中的gdb中添加一个断点。此函数采用名为tinfo
的参数,该参数包含我们有条件地检查我们关心的异常所需的信息。
如果异常== bad_alloc ,我们想要类似 catch throw的东西,那么如何找到正确的比较?
事实证明,tinfo
是指向内部具有变量__name
的结构的指针。该变量具有一个字符串,其中包含异常类型的错误名称。
因此我们可以执行以下操作:如果tinfo-> __ name == mangled_exception_name
我们快到了!
我们需要一种进行字符串比较的方法,事实证明gdb具有内置函数$ _streq(str1,str2)可以完全满足我们的需求。 异常的名称混乱不清,但是您可以尝试猜测它或查看此答案的附录。现在假设它是“ St9bad_alloc”。
最终指示是:</ p>
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,catch throw
一切并运行我之前编写的小型可执行文件即可。当您进入gdb时,可以发出p *tinfo
并从gdb查找__name
描述。
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"}
答案 6 :(得分:2)
我不确定这是否是最新修复程序,但是对于GDB GNU gdb (Debian 9.1-2) 9.1
,我已经成功使用了catch throw std::logical_error
。我不希望过早地对此进行概括,但是现在这有可能在GDB中正确运行(2020年4月)。
答案 7 :(得分:1)
我想我可以回答有关设置条件中断的部分。我不会回答有关异常的问题,因为g ++ 4.5.2中似乎不存在__raise_exception(?)
假设您有以下代码(我使用void从gdb doc获取类似于__raise_exception的内容)
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调用时停止,但不会在第一个调用
上停止我认为,他们在docs中的含义是你设置了函数__raise_exception的中断(非常依赖于实现)
/* addr is where the exception identifier is stored
id is the exception identifier. */
void __raise_exception (void **addr, void *id);
然后如上所述在id上设置条件中断(你必须以某种方式确定你的异常类型的id是什么)。
不幸的是
(gdb) break __raise_exception
结果(g ++ 4.5.2)
Function "__raise_exception" not defined.
答案 8 :(得分:0)
如果问题是没有有效的堆栈跟踪(不中断提高),则在重新编译而不重新启动gdb时似乎是一个问题。 (即在gdb控制台中调用“ make”)。
重新启动gdb后,它在raise.c中正确中断 (我的版本:GNU gdb 8.1.0.20180409-git,gcc 7.4.0,GNU make 4.1)