Meu erro favorito de C/C++: Falha de segmentação

Resolvi criar esse curto post para comentar sobre qual o meu erro favorito na linguagem de programação C, que na minha universidade é relativamente comum devido a ela ser a linguagem usada para introduzir os alunos ao universo da programação.

O quê?

O erro falha de segmentação acontece, principalmente, quando você desreferencia um ponteiro inválido. Quando uma falha de segmentação acontece, o programa é encerrado imediatamente.

Por quê?

Esse é o meu erro favorito, pois quando esse é o erro com o qual você se depara, provavelmente não irá gastar muito tempo com o mesmo.

Tudo que você precisa para identificar exatamente esse erro é (1) identificar o conjunto de ações que ocasionará a falha de segmentação, (2) executar o programa através de um depurador e (3) fazer o backtrace, que lhe dará toda a corrente de chamadas de funções que provocou a falha de segmentação, apontando exatamente para a linha de código que ocasionou o problema, além de outras informações úteis como o valor de cada variável.

Perspectiva

Talvez ironicamente o meu erro favorito é o que eu menos presencio. Dependendo de como eu veja, isso pode ser algo bom (eu me tornei um programador melhor) ou algo ruim (as coisas que eu gosto vão embora), mas uma informação que contribui para o valor desse post é o provável verdadeiro motivo desse fato: Eu uso C++. Com C++, eu raramente utilizo ponteiros (que normalmente são substituídos por referências e, quando gerenciamento manual de memória torna-se necessário, por smart pointers), eliminando grande parte das falhas de segmentação.

Uma questão de perspectiva também é que para as pessoas que não conhecem um depurador, esse pode ser um dos erros mais chatos, não o favorito (como eu acho que seja).

O contra-ataque!

(essa seção foi adicionada a pedido dos leitores, 2 dias após o artigo ser publicado)

Eu criei o seguinte código-fonte que apresenta um erro. De acordo com o padrão de C++ o seu comportamento é indefinido e o compilador tem a liberdade de fazer elefantes rosas aparecerem na tela do seu computador sem perder o título de “compilador de C++”. Entretanto, o que provavelmente irá ocorrer é uma falha de segmentação. Baixe o código-fonte e siga para a próxima etapa, onde irei mostrar como erros desse tipo podem ser descobertos de forma simples.

EDIT(2014/02/23): Eu sempre achei que o texto abaixo seria confuso, pois não representava precisamente como uma sessão do gdb realmente é, então, devido (1) a isso e (2) a descoberta do asciinema, resolvi adicionar mais esse pequeno texto no artigo. Basicamente, recomendo que você veja esse asciicast no asciinema antes de prosseguir para os detalhes.

O primeiro passo é compilar o programa junto com as informações de depuração. Leia o manual do seu compilador para descobrir como fazer isso. Caso utilize uma IDE ou qualquer outro intermediário para compilar o programa, provavelmente isso pode ser feito escolhendo um perfil de debug. No gcc, a opção que habilita os símbolos de depuração é a opção “-g”:

Após isso você pode executar o programa através de um depurador e ficar brincando com o programa até o erro acontecer. Aqui, utilizo o gdb, sem nenhuma interface:

Após o gdb iniciado, você presenciará sua saída padrão, que é algo parecido com o texto a seguir:

Dentro da interface do gdb, execute o comando “run” e brinque com o programa até o erro acontecer. No meu caso a saída foi essa:

Voltando a interface do gdb, após o programa ter “crashado”, os dois principais comandos para navegar na pilha de execução são “backtrace” e “frame”, que, respectivamente, mostram a pilha de execução e escolhem um frame/instante na pilha/linha-de-execução.

E assim descobrimos, usando o gdb, que o programa travou exatamente na linha 42, após a função ler_clientes ter sido chamada pela função main. Podemos usar o comando “print” para imprimir o valor de variáveis/expressões que sejam válidas no contexto.

E assim descobrimos que há um ponteiro inválido na memória e o programa “crashou” quando tentamos acessá-lo. As duas opções a partir de agora são (1) examinar o código e descobrir que código inseriu um ponteiro inválido em memória ou (2) continuar usando o auxílio do gdb para descobrir a fonte do erro. Como o código é muito simples, é fácil ver que eu “esqueci” de inicializar o ponteiro dos objetos que são inseridos na variável “clientes” (isso, é claro, só é fácil depois que já descobrimos qual é o erro com a ajuda do gdb).

