Как мне использовать valgrind для поиска утечек памяти? - PullRequest
117 голосов
/ 27 февраля 2011

Как мне использовать valgrind для обнаружения утечек памяти в программе?

Пожалуйста, кто-нибудь поможет мне и опишите шаги для выполнения процедуры?

Я использую Ubuntu 10.04 и у меня естьпрограмма a.c, пожалуйста, помогите мне.

Ответы [ 3 ]

167 голосов
/ 08 июля 2017

Как запустить Valgrind

Я хотел бы составить более подробное объяснение того, как использовать Valgrind. эффективно и как устранить утечки памяти. Не оскорблять ОП, а для тех кто придет к этому вопросу и все еще плохо знаком с Linux - , возможно, вам придется установите Valgrind в вашей системе.

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

Valgrind легко использовать для кода C / C ++, но может даже использоваться для других языки при правильной настройке (см. это для Python).

Чтобы запустить valgrind , передайте исполняемый файл в качестве аргумента (вместе с любым параметры к программе).

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

Это даст отчет в конце выполнения вашей программы, который (надеюсь) выглядит так:

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)

У меня есть утечка, но ГДЕ ?

Итак, у вас утечка памяти, и Вальгринд не говорит ничего значимого. Возможно, как-то так:

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)

Давайте посмотрим на код C, который я тоже написал:

#include <stdlib.h>

int main() {
    char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
    return 0;
}

Ну, было потеряно 5 байтов. Как это произошло? Сообщение об ошибке просто говорит main и malloc. В более крупной программе это было бы очень сложно выследить Это из-за того, как исполняемый файл был скомпилирован . Мы можем на самом деле получить построчную информацию о том, что пошло не так. Перекомпилируйте вашу программу с флагом отладки (я использую 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

Теперь в этой отладочной сборке Valgrind указывает на точную строку кода выделение памяти, которая просочилась! (Формулировка важна: она может не быть точно там, где твоя утечка, но что утечка. След поможет вам найти , где .)

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)

Методы отладки утечек памяти и ошибок

  • Используйте www.cplusplus.com ! Он имеет отличную документацию по функциям C / C ++.
  • Общие рекомендации по утечкам памяти:
    • Убедитесь, что динамически выделенная память действительно освобождается.
    • Не выделяйте память и не забудьте назначить указатель.
    • Не перезаписывать указатель новым, если старая память не освобождена.
  • Общие рекомендации по ошибкам памяти:
    • Доступ и запись по адресам и индексам, которые, как вы уверены, принадлежат вам. объем памяти ошибки отличаются от утечек; они часто просто IndexOutOfBoundsException проблемы с типом.
    • Не открывайте и не пишите в память после освобождения.
  • Иногда Valgrind не всегда точно указывает, где ваша ошибка. Иногда у вас много ошибок, и устранение одной из них разрешает каскад других.
    • Перечислите функции в вашем коде, которые зависят от / зависят от «оскорбительный» код с ошибкой памяти. Следить за выполнением программы (возможно даже в gdb, возможно), и ищите ошибки предусловия / постусловия.
    • Попробуйте закомментировать «нарушающий» блок кода (в пределах разумного, поэтому ваш код все еще компилируется). Если ошибка Valgrind исчезнет, ​​вы найдете, где она есть.
  • Если ничего не помогает, попробуйте найти его. Valgrind имеет документацию тоже!

Взгляд на общие утечки и ошибки

Следите за своими указателями

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)

и код:

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

Как помощник преподавателя, я часто видел эту ошибку. Студент использует локальная переменная и забывает обновить оригинальный указатель. Ошибка здесь заметив, что realloc действительно может переместить выделенную память в другое место и измените местоположение указателя. Затем мы оставляем resizeArray, не сказав array->data, куда массив был перемещен.

Неверная запись

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)

и код:

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

