2016-07-07 10 views
5

Ich hoffe, diese Frage ist nicht zu offen. Ich stieß auf ein Speicherproblem mit Rust, wo ich an "out of memory" from calling next on an Iterator trait object bekam. Ich bin mir nicht sicher, wie ich es debuggen soll. Drucke haben mich nur zu dem Punkt gebracht, an dem der Fehler auftritt. Ich bin nicht sehr vertraut mit anderen Tools wie ltrace, also obwohl ich eine Spur erstellen konnte (231MiB, pff), wusste ich nicht wirklich was damit zu tun. Ist eine solche Spur nützlich? Würde ich besser gdb/lldb greifen? Oder Valgrind?Wie kann ich ein Speicherproblem in Rust debuggen?

+0

Valgrind ist immer ein guter Anfang. Versuchen Sie auch, Ihren Code zu optimieren. –

+2

@EliSadoff Wie hilft die Optimierung meines Codes beim Debugging von Speicherproblemen? – Apanatshka

Antwort

3

Im Allgemeinen würde ich versuchen, den folgenden Ansatz zu tun:

  1. Boilerplate-Reduktion: Versuchen Sie, das Problem der OOM zu verengen, so dass Sie um zusätzlichen Code nicht zu viel haben. Mit anderen Worten: Je schneller Ihr Programm abstürzt, desto besser. Manchmal ist es auch möglich, ein bestimmtes Stück Code herauszureißen und für die Untersuchung in eine extra Binärdatei zu schreiben.

  2. Problem Größenreduktion: Senken Sie das Problem von OOM zu einem einfachen „zu viel Speicher“, so dass Sie die einige Teil Abfälle etwas, aber eigentlich sagen kann, dass es zu einem OOM führt nicht. Wenn es zu schwierig ist zu sagen, ob das Problem auftritt oder nicht, können Sie das Speicherlimit verringern.

    Versammlung
    ulimit -Sv 500000 # that's 500MB 
    ./path/to/exe --foo 
    
  3. Informationen:: Unter Linux können diese ulimit verwenden getan werden Wenn Sie Problem klein genug ist, sind Sie bereit, Informationen zu sammeln, die einen niedrigeren Geräuschpegel aufweist. Es gibt mehrere Möglichkeiten, die Sie ausprobieren können. Denken Sie daran, Ihr Programm mit Debug-Symbolen zu kompilieren. Es kann auch ein Vorteil sein, die Optimierung auszuschalten, da dies normalerweise zu einem Informationsverlust führt. Beide können archiviert werden, indem das Flag --release während der Kompilierung NICHT verwendet wird.

    • Heap Profilierung: Ein Weg zu gperftools ist zu verwenden:

      LD_PRELOAD="/usr/lib/libtcmalloc.so" HEAPPROFILE=/tmp/profile ./path/to/exe --foo 
      pprof --gv ./path/to/exe /tmp/profile/profile.0100.heap 
      

      Dies zeigt Ihnen ein Diagramm, das symbolisiert, welche Teile des Programms essen, die Menge an Speicher. Weitere Informationen finden Sie unter official docs.

    • rr: Manchmal ist es sehr schwierig herauszufinden, was tatsächlich passiert, besonders nachdem Sie ein Profil erstellt haben. Vorausgesetzt, dass Sie in Schritt 2 einen guten Job, können Sie rr verwenden:

      rr record ./path/to/exe --foo 
      rr replay 
      

      Dies wird eine GDB mit Supermächten laichen. Der Unterschied zu einer normalen Debug-Sitzung ist, dass Sie nicht nur continue, sondern auch reverse-continue. Grundsätzlich wird Ihr Programm von einer Aufnahme ausgeführt, bei der Sie beliebig vor- und zurückspringen können. This wiki page bietet Ihnen einige zusätzliche Beispiele. Eine Sache, die darauf hinweist, ist, dass rr nur mit GDB zu arbeiten scheint.

    • Gutes altes Debugging: Manchmal bekommen Sie Spuren und Aufnahmen, die immer noch viel zu groß sind. In diesem Fall können Sie (in Kombination mit dem ulimit Trick) nur GDB benutzen und warten, bis das Programm abstürzt:

      gdb --args ./path/to/exe --foo 
      

      Sie sollen nun eine normale Debug-Sitzung, wo Sie, was der aktuelle Stand des Programms untersuchen war . GDB kann auch mit Coredumps gestartet werden. Das allgemeine Problem bei diesem Ansatz besteht darin, dass Sie nicht rechtzeitig zurückgehen können und Sie nicht mit der Ausführung fortfahren können. Sie sehen also nur den aktuellen Status einschließlich aller Stack-Frames und Variablen. Hier könnten Sie auch LLDB verwenden, wenn Sie möchten.

  4. (Potential) fix + repeat: Nachdem Sie einen Klebstoff, was Sie könnte schief gehen kann versuchen, den Code zu ändern. Dann versuche es noch einmal. Wenn es immer noch nicht funktioniert, gehen Sie zurück zu Schritt 3 und versuchen Sie es erneut.

1

Im Allgemeinen können Sie zum Debuggen entweder einen protokollbasierten Ansatz verwenden (entweder durch Einfügen der Protokolle selbst oder mit einem Tool wie ltrace, ptrace, ... um die Protokolle für Sie zu generieren) oder Sie können Verwenden Sie einen Debugger.

Beachten Sie, dass ltrace, ptrace oder Debugger-basierte Ansätze erfordern, dass Sie das Problem reproduzieren können; Ich bevorzuge manuelle Protokolle, da ich in einer Branche arbeite, in der Fehlerberichte im Allgemeinen zu ungenau sind, um eine sofortige Reproduktion zu ermöglichen (und daher verwenden wir Protokolle, um das Reproduzier-Szenario zu erstellen).

Rust unterstützt beide Ansätze, und das Standard-Toolset, das für C- oder C++ - Programme verwendet wird, funktioniert gut.

Mein persönlicher Ansatz ist einige Anmeldung an Ort und Stelle, um schnell einzugrenzen, wo das Problem auftritt, und wenn die Protokollierung nicht ausreicht, um einen Debugger für eine feinere Prüfung zu starten. In diesem Fall würde ich empfehlen, sofort zum Debugger zu gehen.

Es wird panic generiert, was bedeutet, dass Sie beim Aufrufen des Call Hooks sowohl den Call Stack als auch den Speicherstatus sehen können, wenn etwas schief läuft.

Starten Sie Ihr Programm mit dem Debugger, setzen Sie einen Breakpoint auf den Panikhaken, führen Sie das Programm, profitieren.

Verwandte Themen