A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Tutoriais e Programação

Assembly - Tratamento de erros

Dom

21

Jun

2009


18:48

(1 voto de 5.00) 


Nível avançado O tratamento adequado de erros é uma ciência à parte, talvez até mais complexa do que a própria programação. Durante a programação, toda a atenção está voltada para eventos previsíveis, o objetivo do programa. No tratamento de erros, no entanto, lidamos com eventos indesejados, quase que "imprevisíveis". É como brincar com uma bola de cristal wink

Neste texto serão abordados apenas os mecanismos envolvidos no caso de ocorrência de erros. Serve apenas como orientação para a aplicação de um sistema eficiente de um tratamento estruturado de erros, indispensável para garantir a robustez e a consistência de aplicativos mais elaborados.

Erros

Denominamos erro ou exceção um evento que ocorre durante a execução de um programa e que requer uma execução fora do fluxo normal de controle. Existem exceções de hardware e de software. Exceções de hardware podem ser resultado da execução de sequências de instruções que tentam acessar endereços de memória inválidos ou efetuar uma divisão por zero. Exceções de software podem ser resultado do uso de parâmetros com valores inválidos ou serem iniciadas explicitamente através do uso da função RaiseException. Em todo caso, são "pecados" cometidos contra o sistema - o "mau comportamento" que gera uma exceção pode comprometer o funcionamento do sistema operacional o qual, para se precaver, interrompe o processo e apresenta uma mensagem de erro do tipo:

Mensagem de erro

Tratamento Estruturado de Exceções

A API do Windows oferece um mecanismo próprio para o tratamento de exceções geradas tanto por hardware quanto por software: é o chamado tratamento estruturado de exceções. Este mecanismo permite um controle absoluto no tratamento de exceções, oferece suporte para depuradores (debuggers) e pode ser usado em todas as linguagens de programação e em todo tipo de máquina.

A API do win32 também permite a manipulação de conclusão. Este tipo de manipulação garante que, sempre que uma área de código vigiado for executada, um bloco de código de conclusão específico também seja executado, tenha ou não ocorrido uma exceção. O código de conclusão é executado independentemente da maneira como o fluxo de controle tenha saído da área vigiada. Por exemplo, o manipulador de conclusão pode garantir que determinadas tarefas, como as de "faxina", sejam cumpridas mesmo que ocorra uma exceção ou algum outro erro durante a execução do código da área vigiada.

Se você programa em "C/C++" ou "Delphi", com certeza conhece as plavras-chave try, except e finally. Try (tentar) identifica a área vigiada, except (exceto) identifica o manipulador de exceções e finally (no final) o manipulador de conclusão. Pondo em linguagem corrente seria o mesmo que "tente executar o código desta área; caso ocorra algum erro, faça uso do manipulador de exceções; no final, acione o manipulador de conclusão". Esta estrutura de atendimento permite que se programe aplicativos mais robustos e confiáveis e é conhecida como SEH ou Structured Exception Handling.

Tipos de Exceção

Como já foi citado, as exceções podem ser iniciadas pelo hardware ou pelo software. Tanto uma quanto a outra podem ocorrer no modo kernel (no "interior" do sistema) ou no código modo usuário (o código que você programou). Daí os tipos de exceção:

  • de hardware no modo kernel
  • de hardware no modo usuário
  • de software no modo kernel
  • de software no modo usuário

Estruturas das Exceções

Quando ocorre uma exceção, o processador pára a execução no ponto onde ela ocorreu e transfere o controle para o sistema. A primeira providência do sistema é a de guardar o "estado de máquina" da linha de processo atual (o thread atual) e as informações que descrevem a exceção. Logo a seguir, o sistema procura por um manipulador de exceções para tratar o erro.

O "estado de máquina" é armazenado numa estrutura do tipo CONTEXT. Esta informação, denominada registro do contexto (context record), vai permitir que o sistema continue a execução a partir do ponto gerador da exceção se esta for tratada com sucesso.

A descrição da exceção, chamada de registro da exceção (exception record), é armazenada numa estrutura do tipo EXCEPTION_RECORD.

Devido ao fato das informações referentes à máquina e as informações referentes à exceção serem guardadas em estruturas diferentes, o mecanismo de tratamento de exceções torna-se portável para as mais diversas plataformas.

A estrutura EXCEPTION_RECORD

A definição desta estrutura é:

typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; DWORD ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS]; }

O membro DWORD ExceptionFlags pode ser zero, indicando uma exceção recuperável, ou ser EXCEPTION_NONCONTINUABLE, que indica uma exceção irrecuperável. Qualquer tentativa de continuar a execução após uma exceção irrecuperável causa uma nova exceção do tipo EXCEPTION_NONCONTINUABLE_EXCEPTION.

O membro struct _EXCEPTION_RECORD *ExceptionRecord aponta para uma estrutura do tipo EXCEPTION_RECORD. Os registros de exceção podem ser encadeados para fornecerem informações adicionais quando ocorrer uma exceção aninhada.

O membro PVOID ExceptionAddress especifica o endereço onde ocorreu a exceção.

