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


68

由于我们的程序由于系统上的访问冲突而崩溃,因此我的公司已经开始吸引许多客户。

崩溃发生在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, eaxandcall eax语句的位置),并且似乎也可以防止此问题。

到目前为止,我发现的最奇怪的结果是,将jneat更改719f9fa0为两条nop指令(无论/的值是什么,控件始终属于该test eax, eax指令)使程序可以运行而不会崩溃。createFlagesi


2
几乎肯定不是CPU错误。您是否考虑过制作一个更简单的测试用例?
奥利弗·查尔斯沃思

2
@Mehrdad:是的,有些代码可以跳到那里,但是如果这样做的话,它在伪造调用栈方面做得很好。
布拉德利·格兰杰

1
我必须同意奥利。从根本上来说,根本test不会出现在内部质量检查测试中不设置错误标志的问题。特别是由于此test then jump操作似乎是非常普遍的编译器优化,已在许多程序中使用。
aroth

3
我只是想插话,说那是一个非常好的书面问题。+1
gahooa

1
@flolo:这是一个在64位Windows(WOW64)上运行的32位进程;此输出是正常的。
布拉德利·格兰杰

Answers:


27

我在Microsoft Build会议上与AMD工程师交谈了此错误,并向他展示了我的再现。他今天早上给我发了电子邮件:

我们进行了调查,发现这是由于Llano APU家族中的已知勘误所致。可以通过BIOS更新(取决于OEM)来修复它-如果可能,请向您的客户推荐(即使您有解决方法)。

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

这是该错误的描述:

665整数除法指令可能会导致无法预期的行为

描述

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

对系统的潜在影响

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

建议的解决方法

BIOS应该设置MSRC001_1029 [31]。

此替代方法更改了《 AMD系列10h和12h处理器软件优化指南》中指定的DIV / IDIV指令延迟,订单号40546。应用此替代方法后,AMD系列12h处理器的DIV / IDIV延迟类似于DIV / IDIV延迟。适用于AMD系列10h处理器。

修复计划

没有


1
在Passmark论坛上讨论了“ 665 Integer Divide”问题: passmark.com/forum/…那里 的评论说,该问题仅在双通道RAM中发生。因此,如果没有BIOS修复程序,那么带有一个4GB RAM棒和一个Llano CPU的计算机可能会很好。但是,如果您花费20美元升级到8 GB,则会遇到问题-您可能(错误地!)将问题归咎于RAM。不幸的是,BIOS“修复”会导致Passmark的整数数学基准测试速度降低80%以上,Passmark得分降低30%以上。
戴夫·伯顿

1

我有点担心,为此生成的代码if (wsdHooks.xBenignBegin)不是很通用。它假定唯一的真实值为,1而实际上应该测试任何非零值。尽管如此,MSVC有时还是令人困惑。可能什么都不是。 没关系:这些说明适用于C未显示的代码。

鉴于eflagZ位清零并且EAX为零,因此代码没有通过执行指令到达此处

719f9fa7    test    eax,eax

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

另一个复杂因素是,对于x86系列,无效的跳转目标(如JE指令的第二个字节)对于相当多的指令执行无干扰(无错误)是很常见的,通常最终会恢复正确的指令对齐。换句话说,您可能不希望跳转至任何这些指令的开头:跳转可能位于其字节的中间,从而导致执行不起眼的操作,如add [al+ebp],al通常不会引起注意的操作。

我预计该test指令处的断点将不会遇到异常。找到此类原因的唯一方法要么是非常幸运,要么是怀疑一切,并逐一证明它们是无辜的。


关于您的第一段:test仅将ZF设置为eax & eax等于零,因此与后一个相当安全je
Michael Foukarakis 2011年

您的asm分析已关闭,检查vs 1是因为C代码正在检查vs 1,因为它if( createFlag==1 ) sqlite3BeginBenignMalloc();不是该行的代码if (wsdHooks.xBenignBegin)(请参阅OP关于sqlite3BeginBenignMalloc内联的注释)
Necrolis 2011年

@Michael Foukarakis:一个公平的观点,所以我删除了我的评论。
wallyk 2011年

1
我并不是要争辩,但我认为您的理论与我的发现相矛盾,因为我发现用指令替换jne之前的似乎可以防止崩溃。(如果没有该更改,则100%的可复制性,使用它进行测试的一天,可复制性为0%。)如果其他指令跳到的中间或直接跳到,则不会受到该更改的影响。此外,如何做的理论,一些其他的代码是跳跃到或解释,这只是发生在Llano的APU的?testnopjecalljecall
布拉德利·格兰杰

-1

在考虑CPU错误的可能性之前,请尝试排除更可能的原因

  1. 调用指令的不同代码路径。使用uf命令来分解功能并查找其他跳转/跳转指令

  2. 从挂钩函数跳转/调用为0。 dps SQLite_Interop!sqlite3Hooks l 2并确认它显示为空。


1.(我已经在评论中回答了此问题,但没有更新原始问题,因此并不明显;我很抱歉。)崩溃的代码是函数中的0x2B字节(几乎在序言之后)。我拆开了整个功能,并没有那么早就跳回去。该语句在函数体中的循环之前发生。当然可以从函数外部跳转,但是很难与堆栈协调。2.dps SQLite_Interop!sqlite3Hooks l 2显示00000000 00000000
布拉德利·格兰杰

我还设置了一个数据断点(ba w 4 SQLite_Interop!sqlite3Hooks),崩溃前没有对该地址的写操作。
布拉德利·格兰杰
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.