Wie verwende ich valgrind, um die Speicherlecks in einem Programm zu finden?
Bitte hilft mir jemand und beschreibt die Schritte zur Durchführung des Verfahrens?
Ich benutze Ubuntu 10.04 und habe ein Programm a.c
, bitte helfen Sie mir.
Wie verwende ich valgrind, um die Speicherlecks in einem Programm zu finden?
Bitte hilft mir jemand und beschreibt die Schritte zur Durchführung des Verfahrens?
Ich benutze Ubuntu 10.04 und habe ein Programm a.c
, bitte helfen Sie mir.
Antworten:
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).
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
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:
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All 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)
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!
return 0;
}
Nun, es gingen 5 Bytes verloren. Wie ist es passiert? Der Fehlerbericht sagt nur
main
und 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 gcc
hier):
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)
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.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)
Und der Code:
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
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.
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a 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);
return 0;
}
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.
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba 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);
return 0;
}
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.
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 pointer
json_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?
return 0;
}
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 die decref
Leitung für herausnehmen value
.
Warum? Die Variable value
wird 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.
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
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!)
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!
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:
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
.
your_program
== der Name der ausführbaren Datei oder ein beliebiger Befehl, mit dem Sie Ihre Anwendung ausführen.
Du kannst rennen:
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
Sie können einen Alias in der .bashrc-Datei wie folgt erstellen
alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'
Wenn Sie also Speicherlecks überprüfen möchten, tun Sie dies einfach
vg ./<name of your executable> <command line parameters to your executable>
Dadurch wird eine Valgrind-Protokolldatei im aktuellen Verzeichnis generiert.