Pourquoi GDB saute de manière imprévisible entre les lignes et imprime les variables sous la forme «<valeur optimisée>»?

84

Quelqu'un peut-il 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 avoir exécuté la ligne 903, il exécute à nouveau la même chose pour 905 908 910?

Une autre chose est foundune boolvariable de type -type, alors pourquoi est-elle affichée value optimized out? Je ne suis pas en mesure de définir la valeur de found.

Cela semble être une optimisation du compilateur (dans ce cas, son -O2); comment puis-je encore définir la valeur de found?

Arpit
la source
8
lors du débogage, c'est généralement une bonne idée de compiler avec -O0 car l'optimisation conduit à ce genre de problèmes.
LiraNuna

Réponses:

115

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

Utilisez le mode GDB TUI. Ma copie de GDB l'active lorsque je tape le moins et Entrée. Tapez ensuite Cx 2 (c'est-à-dire maintenez la touche Control enfoncée et appuyez sur X, relâchez les deux puis appuyez sur 2). Cela le mettra dans l'affichage de la source partagée et du démontage. Utilisez ensuite stepiet nextipour déplacer une instruction machine à la fois. Utilisez Cx o pour basculer entre les fenêtres TUI.

Téléchargez un PDF sur le langage machine de votre CPU et les conventions d'appel de fonction. 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

Zan Lynx
la source
J'ai toujours le problème «optimisé», et la valeur de la variable n'est pas affichée dans les autres fenêtres, mais, quand même, c'est une excellente information, merci!
Tom Brito
16
@TomBrito: Optimisé signifie que la variable n'est pas en mémoire. Il se trouve probablement uniquement dans un registre CPU, ce qui signifie que vous devez lire le démontage et imprimer les valeurs du registre pour le trouver.
Zan Lynx
@Zan Lynx: Je ne suis pas sûr d'être d'accord avec votre analyse. Les symboles DWARF ont suffisamment d'informations pour extraire les valeurs des registres. Peut-être que ce qui veut dire ici est que le compilateur a déterminé que la variable peut être supprimée en toute sécurité au moment où l'exécution atteint la ligne actuelle. Dans ce cas, le stockage où vit la variable a probablement été réutilisé pour autre chose. Je pense que vous avez raison de dire que cela ne se produirait normalement que si la variable était enregistrée.
Ian Ni-Lewis
@ IanNi-Lewis: Je ne sais pas quelle version de DWARF vous utilisez mais d'après mon expérience, GDB ne peut pas imprimer une variable qui a été stockée dans un registre.
Zan Lynx
Je suis sûr que vous avez raison. Mon expérience avec DWARF vient de l'écriture de mon propre analyseur, pas de l'utilisation de gdb, donc je ne sais pas vraiment de quoi gdb est capable.
Ian Ni-Lewis
75

Recompiler sans optimisations (-O0 sur gcc).

D'Nabre
la source
17
Même -O0 peut produire du code optimisé (en essayant de lutter avec cela maintenant), même si je ne sais pas pourquoi.
Chris Gregg
@ChrisGregg J'ai le même problème! Avez-vous découvert quel est le problème?
Paolo M
1
@paolom il semble que ce soit un problème de clang, donc j'ai compilé avec g ++ à la place à des fins de débogage, malheureusement.
Chris Gregg
Souvent, ce n'est pas une solution - surtout si vous avez un vidage de mémoire de la production et / ou que vous ne parvenez pas à reproduire le problème sur l'environnement de développement.
smbear
39

Déclarer trouvé comme "volatile". Cela devrait dire au compilateur de NE PAS l'optimiser.

volatile int found = 0;
Ben B
la source
1
Même lorsque je déclare certaines variables "volatiles" dans le débogueur gdb, il les affiche comme une variable optimisée! Y a-t-il plus là-dedans?
M.Rez
11

Le compilateur commencera à faire des choses très intelligentes avec les optimisations activées. Le débogueur montrera le code sautant en avant et en arrière beaucoup en raison de la manière optimisée de stocker les variables 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 la vitesse, plutôt que d'avoir un emplacement mémoire direct auquel le débogueur peut accéder.

Compiler sans optimisations?

kjfletch
la source
6

En règle générale, les valeurs booléennes utilisées dans les branches immédiatement après avoir été calculées de cette manière ne sont jamais réellement stockées dans des variables. Au lieu de cela, le compilateur dérive simplement directement les codes de condition définis à partir de la comparaison précédente. Par exemple,

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

Compile généralement en quelque chose comme:

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

Remarquez que le "booléen" n'est jamais stocké nulle part.

Crashworks
la source
4

Vous ne pouvez pratiquement pas définir la valeur de found. Déboguer des programmes optimisés en vaut rarement la peine, le compilateur peut réorganiser le code de manière à ce qu'il ne corresponde en aucun cas au code source (autre que de produire le même résultat), déroutant ainsi les débogueurs sans fin.

nos
la source
4

Lors du débogage de programmes optimisés (ce qui peut être nécessaire si le bogue n'apparaît pas dans les versions de débogage), vous devez souvent comprendre le compilateur d'assembly généré.

Dans votre cas particulier, la valeur de retour de cpnd_find_exact_ckptinfosera stockée dans le registre qui est utilisé sur votre plateforme pour les valeurs de retour. Sur ix86, ce serait %eax. Sur x86_64:%rax etc.

Vous pouvez examiner ce registre dans GDBet vous pouvez le définir. Par exemple sur ix86:

(gdb) p $eax
(gdb) set $eax = 0 
Employé russe
la source
0

J'utilise QtCreator avec gdb.

Ajouter

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

Fonctionne bien pour moi

Haselnussstrauch
la source