Bagaimana cara menggunakan valgrind untuk menemukan kebocoran memori?

183

Bagaimana cara menggunakan valgrind untuk menemukan kebocoran memori dalam suatu program?

Tolong seseorang bantu saya dan jelaskan langkah-langkah untuk menjalankan prosedur?

Saya menggunakan Ubuntu 10,04 dan saya punya program a.c, tolong bantu saya.

pengguna484457
sumber
16
Anda menggunakan valgrind untuk menguji program terkompilasi Anda , bukan kode sumber.
Tony
6
Jawaban yang diberikan di bawah ini oleh @RageD benar, mengapa Anda tidak menerimanya?
Pratik Singhal
1
Kebocoran disebabkan oleh sesuatu yang Anda gagal lakukan - yaitu. memori yang dialokasikan gratis. Karenanya Valgrind tidak dapat menunjukkan "di mana" kebocoran itu terjadi - hanya Anda yang tahu di mana memori yang dialokasikan tidak lagi diperlukan. Namun, dengan memberi tahu Anda alokasi mana yang tidak bebas () d, dengan melacak penggunaan memori itu melalui program Anda, Anda harus dapat menentukan di mana ia seharusnya mendapatkan gratis () d. Kesalahan umum adalah kesalahan-keluar fungsi tanpa membebaskan memori yang dialokasikan.
MikeW
1
Terkait: dengan alat apa pun: stackoverflow.com/questions/6261201/…
Ciro Santilli 郝海东 冠状 病 六四 事件 事件

Jawaban:

299

Cara Menjalankan Valgrind

Bukan untuk menghina OP, tetapi bagi mereka yang datang ke pertanyaan ini dan masih baru di Linux - Anda mungkin harus menginstal Valgrind pada sistem Anda.

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

Valgrind siap digunakan untuk kode C / C ++, tetapi bahkan dapat digunakan untuk bahasa lain ketika dikonfigurasi dengan benar (lihat ini untuk Python).

Untuk menjalankan Valgrind , operasikan yang dapat dieksekusi sebagai argumen (bersama dengan parameter apa pun ke program).

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

Singkatnya, bendera:

  • --leak-check=full: "setiap kebocoran individu akan ditampilkan secara detail"
  • --show-leak-kinds=all: Tunjukkan semua "jenis kebocoran" yang pasti, tidak langsung, mungkin, dapat dicapai dalam laporan "penuh".
  • --track-origins=yes: Mendukung keluaran yang berguna daripada kecepatan. Ini melacak asal-usul nilai yang tidak diinisialisasi, yang bisa sangat berguna untuk kesalahan memori. Pertimbangkan mematikan jika Valgrind sangat lambat.
  • --verbose: Dapat memberi tahu Anda tentang perilaku tidak biasa dari program Anda. Ulangi untuk lebih banyak kata.
  • --log-file: Menulis ke file. Berguna saat output melebihi ruang terminal.

Terakhir, Anda ingin melihat laporan Valgrind yang terlihat seperti ini:

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)

Saya mengalami kebocoran, tetapi DI MANA ?

Jadi, Anda mengalami kebocoran memori, dan Valgrind tidak mengatakan sesuatu yang berarti. Mungkin, kira-kira seperti ini:

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)

Mari kita lihat kode C yang saya tulis juga:

#include <stdlib.h>

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

Nah, ada 5 byte yang hilang. Bagaimana hal itu terjadi? Laporan kesalahan hanya mengatakan maindan malloc. Dalam program yang lebih besar, itu akan sangat sulit untuk diburu. Ini karena bagaimana executable dikompilasi . Kami benar-benar bisa mendapatkan detail baris demi baris tentang apa yang salah. Kompilasi ulang program Anda dengan bendera debug (saya gunakan di gccsini):

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

Sekarang dengan build debug ini, Valgrind menunjuk ke baris kode yang tepat mengalokasikan memori yang bocor! (Kata-katanya penting: mungkin tidak persis di mana kebocoran Anda, tetapi apa yang bocor. Jejak membantu Anda menemukan di mana .)

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)

