Pergunta

Então, preciso de ajuda.Estou trabalhando em um projeto em C++.No entanto, acho que de alguma forma consegui corromper minha pilha.Isso se baseia no fato de que eu adicionei um std::string a uma classe e atribuindo-lhe um valor de outra std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

trava no meu sistema com um despejo de pilha.Então basicamente eu preciso parar e examinar todo o meu código e gerenciamento de memória e descobrir onde estraguei tudo.A base de código ainda é pequena (cerca de 1000 linhas), então isso é fácil de fazer.

Ainda assim, estou perdendo a cabeça com esse tipo de coisa, então pensei em jogá-lo fora.Estou em um sistema Linux e dei uma olhada valgrind, e embora não saiba completamente o que estou fazendo, relatou que o std::stringo destruidor de era um free inválido.Tenho que admitir que peguei o termo 'Heap Corruption' em uma pesquisa no Google;quaisquer artigos de uso geral sobre esse tipo de coisa também seriam apreciados.

(Em antes rm -rf ProjectDir, faça novamente em C# :D)

EDITAR:Não deixei isso claro, mas o que estou pedindo são conselhos para diagnosticar esse tipo de problema de memória.Eu sei que o material std::string está certo, então é algo que fiz (ou um bug, mas não há problema com o Select).Tenho certeza de que poderia verificar o código que escrevi e vocês, pessoas muito inteligentes, perceberiam o problema rapidamente, mas quero adicionar esse tipo de análise de código à minha 'caixa de ferramentas', por assim dizer.

Foi útil?

Solução

Estes são mecanismos relativamente baratos para possivelmente resolver o problema:

  1. Fique de olho no meu pergunta sobre corrupção de pilha - Estou atualizando com as respostas conforme elas vão surgindo.O primeiro foi equilibrar new[] e delete[], mas você já está fazendo isso.
  2. Dar valgrind mais uma tentativa;é uma ferramenta excelente e gostaria que estivesse disponível no Windows.Eu desacelero seu programa apenas pela metade, o que é muito bom em comparação com os equivalentes do Windows.
  3. Pense em usar o Ferramentas de desempenho do Google como um substituto malloc/novo.
  4. Você limpou todos os seus arquivos de objetos e começou de novo?Talvez o seu arquivo make seja ..."abaixo do ideal"
  5. Você não está assert()o suficiente em seu código.Como posso saber disso sem ter visto?Como usar fio dental, ninguém assert()é o suficiente em seu código.Adicione uma função de validação para seus objetos e chame-a no início e no final do método.
  6. Você é compilando -wall?Se não, faça-o.
  7. Encontre uma ferramenta de fiapos como PC-Lint.Um aplicativo pequeno como o seu pode caber no Demonstração do PC-lint página, o que significa que não há compra para você!
  8. Verifique se você está anulando os ponteiros depois de excluí-los.Ninguém gosta de um ponteiro pendurado.O mesmo show com ponteiros declarados, mas não alocados.
  9. Pare de usar matrizes.Use um vetor em vez de.
  10. Não use ponteiros brutos.Use um ponteiro inteligente.Não use auto_ptr!Essa coisa é...surpreendente;sua semântica é muito estranha.Em vez disso, escolha um dos Aumente os ponteiros inteligentes, ou algo fora de a biblioteca Loki.

Outras dicas

Certa vez, tivemos um bug que escapou de todas as técnicas regulares, valgrind, purificar etc.A falha só aconteceu em máquinas com muita memória e em grandes conjuntos de dados de entrada.

Eventualmente, nós o rastreamos usando pontos de observação do depurador.Vou tentar descrever o procedimento aqui:

1) Encontre a causa da falha.Pelo seu código de exemplo, parece que a memória de "exampleString" está sendo corrompida e, portanto, não pode ser gravada.Vamos continuar com essa suposição.

2) Defina um ponto de interrupção no último local conhecido em que "exampleString" foi usado ou modificado sem nenhum problema.

