システムでアクセス違反が発生してプログラムがクラッシュしたため、私の会社に多くの顧客から電話がかかってきました。
クラッシュは、アプリケーションの一部として出荷されている SQLite 3.6.23.1 で発生します。(アプリの残りの部分と同じ VC++ ライブラリを使用するために、カスタム ビルドを出荷しますが、これはストック SQLite コードです。)
WinDbg コールスタックで示されているように、 をpcache1Fetch
実行するとクラッシュが発生します。call 00000000
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();
コンパイラは inlinessqlite3BeginBenignMalloc
を次のように定義します。
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
が 0 の場合eax
(これはそうです)、 によってゼロ フラグを設定する必要がありますが、ゼロではありませんtest eax, eax
。ゼロフラグが設定されていないため、je
ジャンプせず、実行しようとしてアプリがクラッシュしcall eax (00000000)
ます。
更新:コードのビルドでは設定されていないeax
ため、ここでは常に 0 にする必要があります。定義済みでsqlite3Hooks.xBenignBegin
SQLite を再構築できます。これにより、コードが有効になり、このコード パスが完全に省略されます。これで問題は解決するかもしれませんが、「本当の」修正とは思えません。他のコードパスでそれが起こるのを止めるものは何ですか?SQLITE_OMIT_BUILTIN_TEST
#define sqlite3BeginBenignMalloc()
これまでのところ、すべての顧客が「Windows 7 Home Premium 64 ビット (6.1、Build 7601) Service Pack 1」を実行しており、次のいずれかの CPU を搭載しているという共通点があります (DxDiag による)。
- AMD A6-3400M APU、Radeon(tm) HD グラフィックス (4 CPU)、~1.4GHz
- AMD A8-3500M APU、Radeon(tm) HD グラフィックス (4 CPU)、~1.5GHz
- AMD A8-3850 APU、Radeon(tm) HD グラフィックス (4 CPU)、~2.9GHz
ウィキペディアのAMD Fusion の記事によると、これらはすべて K10 コアをベースにした「Llano」モデルの AMD Fusion チップであり、2011 年 6 月にリリースされたもので、最初に報告を受け始めた時期です。
最も一般的な顧客システムは Toshiba Satellite L775D ですが、HP Pavilion dv6 & dv7 および Gateway システムからのクラッシュ レポートもあります。
このクラッシュの原因は CPU エラー ( Errata for AMD Family 12h Processors を参照) でしょうか、それとも他に見落としている可能性があるのでしょうか? (Raymond によると、それはオーバークロックである可能性がありますが、もしそうなら、この特定の CPU モデルだけが影響を受けるのは奇妙です。)
正直なところ、これが実際に CPU または OS のエラーである可能性は低いと思われます。なぜなら、顧客はブルースクリーンや他のアプリケーションでのクラッシュを経験していないからです。他にもっと可能性が高い説明があるに違いありませんが、何ですか?
8 月 15 日更新: AMD A6-3400M プロセッサを搭載した Toshiba L745D ノートブックを入手しました。プログラムを実行すると、一貫してクラッシュを再現できます。クラッシュは常に同じ命令で発生します。.time
クラッシュ前のユーザー時間は 1 分 30 秒から 7 分です。最初の投稿で言及しなかった 1 つの事実 (この問題に関連している可能性があります) は、アプリケーションがマルチスレッドであり、CPU と I/O の両方の使用率が高いということです。アプリケーションはデフォルトで 4 つのワーカー スレッドを生成し、クラッシュするまで 80% 以上の CPU 使用率を示します (I/O と SQLite コードのミューテックスのブロックがあります)。2 つのスレッドのみを使用するようにアプリケーションを変更しましたが、それでもクラッシュしました (ただし、発生するまでには時間がかかりました)。現在、スレッドを 1 つだけ使用してテストを実行していますが、まだクラッシュしていません。
また、純粋に 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
およびcall eax
ステートメントの場所) が変更され、問題が回避されるようにも見えます。
私がこれまでに見つけた最も奇妙な結果は、jne
at719f9fa0
を 2 つの命令に変更すると ( /の値に関係なく、nop
制御が常に命令にフォールスルーされるように)、プログラムがクラッシュすることなく実行できるようになることです。test eax, eax
createFlag
esi