Warum springt GDB unvorhersehbar zwischen Zeilen und druckt Variablen als "<Wert optimiert aus>"?


83

Kann jemand dieses Verhalten von GDB erklären?

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.

Warum wird nach Ausführung von Zeile 903 dasselbe für 905 908 910 erneut ausgeführt?

Eine andere Sache ist foundeine boolVariable vom Typ. Warum wird sie angezeigt value optimized out? Ich kann den Wert auch nicht einstellen found.

Dies scheint eine Compiler-Optimierung zu sein (in diesem Fall seine -O2); Wie kann ich den Wert von noch einstellen found?


8
Beim Debuggen ist es im Allgemeinen eine gute Idee, mit -O0 zu kompilieren, da die Optimierung zu solchen Problemen führt.
LiraNuna

Antworten:


115

Lernen Sie zum Debuggen von optimiertem Code die Assembler- / Maschinensprache.

Verwenden Sie den GDB-TUI-Modus. Meine Kopie von GDB aktiviert es, wenn ich das Minus und die Eingabetaste eingebe. Geben Sie dann Cx 2 ein (dh halten Sie die Strg-Taste gedrückt und drücken Sie X, lassen Sie beide los und drücken Sie dann 2). Dadurch wird die Anzeige für geteilte Quellen und Demontage angezeigt. Verwenden Sie dann stepiund nexti, um jeweils eine Maschinenanweisung zu verschieben. Verwenden Sie Cx o, um zwischen den TUI-Fenstern zu wechseln.

Laden Sie ein PDF über die Maschinensprache Ihrer CPU und die Funktionsaufrufkonventionen herunter. Sie werden schnell erkennen, was mit Funktionsargumenten und Rückgabewerten gemacht wird.

Sie können den Wert eines Registers mit einem GDB-Befehl wie anzeigen p $eax


Ich habe immer noch das Problem "optimiert" und der Variablenwert wird in den anderen Fenstern nicht angezeigt, aber es ist trotzdem eine großartige Information, danke!
Tom Brito

16
@ TomBrito: Optimiert aus bedeutet, dass sich die Variable nicht im Speicher befindet. Es befindet sich wahrscheinlich nur in einem CPU-Register. Dies bedeutet, dass Sie die Demontage lesen und die Registerwerte drucken müssen, um sie zu finden.
Zan Lynx

@ Zan Lynx: Ich bin nicht sicher, ob ich Ihrer Analyse zustimme. DWARF-Symbole enthalten genügend Informationen, um Werte aus Registern zu extrahieren. Hier ist möglicherweise gemeint, dass der Compiler festgestellt hat, dass die Variable sicher verworfen werden kann, wenn die Ausführung die aktuelle Zeile erreicht. In diesem Fall wurde der Speicher, in dem sich die Variable befindet, wahrscheinlich für etwas anderes wiederverwendet. Ich denke, Sie haben Recht, dass dies normalerweise nur passieren würde, wenn die Variable registriert wäre.
Ian Ni-Lewis

@ IanNi-Lewis: Ich weiß nicht, welche Version von DWARF Sie verwenden, aber meiner Erfahrung nach kann GDB keine Variable drucken, die in einem Register gespeichert wurde.
Zan Lynx

Ich bin sicher, du hast recht. Meine Erfahrung mit DWARF stammt aus dem Schreiben eines eigenen Parsers, nicht aus der Verwendung von GDB. Daher weiß ich nicht genau, wozu GDB in der Lage ist.
Ian Ni-Lewis

75

Ohne Optimierungen neu kompilieren (-O0 auf gcc).


17
Sogar -O0 kann optimierten Code erzeugen (ich versuche gerade damit zu kämpfen), obwohl ich nicht sicher bin, warum.
Chris Gregg

@ ChrisGregg Ich habe das gleiche Problem! Haben Sie herausgefunden, worum es geht?
Paolo M

1
@paolom es sieht so aus, als ob es ein Clang-Problem sein könnte, also habe ich leider stattdessen zu Debugging-Zwecken mit g ++ kompiliert.
Chris Gregg

Oft ist dies keine Lösung - insbesondere, wenn Sie einen Core-Dump aus der Produktion haben und / oder das Problem in der Entwicklungsumgebung nicht reproduzieren können.
smbear

39

Declare gefunden als „flüchtig“. Dies sollte den Compiler anweisen, es NICHT zu optimieren.

volatile int found = 0;

1
Selbst wenn ich einige Variablen im GDB-Debugger als "flüchtig" deklariere, wird sie als optimierte Variable angezeigt! Ist da noch mehr drin?
M.Rez

11

Der Compiler wird mit aktivierten Optimierungen sehr clevere Dinge tun. Der Debugger zeigt den Code an, der aufgrund der optimierten Art und Weise, wie Variablen in Registern gespeichert werden, viel vorwärts und rückwärts springt. Dies ist wahrscheinlich der Grund, warum Sie Ihre Variable nicht festlegen können (oder in einigen Fällen ihren Wert sehen können), da sie aus Geschwindigkeitsgründen geschickt zwischen den Registern verteilt wurde, anstatt über einen direkten Speicherort zu verfügen, auf den der Debugger zugreifen kann.

Ohne Optimierungen kompilieren?


6

In der Regel werden boolesche Werte, die unmittelbar nach ihrer Berechnung in Zweigen verwendet werden, nie tatsächlich in Variablen gespeichert. Stattdessen verzweigt der Compiler direkt von den Bedingungscodes , die aus dem vorherigen Vergleich festgelegt wurden. Beispielsweise,

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

Kompiliert normalerweise zu etwas wie:

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

Beachten Sie, dass der "Bool" niemals irgendwo gespeichert wird.


4

Sie können den Wert von found so gut wie nicht festlegen. Das Debuggen optimierter Programme ist selten die Mühe wert. Der Compiler kann den Code so neu anordnen, dass er in keiner Weise dem Quellcode entspricht (außer dass das gleiche Ergebnis erzielt wird), wodurch die Debugger ohne Ende verwirrt werden.


4

Beim Debuggen optimierter Programme (was erforderlich sein kann, wenn der Fehler in Debug-Builds nicht auftritt) müssen Sie häufig den generierten Assembly-Compiler verstehen.

In Ihrem speziellen Fall wird der Rückgabewert von cpnd_find_exact_ckptinfoin dem Register gespeichert, das auf Ihrer Plattform für Rückgabewerte verwendet wird. Auf ix86, das wäre %eax. Ein x86_64: %raxusw. Möglicherweise müssen Sie nach der Konvention zum Aufrufen der Prozedur [Ihres Prozessors] googeln, wenn dies nicht der Fall ist.

Sie können dieses Register in überprüfen GDBund festlegen. ZB am ix86:

(gdb) p $eax
(gdb) set $eax = 0 

0

Ich benutze QtCreator mit GDB.

Hinzufügen

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

Funktioniert gut für mich

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.