Archive for June, 2009

Levando a tela azul pra casa

18 de June de 2009

Nada melhor que uma bela choraderia para começar este post. Meu rítmo está baixo por conta da universidade estar sugando todas as minhas energias vitais. Se você tem acompanhado meu blog nos últimos posts, já sabe do que estou falando. No meu tempo livre estive correndo com meu projeto, meu estágio e meu emprego. Meu blog também participa dessa lista de tarefas, mas o coitadinho tem menos prioridade aqui. Alguns de vocês devem saber que sou helimodelista por hobby, mas como eu disse ao meu amigo Heldai outro dia: “Hobby é o nome que se dá àquilo que fazemos para ocupar o tempo que temos livre, mas ainda estou para descobrir o nome que daríamos àquilo que gostaríamos de fazer se tivéssemos tempo livre…”. Enfim, como isso não tem nada a ver com o post de hoje, vamos mudar de assunto.

Entre uma coisa e outra, estive tentando pensar em algo simples para um post pequeno. Foi então que a dúvida do leitor Ismael Rocha (Brasília – DF) gerou este post.

“Existe uma maneira de salvar as BSOD’s para posteriormente verificar eventuais problemas?”

Salvar uma tela azul? Salvar o quê? A máquina já morreu meu amigo! Já era! Acabou! O que você ainda pode tentar salvar é seu emprego.

Brincadeiras à parte, existe sim.

O sistema operacional está pré-configurado para reiniciar automagicamente quando uma falha crítica acontece. Falha crítica é a maneira polida de se dizer que a casa caiu, a vaca foi pro brejo, o jacaré te abraçou, o tambor girou, o ferro berrou, o tempo fechou, ficou pequeno pra você… enfim, uma tela azul aconteceu. Não que eu não goste de telas azuis, mas do efeito colateral que ela nos traz. Pela norma mundial dos consumidores de drivers de terceiros, se você é o autor de um driver que estiver instalado em uma máquina no momento da falha, esteja ele rodando ou não, então a culpa da falha é sua até que se prove o contrário. É triste, mas é a realidade. A partir do momento que uma tela azul acontece, você é o culpado padrão e terá que ficar aguentando piadinhas pelo resto da eternidade. Gostaria de aproveitar o contexto para mandar um abraço pro meu amigo Heldai.

Exibindo a tela azul

Na tentativa de salvar sua dignidade, você tenta provar que a culpa não é sua. Dizer que o reset da máquina é uma feature do seu driver e que felizmente funcionou muito bem não vai colar, não na segunda vez. Mas o que você pode fazer se a tela azul é apenas um flash de informações enquanto a máquina não reinicia? Felizmente você pode mudar isso. Clicando com o botão direito do mouse sobre o “Meu computador”, selecionando “Propriedades”. Daí em diante é só dar uma olhada na figura abaixo para descobrir que você pode evitar que a máquina reinicie automaginamente.


Você terá de desmarcar a opção “Reiniciar automaticamente”, e assim ter todo o tempo que for necessário para mostrar a todos que o problema não é seu. Na maioria das vezes o sistema consegue detectar o driver que provavelmente é o causador de toda essa dor de cabeça e exibir o nome do arquivo na tela azul como podemos ver na figura abaixo.

Desmontando uma tela azul


“Nossa! Então o Windows tem um algorítmo de inteligência artificial, que provalvelmente usa nanotecnologia de alguma forma para descobrir o driver culpado?”

Na verdade é um pouco mais simples que isso, o Windows simplesmemte pega a imagem do driver que lançou uma exceção que não foi manipulada ou que voluntariamente derrubou o sistema por detectar alguma incoerência. Por isso, nem sempre o nome do driver exibido é de fato o nome do driver culpado. Se pensarmos no simples exemplo onde o driver MetralhadoraGiratoria.sys escreve onde não deveria corrompendo algum Pool de alocações, esse erro mais tarde pode ser detectado pelo driver Laranja.sys que, na hora de fazer uma alocação de memória, chama uma rotina de sistema que por sua vez chama a rotina KeBugCheckEx() ao detectar tal incoerência. Consegue adivinhar o nome do driver que aparecerá no BO?

Outras informações ainda podem ser obtidas da tela azul. Se é o nome do seu driver que aparece na tela, então você ainda pode obter o endereço da instrução onde a desgraça ocorreu. Em nosso exemplo o endereço é o 0xF8DD8A415 partir daí podemos chegar na função que estava sendo executada no momento da falha se tivermos o arquivo de mapa gerado pelo linker. Também é possível obter a data da imagem do arquivo e tirar aquela dúvida de que realmente era a versão certa que estava sendo executada. A data do arquivo é obtida no campo DateStamp e é expressa em um valor hexadecimal de 32 bits representando a quantidade de segundos deste meia noite de primeiro de Janeiro de 1970. Difícil mesmo é achar alguém com paciência suficiente para calcular isso diante de uma tela azul. Existem meios bem menos trabalhosos de descobrir que a culpa foi sua mesmo.

