In welchem ​​Szenario ist es nützlich, beim Debuggen die Demontagesprache zu verwenden

StackOverflow https://stackoverflow.com/questions/1565567

  •  21-09-2019
  •  | 
  •  

Frage

Ich habe folgende grundlegende Fragen:

  • Wenn wir die Demontage beim Debuggen beinhalten sollten

  • So interpretieren Sie die Demontage, zum Beispiel unten, wofür jedes Segment steht

00637CE3 8B 55 08             mov         edx,dword ptr [arItem]
00637CE6 52                   push        edx
00637CE7 6A 00                push        0
00637CE9 8B 45 EC             mov         eax,dword ptr [result]
00637CEC 50                   push        eax
00637CED E8 3E E3 FF FF       call        getRequiredFields (00636030)
00637CF2 83 C4 0C             add 

Sprache: C ++

Plattform: Windows

War es hilfreich?

Lösung

Es ist sehr nützlich zu schätzen, wie effizient der vom Compiler emittierte Code ist.

Zum Beispiel, wenn Sie eine verwenden std::vector::operator[] In einer Schleife ohne Demontage ist es ziemlich schwer zu erraten, dass jeder Anruf dazu operator[] Tatsächlich erfordert zwei Speicherzugriffe, aber die Verwendung eines Iterators für dasselbe müsste einen Speicherzugriff erfordern.

In Ihrem Beispiel:

mov         edx,dword ptr [arItem] // value stored at address "arItem" is loaded onto the register
push        edx // that register is pushes into stack
push        0 // zero is pushed into stack
mov         eax,dword ptr [result] // value stored at "result" address us loaded onto the register
push        eax // that register is pushed into stack
call        getRequiredFields (00636030) // getRequiredFields function is called

Dies ist eine typische Sequenz für das Aufrufen einer Funktion - Paramate werden in Stapel gedrückt und dann wird das Steuerelement auf diesen Funktionscode übertragen (call Anweisung).

Auch die Verwendung der Demontage ist sehr nützlich, wenn sie an Argumenten über "Wie es nach der Kompilierung funktioniert" - wie CAF Punkte in Seine Antwort auf diese Frage.

Andere Tipps

1 - Wir sollten (i) die Demontage in das Debuggen als letztes Mittel beinhalten. Im Allgemeinen generiert ein optimierender Compiler Code, der für das menschliche Auge nicht trivial ist. Die Anweisungen werden neu bestellt, ein toter Code wird beseitigt, ein bestimmter Code wird eingebaut usw. usw. Daher ist es nicht notwendig und bei Bedarf nicht einfach, den zerlegten Code zu verstehen. Zum Beispiel schaue ich mir manchmal die Demontage an, um festzustellen, ob Konstanten Teil des Opcode sind oder in const -Variablen gespeichert sind.

2 - Dieser Code -Stück nennt eine Funktion wie getRequiredfields (Ergebnis, 0, Aritem). Sie müssen die Versammlungssprache für den gewünschten Prozessor lernen. Für x86 finden Sie unter www.intel.com und erhalten Sie die Handbücher des IA32.

Wenn Sie die Demontage beinhalten: Wenn Sie genau wissen möchten, was die CPU tut, wenn sie Ihr Programm ausführt, oder wenn Sie den Quellcode in jeder höheren Sprache, in der das Programm geschrieben wurde, nicht (C ++) haben (in Ihrem Fall).

So interpretieren Sie Assembly -Code: Lernen Sie die Assemblersprache. Sie finden eine umfassende Referenz auf Intel x86 -CPU -Anweisungen in Die Prozessorhandbücher von Intel.

Das von Ihnen veröffentlichte Code -Stück erstellt Argumente für einen Funktionsaufruf (indem Sie einige Werte auf den Stapel erhalten und drücken und einen Wert in das Register setzen eax) und ruft dann die Funktion auf getRequiredFields.

Ich begann 1982 mit dem Debuggen von PL/M-Programmen für CP/M-80 und späteres digitales Forschungsosen. In den frühen Tagen von MS-DOS war es gleich, bis Microsoft Symdeb einführte, ein Befehlszeilendebugger, bei dem Quelle und Assembly gleichzeitig angezeigt wurden. Symdeb war ein Sprung nach vorne, aber nicht so groß, da die früheren Debugger mich gezwungen hatten zu lernen, zu erkennen, welcher Assembly -Code zu welcher Quellcode -Linie gehörte. Vor Codeview war der beste Debugger Pfix86 von Phoenix Technologies. Numegas Softice war der beste Debugger (abgesehen von reinen Hardware -ICES), auf die ich jemals begegnet bin, als sie nicht nur meine Anwendung debuggierte, sondern mich mühelos durch die inneren Funktionen von Windows führte. Aber ich schweife ab.