Обратите внимание, что Valgrind указывает нам на закомментированную строку кода выше. Массив размером 26 индексируется [0,25], поэтому *(alphabet + 26) является недействительным напишите & mdash; это вне границ. Неверная запись является распространенным результатом отдельные ошибки. Посмотрите на левую сторону вашей операции присваивания.

Неверное чтение

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)

и код:

#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 указывает нам на прокомментированную строку выше. Посмотрите на последнюю итерацию здесь, что
*(destination + 26) = *(source + 26);. Тем не менее, *(source + 26) является снова за пределы, так же, как недопустимая запись. Неверные чтения также общий результат ошибок "один на один". Посмотрите на правую сторону вашего задания работа.


topia с открытым исходным кодом (U / Dys)

Как я узнаю, когда утечка моя? Как мне найти утечку при использовании чужой код? Я нашел утечку, которая не моя; я должен что-то сделать? Все являются законными вопросами. Во-первых, 2 реальных примера, которые показывают 2 класса общие встречи.

Янссон : библиотека JSON

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

Это простая программа: она читает строку JSON и анализирует ее. В процессе создания, мы используем библиотечные вызовы, чтобы выполнить анализ для нас. Янссон делает необходимое Распределение динамически, так как JSON может содержать собственные вложенные структуры. Однако это не означает, что мы decref или «освобождаем» память, данную нам от каждая функция. На самом деле, этот код, который я написал выше, выдает «Неверное чтение» и "Неверная запись". Эти ошибки исчезают, когда вы выводите строку decref для value.

Почему? Переменная value считается «заимствованной ссылкой» в Jansson API. Янссон следит за своей памятью для вас, и вам просто нужно decref JSON структуры независимы друг от друга. Урок здесь: читать документацию . В самом деле. Иногда это трудно понять, но они говорят вам, почему это происходит. Вместо этого мы имеем существующие вопросы об этой ошибке памяти.

SDL : библиотека графики и игр

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

Что не так с этим кодом ? Он постоянно пропускает ~ 212 КиБ памяти для меня. Найдите минутку, чтобы подумать об этом. Мы включаем SDL, а затем выключаем. Ответ? В этом нет ничего плохого.

Поначалу это может показаться странным . По правде говоря, графика грязная, и иногда вы должны принять некоторые утечки как часть стандартной библиотеки. Урок здесь: вам не нужно подавлять каждую утечку памяти . Иногда вам просто нужно подавить утечки , потому что это известные проблемы, с которыми вы ничего не можете сделать . (Это не мое разрешение игнорировать ваши собственные утечки!)

Ответы на пустоту

Как узнать, какая утечка моя?
Это. (Во всяком случае, на 99%)

Как мне найти утечку, когда я использую чужой код?
Скорее всего, кто-то другой уже нашел это. Попробуйте Google! Если это не поможет, используйте навыки, которые я дал вам выше. Если это не помогло, и вы в основном видите вызовы API и немного собственной трассировки стека, см. Следующий вопрос.

Я обнаружил, что утечка не моя; я должен что-то сделать?
Да! У большинства API есть способы сообщать об ошибках и проблемах. Используй их! Помогите вернуть инструменты, которые вы используете в своем проекте!


Дальнейшее чтение

Спасибо, что остался со мной так долго. Я надеюсь, что вы чему-то научились, так как я пытался склониться к широкому кругу людей, приходящих к этому ответу. Надеюсь, вы спросили кое-что еще: как работает распределитель памяти в C? Что на самом деле утечка памяти и ошибка памяти? Чем они отличаются от сегфо? Как работает Valgrind? Если у вас есть что-то из этого, пожалуйста, подайте свое любопытство:

138 голосов
/ 27 февраля 2011

Попробуйте:

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

Пока установлен valgrind, он будет проходить через вашу программу и сообщать вам, что не так.Это может дать вам указатели и приблизительные места, где ваши утечки могут быть найдены.Если вы segfault'ing, попробуйте запустить его через gdb.

25 голосов
/ 06 октября 2014

Вы можете запустить:

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