83 votes

Pourquoi GDB saute-t-il de manière imprévisible entre les lignes et affiche-t-il les variables comme "<value optimized out>"?

Est-ce que quelqu'un peut expliquer ce comportement 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.

Pourquoi après l'exécution de la ligne 903, il exécute à nouveau la même chose pour 905 908 910 ?

Autre chose, la variable found est de type bool, alors pourquoi affiche-t-elle value optimized out? Je ne suis pas capable de définir la valeur de found non plus.

Cela semble être une optimisation du compilateur (dans ce cas, c'est -O2); comment puis-je quand même définir la valeur de found ?

114voto

Zan Lynx Points 23100

Pour déboguer du code optimisé, apprenez le langage d'assemblage/machine.

Utilisez le mode GDB TUI. Ma copie de GDB l'active lorsque je tape le signe moins et Entrée. Ensuite, tapez C-x 2 (c'est-à-dire maintenez Control enfoncé et appuyez sur X, relâchez les deux et appuyez sur 2). Cela le mettra en mode affichage source et désassemblage. Ensuite, utilisez stepi et nexti pour avancer une instruction machine à la fois. Utilisez C-x o pour passer entre les fenêtres TUI.

Téléchargez un PDF sur le langage machine de votre CPU et les conventions d'appel de fonctions. Vous apprendrez rapidement à reconnaître ce qui est fait avec les arguments de fonction et les valeurs de retour.

Vous pouvez afficher la valeur d'un registre en utilisant une commande GDB comme p $eax

74voto

D'Nabre Points 861

Recompiler sans optimisations (-O0 sur gcc).

38voto

BenB Points 4152

Déclarez found comme "volatile". Cela devrait indiquer au compilateur de NE PAS l'optimiser.

volatile int found = 0;

11voto

kjfletch Points 2913

Le compilateur commencera à faire des choses très intelligentes lorsque les optimisations seront activées. Le débogueur affichera le code en sautant en avant et en arrière beaucoup en raison de la façon optimisée dont les variables sont stockées dans les registres. C'est probablement la raison pour laquelle vous ne pouvez pas définir votre variable (ou dans certains cas voir sa valeur) car elle a été intelligemment répartie entre les registres pour des raisons de vitesse, plutôt que d'avoir un emplacement mémoire direct que le débogueur peut accéder.

Compiler sans optimisations?

6voto

Crashworks Points 22920

Typiquement, les valeurs booléennes qui sont utilisées dans les branches immédiatement après leur calcul de cette façon ne sont jamais réellement stockées dans des variables. Au lieu de cela, le compilateur se branche directement sur les codes de condition qui ont été définis à partir de la comparaison précédente. Par exemple,

int a = SomeFunction();
bool result = --a >= 0; // utiliser la soustraction comme exemple de calcul
if ( result ) 
{
   foo(); 
}
else
{
   bar();
}
return;

Généralement, cela se compile en quelque chose comme:

call .SomeFunction  ; appelle SomeFunction(), qui stocke sa valeur de retour dans eax
sub eax, 1 ; soustrait 1 de eax et stocke dans eax, définit le drapeau S (signe) si le résultat est négatif
jl ELSEBLOCK ; GOTO étiquette "ELSEBLOCK" si le drapeau S est défini
call .foo ; c'est le bloc "if", appeler foo()
j FINISH ; GOTO FINISH; passer au-dessus du bloc "else"
ELSEBLOCK: ; étiqueter cet emplacement pour l'assembleur
call .bar
FINISH: ; les deux chemins aboutissent ici
ret ; retour

Remarquez comment le "bool" n'est jamais réellement stocké nulle part.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X