Ende 1990 hat ein Berater in einem Projekt, an dem ich gearbeitet habe, auf mich zugegangen und sagte, er habe diesen (sehr frühen) C ++ - Fehler, an dem er seit Tagen gearbeitet hatte, aber nicht verstehen könne, was das Problem war. Er durch den Quellcode (auf einem nicht grafischen DOS-Debugger mit Fenster) für mich, während ich alle ungeduldig wurde. Schließlich unterbrach ich ihn und schaute die Debugger -Optionen durch, und es gab sicher, dass es den gemischten Quell-/Montagemodus mit Registern und allem gab. Dies machte es leicht zu erkennen, dass die Anwendung versuchte, einen internen Zeiger (für lokale Variablen) mit Null zu befreien. Für dieses Problem war der Quellcode -Modus überhaupt keine Hilfe. Die heutigen C ++ - Compiler werden wahrscheinlich keinen solchen Fehler mehr enthalten, aber es wird andere geben.

Wenn Sie das Debuggen auf Assembly-Ebene kennen, können Sie die Beziehung zwischen Quellen und Kompilierern in dem Maße verstehen, welcher Code der Compiler generieren wird. Viele Leute hier auf Stackoverflow sagen "Profil-Profil-Profil", aber dies geht noch einen Schritt weiter, als Sie lernen, welche Quellcode konstruiert (ich schreibe in c), wann und welche zu vermeiden sind. Ich vermute, dass dies bei C ++ noch wichtiger ist, was viel Code generieren kann, ohne dass der Entwickler etwas vermutet. Zum Beispiel gibt es eine Standardklasse zum Umgang mit Listen von Objekten, die ohne Nachteile zu sein scheinen - nur ein paar Codezeilen und diese fantastische Funktionalität! - Bis Sie sich die Punktzahlen von seltsamen Verfahren ansehen, die sie generiert. Ich sage nicht, dass es falsch ist, sie zu benutzen. Ich sage nur, dass der Entwickler die Vor- und Nachteile der Verwendung von ihnen bewusst sein sollte. Überladen von Operatoren mag eine großartige Funktionalität sein (etwas seltsam für einen Wysiwyg -Programmierer wie mich), aber wie hoch ist der Preis bei Ausführungsgeschwindigkeit? Wenn Sie "nichts" sagen, sage ich "beweisen Sie es."

Es ist nie falsch, beim Debuggen einen gemischten oder reinen Montagemodus zu verwenden. Schwierige Fehler sind normalerweise einfacher zu finden und der Entwickler lernt, effizientere Code zu schreiben. Entwickler aus dem interpretierten Camp (C# und Java) werden sagen, dass ihr Code genauso effizient ist wie die kompilierten Sprachen. Wenn Sie jedoch die Versammlung kennen, wissen Sie auch, warum sie falsch liegen, warum sie tot sind. Sie können lächeln und denken: "Ja, erzähl mir davon!"

Nachdem Sie mit verschiedenen Compilern zusammengearbeitet haben, werden Sie mit der erstaunlichsten Fähigkeit zur Generstellung der Code-Generation auf eine begegnen. Ein PowerPC -Compiler hat drei verschachtelte Schleifen einfach durch die überlegene Code -Interpretation des Optimierers in eine Schleife zusammengefasst. Neben dem Typ, der schrieb, dass ich ... nun, sagen wir einfach in einer anderen Liga.

Bis vor ungefähr zehn Jahren schrieb ich einiges an reine Montage, aber mit mehrstufigen Pipelines, mehreren Ausführungseinheiten und jetzt mehreren Kernen, die mit dem C-Compiler miteinander verbunden sind, schlägt mich zweifellos. Auf der anderen Seite weiß ich, womit der Compiler gute Arbeit leisten kann und womit er nicht arbeiten sollte: Müll in stillem Müll entspricht. Dies gilt für jeden Compiler, der die Montageausgabe erzeugt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top