3) Adicione um ponto de observação ao membro de dados de 'exampleString'.Com minha versão do g++, a string é armazenada em _M_dataplus._M_p.Queremos saber quando esse membro de dados muda.A técnica GDB para isso é:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

Obviamente estou usando Linux com g++ e gdb aqui, mas acredito que os pontos de observação de memória estão disponíveis na maioria dos depuradores.

4) Continue até que o ponto de observação seja acionado:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

O gdb where O comando fornecerá um rastreamento mostrando o que resultou na modificação.Esta é uma modificação perfeitamente legal e, nesse caso, continue - ou, se tiver sorte, será a modificação devido à corrupção da memória.Neste último caso, agora você poderá revisar o código que está realmente causando o problema e esperamos corrigi-lo.

A causa do nosso bug foi um acesso ao array com índice negativo.O índice foi o resultado da conversão de um ponteiro para um módulo 'int' do tamanho do array.O bug foi perdido por valgrind et al.já que os endereços de memória alocados ao executar essas ferramentas nunca foram "> MAX_INT"e, portanto, nunca resultou em um índice negativo.

Ah, se você quiser saber como depurar o problema, é simples.Primeiro, pegue uma galinha morta.Então, comece a sacudi-lo.

Sério, não encontrei uma maneira consistente de rastrear esse tipo de bug.Como existem tantos problemas potenciais, não há uma lista de verificação simples para analisar.No entanto, eu recomendaria o seguinte:

  1. Fique confortável com um depurador.
  2. Comece a vasculhar o depurador para ver se consegue encontrar algo que pareça suspeito.Verifique especialmente para ver o que está acontecendo durante o exampleString = hello; linha.
  3. Verifique se ele está realmente travando no exampleString = hello; linha, e não ao sair de algum bloco envolvente (o que pode causar o disparo dos destruidores).
  4. Verifique qualquer mágica de ponteiro que você possa estar fazendo.Aritmética de ponteiro, fundição, etc.
  5. Verifique todas as suas alocações e desalocações para ter certeza de que elas correspondem (sem desalocações duplas).
  6. Certifique-se de não retornar nenhuma referência ou ponteiro para objetos na pilha.

Há muitas outras coisas para tentar também.Tenho certeza de que outras pessoas também contribuirão com ideias.

Alguns lugares para começar:

Se você estiver no Windows e usar o visual C++ 6 (espero em Deus que ninguém ainda o use hoje em dia), a implementação de std::string não é thread-safe e pode levar a esse tipo de coisa.

Aqui está um artigo que encontrei que explica muitas das causas comuns de vazamentos e corrupção de memória.

No meu local de trabalho anterior, usamos o Compuware Boundschecker para ajudar com isso.É comercial e muito caro, então pode não ser uma opção.

Aqui estão algumas bibliotecas gratuitas que podem ser de alguma utilidade

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

Espero que ajude.A corrupção da memória é um lugar horrível para se estar!

Pode ser corrupção de heap, mas é igualmente provável que seja corrupção de pilha.Jim está certo.Nós realmente precisamos de um pouco mais de contexto.Essas duas fontes não nos dizem muito isoladamente.Pode haver uma série de coisas causando isso (que é a verdadeira alegria do C/C++).

Se você se sentir confortável em postar seu código, você pode até colocar tudo em um servidor e postar um link.Tenho certeza de que você receberá muito mais conselhos dessa forma (alguns deles, sem dúvida, não relacionados à sua pergunta).

O código era simplesmente um exemplo de onde meu programa estava falhando (ele estava alocado na pilha, Jim).Na verdade, não estou procurando 'o que fiz de errado', mas sim 'como faço para diagnosticar o que fiz de errado'.Ensine um homem a pescar e tudo mais.Embora olhando para a pergunta, não deixei isso claro o suficiente.Graças a Deus pela função de edição.:')

Além disso, eu corrigi o problema std::string.Como?Substituindo-o por um vetor, compilando e substituindo a string novamente.Isto era travando consistentemente lá, e isso foi corrigido, embora ... não pudesse.Há algo desagradável aí e não tenho certeza do quê.Eu queria verificar a única vez em que aloquei memória manualmente no heap:

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

