问题描述
我正在编写一个我想可以移植的库.因此,它不应依赖glibc或Microsoft扩展或标准中未包含的任何其他内容.我有一个很好的类继承自std :: exception的类,用于处理逻辑和输入中的错误.知道在特定文件和行号上抛出了特定类型的异常很有用,但是知道执行如何到达那里可能会更有价值,因此,我一直在研究获取堆栈跟踪的方法.
I am writing a library that I would like to be portable. Thus, it should not depend on glibc or Microsoft extensions or anything else that is not in the standard. I have a nice hierarchy of classes derived from std::exception that I use to handle errors in logic and input. Knowing that a particular type of exception was thrown at a particular file and line number is useful, but knowing how the execution got there would be potentially much more valuable, so I have been looking at ways of acquiring the stack trace.
我知道使用execinfo.h中的函数针对glibc构建此数据时可用(请参见)并通过Microsoft C ++实现中的StackWalk接口(请参见),但我非常想避免所有无法移植的内容.
I am aware that this data is available when building against glibc using the functions in execinfo.h (see question 76822) and through the StackWalk interface in Microsoft's C++ implementation (see question 126450), but I would very much like to avoid anything that is not portable.
我当时正在考虑自己以这种形式实现此功能:
I was thinking of implementing this functionality myself in this form:
class myException : public std::exception
{
public:
...
void AddCall( std::string s )
{ m_vCallStack.push_back( s ); }
std::string ToStr() const
{
std::string l_sRet = "";
...
l_sRet += "Call stack:\n";
for( int i = 0; i < m_vCallStack.size(); i++ )
l_sRet += " " + m_vCallStack[i] + "\n";
...
return l_sRet;
}
private:
...
std::vector< std::string > m_vCallStack;
};
ret_type some_function( param_1, param_2, param_3 )
{
try
{
...
}
catch( myException e )
{
e.AddCall( "some_function( " + param_1 + ", " + param_2 + ", " + param_3 + " )" );
throw e;
}
}
int main( int argc, char * argv[] )
{
try
{
...
}
catch ( myException e )
{
std::cerr << "Caught exception: \n" << e.ToStr();
return 1;
}
return 0;
}
这是一个可怕的主意吗?这将意味着为每个函数添加try/catch块而需要进行大量工作,但是我可以接受.当导致异常的原因是内存损坏或内存不足时,它将无法正常工作,但是到那时您还是很困惑.如果堆栈中的某些函数没有捕获异常,将自身添加到列表中并重新抛出,则它可能会提供误导性信息,但是我至少可以保证我的所有库函数都可以这样做.与真实"堆栈跟踪不同,我不会在调用函数中获得行号,但是至少我会有东西.
Is this a terrible idea? It would mean a lot of work adding try/catch blocks to every function, but I can live with that. It would not work when the cause of the exception is memory corruption or lack of memory, but at that point you are pretty much screwed anyway. It may provide misleading information if some functions in the stack do not catch exceptions, add themselves to the list, and rethrow, but I can at least provide a guarantee that all of my library functions do so. Unlike a "real" stack trace I will not get the line number in calling functions, but at least I would have something.
我主要担心的是,即使实际上未引发任何异常,这也可能导致速度降低.所有这些try/catch块是否都需要在每次函数调用时进行额外的设置和拆除,还是在编译时以某种方式进行处理?还是我没有考虑过其他问题?
My primary concern is the possibility that this will cause a slowdown even when no exceptions are actually thrown. Do all of these try/catch blocks require an additional set-up and tear-down on each function invocation, or is somehow handled at compile-time? Or are there other issues I have not considered?
推荐答案
我认为这是一个非常糟糕的主意.
I think this is a really bad idea.
可移植性是一个非常有价值的目标,但是当它导致侵入性,性能下降和劣等实施的解决方案时,并不是.
Portability is a very worthy goal, but not when it results in a solution that is intrusive, performance-sapping, and an inferior implementation.
我研究过的每个平台(Windows/Linux/PS2/iPhone/etc)都提供了一种在异常发生时遍历堆栈并将地址与函数名匹配的方法.是的,这些都不是可移植的,但是报告框架可以移植,并且编写平台特定版本的堆栈步行代码通常需要不到一两天的时间.
Every platform (Windows/Linux/PS2/iPhone/etc) I've worked on has offered a way to walk the stack when an exception occurs and match addresses to function names. Yes, none of these are portable but the reporting framework can be and it usually takes less than a day or two to write a platform-specific version of stack walking code.
这不仅比创建/维护跨平台解决方案所需的时间更少,而且效果要好得多;
Not only is this less time than it'd take creating/maintaining a cross-platform solution, but the results are far better;
- 无需修改功能
- 标准库或第三方库中的陷阱崩溃
- 无需在每个功能中进行尝试/捕获(速度慢且占用大量内存)
这篇关于可移植的C ++堆栈异常跟踪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!