Разве это "не должно произойти", что привело к сбою ошибки процессора AMD Fusion?

Моя компания начала приглашать нескольких клиентов, потому что наша программа рушится с нарушением прав доступа в своих системах.

Сбой происходит в SQLite 3.6.23.1, который мы отправляем как часть нашего приложения. (Мы отправляем пользовательскую сборку, чтобы использовать те же библиотеки VС++, что и остальные приложения, но это код 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 (оно есть), флаг нуля должен быть установлен test eax, eax, но он отличен от нуля. Поскольку флаг нуля не установлен, je не перескакивает, а затем приложение выходит из строя, пытаясь выполнить call eax (00000000).

Обновление: eax всегда должно быть здесь 0, потому что sqlite3Hooks.xBenignBegin не задано в нашей сборке кода. Я мог бы перестроить SQLite с помощью SQLITE_OMIT_BUILTIN_TEST, который включил бы #define sqlite3BeginBenignMalloc() в коде и полностью опустил бы этот код. Это может решить проблему, но это не похоже на "реальное" исправление; что бы остановить это в другом коде кода?

До сих пор общим фактором является то, что все клиенты работают под управлением Windows 7 Home Premium 64-bit (6.1, Build 7601) с пакетом обновления 1 (SP1) и имеют один из следующих процессоров (в соответствии с DxDiag):

  • AMD A6-3400M APU с графикой Radeon (tm) HD (4 процессора), ~ 1,4 ГГц
  • AMD A8-3500M APU с графикой Radeon (tm) HD (4 процессора), ~ 1,5 ГГц
  • AMD A8-3850 APU с графикой Radeon (tm) HD (4 процессора), ~ 2,9 ГГц

Согласно статье Wikipedia AMD Fusion, это все модели Llano для чипов AMD Fusion на базе ядра K10 и выпущены в июне 2011 года, когда мы сначала начали получать отчеты.

Наиболее распространенной клиентской системой является Toshiba Satellite L775D, но у нас также есть отчеты о сбоях от систем HP Pavilion dv6 и dv7 и Gateway.

Может ли этот сбой произойти из-за ошибки ЦП (см. Исправления для AMD 12h процессоров AMD), или есть некоторые другие возможные объяснения, которые я пропускаю? (По словам Раймонда, он может разгоняться, но странно, что на это влияет только эта конкретная модель процессора.)

Честно говоря, не представляется возможным, что это действительно ошибка процессора или ОС, потому что клиенты не получают bluescreens или сбои в других приложениях. Должно быть какое-то другое, более вероятное объяснение, но что?

Обновление 15 августа: я приобрел ноутбук Toshiba L745D с процессором AMD A6-3400M и способен последовательно воспроизводить авария при запуске программы. Авария всегда выполняется по одной и той же инструкции; .time сообщает от 1 м до 7 м от времени пользователя перед сбоем. Один факт (который может иметь отношение к проблеме), который я забыл упомянуть в оригинальной публикации, заключается в том, что приложение является многопоточным и имеет как высокий уровень использования ЦП, так и ввода-вывода. Приложение порождает четыре рабочих потока по умолчанию и загружает 80%% использования ЦП (есть некоторая блокировка для ввода-вывода, а также для мьютексов в коде SQLite) до тех пор, пока он не завершится сбой. Я изменил приложение, чтобы использовать только два потока, и он по-прежнему разбился (хотя потребовалось больше времени). Теперь я тестирую только один поток, и он еще не разбился.

Обратите также внимание на то, что это не является проблемой загрузки CPU; Я могу запустить Prime95 без ошибок в системе, и это повысит температуру процессора до > 70 ° C, в то время как мое приложение едва достигает температуры выше 50 ° C во время работы.

Обновление 16 августа: Воспрепятствование инструкциям немного заставляет проблему "уйти". Для eaxmple замена загрузки памяти (mov eax,dword ptr [SQLite_Interop!sqlite3Hooks (71a7813c)]) на xor eax, eax предотвращает сбой. Изменение исходного кода C для добавления дополнительной проверки в оператор if( createFlag==1 ) изменяет относительные смещения различных переходов в скомпилированном коде (а также расположение операторов test eax, eax и call eax), а также, как представляется, проблема.

Самый странный результат, который я нашел до сих пор, заключается в том, что изменение jne at 719f9fa0 на две команды nop (так что управление всегда переходит к команде test eax, eax, независимо от того, что значение createFlag/esi) позволяет программе работать без сбоев.

Ответ 1

Я поговорил с инженером AMD на конференции Microsoft Build об этой ошибке и показал ему мой ревью. Он отправил мне письмо по электронной почте сегодня утром:

Мы исследовали и обнаружили, что это связано с известной ошибкой в семейство LLANO APU. Он может быть исправлен с помощью обновления BIOS в зависимости от OEM - если возможно, порекомендуйте его своим клиентам (даже хотя у вас есть обходной путь).

В случае, если вас интересует, ошибка составляет 665 в семье 12h Руководство по пересмотру (см. Стр. 45): http://support.amd.com/TechDocs/44739_12h_Rev_Gd.pdf#page=45

Здесь описание этого erratum:

665 Инструкция по разделению целых чисел может вызвать непредсказуемое поведение

Описание

В соответствии с очень конкретным и подробным набором внутренних условий синхронизации процессорное ядро ​​может прервать спекулятивную инструкцию деления div или idIV (из-за перенаправления спекулятивного исполнения, например, из-за неверно предсказанной ветки), но может зависать или преждевременно завершите первую инструкцию неспекулятивного пути.

Потенциальный эффект для системы

Непредсказуемое поведение системы, обычно приводящее к зависанию системы.

Рекомендуемое временное решение

BIOS должен установить MSRC001_1029 [31].

Это обходное решение изменяет задержку команды DIV/IDIV, указанную в Руководстве по оптимизации программного обеспечения для процессоров AMD Family 10h и 12h, порядок # 40546. При использовании этого обходного решения латентность DIV/IDIV для процессоров AMD 12h похожа на DIV/IDIV для процессоров AMD Family 10h.

Fix Planned

Нет

Ответ 2

Я немного обеспокоен тем, что код, созданный для if (wsdHooks.xBenignBegin), не очень общий. Предполагается, что единственным истинным значением является 1, тогда как оно должно действительно тестироваться для любого ненулевого значения. Тем не менее, MSVC иногда озадачивает этот путь. Это, вероятно, ничего. Ничего: эти инструкции предназначены для кода C, который не представлен.

Учитывая, что бит eflag Z чист и EAX равен нулю, код не попал сюда, выполнив команду

719f9fa7    test    eax,eax

В другом месте должен быть переход из следующей инструкции (719f9fa9 je SQLite_Interop!pcache1Fetch+0x2d) или даже самой инструкции call.

Еще одно осложнение заключается в том, что в семействе x86 обычно используется недопустимый целевой показатель перехода (например, второй байт команды JE) для выполнения невозмутимых (без ошибок) для нескольких инструкций, часто в конечном итоге возвращающихся правильное выравнивание команд. С другой стороны, вы можете не искать перехода к началу любой из этих инструкций: скачок может быть посреди их байтов, в результате чего выполняются такие незаметные операции, как add [al+ebp],al, которые, как правило, не замечаются.

Я предсказываю, что точка останова в команде test не будет удалена для исключения. Единственные способы найти такие причины - либо быть очень удачливыми, либо подозревать все и доказывать их невиновность один за другим.

Ответ 3

Прежде чем рассматривать возможность ошибки ЦП, попытайтесь исключить более вероятные причины

  • Другой путь кода к команде вызова. Используйте команду uf для демонтажа функции и поиска других переходов/ветвей в команде вызова

  • Переключитесь/вызовите 0 из функции hook. dps SQLite_Interop!sqlite3Hooks l 2 и убедитесь, что он показывает нули.