identificar “salto ou movimento condicional depende de valor (es) não inicializado (s)”

166

Então, eu recebi uma mensagem misteriosa de valores não inicializados da valgrind e tem sido um mistério até onde o valor ruim se originou.

Parece que valgrind mostra o local onde o valor unitializado acaba sendo usado, mas não a origem do valor não inicializado.

==11366== Conditional jump or move depends on uninitialised value(s)
==11366==    at 0x43CAE4F: __printf_fp (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x43C6563: vfprintf (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x43EAC03: vsnprintf (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x42D475B: (within /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42E2C9B: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_float<double>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, char, double) const (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42E31B4: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42EE56F: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x81109ED: Snake::SnakeBody::syncBodyPos() (ostream:221)
==11366==    by 0x810B9F1: Snake::Snake::update() (snake.cpp:257)
==11366==    by 0x81113C1: SnakeApp::updateState() (snakeapp.cpp:224)
==11366==    by 0x8120351: RoenGL::updateState() (roengl.cpp:1180)
==11366==    by 0x81E87D9: Roensachs::update() (rs.cpp:321)

Como pode ser visto, fica bastante enigmático .. especialmente porque quando diz por Class :: MethodX, às vezes aponta direto para ostream etc. Talvez isso seja devido à otimização?

==11366==    by 0x81109ED: Snake::SnakeBody::syncBodyPos() (ostream:221)

Bem desse jeito. Tem algo que estou perdendo? Qual é a melhor maneira de capturar valores ruins sem ter que recorrer a trabalhos de detetive de impressão muito longa?

Atualizar:

Eu descobri o que estava errado, mas o estranho é que a valgrind não informou quando o valor ruim foi usado pela primeira vez. Foi utilizado em uma função de multiplicação:

movespeed = stat.speedfactor * speedfac * currentbendfactor.val;

Onde speedfac era um flutuador unitizado. No entanto, naquele momento, não foi relatado e até que o valor seja impresso, eu recebo o erro. Existe uma configuração para o valgrind alterar esse comportamento?

kamziro
fonte

Respostas:

230

Use a opção valgrind --track-origins=yespara que ele rastreie a origem de valores não inicializados. Isso tornará mais lento e consumirá mais memória, mas pode ser muito útil se você precisar rastrear a origem de um valor não inicializado.

Atualização: em relação ao ponto em que o valor não inicializado é relatado, o manual valgrind declara :

É importante entender que seu programa pode copiar em torno de dados indesejados (não inicializados) o quanto quiser. O Memcheck observa isso e controla os dados, mas não reclama. Uma reclamação é emitida apenas quando o seu programa tenta usar dados não inicializados de uma maneira que possa afetar o comportamento visível externamente do seu programa.

No FAQ do Valgrind :

Quanto aos relatórios ansiosos de cópias de valores de memória não inicializados, isso foi sugerido várias vezes. Infelizmente, quase todos os programas copiam legitimamente valores de memória não inicializados (porque os compiladores protegem as estruturas para preservar o alinhamento) e a verificação ágil leva a centenas de falsos positivos. Portanto, o Memcheck não oferece suporte à verificação antecipada no momento.

mark4o
fonte
1
Qual é a versão mínima do valgrind para usar esse recurso? Estou usando o 3.3.0 e parece não gostar da opção.
Robert S. Barnes
8
@Robert: --track-origins foi adicionado no valgrind 3.4.0
mark4o
20

O que isto significa é que você está tentando imprimir / gerar um valor que é pelo menos parcialmente não inicializado. Você pode reduzi-lo para saber exatamente qual é esse valor? Depois disso, rastreie seu código para ver onde ele está sendo inicializado. As chances são de que você verá que não está sendo totalmente inicializado.

Se você precisar de mais ajuda, publicar as seções relevantes do código-fonte pode permitir que alguém ofereça mais orientações.

EDITAR

Vejo que você encontrou o problema. Observe que o valgrind observa salto ou movimento condicional com base em variáveis ​​unitializadas. O que isso significa é que ele só emitirá um aviso se a execução do programa for alterada devido ao valor não inicializado (ou seja, o programa assume uma ramificação diferente em uma instrução if, por exemplo). Como a aritmética real não envolveu um salto ou movimento condicional, a valgrind não o alertou sobre isso. Em vez disso, propagou o status "não inicializado" para o resultado da instrução que o usou.

Pode parecer contra-intuitivo que não o avise imediatamente, mas, como mark4o apontou, faz isso porque valores não inicializados são usados ​​em C o tempo todo (exemplos: preenchimento de estruturas, realloc()chamada, etc.), para que esses avisos não sejam muito útil devido à frequência de falsos positivos.

RarrRarrRarr
fonte
Obrigado. Eu só descobri o que estava errado, mas o estranho é, valgrind não relatou a coisa valor unitialised até que foi usado em outro lugar ..
kamziro
Isso é intencional. Se apenas copiar ou passar valores não inicializados causasse um relatório de erro, você os obteria o tempo todo do preenchimento de estruturas.
mark4o