这是“不应该发生”崩溃的AMD Fusion CPU的错误?

我的公司已经开始有很多客户打来电话了,因为我们的程序在他们的系统上发生访问冲突。

SQLite 3.6.23.1发生崩溃,我们将其作为应用程序的一部分发布。 (我们发布了一个自定义构build,以便使用与应用程序其余部分相同的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(); 

编译器内联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(它是),零标志应该由test eax, eax ,但它不是零。 由于零标志没有设置, je不会跳转,然后应用程序崩溃尝试执行call eax (00000000)

更新eax应该始终为0,因为sqlite3Hooks.xBenignBegin没有在我们的代码构build中设置。 我可以使用定义的SQLITE_OMIT_BUILTIN_TEST来重buildSQLite,这会在代码中打开#define sqlite3BeginBenignMalloc() ,并完全省略这段代码path。 这可能解决这个问题,但它不像是一个“真正的”修复; 什么会阻止它发生在其他代码path?

到目前为止,共同的因素是所有客户都运行“Windows 7家庭高级版64位(6.1,Build 7601)Service Pack 1”,并具有以下CPU之一(根据DxDiag):

  • AMD A6-3400M APU,带有Radeon(tm)高清显卡(4个CPU),〜1.4GHz
  • AMD A8-3500M APU,带有Radeon(tm)高清显卡(4个CPU),〜1.5GHz
  • AMD A8-3850 APU,配有Radeon(tm)高清显卡(4个CPU),〜2.9GHz

根据维基百科的AMD Fusion文章 ,这些都是基于K10内核的“Llano”型AMD Fusion芯片,并于2011年6月发布,这是我们第一次获得报告的时候。

最常见的客户系统是东芝Satellite L775D,但是我们也有惠普Pavilion dv6&dv7和Gateway系统的故障报告。

这个崩溃可能是由CPU错误引起的(请参阅AMD系列12h处理器的勘误表 ),还是有其他可能的解释,我可以忽略? (根据雷蒙德的说法,这可能是超频 ,但如果是这样的话,这个特定的CPU模型就会受到影响,这很奇怪)。

老实说,这似乎不太可能,这实际上是一个CPU或操作系统错误,因为客户没有得到蓝屏或在其他应用程序崩溃。 还有一些其他的更可能的解释 – 但是什么?

8月15日更新:我已经购买了配备AMD A6-3400M处理器的东芝L745D笔记本电脑,可以在运行该程序时一致地重现崩溃。 崩溃总是在同一条指令上; .time在崩溃前报告用户时间从1分30秒到7分的任何时间。 在原来的文章中我忽略提到的一个事实(可能与此问题有关)是应用程序是multithreading的,同时具有较高的CPU和I / O使用率。 应用程序在默认情况下会生成四个工作线程,并且会占用80 +%的CPU使用率(在I / O以及SQLite代码中有一些阻塞)直到崩溃。 我修改了应用程序只使用两个线程,它仍然崩溃(虽然需要更长的时间发生)。 我现在只用一个线程运行一个testing,而且还没有崩溃。

还要注意,它似乎不是纯粹的CPU负载问题; 我可以在系统上运行Prime95而不会出现任何错误,它会将CPU温度提高到70°C,而我的应用程序在运行时温度不会超过50°C。

8月16日更新:稍微干扰指示使问题“消失”。 对于mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]xor eax, eaxreplace内存负载( mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]xor eax, eax可以防止崩溃。 修改原始的C代码,在if( createFlag==1 )语句中增加一个额外的检查来改变编译代码中各种跳转的相对偏移量(以及test eax, eaxcall eax语句的位置)似乎阻止了这个问题。

到目前为止我发现的最奇怪的结果是,将719f9fa0jne改为两个nop指令(这样控制总是落在test eax, eax指令,无论createFlag / esi的值createFlag )都允许程序运行时不会崩溃。

我在Microsoft Build大会上与一位AMD工程师谈了这个错误,并向他展示了我的代码。 他今天早上发了电邮给我:

我们已经调查并发现,这是由于在Llano APU家族中已知的勘误。 它可以通过取决于OEM的BIOS更新修复 – 如果可能的话,请推荐给您的客户(即使您有一个解决方法)。

如果您有兴趣,“家庭12小时修订指南”(参见第45页)中的错误是665: http : //support.amd.com/TechDocs/44739_12h_Rev_Gd.pdf#page=45

以下是对这个错误的描述:

665整数除法指令可能导致不可预知的行为

描述

在高度特定和详细的一组内部时序条件下,处理器内核可以中止推测DIV或IDIV整数除法指令(由于推测执行被redirect,例如由于预测错误的分支),但可能会挂起或过早完成第一个指令的非投机path。

对系统的潜在影响

不可预知的系统行为,通常导致系统挂起。

build议的解决方法

BIOS应该设置MSRC001_1029 [31]。

此解决方法改变了AMD系列10h和12h处理器软件优化指南中指定的DIV / IDIV指令延迟,订购号为#40546。应用此解决方法时,AMD系列12h处理器的DIV / IDIV延迟类似于DIV / IDIV延迟AMD系列10h处理器。

修复计划

没有

我有点担心为if (wsdHooks.xBenignBegin)生成的代码不是很一般。 它假定唯一的真值是1而它应该真的testing任何非零值。 不过,MSVC有时候也是这样的。 这可能是没有的。 没关系:这些说明是针对没有提供的C代码的。

假设eflag Z位清零且EAX为零,那么通过执行指令,代码不会到达此处

 719f9fa7 test eax,eax 

必须从其他地方跳转到指令后面( 719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d )或者甚至是call指令本身。

另一个复杂的情况是,对于x86系列来说,无效的跳转目标(如JE指令的第二个字节)通常会执行不受干扰(无故障)的指令,通常最终会恢复正确的指令alignment。 换句话说,你可能不会想要跳转到任何这些指令的开始处:跳转可能在它们的字节中间,导致执行不起眼的操作,如add [al+ebp],al往往不会被注意到。

我预测test指令中的断点不会受到例外的影响。 find这些原因的唯一途径是要么非常幸运,要么怀疑一切,并一一certificate他们是无辜的。

在考虑CPU错误的可能性之前,尽量排除更可能的原因

  1. 到调用指令的不同代码path。 使用uf命令反汇编该函数,并查找其他跳转/分支到调用指令

  2. 从挂钩function跳转/呼叫到0。 dps SQLite_Interop!sqlite3Hooks l 2并validation它显示为空。