[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

Apêndice A Programas Numéricos - Depurando

Esse capítulo descreve algumas dicas e truques para depurar programas numéricos que usam a GSL.


[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

A.1 Usando o gdb

Quaisquer erros reportados pela biblioteca são informados para a função gsl_error. Executando seus programas dentro do gdb e ajustando um ponto de paradas nessa função você pode automaticamente capturar quaisquer erros de biblioteca. Você pode adicionar um ponto de parada para toda seção colocando

break gsl_error

no seu arquivo ‘.gdbinit’ no diretório onde seu programa é iniciado.

Se o ponto de parada capturar um erro então você pode usar um backtrace (bt) para ver a árvore de chamada, e os argumentos que possivelmente causem o erro. Movendo-se para cima na função que faz a chamada você pode investigar os valores de variáveis naquele ponto. Aqui está um exemplo do programa fft/test_trap, que contém a seguinte linha,

status = gsl_fft_complex_wavetable_alloc (0, &complex_wavetable);

A função gsl_fft_complex_wavetable_alloc toma o comprimento de um FFT como seu primeiro argumento. Quando essa linha é executada um erro irá ser gerado pelo fato de o comprimento de um FFT ter de ser diferente de zero.

Para depurar esse problema iniciamos o gdb gdb, usando o arquivo ‘.gdbinit’ para definir o ponto de parada em gsl_error,

$ gdb test_trap

GDB is free software and you are welcome to distribute copies
of it under certain conditions; type "show copying" to see
the conditions.  There is absolutely no warranty for GDB;
type "show warranty" for details.  GDB 4.16 (i586-debian-linux), 
Copyright 1996 Free Software Foundation, Inc.

Breakpoint 1 at 0x8050b1e: file error.c, line 14.

Quando executamos o programa esse ponto de parada captura o erro e mostra a razão desse referido erro.

(gdb) run
Starting program: test_trap 

Breakpoint 1, gsl_error (reason=0x8052b0d 
    "length n must be positive integer", 
    file=0x8052b04 "c_init.c", line=108, gsl_errno=1) 
    at error.c:14
14        if (gsl_error_handler) 

O primeiro argumento de gsl_error é sempre uma sequência de caracteres descrevendo o erro. Agora podemos olhar no backtrace para ver o que causou o problema,

(gdb) bt
#0  gsl_error (reason=0x8052b0d 
    "length n must be positive integer", 
    file=0x8052b04 "c_init.c", line=108, gsl_errno=1)
    at error.c:14
#1  0x8049376 in gsl_fft_complex_wavetable_alloc (n=0,
    wavetable=0xbffff778) at c_init.c:108
#2  0x8048a00 in main (argc=1, argv=0xbffff9bc) 
    at test_trap.c:94
#3  0x80488be in ___crt_dummy__ ()

Podemos ver que o erro foi gerado na função gsl_fft_complex_wavetable_alloc quando foi chamada com um argumento n=0. A chamada original veio da linha 94 no arquivo ‘test_trap.c’.

Movendo-se para um nível acima da chamada original encontramos a linha que causou o erro,

(gdb) up
#1  0x8049376 in gsl_fft_complex_wavetable_alloc (n=0,
    wavetable=0xbffff778) at c_init.c:108
108   GSL_ERROR ("length n must be positive integer", GSL_EDOM);
(gdb) up
#2  0x8048a00 in main (argc=1, argv=0xbffff9bc) 
    at test_trap.c:94
94    status = gsl_fft_complex_wavetable_alloc (0,
        &complex_wavetable);

Dessa forma encontramos a linha que causou o problema. A partir desse ponto podemos também mostrar os valores de outras variáveis tais como complex_wavetable.


[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

A.2 Examinando registros de ponto flutuante

O conteúdo de registros de ponto flutuantes podem ser examinados usando o comando info float (nas plataformas suportadas).

(gdb) info float
     st0: 0xc4018b895aa17a945000  Valid Normal -7.838871e+308
     st1: 0x3ff9ea3f50e4d7275000  Valid Normal 0.0285946
     st2: 0x3fe790c64ce27dad4800  Valid Normal 6.7415931e-08
     st3: 0x3ffaa3ef0df6607d7800  Spec  Normal 0.0400229
     st4: 0x3c028000000000000000  Valid Normal 4.4501477e-308
     st5: 0x3ffef5412c22219d9000  Zero  Normal 0.9580257
     st6: 0x3fff8000000000000000  Valid Normal 1
     st7: 0xc4028b65a1f6d243c800  Valid Normal -1.566206e+309
   fctrl: 0x0272 53 bit; NEAR; mask DENOR UNDER LOS;
   fstat: 0xb9ba flags 0001; top 7; excep DENOR OVERF UNDER LOS
    ftag: 0x3fff
     fip: 0x08048b5c
     fcs: 0x051a0023
  fopoff: 0x08086820
  fopsel: 0x002b

Registros individuais podem ser examinados usando as variáveis $reg, onde reg é o nome do registro.

(gdb) p $st1 
$1 = 0.02859464454261210347719

[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

A.3 Tratando excessões em ponto flutuante

É possível parar o programa sempre que um SIGFPE de excessão em ponto flutuante ocorrer. Isso pode ser útil para encontrar a causa de um infinito inesperado ou de um NaN. As atuais configurações do controlador podem ser mostradas com o comando info signal SIGFPE.

(gdb) info signal SIGFPE
Signal  Stop  Print  Pass to program Description
SIGFPE  Yes   Yes    Yes             Arithmetic exception

A menos que o programa use um controlador de sinal a configuração padrão pode ser mudada de forma que SIGFPE não seja informado ao programa, uma vez que pode causar o encerramento do mesmo. O comando handle SIGFPE stop nopass evita isso.

(gdb) handle SIGFPE stop nopass
Signal  Stop  Print  Pass to program Description
SIGFPE  Yes   Yes    No              Arithmetic exception

Dependendo da plataforma pode ser necessário instruir o kernel para gerar sinais para excessões em ponto flutuante. Para programas usando GSL isso pode ser alcançado usando a variável de ambiente GSL_IEEE_MODE juntamente com a função gsl_ieee_env_setup como descrito em veja seção Aritmética em Ponto Flutuante IEEE.

(gdb) set env GSL_IEEE_MODE=double-precision

[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

A.4 Opções de alerta do GCC para programas numéricos

Escrevendo programas numéricos confiáveis em C requer grande cuidado. O as seguintes opções de alerta do GCC são recomendadas quando compilando programas numéricos:

gcc -ansi -pedantic -Werror -Wall -W 
  -Wmissing-prototypes -Wstrict-prototypes 
  -Wconversion -Wshadow -Wpointer-arith 
  -Wcast-qual -Wcast-align 
  -Wwrite-strings -Wnested-externs 
  -fshort-enums -fno-common -Dinline= -g -O2

Para detalhes sobre cada opção consulte o manual Using and Porting GCC. A seguinte tabela fornece uma breve explanação de quais tipos de erro essas opções capturam.

-ansi -pedantic

Usando ANSI C, e rejeitando quaisquer extensões não ANSI. Esses sinalizadores ajudam na escrita de programas portáveis que irão compilar nos outros sistemas.

-Werror

considera alertas como sendo erros, de forma que a compilação pare. Isso evita alertas rolando para o topo da tela e sendo perdidos. Você não estará apto a compilar o programa até estar completamente livre de alertas.

-Wall

Habilita um conjunto de alertas para problemas comuns de programação. Você precisa -Wall, mas isso não é suficiente por si só.

-O2

Habilita otimização. Os alertas por variáveis não inicializadas em -Wall garantem que o otimizador analize o código. Se não houver otimizações então esses alertas não são gerados.

-W

Habilita alertas extras não inclídos em -Wall, tais como valores de retorno omitidos e comparações entre inteiros sinalizados e inteiros não sinalizados.

-Wmissing-prototypes -Wstrict-prototypes

Alerta se houver qualquer protótipo inconsistente ou omitido. Sem protótipos é mais difícil detectar problemas com argumentos incorretos.

-Wconversion

O principal uso dessa opção é alertar sobre conversões de inteiro sinalizado para inteiro não sinalizado. Por exemplo, unsigned int x = -1. Se você precisa executar tal conversão você pode usar uma conversão expçícita.

-Wshadow

Alerta sempre que uma variável local oculta outra variável local. Se duas variáveis possuem o mesmo nome então é uma potencial fonte de confusão.

-Wpointer-arith -Wcast-qual -Wcast-align

Essas opções alertam se você tentar fazer aritmética de apontadores para tipos que não possuem um tamanho, tais como void, se você remove coverção de const a partir de um apontador, ou se você converte um apontador para um tipo que tem um tamanho diferente, causando um alinhamento inválido.

-Wwrite-strings

Essa opção fornece a constantes do tipo string um qualificador const de forma que irá ser um erro em tempo de compilação tentar sobrescrevê-la.

-fshort-enums

Essa opção torna o tipo de enum tão curto quanto possível. Normalmente isso torna um enum diferente a partir de um int. Consequentemente quaisquer tentativas de atribuir um apontador a um inteiro para um apontador para um enum irá gerar um alerta de alinhamento de conversão.

-fno-common

Essa opção evita que variáveis globais serjam simultâneamente definidas em diferentes arquivos objetos (você recebe um erro na hora da linkagem). Tal como uma variável deve ser definida em um arquivo e referenciada em outro arquivo com uma declaração extern.

-Wnested-externs

Alerta se uma declaração extern for encontrada dentro de uma função.

-Dinline=

A palavra chave inline não é parte do ANSI C. Dessa forma se você deseja usar -ansi com um programa que usa funções inline você pode usar essa definição de préprocessador para remover as palavras chave inline.

-g

Sempre faz sentido colocar símbolos de depuração no executável de forma que você possa depurá-lo usando o gdb. O único efeito de símbolos de depuração é aumentar o tamanho do arquivo, e você pode usar o comando strip para removêlos mais tarde se necessário.


[ << ] [ < ] [ Acima ] [ > ] [ >> ]         [Topo] [Conteúdo] [Índice] [ ? ]

A.5 Referências e Leituras Adicionais

Os seguintes livros são leituras essenciais para qualquer um que escreve e depura programas numéricos com o GCC e com o GDB.

Para um tutorial de introdução ao compilador C GNU e programas relacionados, veja


[ << ] [ >> ]           [Topo] [Conteúdo] [Índice] [ ? ]

Esse documento foi gerado em 23 de Julho de 2013 usando texi2html 5.0.