O membro DWORD NumberParameters especifica o número de parâmetros associados à exceção. Este é o número de elementos definidos na matriz (array) ExceptionInformation.

O membro DWORD ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS] especifica uma matriz com argumentos de 32 bits adicionais que descrevem a exceção. A função RaiseException pode especificar esta matriz de argumentos. Para a maior parte dos códigos de exceção os elementos da matriz estão indefinidos.

O membro DWORD ExceptionCode indica a razão pela qual a exceção ocorreu. Os valores possíveis estão na tabela abaixo:

EXCEPTION_ACCESS_VIOLATIONO thread tentou ler/escrever num endereço virtual ao qual não tinha acesso.
EXCEPTION_ARRAY_BOUNDS_EXCEEDEDO thread tentou acessar um elemento de array fora dos limites e o hardware possibilita a checagem de limites.
EXCEPTION_BREAKPOINTFoi encontrado um ponto de parada (breakpoint).
EXCEPTION_DATATYPE_MISALIGNMENTO thread tentou ler/escrever dados desalinhados em hardware que não oferece alinhamento. Por exemplo, valores de 16 bits precisam ser alinhados em limites de 2 bytes; valores de 32 bits em limites de 4 bytes, etc.
EXCEPTION_FLT_DENORMAL_OPERANDUm dos operandos numa operação de ponto flutuante está desnormatizado. Um valor desnormatizado é um que seja pequeno demais para poder ser representado no formato de ponto flutuante padrão.
EXCEPTION_FLT_DIVIDE_BY_ZEROO thread tentou dividir um valor em ponto flutuante por um divisor em ponto flutuante igual a zero.
EXCEPTION_FLT_INEXACT_RESULTO resultado de uma operação de ponto flutuante não pode ser representado como uma fração decimal exata.
EXCEPTION_FLT_INVALID_OPERATIONQualquer operação de ponto flutuante não incluída na lista.
EXCEPTION_FLT_OVERFLOWO expoente de uma operação de ponto flutuante é maior que a magnitude permitida pelo tipo correspondente.
EXCEPTION_FLT_STACK_CHECKA pilha ficou desalinhada ("estourou" ou "ficou abaixo") como resultado de uma operação de ponto flutuante.
EXCEPTION_FLT_UNDERFLOWO expoente de uma operação de ponto flutuante é menor que a magnitude permitida pelo tipo correspondente.
EXCEPTION_ILLEGAL_INSTRUCTIONO thread tentou executar uma instrução inválida.
EXCEPTION_IN_PAGE_ERRORO thread tentou acessar uma página que não estava presente e o sistema não foi capaz de carregar a página. Esta exceção pode ocorrer, por exemplo, se uma conexão de rede é perdida durante a execução do programa via rede.
EXCEPTION_INT_DIVIDE_BY_ZEROO thread tentou dividir um valor inteiro por um divisor inteiro igual a zero.
EXCEPTION_INT_OVERFLOWO resultado de uma operação com inteiros causou uma transposição (carry) além do bit mais significativo do resultado.
EXCEPTION_INVALID_DISPOSITIONUm manipulador (handle) de exceções retornou uma disposição inválida para o tratador de exceções. Uma exceção deste tipo nunca deveria ser encontrada em linguagens de médio/alto nível.
EXCEPTION_NONCONTINUABLE_EXCEPTIONO thread tentou continuar a execução após a ocorrência de uma exceção irrecuperável.
EXCEPTION_PRIV_INSTRUCTIONO thread tentou executar uma instrução cuja operação não é permitida no modo de máquina atual.
EXCEPTION_SINGLE_STEPUm interceptador de passos ou outro mecanismo de instrução isolada sinalizou que uma instrução foi executada.
EXCEPTION_STACK_OVERFLOWO thread esgotou sua pilha (estouro de pilha).

Quando ocorre uma exceção no código modo usuário o sistema realiza a seguinte procura por um manipulador de exceções:

  1. Primeiro, o sistema tenta notificar o depurador do processo (debugger), se este existir.
  2. Se o processo não estiver sendo depurado ou se o depurador não tratar a exceção, o sistema tenta localizar um manipulador de exceções emolduradas (frame-based) fazendo uma procura nas molduras da pilha do thread onde ocorreu a exceção. O sistema procura primeiro na moldura atual da pilha, depois segue procurando em molduras de pilha precedentes. (Leia sobre molduras de pilha logo abaixo).
  3. Se não foi possível encontrar um manipulador emoldurado ou se nenhum deles tratar a exceção, o sistema faz uma segunda tentativa de notificar o depurador do processo.
  4. Se o processo não estiver sendo depurado ou se o depurador associado não tratar a exceção, o sistema fornece um tratamento padrão de acordo com o tipo de exceção. Para a maioria das exceções, a ação padrão é chamar a função ExitProcess.

Quando ocorre uma uma exceção no código modo kernel, o sistema procura nas molduras da pilha do kernel na tentativa de localizar um manipulador de exceções. Se o manipulador não puder ser encontrado ou se nenhum dos manipuladores tratar a exceção, o sistema encerra sua operação como se a função ExitProcess tivesse sido chamada.

Informações adicionais