Eu tenho um problema do tipo "Gato de Schroedinger" aqui - meu programa (na verdade, o conjunto de testes do meu programa, mas um programa mesmo assim) está travando, mas apenas quando construído no modo de lançamento e apenas quando iniciado a partir da linha de comando . Por meio da depuração do homem das cavernas (ou seja, mensagens de printf () desagradáveis em todo o lugar), determinei o método de teste em que o código está travando, embora, infelizmente, a falha real pareça acontecer em algum destruidor, já que as últimas mensagens de rastreamento que vejo estão em outros destruidores que são executados de forma limpa.
Quando tento executar este programa dentro do Visual Studio, ele não falha. O mesmo acontece ao iniciar a partir do WinDbg.exe. O travamento ocorre apenas ao iniciar a partir da linha de comando. A propósito, isso está acontecendo no Windows Vista e, infelizmente, não tenho acesso a uma máquina XP agora para testar.
Seria muito bom se eu pudesse fazer com que o Windows imprimisse um rastreamento de pilha ou algo diferente do que simplesmente encerrar o programa como se ele tivesse sido encerrado corretamente. Alguém tem algum conselho sobre como eu poderia obter informações mais significativas aqui e, com sorte, corrigir esse bug?
Edit: O problema foi realmente causado por uma matriz out-of-bounds, que descrevo mais neste post . Obrigado a todos pela ajuda para encontrar este problema!
Respostas:
Em 100% dos casos que vi ou ouvi falar, em que um programa C ou C ++ funciona bem no depurador, mas falha quando executado externamente, a causa foi a escrita além do final de um array local de função. (O depurador coloca mais na pilha, então é menos provável que você substitua algo importante.)
fonte
Quando eu encontrei problemas como esse antes, geralmente era devido à inicialização de variável. No modo de depuração, variáveis e ponteiros são inicializados para zero automaticamente, mas não no modo de liberação. Portanto, se você tiver um código como este
No modo de depuração, o código em if não é executado, mas no modo de liberação p contém um valor indefinido, que é improvável que seja 0, portanto, o código é executado frequentemente causando um travamento.
Gostaria de verificar seu código para variáveis não inicializadas. Isso também pode se aplicar ao conteúdo de matrizes.
fonte
Nenhuma resposta até agora tentou dar uma visão geral séria sobre as técnicas disponíveis para depurar aplicativos de lançamento:
As compilações Release e Debug se comportam de maneira diferente por vários motivos. Aqui está uma excelente visão geral. Cada uma dessas diferenças pode causar um bug na versão Release que não existe na versão Debug.
A presença de um depurador também pode alterar o comportamento de um programa , tanto para versões de lançamento quanto para depuração. Veja esta resposta. Resumindo, pelo menos o Visual Studio Debugger usa o Debug Heap automaticamente quando anexado a um programa. Você pode desativar o heap de depuração usando a variável de ambiente _NO_DEBUG_HEAP. Você pode especificar isso nas propriedades do computador ou nas Configurações do Projeto no Visual Studio. Isso pode tornar a falha reproduzível com o depurador anexado.
Mais informações sobre depuração de corrupção de heap aqui.
Se a solução anterior não funcionar, você precisa capturar a exceção não tratada e anexar um depurador post-mortem à instância em que a falha ocorre. Você pode usar, por exemplo, WinDbg para isso, detalhes sobre os depuradores post-mortem disponíveis e sua instalação no MSDN
Você pode melhorar seu código de tratamento de exceções e, se este for um aplicativo de produção, você deve:
uma. Instale um manipulador de encerramento personalizado usando
std::set_terminate
Se você quiser depurar esse problema localmente, poderá executar um loop infinito dentro do manipulador de terminação e enviar algum texto para o console para notificá-lo de que
std::terminate
foi chamado. Em seguida, anexe o depurador e verifique a pilha de chamadas. Ou você imprime o rastreamento de pilha conforme descrito nesta resposta.Em um aplicativo de produção, você pode enviar um relatório de erro de volta para casa, de preferência junto com um pequeno despejo de memória que permite analisar o problema conforme descrito aqui.
b. Use o mecanismo estruturado de tratamento de exceções da Microsoft, que permite capturar as exceções de hardware e software. Veja MSDN . Você pode proteger partes do seu código usando SEH e usar a mesma abordagem de a) para depurar o problema. SEH fornece mais informações sobre a exceção ocorrida que você pode usar ao enviar um relatório de erro de um aplicativo de produção.
fonte
Coisas a serem observadas:
Array overruns - o depurador do Visual Studio insere preenchimento que pode interromper travamentos.
Condições de corrida - você tem vários encadeamentos envolvidos? Se for assim, uma condição de corrida só aparece quando um aplicativo é executado diretamente.
Linking - é a sua versão de lançamento puxando as bibliotecas corretas.
Coisas para tentar:
Minidump - realmente fácil de usar (basta procurar no msdn) fornecerá um crash dump completo para cada thread. Você apenas carrega a saída no Visual Studio e é como se você estivesse depurando no momento da falha.
fonte
Você pode definir o WinDbg como seu depurador post-mortem. Isso iniciará o depurador e o anexará ao processo quando a falha ocorrer. Para instalar o WinDbg para depuração post-mortem, use a opção / I (observe que está maiúsculo ):
Mais detalhes aqui .
Quanto à causa, é muito provavelmente uma variável unitializada, como sugerem as outras respostas.
fonte
Depois de muitas horas de depuração, finalmente encontrei a causa do problema, que na verdade foi causado por um estouro de buffer, causando uma única diferença de byte:
Este é um erro de cerca (erro off-by-one) e foi corrigido por:
O estranho foi que coloquei várias chamadas para _CrtCheckMemory () em várias partes do meu código e elas sempre retornaram 1. Consegui encontrar a origem do problema colocando "return false;" chamadas no caso de teste e, em seguida, determinar por tentativa e erro onde estava a falha.
Obrigado a todos por seus comentários - aprendi muito sobre windbg.exe hoje! :)
fonte
Mesmo que você tenha construído seu exe como um release, você ainda pode gerar arquivos PDB (banco de dados do programa) que permitirão que você empilhe rastreio e faça uma quantidade limitada de inspeção de variáveis. Em suas configurações de construção, há uma opção para criar os arquivos PDB. Ligue e vincule novamente. Em seguida, tente primeiro executar a partir do IDE para ver se consegue travar. Se sim, ótimo - você está pronto para ver as coisas. Caso contrário, ao executar a partir da linha de comando, você pode fazer uma das duas coisas:
Quando solicitado a apontar para arquivos PDB, navegue para encontrá-los. Se os PDBs foram colocados na mesma pasta de saída do EXE ou DLLs, provavelmente serão selecionados automaticamente.
Os PDBs fornecem um link para a fonte com informações de símbolo suficientes para possibilitar a visualização de rastreamentos de pilha, variáveis etc. Você pode inspecionar os valores normalmente, mas esteja ciente de que pode obter leituras falsas, pois a passagem de otimização pode significar apenas coisas aparecem nos registros ou as coisas acontecem em uma ordem diferente da esperada.
NB: Estou assumindo um ambiente Windows / Visual Studio aqui.
fonte
Falhas como essa quase sempre são causadas porque um IDE geralmente define o conteúdo de uma variável não inicializada como zeros, nulo ou algum outro valor 'sensível', ao passo que, ao executar nativamente, você obterá qualquer lixo aleatório que o sistema coletar.
Portanto, seu erro é quase certo que você está usando algo como se estivesse usando um ponteiro antes de ter sido inicializado corretamente e você está fugindo com ele no IDE porque ele não aponta para nenhum lugar perigoso - ou o valor é manipulado por seu verificação de erros - mas no modo de liberação, ele faz algo desagradável.
fonte
Para ter um despejo de memória que você possa analisar:
Você também deve verificar as ferramentas em Ferramentas de depuração para janelas . Você pode monitorar o aplicativo e ver todas as exceções de primeira chance anteriores à sua exceção de segunda chance.
Espero que ajude...
fonte
Uma ótima maneira de depurar um erro como esse é habilitar otimizações para sua compilação de depuração.
fonte
Uma vez eu tive um problema quando o aplicativo se comportou de forma semelhante ao seu. Acabou sendo um estouro de buffer desagradável no sprintf. Naturalmente, funcionou quando executado com um depurador conectado. O que fiz, foi instalar um filtro de exceção não tratada ( SetUnhandledExceptionFilter ) no qual eu simplesmente bloqueei infinitamente (usando WaitForSingleObject em um identificador falso com um valor de tempo limite de INFINITO).
Então, você poderia algo como:
Em seguida, anexei o depurador depois que o bug se manifestou (o programa gui parou de responder).
Então você pode fazer um despejo e trabalhar com isso mais tarde:
Ou depure-o imediatamente. A maneira mais simples é rastrear onde o contexto do processador foi salvo pelo mecanismo de tratamento de exceções do tempo de execução:
O comando pesquisará o espaço de endereço da pilha para registro (s) CONTEXTO, desde o comprimento da pesquisa. Eu geralmente uso algo como 'l? 10000' . Observe, não use números extraordinariamente grandes como o registro que você está procurando, geralmente próximo ao quadro de filtro de exceção não manipulado. 1003f é a combinação de sinalizadores (acredito que corresponda a CONTEXT_FULL) usada para capturar o estado do processador. Sua pesquisa seria semelhante a esta:
Depois de obter os resultados de volta, use o endereço no comando cxr:
Isso o levará a este novo CONTEXTO, exatamente no momento do travamento (você obterá exatamente o rastreamento de pilha no momento em que seu aplicativo travou). Além disso, use:
para descobrir exatamente qual exceção ocorreu.
Espero que ajude.
fonte
Às vezes, isso acontece porque você envolveu uma operação importante dentro da macro "assert". Como você deve saber, "assert" avalia expressões apenas no modo de depuração.
fonte
Com relação aos problemas para obter informações de diagnóstico, você tentou usar adplus.vbs como alternativa ao WinDbg.exe? Para anexar a um processo em execução, use
Ou para iniciar o aplicativo caso a falha aconteça rapidamente:
Informações completas sobre adplus.vbs podem ser encontradas em: http://support.microsoft.com/kb/286350
fonte
Ntdll.dll com depurador anexado
Uma pequena diferença conhecida entre iniciar um programa a partir do IDE ou WinDbg em oposição a iniciá-lo a partir da linha de comando / área de trabalho é que, ao iniciar com um depurador conectado (ou seja, IDE ou WinDbg), o ntdll.dll usa uma implementação de heap diferente que executa alguma validação. na alocação / liberação de memória.
Você pode ler algumas informações relevantes no ponto de interrupção inesperado do usuário em ntdll.dll . Uma ferramenta que pode ajudá-lo a identificar o problema é o PageHeap.exe .
Análise de falhas
Você não escreveu qual é a "falha" que está ocorrendo. Assim que o programa travar e oferecer a você o envio das informações de erro para a Microsoft, você deverá ser capaz de clicar nas informações técnicas e verificar pelo menos o código de exceção e, com algum esforço, poderá até realizar análises post mortem (ver Heisenbug : O programa WinApi trava em alguns computadores) para obter instruções)
fonte
O Vista SP1 tem um gerador de despejo de memória muito bom embutido no sistema. Infelizmente, ele não é ativado por padrão!
Consulte este artigo: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
O benefício dessa abordagem é que nenhum software extra precisa ser instalado no sistema afetado. Agarre e rasgue, baby!
fonte
De acordo com minha experiência, esses são problemas de corrupção de memória.
Por exemplo :
é muito possível ser normal no modo de depuração quando se executa o código.
Mas no lançamento, isso seria / poderia ser um crash.
Para mim, vasculhar onde a memória está fora dos limites é muito trabalhoso.
Use algumas ferramentas como Visual Leak Detector (windows) ou valgrind (linux) são escolhas mais sábias.
fonte
Eu vi muitas respostas certas. No entanto, não há ninguém que me ajudou. No meu caso, houve um uso incorreto das instruções SSE com a memória não alinhada . Dê uma olhada em sua biblioteca matemática (se você usar uma) e tente desabilitar o suporte SIMD, recompilar e reproduzir a falha.
Exemplo:
Um projeto inclui mathfu e usa as classes com STL vector: std :: vector <mathfu :: vec2> . Tal uso provavelmente causará um travamento no momento da construção do item mathfu :: vec2, uma vez que o alocador padrão STL não garante o alinhamento de 16 bytes necessário. Neste caso para comprovar a ideia, pode-se definir
#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1
antes de cada inclusão do mathfu , recompilar na configuração do Release e verificar novamente.As configurações Debug e RelWithDebInfo funcionaram bem para meu projeto, mas não para o Release . A razão por trás desse comportamento é provavelmente porque o depurador processa solicitações de alocação / desalocação e faz alguma contabilidade de memória para verificar e verificar os acessos à memória.
Eu experimentei a situação nos ambientes Visual Studio 2015 e 2017.
fonte
Algo semelhante aconteceu comigo uma vez com o GCC. Acabou sendo uma otimização muito agressiva que foi habilitada apenas na criação da versão final e não durante o processo de desenvolvimento.
Bem, para falar a verdade, a culpa foi minha, não do gcc, pois não percebi que meu código estava contando com o fato de que aquela otimização em particular não teria sido feita.
Levei muito tempo para rastreá-lo e só cheguei a ele porque perguntei em um newsgroup e alguém me fez pensar sobre isso. Portanto, deixe-me retribuir o favor para o caso de isso também estar acontecendo com você.
fonte
Achei este artigo útil para o seu cenário. As opções do compilador ISTR estavam um pouco desatualizadas. Dê uma olhada nas opções de projeto do Visual Studio para ver como gerar arquivos PDB para sua versão de lançamento, etc.
fonte
É suspeito que isso aconteceria fora do depurador e não dentro; a execução no depurador normalmente não altera o comportamento do aplicativo. Gostaria de verificar as diferenças de ambiente entre o console e o IDE. Além disso, obviamente, compile a versão sem otimizações e com informações de depuração e veja se isso afeta o comportamento. Finalmente, verifique as ferramentas de depuração post-mortem que outras pessoas sugeriram aqui, geralmente você pode obter alguma pista delas.
fonte
Depurar compilações de versão pode ser uma dor devido às otimizações que alteram a ordem em que as linhas de seu código parecem ser executadas. Isso pode realmente ficar confuso!
Uma técnica para pelo menos reduzir o problema é usar MessageBox () para exibir instruções rápidas informando a que parte do programa seu código deve ser ("Iniciando Foo ()", "Iniciando Foo2 ()"); comece a colocá-los no topo das funções na área de seu código que você suspeita (o que você estava fazendo no momento em que ele travou?). Quando você souber qual função, altere as caixas de mensagem para blocos de código ou até mesmo linhas individuais dentro dessa função até reduzi-la a algumas linhas. Então você pode começar a imprimir o valor das variáveis para ver em que estado elas estão no ponto de travamento.
fonte
Tente usar _CrtCheckMemory () para ver em que estado está a memória alocada. Se tudo correr bem, _CrtCheckMemory retorna TRUE , senão FALSE .
fonte
Você pode executar seu software com Global Flags habilitado (procure em Debugging Tools for Windows). Muitas vezes, ajuda a resolver o problema.
fonte
Faça seu programa gerar um mini dump quando a exceção ocorrer e, em seguida, abra-o em um depurador (por exemplo, no WinDbg). As principais funções a serem observadas: MiniDumpWriteDump, SetUnhandledExceptionFilter
fonte
Aqui está um caso que alguém pode achar instrutivo. Ele só travou no lançamento no Qt Creator - não na depuração. Eu estava usando arquivos .ini (porque prefiro aplicativos que possam ser copiados para outras unidades do que aqueles que perdem suas configurações se o Registro for corrompido). Isso se aplica a todos os aplicativos que armazenam suas configurações na árvore de diretórios dos aplicativos. Se as compilações de depuração e liberação estiverem em diretórios diferentes, você também pode ter uma configuração diferente entre elas. Eu tinha uma preferência marcada em um que não foi marcada no outro. Acabou sendo a fonte do meu acidente. Ainda bem que encontrei.
Odeio dizer isso, mas só diagnostiquei a falha no MS Visual Studio Community Edition; após ter instalado o VS, deixando meu aplicativo travar no Qt Creator e escolhendo abri-lo no depurador do Visual Studio . Embora meu aplicativo Qt não tivesse informações de símbolo, descobri que as bibliotecas Qt tinham algumas. Isso me levou à linha ofensiva; pois pude ver qual método estava sendo chamado. (Mesmo assim, acho que Qt é uma estrutura LGPL conveniente, poderosa e de plataforma cruzada.)
fonte
Eu tive esse erro e travou mesmo ao tentar! Limpar! meu projeto. Portanto, apaguei os arquivos obj manualmente do diretório Release e, depois disso, ele foi compilado perfeitamente.
fonte
Eu concordo com Rolf. Como a reprodutibilidade é tão importante, você não deve ter um modo sem depuração. Todas as suas compilações devem ser depuráveis. Ter dois destinos para depurar mais do que duplica sua carga de depuração. Apenas envie a versão do "modo de depuração", a menos que seja inutilizável. Nesse caso, torne-o utilizável.
fonte