Na minha opinião, a informação mais relevante que a tela azul oferece é o Stop Code. Como o nome já sugere, Stop Code é um código que vai indicar o motivo da falha do sistema. você pode consultar a lista de Stop Codes neste link ou ainda dar uma olhada no arquivo C:\WinDDK\6001.18002\inc\api\BugCodes.h que vem no WDK.


Stop Codes vêm com até quatro parâmetros que trazem informações adicionais ao código de parada. A interpretação destes valores dependerá do código de falha, que em nosso exemplo é 0x7E. Consultando no link que informei a pouco, teremos a seguinte interpretação para os valores que nos foi apresentado.


Mas existe um jeito de salvar a BSOD ou não?

Tá tá tá… É que começo a escrever e acabo me empolgando. Mas enfim, quando uma falha crítica ocorre, o sistema cria um arquivo conhecido como Crash Dump. Existem três opções de crash dumps que podem ser geradas.

  • Dump Completo: Nesta opção, todo o conteúdo da memória física no momento da falha será copiado em um arquivo. Obviamente o tamanho deste arquivo será a quantidade de memória presente na máquina com um acréssimo de 1MB de header. Essa opção não aparece nas máquinas que possuam mais de 2GB de memória física, mas ainda é possivel configurar o dump completo sem utilizar essa interface gráfica escrevendo diretamente no registro. Esse método é também conhecido como “configurar na unha”. O dump completo é muito útil quando a informação presente em páginas de memória em User Space for relevante para o problema, tal como situações de Dead Locks. Se você não sabe o que significa User Space, este post pode ajudar.

  • Dump de Kernel: Aqui somente as páginas em System Space serão copiadas para disco. O tamanho deste arquivo vai variar dependendo de quantidade de memória física a máquina tem instalada, mas não existe uma proporção exata. Muito do balanceamento de páginas utilizado pelo gerenciador de memória virtual vai determinar o tamanho deste arquivo, mas ele fica pela ordem de 200MB num sistema com 4GB de memória total (já dá pra levar no pen drive). Essa opção é normalmente a mais viável, já que só carrega a informação mais relevante para um crash de sistema.

  • Dump Mínimo: Aqui um arquivo de 64KB será gerado para sistemas 32 bits ( 128KB para sistemas 64 bits). Neste arquivo temos apenas o Stop Code e seus parâmetros, a lista de drivers carregados no momento da falha, informações sobre o processo e thread corrente e o Call Stack da thread que causou a falha.

Na mesma janela onde você configura o reinicio automático do sistema, existem dois outros campos que vão configurar o tipo de dump desejado e o caminho onde este será gerado. Agora você já pode levar sua tela azul no coração e depurar onde você quiser. Em casa, no trabalho, no trêm, no metrô… Você pode ainda pedir que clientes enviem seus crash dumps para que você possa diagnosticar o problema ser ter que se deslocar através de rios e montanhas sob o frio e a chuva.

Tenho o Crash Dump, e agora?

Agora que você é um feliz proprietário de um maravilhoso arquivo de Crash Dump, o que mais você poderia querer da vida? Talvez ser capaz descobrir a causa do problema já seria um bom começo. Para isso vamos utilizar o depurador nativo do sistema operacional. Se você ainda não conhece o WinDbg, então dê uma olhada neste post para que você sabia do que estamos falando aqui.

Admitindo que você tenha Windbg instalado em sua máquina de desenvolvimento, e que este esteja com o servidor de símbolos configurado, tudo que temos a fazer agora é abrir o WinDbg, selecionar o ítem “Open Crash Dump…” no menu “File” e apontar o caminho do arquivo de dump que você copiou da pobre máquina que ousou rodar seu driver. O texto abaixo é o resultado exibido na janela de comandos quando o Crash Dump é aberto.

Microsoft (R) Windows Debugger Version 6.11.0001.404 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
 
 
Loading Dump File [Z:\Sources\MEMORY.DMP]
Kernel Summary Dump File: Only kernel address space is available
 
Symbol search path is: srv*
Executable search path is: 
Windows XP Kernel Version 2600 (Service Pack 3) UP Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS
Built by: 2600.xpsp.080413-2111
Machine Name:
Kernel base = 0x804d7000 PsLoadedModuleList = 0x80553fc0
Debug session time: Thu Jun 18 14:46:24.969 2009 (GMT-3)
System Uptime: 0 days 0:03:20.375
Loading Kernel Symbols
...............................................................
.........................................................
Loading User Symbols
 
