¿Por qué GDB salta de forma impredecible entre líneas e imprime variables como "<valor optimizado fuera>"?

84

¿Alguien puede explicar este comportamiento de gdb?

900         memset(&new_ckpt_info,'\0',sizeof(CKPT_INFO));
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_** HDR),i_offset);
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_ HDR),i_offset);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
913         found = cpnd_find_exact_ckptinfo(cb , &ckpt_info , bitmap_offset , &offset , &prev_offset);
(gdb)
916         if(!found)
(gdb) p found
$1 = <value optimized out>
(gdb) set found=0
Left operand of assignment is not an lvalue.

¿Por qué después de ejecutar la línea 903 vuelve a ejecutar lo mismo para 905 908 910?

Otra cosa es que foundes una boolvariable de tipo, entonces, ¿por qué se muestra value optimized out? No puedo establecer el valor de foundtambién.

Esto parece ser una optimización del compilador (en este caso es -O2); ¿Cómo puedo establecer el valor de found?

Arpit
fuente
8
al depurar, generalmente es una buena idea compilar con -O0 ya que la optimización conduce a ese tipo de problemas.
LiraNuna

Respuestas:

115

Para depurar código optimizado, aprenda lenguaje ensamblador / máquina.

Utilice el modo GDB TUI. Mi copia de GDB lo habilita cuando escribo el signo menos y Enter. Luego escriba Cx 2 (es decir, mantenga presionado Control y presione X, suelte ambos y luego presione 2). Eso lo pondrá en fuente dividida y pantalla de desmontaje. Luego use stepiy nextipara mover una instrucción de máquina a la vez. Utilice Cx o para cambiar entre las ventanas TUI.

Descargue un PDF sobre el lenguaje de máquina de su CPU y las convenciones de llamadas a funciones. Aprenderá rápidamente a reconocer lo que se está haciendo con los argumentos de función y los valores de retorno.

Puede mostrar el valor de un registro utilizando un comando GDB como p $eax

Zan Lynx
fuente
Todavía tengo el problema "optimizado", y el valor de la variable no se muestra en las otras ventanas, pero, aún así, es una gran información, ¡gracias!
Tom Brito
16
@TomBrito: Optimizado significa que la variable no está en la memoria. Probablemente solo esté en un registro de CPU, lo que significa que debe leer el desmontaje e imprimir los valores del registro para encontrarlo.
Zan Lynx
@Zan Lynx: No estoy seguro de estar de acuerdo con su análisis. Los símbolos DWARF tienen suficiente información para extraer valores de los registros. Quizás lo que se quiere decir aquí es que el compilador ha determinado que la variable puede descartarse de forma segura cuando la ejecución alcance la línea actual. En ese caso, el almacenamiento donde vive la variable probablemente se haya reutilizado para otra cosa. Creo que tienes razón en que esto normalmente solo sucedería si la variable estuviera registrada.
Ian Ni-Lewis
@ IanNi-Lewis: No sé qué versión de DWARF está usando, pero en mi experiencia, GDB no puede imprimir una variable que haya sido almacenada en un registro.
Zan Lynx
Estoy seguro de que tienes razón. Mi experiencia con DWARF proviene de escribir mi propio analizador, no de usar gdb, por lo que realmente no sé de qué es capaz gdb.
Ian Ni-Lewis
75

Vuelva a compilar sin optimizaciones (-O0 en gcc).

D'Nabre
fuente
17
Incluso -O0 puede producir un código optimizado (tratando de luchar con esto en este momento), aunque no estoy seguro de por qué.
Chris Gregg
@ChrisGregg ¡Tengo el mismo problema! ¿Ha averiguado cuál es el problema?
Paolo M
1
@paolom, parece que podría ser un problema de clang, por lo que he estado compilando con g ++ en su lugar con fines de depuración, desafortunadamente.
Chris Gregg
A menudo, esta no es una solución, especialmente si tiene un volcado de memoria de producción y / o no puede reproducir el problema en el entorno de desarrollo.
smbear el
39

Declarar encontrado como "volátil". Esto debería decirle al compilador que NO lo optimice.

volatile int found = 0;
Ben B
fuente
1
Incluso cuando declaro algunas variables "volátiles" en el depurador de gdb, ¡las muestra como una variable optimizada! ¿Hay algo más en esto?
M.Rez
11

El compilador comenzará a hacer cosas muy inteligentes con las optimizaciones activadas. El depurador mostrará el código saltando mucho hacia adelante y hacia atrás debido a la forma optimizada en que se almacenan las variables en los registros. Esta es probablemente la razón por la que no puede establecer su variable (o en algunos casos ver su valor), ya que se ha distribuido inteligentemente entre registros para la velocidad, en lugar de tener una ubicación de memoria directa a la que el depurador pueda acceder.

¿Compilar sin optimizaciones?

kjfletch
fuente
6

Por lo general, los valores booleanos que se utilizan en las ramas inmediatamente después de que se calculan así nunca se almacenan en las variables. En cambio, el compilador simplemente se ramifica directamente de los códigos de condición que se establecieron en la comparación anterior. Por ejemplo,

int a = SomeFunction();
bool result = --a >= 0; // use subtraction as example computation
if ( result ) 
{
   foo(); 
}
else
{
   bar();
}
return;

Por lo general, se compila en algo como:

call .SomeFunction  ; calls to SomeFunction(), which stores its return value in eax
sub eax, 1 ; subtract 1 from eax and store in eax, set S (sign) flag if result is negative
jl ELSEBLOCK ; GOTO label "ELSEBLOCK" if S flag is set
call .foo ; this is the "if" black, call foo()
j FINISH ; GOTO FINISH; skip over the "else" block
ELSEBLOCK: ; label this location to the assembler
call .bar
FINISH: ; both paths end up here
ret ; return

Observe cómo el "bool" nunca se almacena en ningún lugar.

Crashworks
fuente
4

Prácticamente no se puede establecer el valor de encontrado. La depuración de programas optimizados rara vez vale la pena, el compilador puede reorganizar el código de manera que no corresponda de ninguna manera al código fuente (aparte de producir el mismo resultado), lo que confunde a los depuradores sin fin.

nos
fuente
4

Al depurar programas optimizados (que puede ser necesario si el error no aparece en las compilaciones de depuración), a menudo debe comprender el compilador de ensamblado generado.

En su caso particular, el valor de retorno de cpnd_find_exact_ckptinfose almacenará en el registro que se utiliza en su plataforma para los valores de retorno. Sí ix86, eso sería %eax. Activado x86_64:, %raxetc. Es posible que deba buscar en Google '[su procesador] procedimiento que llama a la convención' si no es ninguno de los anteriores.

Puede examinar ese registro GDBy configurarlo. Por ejemplo, en ix86:

(gdb) p $eax
(gdb) set $eax = 0 
Ruso empleado
fuente
0

Estoy usando QtCreator con gdb.

Añadiendo

QMAKE_CXXFLAGS += -O0
QMAKE_CXXFLAGS -= -O1
QMAKE_CXXFLAGS -= -O2
QMAKE_CXXFLAGS -= -O3

Funciona bien para mi

Haselnussstrauch
fuente