e excluí-lo:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

Eu não aloquei um array 2D com C++ antes.Parece funcionar.

Além disso, eu corrigi o problema std::string.Como?Substituindo-o por um vetor, compilando e substituindo a string novamente.Ele estava travando consistentemente lá, e isso foi corrigido, embora... não pudesse.Há algo desagradável aí e não tenho certeza do quê.

Parece que você realmente sacudiu uma galinha com isso.Se você não sabe por que está funcionando agora, então ainda está quebrado e é praticamente garantido que irá mordê-lo novamente mais tarde (depois de adicionar ainda mais complexidade).

Execute o Purificar.

É uma ferramenta quase mágica que irá relatar quando você está destruindo a memória que não deveria tocar, vazando memória por não liberar coisas, liberando duas vezes, etc.

Funciona no nível do código da máquina, então você nem precisa ter o código-fonte.

Uma das teleconferências de fornecedores mais divertidas que já participei foi quando o Purify encontrou um vazamento de memória em seu código e pudemos perguntar: "é possível que você não esteja liberando memória em sua função foo()" e ouvimos o espanto em suas vozes.

Eles pensaram que estávamos depurando deuses, mas então contamos a eles o segredo para que pudessem executar o Purify antes que precisássemos usar o código deles.:-)

http://www-306.ibm.com/software/awdtools/purify/unix/

(É muito caro, mas eles têm um download de avaliação gratuito)

Uma das técnicas de depuração que utilizo com frequência (exceto em casos de extrema estranheza) é dividir para conquistar.Se o seu programa atualmente falha com algum erro específico, divida-o ao meio de alguma forma e veja se ele ainda apresenta o mesmo erro.Obviamente o truque é decidir onde dividir o seu programa!

Seu exemplo fornecido não mostra contexto suficiente para determinar onde pode estar o erro.Se alguém tentasse seu exemplo, funcionaria bem.Então, em seu programa, tente remover o máximo de coisas extras que você não nos mostrou e veja se funciona.Nesse caso, adicione o outro código aos poucos, até que ele comece a falhar.Então, o que você acabou de adicionar é provavelmente o problema.

Observe que se o seu programa for multithread, provavelmente você terá problemas maiores.Caso contrário, você poderá restringi-lo dessa maneira.Boa sorte!

Além de ferramentas como Boundschecker ou Purify, sua melhor aposta para resolver problemas como esse é ficar realmente bom na leitura de código e familiarizar-se com o código no qual você está trabalhando.

A corrupção de memória é uma das coisas mais difíceis de solucionar e geralmente esses tipos de problemas são resolvidos passando horas/dias em um depurador e percebendo algo como "ei, o ponteiro X está sendo usado depois de ter sido excluído!".

Se ajudar, é algo em que você melhora à medida que ganha experiência.

Sua alocação de memória para o array parece correta, mas certifique-se de verificar também todos os locais onde você acessa o array.

Seu código, como posso ver, não contém erros.Como foi dito, é necessário mais contexto.

Se você ainda não tentou, instale o gdb (o depurador gcc) e compile o programa com -g.Isto irá compilar símbolos de depuração que o gdb pode usar.Depois de instalar o gdb, execute-o com o programa (gdb). Esse é um cheatsheat útil para usar o gdb.

Defina um ponto de interrupção para a função que está produzindo o bug e veja qual é o valor de exampleString.Faça também o mesmo para qualquer parâmetro que você esteja passando para exampleString.Isso deve pelo menos informar se std::strings são válidos.

Eu encontrei a resposta de Este artigo para ser um bom guia sobre ponteiros.

Pelo que posso dizer, seu código está correto.Supondo que exampleString seja um std::string que tenha escopo de classe como você descreve, você deverá ser capaz de inicializá-lo/atribuí-lo dessa maneira.Talvez haja algum outro problema?Talvez um trecho de código real ajudasse a contextualizar.

Pergunta:exampleString é um ponteiro para um objeto string criado com new?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top