Caso você queira continuar a usar a ajuda do gdb para inspecionar o estado do programa em tempo de execução, os próximos comandos que seriam lhe úteis seriam “watch”, “break”, “step” e “continue”.

Para referência, deixo abaixo o resto de minha sessão do gdb, onde continuo examinando o programa até descobrir que código faz a inserção de um ponteiro inválido. E recomendo que você já tenha executado o gdb nesse momento, ou então irá ver esse monte de texto e não vai entender nada, achando que é algo muito difícil…

Algo que quero destacar é que o código-fonte é de “um programa em desenvolvimento” e, consequentemente, bem curto. Mas reflita sobre o que aconteceria se ele fosse grande (4000 linhas ou algo parecido). Usando o gdb, o processo de encontrar o erro seria o mesmo, bem fácil.

Tags:,

9 responses to “Meu erro favorito de C/C++: Falha de segmentação”

  1. Rodrigo Paes says :

    Vinícius, que tal escrever um tutorial explicando e dando exemplos dos 03 passos que você colocou acima? Ou seja, algo como “Usando o GDB para identificar a origem das falhas de segmentação em C”

  2. Sales says :

    Também acho que um exemplo de uso do GDB seria interessante.

  3. Vinicius Lisboa says :

    Beleza, Vinicius.

    Tive o problema de falha de segmentação, segui os passo aqui. Porém a saída foi esta:

    a.out* FTDI* FTDISerial* FTDISerial.c FTDISerial.c~ FTDISerial.o* ufdtd* ufdtd.c ufdtd.c~ ufdtdNP3.c ufdtdNP3.c~ uftd2d.c
    vllisboa@vllisboa-Inspiron-5421:~/Documentos/computacao_cientifica/UFTDT$ gdb a.out
    GNU gdb (Ubuntu 7.7-0ubuntu3.1) 7.7
    Copyright (C) 2014 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law. Type “show copying”
    and “show warranty” for details.
    This GDB was configured as “x86_64-linux-gnu”.
    Type “show configuration” for configuration details.
    For bug reporting instructions, please see:
    .
    Find the GDB manual and other documentation resources online at:
    .
    For help, type “help”.
    Type “apropos word” to search for commands related to “word”…
    Lendo símbolos de a.out…concluído.
    (gdb) run
    Starting program: /home/vllisboa/Documentos/computacao_cientifica/UFTDT/a.out
    Escrita do ficheiro terminada

    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000000000 in ?? ()
    (gdb) backtrace
    #0 0x0000000000000000 in ?? ()
    #1 0x0000000000000000 in ?? ()
    (gdb) frame
    #0 0x0000000000000000 in ?? ()
    (gdb) Quit
    (gdb)
    #0 0x0000000000000000 in ?? ()
    (gdb) Quit
    A debugging session is active.

    Inferior 1 [process 22917] will be killed.

    Quit anyway? (y or n) y

    E não sei como prosseguir, caso tenha uma ideia, por favor me ajude.

    Abraços

  4. gabi says :

    poxa vida, eu sou um lixo mesmo, se isso é um erro fácil não quero nem imaginar o difícil, parabéns pra quem é da área… meu curso felizmente pra mim só tem prog. I e AEDS I

    • Vinipsmaker says :

      Eu tenho facilidade com CLIs, talvez seja só isso. Talvez você mude de opinião verificando GUIs (ou mesmo TUIs) para o GDB. Ou talvez experimentando e brincando com o problema, você perceba. Sei lá.

      Comentário do meu amigo:

      ahh tem nego na computação se sentido merda desdo 3 periodo e ainda nao saiu

      Recentemente, achei legal ver uma galera com mais conhecimento declarando a dificuldade que tem e mostrando só que é esforço, tipo o Niall ao anunciar versão nova da AFIO:

      This release took far longer than I originally intended mainly due to
      the
      extensive debugging of weird filing system races, and for six weeks
      now I’ve
      been working till 5am every night after my day job.

      Mas não veja minha resposta como tentativa motivacional. Estou tentando motivar ninguém com essa resposta e caso você tenha visto dessa forma, talvez tenha me interpretado mal.

  5. vn says :

    Opa, blz?
    seguindo tutorial do gdb apareceu isso:

    Program received signal SIGSEGV, Segmentation fault.
    0xb7e90a0e in _int_free (av=0xb7fc0440 , p=0x804b000, have_lock=0)
    at malloc.c:4085

    alguma idéia do que seja?

Comentários (with MarkDown support)

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: