(gdb) n
134 a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
(gdb) n
(gdb) p a
$30 = <value optimized out>
(gdb) p b
$31 = <value optimized out>
(gdb) p c
$32 = 3735928563
gcc -O3Dies bedeutet, dass Sie mit eg kompiliert haben und der gcc-Optimierer festgestellt hat, dass einige Ihrer Variablen auf irgendeine Weise redundant waren, sodass sie wegoptimiert werden konnten. In diesem speziellen Fall scheinen Sie drei Variablen a, b, c mit demselben Wert zu haben, und vermutlich können sie alle zu einer einzigen Variablen zusammengefasst werden. Kompilieren Sie mit deaktivierter Optimierung, z. B. gcc -O0wenn Sie solche Variablen sehen möchten (dies ist in der Regel in jedem Fall eine gute Idee für Debug-Builds).
Aber hier aist das nicht redundant, es muss später verwendet werden ..177 case 3 : a+=k[0]&0xffffff; break;
GDB
3
Sie müssen den gesamten relevanten Code veröffentlichen, wenn Sie weitere Analysen wünschen.
Paul R
1
Der Optimierer speichert temporäre Variablen nach Möglichkeit in Registern. Es kann auch mehrere Variablen auf dasselbe Register aliasen, wenn sie alle denselben Wert haben, bis zu einem Punkt, an dem eine von ihnen geändert wird. An diesem Punkt kann sie dann einem anderen Register zugewiesen werden. Daher kann die Lebensdauer Ihrer Variablen im optimierten Code anders sein als im Quellcode. Deaktivieren Sie die Optimierung, wenn Sie durch diese Art von Verhalten nicht verwirrt werden möchten.
Paul R
2
Neuere gccs haben eine Option. -OgEs werden nur die Optimierungen angewendet, die die Debugbarkeit nicht beeinträchtigen - sehr nützlich (auch man gccfür -gdwarf4). Sie können die Variable, die Sie nicht verlieren möchten, auch vorübergehend als definieren volatile, wenn Sie keine Compiler-Optimierungen wünschen, aber die Optimierung nicht für den gesamten Build deaktivieren möchten! Beide Informationen von hier: ask.xmodulo.com/print-optimized-out-value-gdb.html
kavadias
3
@kavadias, die -OgOption könnte genau das Problem sein, das dazu führt, dass Variablen optimiert werden! Siehe meine Antwort hier: stackoverflow.com/a/63386263/4561887 . Also, wenn Sie irgendwelche Fehler haben, die sagen <optimized out>oder Can't take address of "var" which isn't an lvalue., dann müssen Sie -O0anstelle von verwenden-Og !
Gabriel Staples
6
Minimal lauffähiges Beispiel mit Demontageanalyse
Wie immer sehe ich gerne eine Demontage, um besser zu verstehen, was los ist.
Dies kann natürlich nur passieren, wenn die betreffende Variable nicht mehr benötigt wird, sonst würde das Programm seinen Wert verlieren. Daher kann es vorkommen, dass Sie am Anfang der Funktion den Variablenwert sehen können, am Ende jedoch <optimized out>.
Ein typischer Fall, an dem wir oft interessiert sind, ist der der Funktionsargumente selbst, da dies sind:
immer zu Beginn der Funktion definiert
wird möglicherweise gegen Ende der Funktion nicht verwendet, da mehr Zwischenwerte berechnet werden.
neigen dazu, durch weitere Funktionsunteraufrufe überschrieben zu werden, die genau dieselben Register einrichten müssen, um die aufrufende Konvention zu erfüllen
da iist das erste Argument von f1und daher in RDI gespeichert.
Nun, während wir bei beiden waren:
i += 1;
j += f2(i);
Der Wert von RDI wurde nicht geändert, und daher konnte GDB ihn jederzeit in diesen Zeilen abfragen.
Sobald der f2Anruf getätigt wird:
Der Wert von iwird im Programm nicht mehr benötigt
lea 0x1(%rax),%editut EDI = j + RAX + 1, was beide:
initialisiert j = 1
Legt das erste Argument des nächsten f2Aufrufs von festRDI = j
Daher, wenn die folgende Zeile erreicht ist:
k += f2(j);
Beide der folgenden Anweisungen haben / haben möglicherweise RDI geändert. Dies ist der einzige Ort i, an dem gespeichert wurde ( f2möglicherweise wird es als Scratch-Register verwendet und leadefinitiv auf RAX + 1 gesetzt):
und so enthält RDI nicht mehr den Wert von i. In der Tat war der Wert von ivöllig verloren! Daher ist das einzig mögliche Ergebnis:
$3 = <optimized out>
Ähnliches passiert mit dem Wert von j, obwohl er jerst eine Zeile später nach dem Aufruf unnötig wird k += f2(j);.
Wenn jwir darüber nachdenken, erhalten wir auch einen Einblick, wie intelligent GDB ist. Insbesondere war i += 1;der Wert von jnoch in keinem Register oder in keiner Speicheradresse angegeben, und GDB muss ihn ausschließlich anhand von Debug-Informationsmetadaten gekannt haben.
-O0 Analyse
Wenn wir -O0statt -O3für die Kompilierung verwenden:
Daher ist es für GDB jederzeit einfach, die Werte dieser Variablen zu finden: Sie sind immer im Speicher vorhanden!
Dies gibt uns auch einen Einblick, warum es nicht möglich ist, <optimized out>optimierten Code zu vermeiden : Da die Anzahl der Register begrenzt ist, besteht die einzige Möglichkeit darin, nicht benötigte Register tatsächlich in den Speicher zu verschieben, was den Vorteil von teilweise zunichte machen würde -O3.
Verlängern Sie die Lebensdauer von i
Wenn wir bearbeitet haben f1, um l + iwie folgt zurückzukehren :
int __attribute__((noinline)) f1(int i) {
int j = 1, k = 2, l = 3;
i += 1;
j += f2(i);
k += f2(j);
l += f2(k);
return l + i;
}
dann stellen wir fest, dass dies die Sichtbarkeit ibis zum Ende der Funktion effektiv erweitert .
Dies liegt daran, dass wir GCC dazu zwingen, eine zusätzliche Variable zu verwenden, ium bis zum Ende zu bleiben :
Die Werte von Argumenten, die nicht in ihren Stapelrahmen gespeichert wurden, werden als "Wert optimiert" angezeigt.
Ich vermute, Sie haben mit -O (ein Wert) kompiliert und greifen in einer Funktion, in der eine Optimierung stattgefunden hat, auf die Variablen a, b, c zu.
Wenn Sie an einer bestimmten Variablen in gdb interessiert sind, können Sie die Variable als "flüchtig" löschen und den Code neu kompilieren. Dadurch deaktiviert der Compiler die Compileroptimierung für diese Variable.
Führen Sie einfach "export COPTS = '- g -O0';" und erstellen Sie Ihren Code neu. Debuggen Sie es nach dem Wiederherstellen mit gdb. Sie werden einen solchen Fehler nicht sehen. Vielen Dank.
Antworten:
gcc -O3
Dies bedeutet, dass Sie mit eg kompiliert haben und der gcc-Optimierer festgestellt hat, dass einige Ihrer Variablen auf irgendeine Weise redundant waren, sodass sie wegoptimiert werden konnten. In diesem speziellen Fall scheinen Sie drei Variablen a, b, c mit demselben Wert zu haben, und vermutlich können sie alle zu einer einzigen Variablen zusammengefasst werden. Kompilieren Sie mit deaktivierter Optimierung, z. B.gcc -O0
wenn Sie solche Variablen sehen möchten (dies ist in der Regel in jedem Fall eine gute Idee für Debug-Builds).quelle
a
ist das nicht redundant, es muss später verwendet werden ..177 case 3 : a+=k[0]&0xffffff; break;
-Og
Es werden nur die Optimierungen angewendet, die die Debugbarkeit nicht beeinträchtigen - sehr nützlich (auchman gcc
für-gdwarf4
). Sie können die Variable, die Sie nicht verlieren möchten, auch vorübergehend als definierenvolatile
, wenn Sie keine Compiler-Optimierungen wünschen, aber die Optimierung nicht für den gesamten Build deaktivieren möchten! Beide Informationen von hier: ask.xmodulo.com/print-optimized-out-value-gdb.html-Og
Option könnte genau das Problem sein, das dazu führt, dass Variablen optimiert werden! Siehe meine Antwort hier: stackoverflow.com/a/63386263/4561887 . Also, wenn Sie irgendwelche Fehler haben, die sagen<optimized out>
oderCan't take address of "var" which isn't an lvalue.
, dann müssen Sie-O0
anstelle von verwenden-Og
!Minimal lauffähiges Beispiel mit Demontageanalyse
Wie immer sehe ich gerne eine Demontage, um besser zu verstehen, was los ist.
In diesem Fall erhalten wir die Erkenntnis, dass, wenn eine Variable so optimiert ist, dass sie nur in einem Register und nicht im Stapel gespeichert wird , und das Register, in dem sie sich befand, überschrieben wird, dies
<optimized out>
wie von R. erwähnt angezeigt wird .Dies kann natürlich nur passieren, wenn die betreffende Variable nicht mehr benötigt wird, sonst würde das Programm seinen Wert verlieren. Daher kann es vorkommen, dass Sie am Anfang der Funktion den Variablenwert sehen können, am Ende jedoch
<optimized out>
.Ein typischer Fall, an dem wir oft interessiert sind, ist der der Funktionsargumente selbst, da dies sind:
Dieses Verständnis hat tatsächlich eine konkrete Anwendung: Wenn Sie das Reverse-Debugging verwenden , können Sie möglicherweise den Wert von interessierenden Variablen wiederherstellen, indem Sie einfach zu ihrem letzten Verwendungspunkt zurückkehren: Wie kann ich den Wert einer <optimierten> -Variablen in anzeigen? C ++?
Haupt c
Kompilieren und ausführen:
Dann haben wir in GDB die folgende Sitzung:
Um zu verstehen, was vor sich geht, beachten Sie die x86 Linux-Aufrufkonvention: Welche Aufrufkonventionen für UNIX- und Linux-Systemaufrufe auf i386 und x86-64 gelten , sollten Sie Folgendes wissen:
Daraus schließen wir:
entspricht dem:
da
i
ist das erste Argument vonf1
und daher in RDI gespeichert.Nun, während wir bei beiden waren:
Der Wert von RDI wurde nicht geändert, und daher konnte GDB ihn jederzeit in diesen Zeilen abfragen.
Sobald der
f2
Anruf getätigt wird:i
wird im Programm nicht mehr benötigtlea 0x1(%rax),%edi
tutEDI = j + RAX + 1
, was beide:j = 1
f2
Aufrufs von festRDI = j
Daher, wenn die folgende Zeile erreicht ist:
Beide der folgenden Anweisungen haben / haben möglicherweise RDI geändert. Dies ist der einzige Ort
i
, an dem gespeichert wurde (f2
möglicherweise wird es als Scratch-Register verwendet undlea
definitiv auf RAX + 1 gesetzt):und so enthält RDI nicht mehr den Wert von
i
. In der Tat war der Wert voni
völlig verloren! Daher ist das einzig mögliche Ergebnis:Ähnliches passiert mit dem Wert von
j
, obwohl erj
erst eine Zeile später nach dem Aufruf unnötig wirdk += f2(j);
.Wenn
j
wir darüber nachdenken, erhalten wir auch einen Einblick, wie intelligent GDB ist. Insbesondere wari += 1;
der Wert vonj
noch in keinem Register oder in keiner Speicheradresse angegeben, und GDB muss ihn ausschließlich anhand von Debug-Informationsmetadaten gekannt haben.-O0
AnalyseWenn wir
-O0
statt-O3
für die Kompilierung verwenden:dann würde die Demontage wie folgt aussehen:
Aus dieser schrecklichen Demontage geht hervor, dass der Wert von RDI gleich zu Beginn der Programmausführung auf den Stapel verschoben wird:
und es wird dann bei Bedarf aus dem Speicher in Register abgerufen, z.
Das gleiche passiert im Grunde genommen,
j
wenn es bei der Initialisierung sofort auf den Stapel verschoben wird:Daher ist es für GDB jederzeit einfach, die Werte dieser Variablen zu finden: Sie sind immer im Speicher vorhanden!
Dies gibt uns auch einen Einblick, warum es nicht möglich ist,
<optimized out>
optimierten Code zu vermeiden : Da die Anzahl der Register begrenzt ist, besteht die einzige Möglichkeit darin, nicht benötigte Register tatsächlich in den Speicher zu verschieben, was den Vorteil von teilweise zunichte machen würde-O3
.Verlängern Sie die Lebensdauer von
i
Wenn wir bearbeitet haben
f1
, uml + i
wie folgt zurückzukehren :dann stellen wir fest, dass dies die Sichtbarkeit
i
bis zum Ende der Funktion effektiv erweitert .Dies liegt daran, dass wir GCC dazu zwingen, eine zusätzliche Variable zu verwenden,
i
um bis zum Ende zu bleiben :Dies tut der Compiler, indem er
i += i
beim ersten Befehl in RDX speichert .Getestet in Ubuntu 18.04, GCC 7.4.0, GDB 8.1.0.
quelle
Es war nicht so. Ihr Compiler hat dies getan, aber es gibt immer noch ein Debug-Symbol für den ursprünglichen Variablennamen.
quelle
Von https://idlebox.net/2010/apidocs/gdb-7.0.zip/gdb_9.html
Die Werte von Argumenten, die nicht in ihren Stapelrahmen gespeichert wurden, werden als "Wert optimiert" angezeigt.
Ich vermute, Sie haben mit -O (ein Wert) kompiliert und greifen in einer Funktion, in der eine Optimierung stattgefunden hat, auf die Variablen a, b, c zu.
quelle
Sie müssen die Compileroptimierung deaktivieren.
Wenn Sie an einer bestimmten Variablen in gdb interessiert sind, können Sie die Variable als "flüchtig" löschen und den Code neu kompilieren. Dadurch deaktiviert der Compiler die Compileroptimierung für diese Variable.
flüchtige int Menge = 0;
quelle
Führen Sie einfach "export COPTS = '- g -O0';" und erstellen Sie Ihren Code neu. Debuggen Sie es nach dem Wiederherstellen mit gdb. Sie werden einen solchen Fehler nicht sehen. Vielen Dank.
quelle
COPTS
ist keine Umgebungsvariable, diegcc
akzeptiert, vorausgesetzt, siegcc
wird verwendet.$COPTS
an Ihren Kompilierungsbefehl anzuhängen .