Sie verwenden valgrind, um Ihr kompiliertes Programm zu testen , nicht den Quellcode.
Tony
6
Die unten von @RageD gegebene Antwort ist richtig. Warum akzeptierst du sie nicht?
Pratik Singhal
1
Ein Leck wird durch etwas verursacht, das Sie nicht tun - dh. frei zugeteilter Speicher. Daher kann Valgrind Ihnen nicht zeigen, wo sich das Leck befindet - nur Sie wissen, wo der zugewiesene Speicher nicht mehr benötigt wird. Indem Sie jedoch mitteilen, welche Zuordnung nicht frei ist () d, indem Sie die Verwendung dieses Speichers durch Ihr Programm verfolgen, sollten Sie in der Lage sein, zu bestimmen, wo er frei werden soll () d. Ein häufiger Fehler ist das Beenden einer Funktion, ohne den zugewiesenen Speicher freizugeben.
Nicht um das OP zu beleidigen, sondern für diejenigen, die auf diese Frage kommen und noch neu in Linux sind - möglicherweise müssen Sie Valgrind auf Ihrem System installieren .
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
Valgrind kann problemlos für C / C ++ - Code verwendet werden, kann aber bei ordnungsgemäßer Konfiguration auch für andere Sprachen verwendet werden (siehe dies für Python).
Übergeben Sie zum Ausführen von Valgrind die ausführbare Datei als Argument (zusammen mit allen Parametern an das Programm).
--leak-check=full: "Jedes einzelne Leck wird detailliert dargestellt"
--show-leak-kinds=all: Zeigen Sie im "vollständigen" Bericht alle "bestimmten, indirekten, möglichen, erreichbaren" Leckarten an.
--track-origins=yes: Bevorzugen Sie die nützliche Ausgabe gegenüber der Geschwindigkeit. Dies verfolgt die Ursprünge nicht initialisierter Werte, die bei Speicherfehlern sehr nützlich sein können. Schalten Sie das Gerät aus, wenn Valgrind nicht akzeptabel langsam ist.
--verbose: Kann Sie über ungewöhnliches Verhalten Ihres Programms informieren. Wiederholen Sie dies für mehr Ausführlichkeit.
--log-file: In eine Datei schreiben. Nützlich, wenn die Ausgabe den Terminalraum überschreitet.
Schließlich möchten Sie einen Valgrind-Bericht sehen, der folgendermaßen aussieht:
HEAP SUMMARY:
in use at exit:0 bytes in 0 blocks
total heap usage:636 allocs,636 frees,25,393 bytes allocatedAll heap blocks were freed -- no leaks are possible
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
ERROR SUMMARY:0 errors from 0 contexts (suppressed:0 from 0)
Ich habe ein Leck, aber WO ?
Sie haben also einen Speicherverlust und Valgrind sagt nichts Sinnvolles. Vielleicht so etwas:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
Werfen wir einen Blick auf den C-Code, den ich auch geschrieben habe:
#include<stdlib.h>int main(){char* string = malloc(5*sizeof(char));//LEAK: not freed!return0;}
Nun, es gingen 5 Bytes verloren. Wie ist es passiert? Der Fehlerbericht sagt nur
mainund malloc. In einem größeren Programm wäre es sehr mühsam, nach etwas zu suchen. Dies liegt daran, wie die ausführbare Datei kompiliert wurde . Wir können tatsächlich zeilenweise Details darüber erhalten, was schief gelaufen ist. Kompilieren Sie Ihr Programm mit einem Debug-Flag neu (ich verwende es gcchier):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall-ggdb3 main.c # add -ggdb3 to it
Mit diesem Debug-Build zeigt Valgrind auf die genaue Codezeile
, die den durchgesickerten Speicher zuweist! (Der Wortlaut ist wichtig: Es ist möglicherweise nicht genau der Ort, an dem sich Ihr Leck befindet, sondern das , was durchgesickert ist. Die Spur hilft Ihnen dabei, herauszufinden,
wo .)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
Techniken zum Debuggen von Speicherlecks und -fehlern
Nutzen Sie www.cplusplus.com ! Es hat eine großartige Dokumentation zu C / C ++ - Funktionen.
Allgemeine Hinweise für Speicherlecks:
Stellen Sie sicher, dass Ihr dynamisch zugewiesener Speicher tatsächlich freigegeben wird.
Ordnen Sie keinen Speicher zu und vergessen Sie nicht, den Zeiger zuzuweisen.
Überschreiben Sie einen Zeiger nicht mit einem neuen, es sei denn, der alte Speicher wird freigegeben.
Allgemeine Hinweise für Speicherfehler:
Greifen Sie auf Adressen und Indizes zu und schreiben Sie auf diese, von denen Sie sicher sind, dass sie Ihnen gehören. Speicherfehler unterscheiden sich von Lecks. Sie sind oft nur IndexOutOfBoundsException
Typprobleme.
Greifen Sie nach dem Freigeben nicht auf den Speicher zu oder schreiben Sie nicht in den Speicher.
Manchmal können Ihre Lecks / Fehler miteinander verknüpft sein, ähnlich wie bei einer IDE, die feststellt, dass Sie noch keine schließende Klammer eingegeben haben. Durch die Lösung eines Problems können andere Probleme behoben werden. Suchen Sie daher nach einem Problem, das als guter Schuldiger gilt, und wenden Sie einige der folgenden Ideen an:
Listen Sie die Funktionen in Ihrem Code auf, die von dem "fehlerhaften" Code abhängen, der den Speicherfehler aufweist. Folgen Sie der Programmausführung (vielleicht sogar in gdbvielleicht) und suchen Sie nach Vor- / Nachbedingungsfehlern. Die Idee ist, die Ausführung Ihres Programms zu verfolgen und sich dabei auf die Lebensdauer des zugewiesenen Speichers zu konzentrieren.
Versuchen Sie, den "beleidigenden" Codeblock auskommentieren (im Rahmen des Zumutbaren, damit Ihr Code weiterhin kompiliert wird). Wenn der Valgrind-Fehler behoben ist, haben Sie gefunden, wo er sich befindet.
Wenn alles andere fehlschlägt, versuchen Sie es nachzuschlagen. Valgrind hat auch Dokumentation !
Ein Blick auf häufige Lecks und Fehler
Achte auf deine Zeiger
60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
Als Lehrassistent habe ich diesen Fehler oft gesehen. Der Schüler verwendet eine lokale Variable und vergisst, den ursprünglichen Zeiger zu aktualisieren. Der Fehler besteht darin, dass reallocder zugewiesene Speicher tatsächlich an einen anderen Ort verschoben und die Position des Zeigers geändert werden kann. Wir gehen dann, resizeArrayohne zu sagen,
array->datawohin das Array verschoben wurde.
Ungültiges Schreiben
1 errors in context 1 of 1:Invalid write of size 1
at 0x4005CA: main (main.c:10)Address0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
Und der Code:
#include<stdlib.h>#include<stdint.h>int main(){char* alphabet = calloc(26,sizeof(char));for(uint8_t i =0; i <26; i++){*(alphabet + i)='A'+ i;}*(alphabet +26)='\0';//null-terminate the string?
free(alphabet);return0;}
Beachten Sie, dass Valgrind uns auf die kommentierte Codezeile oben verweist. Das Array der Größe 26 ist indiziert [0,25], weshalb *(alphabet + 26)es sich um ein ungültiges Schreiben handelt - es ist außerhalb der Grenzen. Ein ungültiger Schreibvorgang ist eine häufige Folge von Fehlern nacheinander. Schauen Sie sich die linke Seite Ihrer Zuweisungsoperation an.
Ungültiges Lesen
1 errors in context 1 of 1:Invalid read of size 1
at 0x400602: main (main.c:9)Address0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
Und der Code:
#include<stdlib.h>#include<stdint.h>int main(){char* destination = calloc(27,sizeof(char));char* source = malloc(26*sizeof(char));for(uint8_t i =0; i <27; i++){*(destination + i)=*(source + i);//Look at the last iteration.}
free(destination);
free(source);return0;}
Valgrind zeigt uns auf die kommentierte Zeile oben. Schauen Sie sich hier die letzte Iteration an *(destination + 26) = *(source + 26);. Jedoch *(source + 26)ist wieder die Grenzen aus, ähnlich wie bei der ungültigen Schreib. Ungültige Lesevorgänge sind auch eine häufige Folge von Fehlern nacheinander. Schauen Sie sich die rechte Seite Ihrer Zuweisungsoperation an.
Die Open Source (U / Dys) Topia
Woher weiß ich, wann das Leck mir gehört? Wie finde ich mein Leck, wenn ich den Code eines anderen verwende? Ich habe ein Leck gefunden, das nicht meins ist. soll ich etwas tun Alle sind berechtigte Fragen. Zunächst zwei Beispiele aus der Praxis, die zwei Klassen gemeinsamer Begegnungen zeigen.
#include<jansson.h>#include<stdio.h>int main(){char* string ="{ \"key\": \"value\" }";json_error_t error;json_t* root = json_loads(string,0,&error);//obtaining a pointerjson_t* value = json_object_get(root,"key");//obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value));//use value
json_decref(value);//Do I free this pointer?
json_decref(root);//What about this one? Does the order matter?return0;}
Dies ist ein einfaches Programm: Es liest eine JSON-Zeichenfolge und analysiert sie. Bei der Erstellung verwenden wir Bibliotheksaufrufe, um das Parsen für uns durchzuführen. Jansson nimmt die erforderlichen Zuordnungen dynamisch vor, da JSON verschachtelte Strukturen von sich selbst enthalten kann. Dies bedeutet jedoch nicht, dass wir decrefden uns gegebenen Speicher von jeder Funktion "befreien" oder "befreien". Tatsächlich löst dieser Code, den ich oben geschrieben habe, sowohl ein "ungültiges Lesen" als auch ein "ungültiges Schreiben" aus. Diese Fehler verschwinden, wenn Sie die decrefLeitung für herausnehmen value.
Warum? Die Variable valuewird in der Jansson-API als "geliehene Referenz" betrachtet. Jansson verfolgt den Speicher für Sie und Sie müssen einfach decref
JSON-Strukturen unabhängig voneinander erstellen. Die Lektion hier:
Lesen Sie die Dokumentation . Ja wirklich. Es ist manchmal schwer zu verstehen, aber sie sagen dir, warum diese Dinge passieren. Stattdessen haben wir
bereits Fragen zu diesem Speicherfehler.
Was ist los mit diesem Code ? Es verliert durchweg ~ 212 KiB Speicher für mich. Nehmen Sie sich einen Moment Zeit, um darüber nachzudenken. Wir schalten SDL ein und dann aus. Antworten? Da ist nichts falsch.
Das mag zunächst bizarr klingen . Um ehrlich zu sein, Grafiken sind chaotisch und manchmal muss man einige Lecks als Teil der Standardbibliothek akzeptieren. Die Lehre hier: Sie müssen nicht jedes Speicherleck unterdrücken . Manchmal müssen Sie nur die Lecks unterdrücken, da es sich um bekannte Probleme handelt, gegen die Sie nichts tun können . (Dies ist nicht meine Erlaubnis, Ihre eigenen Lecks zu ignorieren!)
Antworten auf die Leere
Woher weiß ich, wann das Leck mir gehört?
Es ist. (99% sicher jedenfalls)
Wie finde ich mein Leck, wenn ich den Code eines anderen verwende?
Wahrscheinlich hat es jemand anderes bereits gefunden. Probieren Sie Google aus! Wenn dies fehlschlägt, verwenden Sie die Fähigkeiten, die ich Ihnen oben gegeben habe. Wenn dies fehlschlägt und Sie meistens API-Aufrufe und wenig von Ihrem eigenen Stack-Trace sehen, lesen Sie die nächste Frage.
Ich habe ein Leck gefunden, das nicht meins ist. soll ich etwas tun
Ja! Die meisten APIs bieten Möglichkeiten, Fehler und Probleme zu melden. Benutze sie! Helfen Sie dabei, die Tools zurückzugeben, die Sie in Ihrem Projekt verwenden!
Weiterführende Literatur
Danke, dass du so lange bei mir bist. Ich hoffe, Sie haben etwas gelernt, als ich versuchte, mich um das breite Spektrum der Menschen zu kümmern, die zu dieser Antwort kamen. Einige Dinge, die Sie hoffentlich unterwegs gefragt haben: Wie funktioniert der Speicherzuweiser von C? Was ist eigentlich ein Speicherverlust und ein Speicherfehler? Wie unterscheiden sie sich von Segfaults? Wie funktioniert Valgrind? Wenn Sie eines davon hatten, füttern Sie bitte Ihre Neugier:
Weitaus bessere Antwort, schade, dass dies nicht die akzeptierte Antwort ist.
A. Smoliak
Ich glaube, es ist eine gute Praxis, so etwas zu tun, ich habe einige selbst gemacht
A. Smoliak
1
Kann ich diese Antwort markieren und als zukünftige Referenz für mich selbst verwenden? Gute Arbeit!
Zap
Ist das memcheckTool standardmäßig aktiviert?
Abhiarora
@abhiarora Ja. Die Manpage sagt uns, dass dies memcheckdas Standardwerkzeug ist:--tool=<toolname> [default: memcheck]
Joshua Detwiler
146
Versuche dies:
valgrind --leak-check=full -v ./your_program
Solange valgrind installiert ist, wird es Ihr Programm durchlaufen und Ihnen mitteilen, was falsch ist. Es kann Ihnen Hinweise und ungefähre Stellen geben, an denen Ihre Lecks gefunden werden können. Wenn Sie einen Segfault haben, versuchen Sie es durchzulaufen gdb.
Antworten:
Wie man Valgrind ausführt
Nicht um das OP zu beleidigen, sondern für diejenigen, die auf diese Frage kommen und noch neu in Linux sind - möglicherweise müssen Sie Valgrind auf Ihrem System installieren .
Valgrind kann problemlos für C / C ++ - Code verwendet werden, kann aber bei ordnungsgemäßer Konfiguration auch für andere Sprachen verwendet werden (siehe dies für Python).
Übergeben Sie zum Ausführen von Valgrind die ausführbare Datei als Argument (zusammen mit allen Parametern an das Programm).
Die Flaggen sind kurz:
--leak-check=full
: "Jedes einzelne Leck wird detailliert dargestellt"--show-leak-kinds=all
: Zeigen Sie im "vollständigen" Bericht alle "bestimmten, indirekten, möglichen, erreichbaren" Leckarten an.--track-origins=yes
: Bevorzugen Sie die nützliche Ausgabe gegenüber der Geschwindigkeit. Dies verfolgt die Ursprünge nicht initialisierter Werte, die bei Speicherfehlern sehr nützlich sein können. Schalten Sie das Gerät aus, wenn Valgrind nicht akzeptabel langsam ist.--verbose
: Kann Sie über ungewöhnliches Verhalten Ihres Programms informieren. Wiederholen Sie dies für mehr Ausführlichkeit.--log-file
: In eine Datei schreiben. Nützlich, wenn die Ausgabe den Terminalraum überschreitet.Schließlich möchten Sie einen Valgrind-Bericht sehen, der folgendermaßen aussieht:
Ich habe ein Leck, aber WO ?
Sie haben also einen Speicherverlust und Valgrind sagt nichts Sinnvolles. Vielleicht so etwas:
Werfen wir einen Blick auf den C-Code, den ich auch geschrieben habe:
Nun, es gingen 5 Bytes verloren. Wie ist es passiert? Der Fehlerbericht sagt nur
main
undmalloc
. In einem größeren Programm wäre es sehr mühsam, nach etwas zu suchen. Dies liegt daran, wie die ausführbare Datei kompiliert wurde . Wir können tatsächlich zeilenweise Details darüber erhalten, was schief gelaufen ist. Kompilieren Sie Ihr Programm mit einem Debug-Flag neu (ich verwende esgcc
hier):Mit diesem Debug-Build zeigt Valgrind auf die genaue Codezeile , die den durchgesickerten Speicher zuweist! (Der Wortlaut ist wichtig: Es ist möglicherweise nicht genau der Ort, an dem sich Ihr Leck befindet, sondern das , was durchgesickert ist. Die Spur hilft Ihnen dabei, herauszufinden, wo .)
Techniken zum Debuggen von Speicherlecks und -fehlern
IndexOutOfBoundsException
Typprobleme.Manchmal können Ihre Lecks / Fehler miteinander verknüpft sein, ähnlich wie bei einer IDE, die feststellt, dass Sie noch keine schließende Klammer eingegeben haben. Durch die Lösung eines Problems können andere Probleme behoben werden. Suchen Sie daher nach einem Problem, das als guter Schuldiger gilt, und wenden Sie einige der folgenden Ideen an:
gdb
vielleicht) und suchen Sie nach Vor- / Nachbedingungsfehlern. Die Idee ist, die Ausführung Ihres Programms zu verfolgen und sich dabei auf die Lebensdauer des zugewiesenen Speichers zu konzentrieren.Ein Blick auf häufige Lecks und Fehler
Achte auf deine Zeiger
Und der Code:
Als Lehrassistent habe ich diesen Fehler oft gesehen. Der Schüler verwendet eine lokale Variable und vergisst, den ursprünglichen Zeiger zu aktualisieren. Der Fehler besteht darin, dass
realloc
der zugewiesene Speicher tatsächlich an einen anderen Ort verschoben und die Position des Zeigers geändert werden kann. Wir gehen dann,resizeArray
ohne zu sagen,array->data
wohin das Array verschoben wurde.Ungültiges Schreiben
Und der Code:
Beachten Sie, dass Valgrind uns auf die kommentierte Codezeile oben verweist. Das Array der Größe 26 ist indiziert [0,25], weshalb
*(alphabet + 26)
es sich um ein ungültiges Schreiben handelt - es ist außerhalb der Grenzen. Ein ungültiger Schreibvorgang ist eine häufige Folge von Fehlern nacheinander. Schauen Sie sich die linke Seite Ihrer Zuweisungsoperation an.Ungültiges Lesen
Und der Code:
Valgrind zeigt uns auf die kommentierte Zeile oben. Schauen Sie sich hier die letzte Iteration an
*(destination + 26) = *(source + 26);
. Jedoch*(source + 26)
ist wieder die Grenzen aus, ähnlich wie bei der ungültigen Schreib. Ungültige Lesevorgänge sind auch eine häufige Folge von Fehlern nacheinander. Schauen Sie sich die rechte Seite Ihrer Zuweisungsoperation an.Die Open Source (U / Dys) Topia
Woher weiß ich, wann das Leck mir gehört? Wie finde ich mein Leck, wenn ich den Code eines anderen verwende? Ich habe ein Leck gefunden, das nicht meins ist. soll ich etwas tun Alle sind berechtigte Fragen. Zunächst zwei Beispiele aus der Praxis, die zwei Klassen gemeinsamer Begegnungen zeigen.
Jansson : eine JSON-Bibliothek
Dies ist ein einfaches Programm: Es liest eine JSON-Zeichenfolge und analysiert sie. Bei der Erstellung verwenden wir Bibliotheksaufrufe, um das Parsen für uns durchzuführen. Jansson nimmt die erforderlichen Zuordnungen dynamisch vor, da JSON verschachtelte Strukturen von sich selbst enthalten kann. Dies bedeutet jedoch nicht, dass wir
decref
den uns gegebenen Speicher von jeder Funktion "befreien" oder "befreien". Tatsächlich löst dieser Code, den ich oben geschrieben habe, sowohl ein "ungültiges Lesen" als auch ein "ungültiges Schreiben" aus. Diese Fehler verschwinden, wenn Sie diedecref
Leitung für herausnehmenvalue
.Warum? Die Variable
value
wird in der Jansson-API als "geliehene Referenz" betrachtet. Jansson verfolgt den Speicher für Sie und Sie müssen einfachdecref
JSON-Strukturen unabhängig voneinander erstellen. Die Lektion hier: Lesen Sie die Dokumentation . Ja wirklich. Es ist manchmal schwer zu verstehen, aber sie sagen dir, warum diese Dinge passieren. Stattdessen haben wir bereits Fragen zu diesem Speicherfehler.SDL : eine Grafik- und Spielebibliothek
Was ist los mit diesem Code ? Es verliert durchweg ~ 212 KiB Speicher für mich. Nehmen Sie sich einen Moment Zeit, um darüber nachzudenken. Wir schalten SDL ein und dann aus. Antworten? Da ist nichts falsch.
Das mag zunächst bizarr klingen . Um ehrlich zu sein, Grafiken sind chaotisch und manchmal muss man einige Lecks als Teil der Standardbibliothek akzeptieren. Die Lehre hier: Sie müssen nicht jedes Speicherleck unterdrücken . Manchmal müssen Sie nur die Lecks unterdrücken, da es sich um bekannte Probleme handelt, gegen die Sie nichts tun können . (Dies ist nicht meine Erlaubnis, Ihre eigenen Lecks zu ignorieren!)
Antworten auf die Leere
Woher weiß ich, wann das Leck mir gehört?
Es ist. (99% sicher jedenfalls)
Wie finde ich mein Leck, wenn ich den Code eines anderen verwende?
Wahrscheinlich hat es jemand anderes bereits gefunden. Probieren Sie Google aus! Wenn dies fehlschlägt, verwenden Sie die Fähigkeiten, die ich Ihnen oben gegeben habe. Wenn dies fehlschlägt und Sie meistens API-Aufrufe und wenig von Ihrem eigenen Stack-Trace sehen, lesen Sie die nächste Frage.
Ich habe ein Leck gefunden, das nicht meins ist. soll ich etwas tun
Ja! Die meisten APIs bieten Möglichkeiten, Fehler und Probleme zu melden. Benutze sie! Helfen Sie dabei, die Tools zurückzugeben, die Sie in Ihrem Projekt verwenden!
Weiterführende Literatur
Danke, dass du so lange bei mir bist. Ich hoffe, Sie haben etwas gelernt, als ich versuchte, mich um das breite Spektrum der Menschen zu kümmern, die zu dieser Antwort kamen. Einige Dinge, die Sie hoffentlich unterwegs gefragt haben: Wie funktioniert der Speicherzuweiser von C? Was ist eigentlich ein Speicherverlust und ein Speicherfehler? Wie unterscheiden sie sich von Segfaults? Wie funktioniert Valgrind? Wenn Sie eines davon hatten, füttern Sie bitte Ihre Neugier:
malloc
Cs Speicherzuordnungquelle
memcheck
Tool standardmäßig aktiviert?memcheck
das Standardwerkzeug ist:--tool=<toolname> [default: memcheck]
Versuche dies:
valgrind --leak-check=full -v ./your_program
Solange valgrind installiert ist, wird es Ihr Programm durchlaufen und Ihnen mitteilen, was falsch ist. Es kann Ihnen Hinweise und ungefähre Stellen geben, an denen Ihre Lecks gefunden werden können. Wenn Sie einen Segfault haben, versuchen Sie es durchzulaufen
gdb
.quelle
your_program
== der Name der ausführbaren Datei oder ein beliebiger Befehl, mit dem Sie Ihre Anwendung ausführen.Du kannst rennen:
quelle
Sie können einen Alias in der .bashrc-Datei wie folgt erstellen
Wenn Sie also Speicherlecks überprüfen möchten, tun Sie dies einfach
Dadurch wird eine Valgrind-Protokolldatei im aktuellen Verzeichnis generiert.
quelle