C++ EH exception

介绍

某个代理服务器在压力测试时发生异常,以下是定位过程

保存dump

因为提早有知道程序会出现异常,所以中间省去了很多步骤,在服务启动时就已经使用windbg attach到了服务进程上,这样一旦程序触发异常windbg立即就会捕获。
dump下载:链接:https://pan.baidu.com/s/1Ntm3MRQnbMqGtLkAushSvg 提取码:nvu1

分析过程

程序异常

在程序运行了15小时之后,程序触发了异常,windbg 上使用 命令 !analyze -v 分析异常原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Failed calling InternetOpenUrl, GLE=12002

FAULTING_IP:
KERNELBASE!RaiseException+58
76dcc42d c9 leave

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 76dcc42d (KERNELBASE!RaiseException+0x00000058)
ExceptionCode: e06d7363 (C++ EH exception)
ExceptionFlags: 00000001
NumberParameters: 3
Parameter[0]: 19930520
Parameter[1]: 026ffa68
Parameter[2]: 746dc7fc

DEFAULT_BUCKET_ID: APPLICATION_FAULT

STACK_TEXT:
026ffa18 746c9339 e06d7363 00000001 00000003 KERNELBASE!RaiseException+0x58
026ffa58 7470da6a 026ffa68 746dc7fc 746dd1cc msvcr120!_CxxThrowException+0x5b [f:\dd\vctools\crt\crtw32\eh\throw.cpp @ 152]
026ffa78 009ba351 00004400 03d95cc8 03d95d20 msvcr120!operator new+0x50 [f:\dd\vctools\crt\crtw32\heap\new.cpp @ 62]
026ffa8c 009c9c01 00004400 03d95cc8 009c9824 roommpsvr!std::vector<unsigned char,std::allocator<unsigned char> >::_Buy+0x41
026ffad4 009c9b9f 0324d520 00621598 2cf7d616 roommpsvr!std::vector<unsigned char,std::allocator<unsigned char> >::vector<unsigned char,std::allocator<unsigned char> >+0x21
026ffb08 009c2858 08cb66d8 2cf7d606 045aad60 roommpsvr!mp::MPSocket::create_ssl+0x5f
026ffb9c 009c5987 026ffbf8 0000020f 026ffc18 roommpsvr!mp::WSSConnection::handle_judge_protocol+0x248
026ffbac 009c5341 026ffbe8 026ffbe8 2cf7d6de roommpsvr!boost::asio::asio_handler_invoke<boost::asio::detail::binder2<boost::_bi::bind_t<void,boost::_mfi::mf2<void,mp::WSConnection,boost::system::error_code const &,unsigned int>,boost::_bi::list3<boost::_bi::value<boost::shared_ptr<mp::WSSConnection> >,boost::arg<1>,boost::arg<2> > >,boost::system::error_code,unsigned int> >+0x17
026ffc18 009b603d 00643940 045aad60 026ffc4c roommpsvr!boost::asio::detail::win_iocp_socket_recv_op<boost::asio::mutable_buffers_1,boost::_bi::bind_t<void,boost::_mfi::mf2<void,mp::WSConnection,boost::system::error_code const &,unsigned int>,boost::_bi::list3<boost::_bi::value<boost::shared_ptr<mp::WSSConnection> >,boost::arg<1>,boost::arg<2> > > >::do_complete+0x101
026ffc78 009b5ba7 2cf7d166 026ffcc8 2cf7d1aa roommpsvr!boost::asio::detail::win_iocp_io_service::do_one+0x17d
026ffcb4 009b63fa 026ffcc8 00647e70 00647e70 roommpsvr!boost::asio::detail::win_iocp_io_service::run+0xc7
026ffcd0 00b182de 2cf7d1e6 00000000 00651090 roommpsvr!boost::asio::io_service::run+0x2a
026ffcf8 746dc01d 00647e70 ff4a9697 00000000 roommpsvr!boost::`anonymous namespace'::thread_start_function+0x5e
026ffd30 746dc001 00000000 026ffd48 769f337a msvcr120!_callthreadstartex+0x1b [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
026ffd3c 769f337a 00650900 026ffd88 77389882 msvcr120!_threadstartex+0x7c [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
026ffd48 77389882 00650900 a6a6cb5f 00000000 kernel32!BaseThreadInitThunk+0xe
026ffd88 77389855 746dbfb4 00650900 ffffffff ntdll!__RtlUserThreadStart+0x70
026ffda0 00000000 746dbfb4 00650900 00000000 ntdll!_RtlUserThreadStart+0x1b

查看堆栈

从堆栈信息中可以看到,在mp::MPSocket::create_ssl 中new了对象,然后new.cpp@62 抛出了一个异常

1
2
3
4
5
6
7
8
9
10
11
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ // report no memory
_THROW_NCEE(_XSTD bad_alloc, ); ---------->>> @62
}

return (p);
}

查看内存信息

使用工具processxp 查看对应服务的内存状态,虚拟内存、提交内存 都是很低的,不太可能会出现内存申请失败。

进一步确认原因

1
2
3
4
5
6
7
8
EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 76dcc42d (KERNELBASE!RaiseException+0x00000058)
ExceptionCode: e06d7363 (C++ EH exception)
ExceptionFlags: 00000001
NumberParameters: 3
Parameter[0]: 19930520
Parameter[1]: 026ffa68
Parameter[2]: 746dc7fc

google 搜索 “e06d7363 (C++ EH exception)”, 查看链接:https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
按照它的步骤进一步确认C++ 异常原因:

1
2
3
4
5
6
7
8
0:008> dd 746dc7fc l4         ---> 是小写的“L"
746dc7fc 00000000 746e3cc0 00000000 746dc80c
0:008> dd 746dc80c l2
746dc80c 00000002 746dd1ac
0:008> dd 746dd1ac l2
746dd1ac 00000010 74790d38
0:008> da 74790d38 + 8
74790d40 ".?AVbad_alloc@std@@"

发现的确是 “bad_alloc@std” 导致的异常。

仔细观察物理机器的状态,发现其中一个redis服务吃了大概2G多的内存,会不会是这个原因导致服务进程在申请pagefile时失败呢? 杀掉异常进程继续观察。

总结

长时间观察程序正常。本文的目的主要是介绍如何解码c++异常的具体原因。

附其他操作

1
026ffa58 7470da6a 026ffa68 746dc7fc 746dd1cc msvcr120!_CxxThrowException+0x5b (FPO: [Non-Fpo]) (CONV: stdcall) [f:\dd\vctools\crt\crtw32\eh\throw.cpp @ 152]
1
2
3
__CxxThrowException(
void* pExceptionObject, // The object thrown
_ThrowInfo* pThrowInfo // Everything we need to know about it

746dc7fc 其实就是_ThrowInfo 结构体的指针信息,通过watch查看

1
2
3
4
5
6
7
typedef const struct _s__ThrowInfo
{
unsigned int attributes;
_PMFN pmfnUnwind;
int (__cdecl*pForwardCompat)(...);
_CatchableTypeArray *pCatachableTypeArray;
} _ThrowInfo;

结构体中重要的成员是_CatchableTypeArray。它包含了程序运行时抛出对象的类新信息(RTTI).
如果你的程序运行时抛出一个my_exception类型的对象,那么抛出的数据参数pCatchableTypeArray包含了两个重要子数据信息。一个是typeid(my_exception),另外一个是typeid(std::exception)。

所以通过watch查看:

1
2
3
4
5
(ThrowInfo*)0x746dc7fc     0x746dc7fc struct _s_ThrowInfo *
-attributes 0
-pmfnUnwind 0x746e3cc0
-pForwardCompat 0x00000000
-pCatchableTypeArray 0x746dc80c struct _s_CatchableTypeArray *

1
2
3
dd 0x746dc80c 
746dc80c 00000002 746dd1ac 746e3b04 eb0cc483
746dc81c be575612 7478fcb0 790d68bf 047e8374
1
2
3
4
5
6
7
8
9
10
watch  中查看(_s_CatchableType*)0x746dd1ac
(_s_CatchableType*)0x746dd1ac 0x746dd1ac struct _s_CatchableType *
-properties 0x10
-pType 0x74790d38 struct TypeDescriptor *
--hash 0x746bec94
-spare 0x00000000
--name char [] ".?AVbad_alloc@std@@"
-thisDisplacement struct PMD
-sizeOrOffset 0n12
-copyFunction 0x746dd190