我有一个程序在某处抛出未捕获的异常。我得到的只是一个异常被抛出的报告,并且没有关于它被抛出的位置的信息。编译为包含调试符号的程序不通知我在我的代码中生成异常的位置似乎不合逻辑。
如果没有在 gdb 中设置“catch throw”并为每个抛出的异常调用回溯,有什么方法可以判断我的异常来自哪里?
最佳答案
如果未捕获异常,则特殊库函数 std::terminate()被自动调用。 Terminate 实际上是指向函数的指针,默认值是标准 C 库函数 std::abort() .如果未对未捕获的异常进行清理†,它可能实际上有助于调试此问题,因为没有调用析构函数。
†在调用 std::terminate() 之前堆栈是否展开由实现定义。
对abort() 的调用通常有助于生成可分析以确定异常原因的核心转储。确保通过 ulimit -c unlimited (Linux) 启用核心转储。
您可以使用 std::set_terminate() 安装自己的 terminate() 函数。您应该能够在 gdb 中的终止函数上设置断点。您可能能够从您的 terminate() 函数生成堆栈回溯,并且此回溯可能有助于识别异常的位置。
关于uncaught exceptions有一个简短的讨论在 Bruce Eckel's Thinking in C++, 2nd Ed这也可能会有所帮助。
由于 terminate() 默认调用 abort() (默认会导致 SIGABRT 信号),您可以 能够设置 SIGABRT 处理程序,然后 print a stack backtrace from within the signal handler .此回溯可能有助于识别异常的位置。
注意:我说可能是因为 C++ 通过使用语言结构将错误处理和报告代码与普通代码分开来支持非本地错误处理。 catch block 可以并且通常位于与抛出点不同的函数/方法中。在评论中还向我指出(感谢 Dan),在调用 terminate() 之前是否展开堆栈是实现定义的。
更新:我拼凑了一个名为的 Linux 测试程序,该程序在通过 set_terminate() 设置的 terminate() 函数中生成回溯,并且SIGABRT 的信号处理程序中的另一个。两个回溯都正确显示了未处理异常的位置。
更新 2:感谢 Catching uncaught exceptions within terminate 上的博文,我学到了一些新的技巧;包括在终止处理程序中重新抛出未捕获的异常。需要注意的是,自定义终止处理程序中的空 throw 语句适用于 GCC,而不是可移植的解决方案。
代码:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <execinfo.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <cstdlib>
#include <stdexcept>
void my_terminate(void);
namespace {
// invoke set_terminate as part of global constant initialization
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;
// Get the address at the time the signal was raised from the EIP (x86)
void * caller_address = (void *) uc->uc_mcontext.eip;
std::cerr << "signal " << sig_num
<< " (" << strsignal(sig_num) << "), address is "
<< info->si_addr << " from "
<< caller_address << std::endl;
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
// overwrite sigaction with caller's address
array[1] = caller_address;
char ** messages = backtrace_symbols(array, size);
// skip first stack frame (points here)
for (int i = 1; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
exit(EXIT_FAILURE);
}
void my_terminate() {
static bool tried_throw = false;
try {
// try once to re-throw currently active exception
if (!tried_throw++) throw;
}
catch (const std::exception &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
<< e.what() << std::endl;
}
catch (...) {
std::cerr << __FUNCTION__ << " caught unknown/unhandled exception."
<< std::endl;
}
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
char ** messages = backtrace_symbols(array, size);
for (int i = 0; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
abort();
}
int throw_exception() {
// throw an unhandled runtime error
throw std::runtime_error("RUNTIME ERROR!");
return 0;
}
int foo2() {
throw_exception();
return 0;
}
int foo1() {
foo2();
return 0;
}
int main(int argc, char ** argv) {
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
std::cerr << "error setting handler for signal " << SIGABRT
<< " (" << strsignal(SIGABRT) << ")\n";
exit(EXIT_FAILURE);
}
foo1();
exit(EXIT_SUCCESS);
}
输出:
my_terminate caught unhanded exception. what(): RUNTIME ERROR! my_terminate backtrace returned 10 frames [bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52] [bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa] [bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5] [bt]: (3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf] [bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008] [bt]: (5) ./test(foo2__Fv+0xb) [0x8049043] [bt]: (6) ./test(foo1__Fv+0xb) [0x8049057] [bt]: (7) ./test(main+0xc1) [0x8049121] [bt]: (8) ./test(__libc_start_main+0x95) [0x42017589] [bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21] signal 6 (Aborted), address is 0x1239 from 0x42029331 crit_err_hdlr backtrace returned 13 frames [bt]: (1) ./test(kill+0x11) [0x42029331] [bt]: (2) ./test(abort+0x16e) [0x4202a8c2] [bt]: (3) ./test [0x8048f9f] [bt]: (4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa] [bt]: (5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5] [bt]: (6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf] [bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008] [bt]: (8) ./test(foo2__Fv+0xb) [0x8049043] [bt]: (9) ./test(foo1__Fv+0xb) [0x8049057] [bt]: (10) ./test(main+0xc1) [0x8049121] [bt]: (11) ./test(__libc_start_main+0x95) [0x42017589] [bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21]
关于c++ - 如何找到在 C++ 中引发异常的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2443135/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
我有一大串格式化数据(例如JSON),我想使用Psychinruby同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解