Отладка / обход BSOD без исходного кода

Привет и доброго тебе дня.

Здесь нужна помощь:

Ситуация:
У меня есть непонятное приложение DirectX 9 (имя и детали приложения не имеют отношения к вопросу), которое вызывает синий экран смерти на всех картах nvidia (GeForce 8400GS и выше), начиная с определенной версии драйвера. . Я считаю, что проблема косвенно вызвана вызовом DirectX 9 или флагом, который запускает ошибку драйвера.

Цель:
я хотел бы отследить вызывающий нарушение флаг / вызов функции (для удовольствия, это не моя работа / домашнее задание) и обойти состояние ошибки, написав прокси dll. У меня уже есть готовая прокси-библиотека, которая предоставляет оболочки для IDirect3D9, IDirect3DDevice9, IDirect3DVertexBuffer9 и IDirect3DIndexBuffer9 и обеспечивает базовое ведение журнала / трассировку вызовов Direct3D. Однако я не могу определить функцию, которая вызывает сбой.

Проблемы:

  1. Исходный код или техническая поддержка недоступны. Помощи не будет, и никто другой проблему не решит.
  2. Дамп памяти, созданный ядром, не помог - очевидно, нарушение прав доступа происходит в nv4_disp.dll, но я не могу использовать stacktrace для перехода к вызову метода IDirect3DDevice9, плюс есть вероятность, что ошибка происходит асинхронно.
  3. (Main problem) Because of large number of Direct3D9Device method calls, I can't reliably log them into file or over network:
    1. Logging into file causes significant slowdown even without flushing, and because of that all last contents of the log are lost when system BSODs.
    2. Ведение журнала по сети (с использованием UDP и WINSOck sendto) также вызывает значительное замедление и не должно выполняться асинхронно (асинхронные пакеты теряются в BSOD), плюс пакеты (те, которые находятся вокруг сбоя) иногда теряются даже при синхронной отправке.
    3. Когда приложение "замедляется" из-за подпрограмм журналирования, вероятность возникновения BSOD снижается, что затрудняет его отслеживание.

Вопрос:
Я обычно не пишу драйверы и не занимаюсь отладкой на этом уровне, поэтому у меня создается впечатление, что я упускаю что-то важное, есть более тривиальный способ отследить проблема, чем написание dll прокси IDirect3DDevice9 с настраиваемым механизмом ведения журнала. Что это? Каков стандартный способ диагностики / обработки / исправления подобной проблемы (без исходного кода, метод интерфейса COM запускает BSOD)?

Анализ минидампа (WinDBG):

Loading User Symbols
Loading unloaded module list
...........
Unable to load image nv4_disp.dll, Win32 error 0n2
*** WARNING: Unable to verify timestamp for nv4_disp.dll
*** ERROR: Module load completed but symbols could not be loaded for nv4_disp.dll
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

BugCheck 1000008E, {c0000005, bd0a2fd0, b0562b40, 0}

Probably caused by : nv4_disp.dll ( nv4_disp+90fd0 )

Followup: MachineOwner
---------

0: kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

KERNEL_MODE_EXCEPTION_NOT_HANDLED_M (1000008e)
This is a very common bugcheck.  Usually the exception address pinpoints
the driver/function that caused the problem.  Always note this address
as well as the link date of the driver/image that contains this address.
Some common problems are exception code 0x80000003.  This means a hard
coded breakpoint or assertion was hit, but this system was booted
/NODEBUG.  This is not supposed to happen as developers should never have
hardcoded breakpoints in retail code, but ...
If this happens, make sure a debugger gets connected, and the
system is booted /DEBUG.  This will let us see why this breakpoint is
happening.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: bd0a2fd0, The address that the exception occurred at
Arg3: b0562b40, Trap Frame
Arg4: 00000000

Debugging Details:
------------------


EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s".

FAULTING_IP: 
nv4_disp+90fd0
bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi

TRAP_FRAME:  b0562b40 -- (.trap 0xffffffffb0562b40)
ErrCode = 00000000
eax=00000808 ebx=e37f8200 ecx=e4ae1c68 edx=e37f8328 esi=e37f8400 edi=00000000
eip=bd0a2fd0 esp=b0562bb4 ebp=e37e09c0 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202
nv4_disp+0x90fd0:
bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi ds:0023:00000900=????????
Resetting default scope

