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
main
dan 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 gcc
sini):
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
gdb
mungkin), 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 realloc
sebenarnya dapat memindahkan memori yang dialokasikan di tempat lain dan mengubah lokasi pointer. Kami kemudian pergi resizeArray
tanpa memberi tahu ke
array->data
mana 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 decref
atau "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 decref
garis untuk value
.
Mengapa? Variabel value
dianggap 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: