Hoe gebruik ik valgrind om geheugenlekken te vinden?

Hoe gebruik ik valgrind om geheugenlekken in een programma te vinden?

Kan iemand me helpen en de stappen beschrijven om de procedure uit te voeren?

Ik gebruik Ubuntu 10.04 en ik heb een programma a.c, help me alsjeblieft.


Antwoord 1, autoriteit 100%

Hoe Valgrind te draaien

Niet om de OP te beledigen, maar voor degenen die op deze vraag komen en nog niet bekend zijn met Linux misschien moet je Valgrindop je systeem installeren.

sudo apt install valgrind  # Ubuntu, Debian, etc.
sudo yum install valgrind  # RHEL, CentOS, Fedora, etc.

Valgrind is gemakkelijk te gebruiken voor C/C++-code, maar kan zelfs voor andere worden gebruikt
talen indien correct geconfigureerd (zie ditvoor Python).

Om Valgrind uit te voeren, geef het uitvoerbare bestand door als argument (samen met eventuele
parameters aan het programma).

valgrind --leak-check=full \
         --show-leak-kinds=all \
         --track-origins=yes \
         --verbose \
         --log-file=valgrind-out.txt \
         ./executable exampleParam1

De vlaggen zijn in het kort:

  • --leak-check=full: “elk afzonderlijk lek wordt in detail getoond”
  • --show-leak-kinds=all: Toon alle “definitieve, indirecte, mogelijke, bereikbare” leksoorten in het “volledige” rapport.
  • --track-origins=yes: Geef de voorkeur aan nuttige uitvoer boven snelheid. Hiermee wordt de oorsprong van niet-geïnitialiseerde waarden bijgehouden, wat erg handig kan zijn voor geheugenfouten. Overweeg om uit te schakelen als Valgrind onaanvaardbaar traag is.
  • --verbose: kan u vertellen over ongebruikelijk gedrag van uw programma. Herhaal dit voor meer breedsprakigheid.
  • --log-file: Schrijf naar een bestand. Handig wanneer de uitvoer de terminalruimte overschrijdt.

Ten slotte wilt u een Valgrind-rapport zien dat er als volgt uitziet:

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)

Ik heb een lek, maar WAAR?

Dus je hebt een geheugenlek en Valgrind zegt niets zinnigs.
Misschien zoiets als dit:

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)

Laten we eens kijken naar de C-code die ik ook heb geschreven:

#include <stdlib.h>
int main() {
    char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
    return 0;
}

Nou, er zijn 5 bytes verloren gegaan. Hoe is het gebeurd? Het foutenrapport zegt alleen:
mainen malloc. In een groter programma zou dat erg lastig zijn om
opsporen. Dit komt door de manier waarop het uitvoerbare bestand is gecompileerd. Wij kunnen
daadwerkelijk regel voor regel details krijgen over wat er mis is gegaan. Compileer uw programma opnieuw
met een debug-vlag (ik gebruik hier gcc):

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

Nu met deze debug-build, Valgrind verwijst naar de exacte regel code
het toewijzen van het geheugen dat is gelekt! (De formulering is belangrijk: misschien niet)
wees precies waar uw lek is, maar watis gelekt. Het spoor helpt je te vinden
waar.)

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)

Technieken voor het debuggen van geheugenlekken & Fouten

  • Maak gebruik van www.cplusplus.com! Het heeft geweldige documentatie over C/C++-functies.
  • Algemeen advies voor geheugenlekken:
    • Zorg ervoor dat uw dynamisch toegewezen geheugen ook daadwerkelijk vrijkomt.
    • Wijs geen geheugen toe en vergeet de aanwijzer niet toe te wijzen.
    • Overschrijf een aanwijzer niet met een nieuwe, tenzij het oude geheugen is vrijgemaakt.
  • Algemeen advies voor geheugenfouten:
    • Toegang tot en schrijven naar adressen en indexen waarvan u zeker weet dat ze van u zijn. Geheugen
      fouten zijn anders dan lekken; ze zijn vaak gewoon IndexOutOfBoundsException
      typ problemen.
    • Maak geen toegang tot het geheugen en schrijf er niet naar nadat het is vrijgemaakt.
  • Soms kunnen uw lekken/fouten aan elkaar worden gekoppeld, net zoals een IDE ontdekt dat u nog geen haakje hebt getypt. Door het ene probleem op te lossen, kunnen andere problemen worden opgelost, dus zoek een probleem dat er goed uitziet en pas enkele van deze ideeën toe:

    • Maak een lijst van de functies in je code die afhankelijk zijn/zijn van de
      “aanstootgevende” code die de geheugenfout heeft. Volg de uitvoering van het programma
      (misschien zelfs in gdbmisschien), en zoek naar precondition/postcondition-fouten. Het idee is om de uitvoering van uw programma te traceren terwijl u zich richt op de levensduur van het toegewezen geheugen.
    • Probeer commentaar te geven op het “aanstootgevende” codeblok (binnen redelijke grenzen, dus uw code)
      compileert nog steeds). Als de Valgrind-fout verdwijnt, heb je gevonden waar het is.
  • Als al het andere faalt, probeer het dan op te zoeken. Valgrind heeft ook documentatie!

Een blik op veelvoorkomende lekken en fouten

Let op je aanwijzingen

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)

En de 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 onderwijsassistent heb ik deze fout vaak gezien. De student maakt gebruik van
een lokale variabele en vergeet de oorspronkelijke aanwijzer bij te werken. De fout hier is:
opmerken dat reallochet toegewezen geheugen daadwerkelijk ergens anders kan verplaatsen
en verander de locatie van de aanwijzer. We verlaten dan resizeArrayzonder het te vertellen
array->datawaar de array naartoe is verplaatst.

Ongeldig schrijven

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)

En de 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;
}

Merk op dat Valgrind ons verwijst naar de bovenstaande regel code met commentaar. de reeks
van maat 26 is geïndexeerd [0,25] daarom is *(alphabet + 26)een ongeldige
schrijven, het is verboden terrein. Een ongeldig schrijven is een veelvoorkomend resultaat van
off-by-one fouten. Kijk naar de linkerkant van je opdrachtbewerking.

Ongeldig gelezen

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)

En de 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 wijst ons op de commentaarregel hierboven. Bekijk hier de laatste iteratie,
dat is
*(destination + 26) = *(source + 26);. *(source + 26)is echter
weer buiten de grenzen, vergelijkbaar met de ongeldige schrijfactie. Ongeldige reads zijn ook een
veelvoorkomend resultaat van off-by-one fouten. Kijk naar de rechterkant van je opdracht
operatie.


Het Open Source (U/Dys)topia

Hoe weet ik wanneer het lek van mij is? Hoe vind ik mijn lek als ik gebruik?
de code van iemand anders? Ik heb een lek gevonden dat niet van mij is; moet ik iets doen? Alle
zijn legitieme vragen. Eerst 2 voorbeelden uit de echte wereld die 2 klassen van laten zien
veelvoorkomende ontmoetingen.

Jansson: een JSON-bibliotheek

#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;
}

Dit is een eenvoudig programma: het leest een JSON-string en parseert het. In de maak,
we gebruiken bibliotheekaanroepen om het parseren voor ons te doen. Jansson maakt het nodige
toewijzingen dynamisch omdat JSON geneste structuren van zichzelf kan bevatten.
Dit betekent echter niet dat we de aan ons gegeven geheugen decrefof “bevrijden” van
elke functie. In feite geeft deze code die ik hierboven heb geschreven zowel een “Ongeldige lees”
en een “Ongeldige schrijven”. Die fouten verdwijnen wanneer u de regel decrefverwijdert
voor value.

Waarom? De variabele valuewordt beschouwd als een “geleende referentie” in de Jansson
API. Jansson houdt zijn geheugen voor u bij, en u hoeft alleen maar te decref
JSON-structuren onafhankelijk van elkaar. De les hier:
lees de documentatie. Werkelijk. Het is soms moeilijk te begrijpen, maar
ze vertellen je waarom deze dingen gebeuren. In plaats daarvan hebben we
bestaande vragenover deze geheugenfout.

SDL: een grafische en gamebibliotheek

#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;
}

Wat is er mis met deze code? Het lekt constant ~ 212 KiB geheugen voor mij. Denk er even over na. We zetten SDL aan en weer uit. Antwoord geven? Er is niets aan de hand.

Dat klinkt in eerste instantie misschien bizar. De waarheid is dat graphics rommelig zijn en soms moet je sommige lekken accepteren als onderdeel van de standaardbibliotheek. De les hier: je hoeft niet elk geheugenlek te onderdrukken. Soms moet je gewoon de lekken onderdrukkenomdat het bekende problemen zijn, je kunt er niets aan doen over. (Dit is niet mijn toestemming om je eigen lekken te negeren!)

Antwoorden op de leegte

Hoe weet ik wanneer het lek van mij is?
Het is. (99% zeker, toch)

Hoe vind ik mijn lek als ik de code van iemand anders gebruik?
De kans is groot dat iemand anders het al heeft gevonden. Probeer Google! Als dat niet lukt, gebruik dan de vaardigheden die ik je hierboven heb gegeven. Als dat niet lukt en je vooral API-aanroepen ziet en weinig van je eigen stacktracering, zie dan de volgende vraag.

Ik heb een lek gevonden dat niet van mij is; moet ik iets doen?
Ja! De meeste API’s hebben manieren om bugs en problemen te melden. Gebruik ze! Help terug te geven aan de tools die je in je project gebruikt!


Verder lezen

Bedankt dat je zo lang bij me bent gebleven. Ik hoop dat je iets hebt geleerd, want ik heb geprobeerd om het brede spectrum van mensen die tot dit antwoord kwamen te helpen. Ik hoop dat je je onderweg een paar dingen hebt gevraagd: Hoe werkt de geheugentoewijzer van C? Wat is eigenlijk een geheugenlek en een geheugenfout? Hoe verschillen ze van segfaults? Hoe werkt Valgrind? Als je een van deze had, voed dan je nieuwsgierigheid:


Antwoord 2, autoriteit 31%

Probeer dit:

valgrind --leak-check=full -v ./your_program

Zolang valgrind is geïnstalleerd, zal het je programma doorlopen en je vertellen wat er mis is. Het kan u aanwijzingen en geschatte plaatsen geven waar uw lekken kunnen worden gevonden. Als je segfouten maakt, probeer het dan via gdb.


Antwoord 3, autoriteit 6%

U kunt uitvoeren:

valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]

Antwoord 4, autoriteit 2%

U kunt als volgt een alias maken in het .bashrc-bestand

alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'

Dus wanneer u geheugenlekken wilt controleren, doet u dat gewoon

vg ./<name of your executable> <command line parameters to your executable>

Dit genereert een Valgrind-logbestand in de huidige map.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes