我收到了一个“第一次机会异常”信息,它来自于我编写的一个DLL,这个DLL正在运行在一个非我所编写的可执行文件中。也就是说,这个DLL是一个插件。当这个异常第一次触发时,尝试打开一个共享内存映射文件失败了。如果我忽略第一次机会异常并继续运行,应用程序最终会冻结或崩溃。
First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644..
数小时后,问题似乎是由一段代码块引起的,该代码块无限循环,直到预期的异常条件清除为止。结果发现,如果它永远不会被清除,那么最终这个异常会转变成另一个低级别异常条件和/或堆损坏。所有这些都只是为了使用Boost::interprocess打开一个共享内存区域。
第一个复杂的boost-flavor模板代码所在位置未被识别,因此在基于Visual C++ 2008的项目中,第一个boost::interprocess::interprocess_exception
first-chance异常未被抛出或标识。然而,通过单步调试汇编语言视图,我找到了出错的代码。
导致所有问题开始恶化的是我的代码的顶层行:
segment = new managed_shared_memory( open_or_create
, MEMORY_AREA_NAME
, SHARED_AREA_SIZE );
上面的managed_shared_memory
类来自于interprocess_fwd.hpp,是Boost共享内存API/头文件的标准部分。由于它是基于模板的,以上内容会扩展为一个大约2K字符长的C++ Boost模板表达式,该表达式被链接器和调试器截断在不同的长度处。当这些限制生效时,Visual C++ 2008似乎没有更多的源代码调试能力。
例如,当它崩溃时,我会得到这个调用堆栈:
KernelBase.dll!76a7c41f()
[Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]
KernelBase.dll!76a7c41f()
> msvcr90d.dll!_malloc_dbg(unsigned int nSize=2290875461, int nBlockUse=264, const char * szFileName=0x01fcb983, int nLine=1962999808) Line 160 + 0x1b bytes C++
8bfc4d89()
上面的堆栈转储中没有实际的终端用户编写的源函数。
我该如何调试?其次,boost-interprocess在Visual C++ 2008中是否存在某些已知问题?第三,下面的boost代码在做什么,为什么必须无限循环?
boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,
boost::interprocess::iset_index>::basic_managed_shared_memory<char,boo...
进一步向下,在这里我们看到:
basic_managed_shared_memory (open_or_create_t,
const char *name, size_type size,
const void *addr = 0, const permissions& perm = permissions())
: base_t()
, base2_t(open_or_create, name, size, read_write, addr,
create_open_func_t(get_this_pointer(),
ipcdetail::DoOpenOrCreate), perm)
{}
总之,不要试图在家里调试这个问题,孩子们。以下是发生的情况:
最终,我利用类忍者的能力单步跟踪了数百万行汇编代码,克服了 Visual C++ 2008 的恶意调试器限制,并找到了有问题的代码。
事实上,这就是引起问题的地方:create_device<FileBased>(dev...
。
这里提供一些背景:managed_open_or_create_impl.h 第 351 行...
else if(type == DoOpenOrCreate){
//This loop is very ugly, but brute force is sometimes better
//than diplomacy. If someone knows how to open or create a
//file and know if we have really created it or just open it
//drop me a e-mail!
bool completed = false;
while(!completed){
try{
create_device<FileBased>(dev, id, size, perm, file_like_t()); // <-- KABOOM!
created = true;
completed = true;
}
catch(interprocess_exception &ex){
if(ex.get_error_code() != already_exists_error){
throw;
}
else{
try{
DeviceAbstraction tmp(open_only, id, read_write);
dev.swap(tmp);
created = false;
completed = true;
}
catch(interprocess_exception &e){
if(e.get_error_code() != not_found_error){
throw;
}
}
catch(...){
throw;
}
}
}
catch(...){
throw;
}
thread_yield();
}
}