CUSTOMER_CRASH_COUNT:  3

DEFAULT_BUCKET_ID:  DRIVER_FAULT

BUGCHECK_STR:  0x8E

LAST_CONTROL_TRANSFER:  from bd0a2e33 to bd0a2fd0

STACK_TEXT:  
WARNING: Stack unwind information not available. Following frames may be wrong.
b0562bc4 bd0a2e33 e37f8200 e37f8200 e4ae1c68 nv4_disp+0x90fd0
b0562c3c bf8edd6b b0562cfc e2601714 e4ae1c58 nv4_disp+0x90e33
b0562c74 bd009530 b0562cfc bf8ede06 e2601714 win32k!WatchdogDdDestroySurface+0x38
b0562d30 bd00b3a4 e2601008 e4ae1c58 b0562d50 dxg!vDdDisableSurfaceObject+0x294
b0562d54 8054161c e2601008 00000001 0012c518 dxg!DxDdDestroySurface+0x42
b0562d54 7c90e4f4 e2601008 00000001 0012c518 nt!KiFastCallEntry+0xfc
0012c518 00000000 00000000 00000000 00000000 0x7c90e4f4


STACK_COMMAND:  kb

FOLLOWUP_IP: 
nv4_disp+90fd0
bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  nv4_disp+90fd0

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: nv4_disp

IMAGE_NAME:  nv4_disp.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  4e390d56

FAILURE_BUCKET_ID:  0x8E_nv4_disp+90fd0

BUCKET_ID:  0x8E_nv4_disp+90fd0

Followup: MachineOwner

person SigTerm    schedule 29.09.2011    source источник
comment
Я тоже ничего не знаю об отладке видеодрайверов. Можете ли вы обновить драйвер до версии, в которой нет ошибок? Код приложения не должен выполнять действия, вызывающие сбои драйверов.   -  person Greg Hewgill    schedule 29.09.2011
comment
@Greg Hewgill: апгрейд ...? Ошибка появилась как минимум год назад (может, два), затрагивает только одно приложение и не исчезает при обновлении драйвера. Вместо того, чтобы отлаживать его, мне интереснее определить условие, которое его запускает. Моя основная проблема - это регистрация вызовов - это вызывает значительное замедление (даже когда я регистрирую вызовы по сети вместо записи на жесткий диск) и недостаточно надежно. Код приложения не предполагается. Есть ошибка драйвера, но больше ничего не вызывает ее. На данный момент, я думаю, NVidia не собирается исправлять это в ближайшее время, поэтому есть смысл написать прокси dll / интерфейс, чтобы обойти это.   -  person SigTerm    schedule 29.09.2011


Ответы (3)


nv4_disp+90fd0
bd0a2fd0 39b8f8000000    cmp     dword ptr [eax+0F8h],edi

Это важная часть. Глядя на это, наиболее вероятно, что eax недействителен, следовательно, попытка доступа к недопустимому адресу памяти.

Что вам нужно сделать, так это загрузить nv4_disp.dll в IDA (вы можете получить бесплатную версию), проверить базу изображений, в которую IDA загружает nv4_disp, и нажать 'g' для перехода по адресу, попробуйте добавить 90fd0 в базу изображений, которую использует IDA, и он должен перенаправить вас прямо к нарушающей инструкции (в зависимости от структуры раздела).

Отсюда вы можете проанализировать поток управления, а также то, как eax устанавливается и используется. Если у вас есть хороший отладчик уровня ядра, вы можете установить точку останова на этом адресе и попытаться заставить ее сработать.

Анализируя функцию, вы должны попытаться выяснить, что она делает, на что должен указывать eax в этой точке, на что он на самом деле указывает и почему. Это сложная часть, составляющая большую часть сложности и мастерства обратного инжиниринга.

person fileoffset    schedule 29.09.2011
comment
Технически вы говорите мне перепроектировать весь драйвер nvidia, понять его, найти и исправить проблему. Хотя это правильное решение и к проблеме можно подойти с этой точки зрения, это не то, что я ищу (решение проблемы таким образом требует нескольких месяцев моего времени, рекомендуется иметь исходный код и информацию об отладке). Я ищу способ определить высокоуровневую последовательность вызовов D3D, которая запускает ошибку, и предотвратить эту последовательность. Основная проблема заключается в ведении журнала - текущее ведение журнала вызовов функций слишком медленное, и результаты часто теряются в BSOD. - person SigTerm; 29.09.2011