Loading unloaded module list
...........
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************
 
Use !analyze -v to get detailed debugging information.
 
BugCheck 7E, {c0000005, f8d9f415, f8af1bb4, f8af18b0}
 
Probably caused by : Useless.sys ( Useless!DriverEntry+5 )
 
Followup: MachineOwner
---------

Agora se simplesmente executarmos o comando sugerido, já teremos uma boa descrição do que aconteceu com máquina que sofreu a falha crítica.

kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************
 
SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)
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.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: f8d9f415, The address that the exception occurred at
Arg3: f8af1bb4, Exception Record Address
Arg4: f8af18b0, Context Record Address
 
Debugging Details:
------------------
 
 
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced
memory at 0x%08lx. The memory could not be %s.
 
FAULTING_IP: 
Useless!DriverEntry+5 [z:\sources\driverentry\useless\useless.c @ 7]
f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0
 
EXCEPTION_RECORD:  f8af1bb4 -- (.exr 0xfffffffff8af1bb4)
ExceptionAddress: f8d9f415 (Useless!DriverEntry+0x00000005)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[1]: 00000000
Attempt to write to address 00000000
 
CONTEXT:  f8af18b0 -- (.cxr 0xfffffffff8af18b0)
eax=07263867 ebx=00000000 ecx=bb40e64e edx=1be10003 esi=e19feea8 edi=81eb41d0
eip=f8d9f415 esp=f8af1c7c ebp=f8af1c7c iopl=0         nv up ei ng nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010282
Useless!DriverEntry+0x5:
f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0  ds:0023:00000000=????????
Resetting default scope
 
PROCESS_NAME:  System
 
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory
at 0x%08lx. The memory could not be %s.
 
EXCEPTION_PARAMETER1:  00000001
 
EXCEPTION_PARAMETER2:  00000000
 
WRITE_ADDRESS:  00000000 
 
FOLLOWUP_IP: 
Useless!DriverEntry+5 [z:\sources\driverentry\useless\useless.c @ 7]
f8d9f415 c7050000000000000000 mov dword ptr ds:[0],0
 
BUGCHECK_STR:  0x7E
 
DEFAULT_BUCKET_ID:  NULL_DEREFERENCE
 
LAST_CONTROL_TRANSFER:  from 8057677f to f8d9f415
 
STACK_TEXT:  
f8af1c7c 8057677f 81eb41d0 81d46000 00000000 Useless!DriverEntry+0x5
 [z:\sources\driverentry\useless\useless.c @ 7]
f8af1d4c 8057688f 80000360 00000001 00000000 nt!IopLoadDriver+0x66d
f8af1d74 80534c02 80000360 00000000 823c68b8 nt!IopLoadUnloadDriver+0x45
f8af1dac 805c6160 b29accf4 00000000 00000000 nt!ExpWorkerThread+0x100
f8af1ddc 80541dd2 80534b02 00000001 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
 
 
FAULTING_SOURCE_CODE:  
     3: NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
     4:                      IN PUNICODE_STRING pusRegistryPath)
     5: {
     6:     //-f--> Diga olá à BSOD e vá se acostumando com ela...
>    7:     *(PVOID*)0x00000000 = 0;
     8:
     9:     //-f--> Não vamos viver para ver isso.
    10:     return STATUS_SUCCESS;
    11: }
 
 
SYMBOL_STACK_INDEX:  0
 
SYMBOL_NAME:  Useless!DriverEntry+5
 
FOLLOWUP_NAME:  MachineOwner
 
MODULE_NAME: Useless
 
IMAGE_NAME:  Useless.sys
 
DEBUG_FLR_IMAGE_TIMESTAMP:  4a3844ef
 
STACK_COMMAND:  .cxr 0xfffffffff8af18b0 ; kb
 
FAILURE_BUCKET_ID:  0x7E_Useless!DriverEntry+5
 
BUCKET_ID:  0x7E_Useless!DriverEntry+5
 
Followup: MachineOwner
---------

Se a máquina que está abrindo o arquivo de dump for a máquina de desenvolvimento do seu driver, o Windbg será capaz de automagicamente achar os fontes do seu driver e apontar a causa da falha com grandes detalhes. Então certifique-se que seu gerente não esteja por perto nesse momento. Isso já é muito mais informação do que você poderia obter simplesmente olhando para a tela azul do computador. Neste exemplo utilizei o driver de exemplo do post Getting Started para reproduzir a tela azul. Mas não se preocupe com isso, mesmo sendo um programador novato em drivers, uma das primeiras coisas que você aprenderá é como gerar telas azuis.

Mais uma vez espero ter ajudado.
Have fun!