由于我们的程序由于系统上的访问冲突而崩溃,因此我的公司已经开始吸引许多客户。
崩溃发生在SQLite 3.6.23.1中,我们将其作为应用程序的一部分提供。(为了提供与应用程序其余部分相同的VC ++库,我们提供了一个自定义版本,但这是库存的SQLite代码。)
当pcache1Fetch
执行时发生崩溃call 00000000
,如WinDbg调用栈所示:
0b50e5c4 719f9fad 06fe35f0 00000000 000079ad 0x0
0b50e5d8 719f9216 058d1628 000079ad 00000001 SQLite_Interop!pcache1Fetch+0x2d [sqlite3.c @ 31530]
0b50e5f4 719fd581 000079ad 00000001 0b50e63c SQLite_Interop!sqlite3PcacheFetch+0x76 [sqlite3.c @ 30651]
0b50e61c 719fff0c 000079ad 0b50e63c 00000000 SQLite_Interop!sqlite3PagerAcquire+0x51 [sqlite3.c @ 36026]
0b50e644 71a029ba 0b50e65c 00000001 00000e00 SQLite_Interop!getAndInitPage+0x1c [sqlite3.c @ 40158]
0b50e65c 71a030f8 000079ad 0aecd680 071ce030 SQLite_Interop!moveToChild+0x2a [sqlite3.c @ 42555]
0b50e690 71a0c637 0aecd6f0 00000000 0001edbe SQLite_Interop!sqlite3BtreeMovetoUnpacked+0x378 [sqlite3.c @ 43016]
0b50e6b8 71a109ed 06fd53e0 00000000 071ce030 SQLite_Interop!sqlite3VdbeCursorMoveto+0x27 [sqlite3.c @ 50624]
0b50e824 71a0db76 071ce030 0b50e880 071ce030 SQLite_Interop!sqlite3VdbeExec+0x14fd [sqlite3.c @ 55409]
0b50e850 71a0dcb5 0b50e880 21f9b4c0 00402540 SQLite_Interop!sqlite3Step+0x116 [sqlite3.c @ 51744]
0b50e870 00629a30 071ce030 76897ff4 70f24970 SQLite_Interop!sqlite3_step+0x75 [sqlite3.c @ 51806]
C代码的相关行是:
if( createFlag==1 ) sqlite3BeginBenignMalloc();
编译器inlines sqlite3BeginBenignMalloc
,其定义为:
typedef struct BenignMallocHooks BenignMallocHooks;
static SQLITE_WSD struct BenignMallocHooks {
void (*xBenignBegin)(void);
void (*xBenignEnd)(void);
} sqlite3Hooks = { 0, 0 };
# define wsdHooksInit
# define wsdHooks sqlite3Hooks
SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void){
wsdHooksInit;
if( wsdHooks.xBenignBegin ){
wsdHooks.xBenignBegin();
}
}
程序集为:
719f9f99 mov esi,dword ptr [esp+1Ch]
719f9f9d cmp esi,1
719f9fa0 jne SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fa2 mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]
719f9fa7 test eax,eax
719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d (719f9fad)
719f9fab call eax ; *** CRASH HERE ***
719f9fad mov ebx,dword ptr [esp+14h]
这些寄存器是:
eax=00000000 ebx=00000001 ecx=000013f0 edx=fffffffe esi=00000001 edi=00000000
eip=00000000 esp=0b50e5c8 ebp=000079ad iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
如果eax
为0(即为0),则零标志应由设置test eax, eax
,但非零。由于未设置零标志,因此je
不会跳转,然后应用程序在尝试执行时崩溃call eax (00000000)
。
更新:eax
此处应始终为0,因为sqlite3Hooks.xBenignBegin
未在我们的代码构建中设置。我可以用define重建SQLite SQLITE_OMIT_BUILTIN_TEST
,这将#define sqlite3BeginBenignMalloc()
在代码中打开并完全忽略此代码路径。也许可以解决问题,但感觉不像是“真正的”解决方案。什么会阻止它在其他代码路径中发生?
到目前为止,普遍的因素是所有客户都在运行“ Windows 7 Home Premium 64位(6.1,内部版本7601)Service Pack 1”,并且具有以下CPU之一(根据DxDiag):
- 带有Radeon(tm)高清显卡(4个CPU),〜1.4GHz的AMD A6-3400M APU
- 带有Radeon(tm)高清显卡(4个CPU),〜1.5GHz的AMD A8-3500M APU
- 带有Radeon(tm)高清显卡(4个CPU),〜2.9GHz的AMD A8-3850 APU
根据Wikipedia的AMD Fusion文章,这些都是基于K10内核的“ Llano”模型AMD Fusion芯片,于2011年6月发布,这是我们首次开始获得报道的时间。
最常见的客户系统是东芝Satellite L775D,但是我们也有来自HP Pavilion dv6和dv7以及网关系统的崩溃报告。
崩溃可能是由CPU错误引起的(请参阅AMD系列12h处理器的勘误表),还是我忽略了其他可能的解释?(根据雷蒙德(Raymond)的说法,可能是超频,但奇怪的是,只有这种特定的CPU模型会受到影响。)
坦白地说,这似乎不太可能是CPU或OS错误,因为客户不会在其他应用程序中出现蓝屏或崩溃的情况。还必须有其他更可能的解释-但是呢?
8月15日更新:我已经购买了配备AMD A6-3400M处理器的Toshiba L745D笔记本电脑,可以在运行该程序时始终如一地再现崩溃信息。崩溃总是在同一条指令上。.time
报告崩溃前1m30到7m的用户时间。我在原始文章中忽略提及的一个事实(可能与问题有关)是该应用程序是多线程的,并且具有很高的CPU和I / O使用率。该应用程序默认情况下会产生四个工作线程,并释放80%以上的CPU使用率(SQLite代码中存在一些I / O和互斥锁的阻塞),直到崩溃为止。我将应用程序修改为仅使用两个线程,并且仍然崩溃(尽管花费了更长的时间)。我现在只用一个线程运行一个测试,它还没有崩溃。
还要注意,这似乎并不是纯粹的CPU负载问题。我可以在系统上没有错误地运行Prime95,它将CPU温度提高到> 70°C,而我的应用程序在运行时几乎无法使温度超过50°C。
8月16日更新:稍微干扰说明会使问题“消失”。例如,用替换内存负载(mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]
)可以xor eax, eax
防止崩溃。修改原始C代码以向该if( createFlag==1 )
语句添加额外的检查会更改编译后的代码中各种跳转的相对偏移量(以及test eax, eax
andcall eax
语句的位置),并且似乎也可以防止此问题。
到目前为止,我发现的最奇怪的结果是,将jne
at更改719f9fa0
为两条nop
指令(无论/的值是什么,控件始终属于该test eax, eax
指令)使程序可以运行而不会崩溃。createFlag
esi