Teknik untuk Debugging Memori Kebocoran & Kesalahan

  • Manfaatkan www.cplusplus.com ! Ini memiliki dokumentasi yang bagus tentang fungsi C / C ++.
  • Saran umum untuk kebocoran memori:
    • Pastikan memori yang dialokasikan secara dinamis ternyata dibebaskan.
    • Jangan mengalokasikan memori dan lupa menetapkan penunjuk.
    • Jangan menimpa pointer dengan yang baru kecuali memori lama dibebaskan.
  • Saran umum untuk kesalahan memori:
    • Akses dan tulis ke alamat dan indeks yang Anda yakini milik Anda. Kesalahan memori berbeda dari kebocoran; mereka sering hanya IndexOutOfBoundsException mengetik masalah.
    • Jangan mengakses atau menulis ke memori setelah membebaskannya.
  • Kadang-kadang kebocoran / kesalahan Anda dapat dihubungkan satu sama lain, seperti IDE yang menemukan bahwa Anda belum mengetikkan braket penutup. Menyelesaikan satu masalah dapat menyelesaikan yang lainnya, jadi cari yang terlihat sebagai pelakunya yang baik dan terapkan beberapa dari gagasan ini:

    • Buat daftar fungsi dalam kode Anda yang bergantung pada / tergantung pada kode "menyinggung" yang memiliki kesalahan memori. Ikuti eksekusi program (mungkin bahkan di gdbmungkin), dan cari kesalahan prekondisi / postkondisi. Idenya adalah untuk melacak eksekusi program Anda sambil berfokus pada masa pakai memori yang dialokasikan.
    • Cobalah mengomentari blok kode "menyinggung" (karena alasan, jadi kode Anda masih dikompilasi). Jika kesalahan Valgrind hilang, Anda telah menemukannya.
  • Jika semuanya gagal, coba cari. Valgrind juga memiliki dokumentasi !

Pandangan Kebocoran dan Kesalahan Umum

Perhatikan petunjuk Anda

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)

Dan kodenya:

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

Sebagai asisten pengajar, saya sering melihat kesalahan ini. Siswa menggunakan variabel lokal dan lupa untuk memperbarui pointer asli. Kesalahan di sini adalah memperhatikan bahwa reallocsebenarnya dapat memindahkan memori yang dialokasikan di tempat lain dan mengubah lokasi pointer. Kami kemudian pergi resizeArraytanpa memberi tahu ke array->datamana array dipindahkan.

Tulisan tidak valid

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)

Dan kodenya:

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

Perhatikan bahwa Valgrind mengarahkan kita ke baris kode yang dikomentari di atas. Array ukuran 26 diindeks [0,25] yang mengapa *(alphabet + 26)merupakan penulisan yang tidak valid — di luar batas. Tulisan yang tidak valid adalah hasil umum dari kesalahan satu per satu. Lihatlah sisi kiri operasi tugas Anda.

Baca tidak valid

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)

Dan kodenya:

#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 mengarahkan kami ke baris komentar di atas. Lihatlah iterasi terakhir di sini, yaitu
*(destination + 26) = *(source + 26);. Namun, *(source + 26)sudah di luar batas lagi, sama dengan penulisan yang tidak valid. Pembacaan tidak valid juga merupakan hasil umum dari kesalahan tidak langsung. Lihatlah sisi kanan operasi penugasan Anda.


Sumber Terbuka (U / Dys) topia

Bagaimana saya tahu kapan kebocoran itu milik saya? Bagaimana cara menemukan kebocoran saya ketika saya menggunakan kode orang lain? Saya menemukan kebocoran yang bukan milik saya; haruskah saya melakukan sesuatu? Semua adalah pertanyaan yang sah. Pertama, 2 contoh dunia nyata yang menunjukkan 2 kelas pertemuan umum.

Jansson : perpustakaan 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;
}

Ini adalah program sederhana: ia membaca string JSON dan mem-parsingnya. Dalam pembuatannya, kami menggunakan panggilan pustaka untuk melakukan parsing bagi kami. Jansson membuat alokasi yang diperlukan secara dinamis karena JSON dapat berisi struktur bersarang itu sendiri. Namun, ini tidak berarti kita decrefatau "membebaskan" memori yang diberikan kepada kita dari setiap fungsi. Faktanya, kode yang saya tulis di atas melontarkan "Pembacaan tidak valid" dan "Penulis tidak valid". Kesalahan itu hilang ketika Anda mengambil decrefgaris untuk value.

Mengapa? Variabel valuedianggap sebagai "referensi yang dipinjam" di Jansson API. Jansson melacak ingatannya untuk Anda, dan Anda hanya perlu decref struktur JSON yang independen satu sama lain. Pelajaran di sini: baca dokumentasi . Betulkah. Terkadang sulit untuk dipahami, tetapi mereka memberi tahu Anda mengapa hal-hal ini terjadi. Sebagai gantinya, kami memiliki pertanyaan yang ada tentang kesalahan memori ini.

SDL : perpustakaan grafik dan permainan

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

Apa yang salah dengan kode ini ? Secara konsisten kebocoran ~ 212 KiB memori untuk saya. Luangkan waktu sejenak untuk memikirkannya. Kami menghidupkan dan mematikan SDL. Menjawab? Tidak ada yang salah.

Itu mungkin terdengar aneh pada awalnya . Sejujurnya, gambarnya berantakan dan terkadang Anda harus menerima beberapa kebocoran sebagai bagian dari perpustakaan standar. Pelajaran di sini: Anda tidak perlu memadamkan setiap kebocoran memori . Terkadang Anda hanya perlu menekan kebocoran karena itu adalah masalah yang diketahui tidak dapat Anda lakukan . (Ini bukan izin saya untuk mengabaikan kebocoran Anda sendiri!)

Jawaban untuk kekosongan

Bagaimana saya tahu kapan kebocoran itu milik saya?
Ini. (Lagi pula, 99% yakin)

Bagaimana cara menemukan kebocoran saya ketika saya menggunakan kode orang lain?
Peluangnya adalah orang lain sudah menemukannya. Coba Google! Jika itu gagal, gunakan keterampilan yang saya berikan kepada Anda di atas. Jika itu gagal dan Anda sebagian besar melihat panggilan API dan sedikit jejak stack Anda sendiri, lihat pertanyaan berikutnya.

Saya menemukan kebocoran yang bukan milik saya; haruskah saya melakukan sesuatu?
Iya! Sebagian besar API memiliki cara untuk melaporkan bug dan masalah. Gunakan mereka! Bantu memberikan kembali alat yang Anda gunakan dalam proyek Anda!


Bacaan lebih lanjut

Terima kasih sudah tinggal bersamaku selama ini. Saya harap Anda telah mempelajari sesuatu, karena saya mencoba untuk cenderung ke spektrum yang luas dari orang-orang yang tiba pada jawaban ini. Beberapa hal yang saya harap sudah Anda tanyakan: Bagaimana cara kerja pengalokasi memori C? Apa sebenarnya kebocoran memori dan kesalahan memori? Apa bedanya dengan segfault? Bagaimana cara kerja Valgrind? Jika Anda memiliki salah satu dari ini, tolong beri makan rasa ingin tahu Anda:

Joshua Detwiler
sumber
4
Jawaban yang jauh lebih baik, sayang ini bukan jawaban yang diterima.
A. Smoliak
Saya percaya ini adalah praktik yang baik untuk melakukan hal seperti itu, saya melakukan beberapa hal sendiri
A. Smoliak
1
Bisakah saya memberi bintang pada jawaban ini dan menggunakannya sebagai referensi di masa depan untuk diri saya sendiri? Kerja bagus!
Zap
apakah memcheckalat diaktifkan secara default?
abhiarora
@abhiarora Ya. Halaman manual memberitahu kita bahwa itu memcheckadalah alat default:--tool=<toolname> [default: memcheck]
Joshua Detwiler
146

Coba ini:

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

Selama valgrind diinstal, ia akan melewati program Anda dan memberi tahu Anda apa yang salah. Ini dapat memberi Anda petunjuk dan perkiraan tempat-tempat di mana kebocoran Anda dapat ditemukan. Jika Anda melakukan segmentasi, coba jalankan gdb.

RageD
sumber
Apa yang dimaksud dengan "your_program"? Apakah lokasi kode sumber ini atau nama aplikasi seperti file apk?
HoangVu
7
your_program== nama yang dapat dieksekusi atau perintah apa pun yang Anda gunakan untuk menjalankan aplikasi Anda.
RageD
27

Anda dapat menjalankan:

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

Anda dapat membuat alias dalam file .bashrc sebagai berikut

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

Jadi, setiap kali Anda ingin memeriksa kebocoran memori, lakukan saja

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

Ini akan menghasilkan file log Valgrind di direktori saat ini.

Sachin Rastogi
sumber