Нашел решение.

Проблема:
Ведение журнала ненадежно, так как сообщения (при выгрузке в файл) исчезают во время bsod, пакеты иногда теряются при входе в сеть, а также происходит замедление из-за ведения журнала.

Решение:
Вместо ведения журнала в файл или по сети настройте систему на создание полного дампа физической памяти на BSOD и регистрацию всех сообщений в любом буфере памяти. Будет быстрее. После сбоя системы вся память будет выгружена в файл, и можно будет либо просмотреть содержимое буфера файла журнала с помощью команды WinDBG dt (если у вас есть символы отладки), либо вы сможете найти и найти файл журнала, хранящийся в памяти с использованием представления «память».

Я использовал кольцевой буфер std :: strings для хранения сообщений и отдельный массив const char *, чтобы упростить чтение в WinDBG, но вы могли просто создать огромный массив char и хранить все сообщения в нем в виде открытого текста.

Подробности:
Весь процесс на winxp:

  1. Убедитесь, что минимальный размер файла подкачки равен или превышает общий объем ОЗУ + 1 мегабайт. (Щелкните правой кнопкой мыши «Мой компьютер» -> Свойства-> Дополнительно-> Производительность-> Дополнительно-> Изменить)
  2. Настройте систему для создания полного дампа памяти на BSOD (вправо нажмите «Мой компьютер» -> Свойства-> Дополнительно-> Запуск и восстановление-> Настройки-> Запись отладочной информации. Выберите «Полный дамп памяти» и укажите нужный путь).
  3. Убедитесь, что на диске (на котором будет записан файл) есть необходимое количество свободного места (общий объем ОЗУ в вашей системе.
  4. Создайте приложение / dll (тот, который ведет журнал) с символом отладки и запуском BSOD.
  5. Дождитесь завершения дампа памяти, перезагрузитесь. Смело ругайтесь на разработчика драйвера, пока система пишет дамп памяти и перезагружается.
  6. Скопируйте созданную систему MEMORY.DMP в безопасное место, чтобы не потерять все, если система снова выйдет из строя.
  7. Запустите windbg.
  8. Откройте дамп памяти (File-> Open Crash Dump).
  9. Если вы хотите увидеть, что произошло, используйте команду !analyze -v.
  10. Access memory buffer that stores logged messages using one of those methods:
    1. To see contents of global variable, use dt module!variable where "module" is name of your library (without *.dll), and "variable" is name of variable. You can use wildcards. You can use address without module!variable
    2. Чтобы увидеть содержимое одного поля глобальной переменной (если глобальная переменная является структурой), используйте dt module!variable field, где «поле» - это член переменной.
    3. Чтобы увидеть более подробную информацию о varaible (содержимое массивов и подструктур), используйте dt -b module!variable field или dt -b module!variable
    4. Если у вас нет символов, вам нужно будет найти свой «файл журнала», используя окно памяти.

На этом этапе вы сможете увидеть содержимое журнала, которое было сохранено в памяти, а также у вас будет моментальный снимок всей системы в момент ее сбоя.

Также...

  1. Чтобы увидеть информацию о процессе, который привел к сбою системы, используйте !process.
  2. Чтобы увидеть загруженные модули, используйте lm
  3. Для информации о потоке есть !thread id, где id - это шестнадцатеричный идентификатор, который вы видели в !process выводе.
person SigTerm    schedule 30.09.2011

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

Вы можете попытаться выяснить, какая последовательность событий приводит к этому повреждению кучи, но здесь нет серебряной пули - согласно предложению fileoffset, вам нужно будет выполнить обратный инжиниринг драйвера, чтобы понять, почему это происходит (это может помочь сравнить драйверы до и после проблемной версии драйвера!)

person bdonlan    schedule 30.09.2011
comment
Скорее всего, вы правы насчет плохих указателей, поскольку, судя по содержимому журнала (трассировка всех вызовов IDirect3DDevice9), я смог прочитать, используя методы, которые я упомянул в моем собственном ответе, bsod происходит после, когда приложение выпускает directx память и уничтожает IDirect3DVertexBuffer9. Моя основная проблема заключалась в восстановлении файла журнала, поэтому я смогу взять его оттуда. - person SigTerm; 01.10.2011