Libelektra: KeySet menutup kebocoran memori

Dibuat pada 4 Nov 2019  ·  32Komentar  ·  Sumber: ElektraInitiative/libelektra

Saat melakukan benchmarking elektrad baru saya mengalami gejala kebocoran memori.
Penggunaan memori akan naik setelah membuat pegangan baru dan memanggil kdbGet() tetapi tidak pernah turun setelah ksClose() dan kdbClose() .

Kecurigaan pertama saya adalah mmap caching - yang mungkin juga menyebabkan kebocoran memori karena memori mmap tidak pernah dibebaskan - tetapi menonaktifkan caching dengan membangun dengan -DPLUGINS="ALL;-cache" tidak menyelesaikan masalah saya.

Langkah-langkah untuk Mereproduksi Masalah

Pergi> = 1.13

Saya telah membuat dua tes di repo go-elektra . Keduanya membuat kumpulan kunci uji dengan 100000 kunci.

  1. TestKeySetMemory membuat pegangan dan kdbGets keysets dalam satu lingkaran dan setelah menunggu selama 1 detik - segera menutup keyset + pegangan lagi sebelum memulai kembali.
  2. TestKeySetMemoryWithDelayedClose juga membuat pegangan dan mengisi set kunci dengan kdbGets - tetapi menunda penutupan pegangan dan set kunci sampai 20 set kunci telah dimuat. Ini meniru perilaku server web elektrad.

Kedua tes menunggu selama 20 detik setelah selesai untuk memungkinkan penguji melihat konsumsi memori tes melalui htop atau alat serupa.

Tes pertama, yang segera menutup pegangan dan set kunci mempertahankan jejak memori yang sama selama tes.

Tes kedua yang mulai menutup pegangan dan set kunci hanya setelah setiap set kunci 'dimuat' tidak pernah membebaskan memori apa pun. Bahkan setelah memaksa pengumpulan sampah dan menunggu selama 20 detik.

Saat ini saya tidak mengerti mengapa perilaku tes ini berbeda.

Anda dapat menjalankan pengujian dengan mengkloning go-elektra repo dan menjalankan dua perintah ini di subdirektori ./kdb :

PKG_CONFIG_PATH=<PATH TO elektra.pc FILE> go test  -run "^(TestKeySetMemory)\$"

PKG_CONFIG_PATH=<PATH TO elektra.pc FILE> go test  -run "^(TestKeySetMemoryWithDelayedClose)\$"

Hasil yang diharapkan

Memori dibebaskan setelah kdbClose dan ksClose

Hasil Aktual

Memori tidak dibebaskan

Sistem Informasi

  • Versi Elektra: master
  • Sistem Operasi: Arch Linux
  • Terbaru versi go-elektra
bug urgent work in progress

Semua 32 komentar

Tampaknya tidak mungkin bagi saya bahwa hal seperti itu akan lolos dari tes valgrind dan ASAN kami. Saya telah menjalankan benchmark serupa di C dan saya tidak bisa mengamati hal yang sama. Bahkan dengan mmap saya tidak bisa mengamati hal serupa. Saya tidak mengesampingkan itu.

Apakah Anda yakin binding tidak bocor di suatu tempat?

Ya, saya yakin - adalah mungkin untuk memeriksa penggunaan memori dari aplikasi go selama runtime, statistik ini TIDAK berisi alokasi berdasarkan kode C dan tidak mendekati penggunaan memori total dari aplikasi / pengujian yang berarti bahwa sebagian besar memori dialokasikan dalam C.

Kecurigaan saya adalah bahwa kunci tidak dibebaskan karena penghitung referensi adalah != 0 meskipun seharusnya 0 .

Artinya, binding memanggil C API dengan cara yang memicu memleak ini. Sayangnya saya tidak tahu pergi, jadi saya tidak tahu mengapa ini terjadi.

Bisakah Anda mengekstrak contoh C kecil yang memicu kebocoran? Saya melakukan sesuatu yang sangat mirip di benchmarks/kdb.c dan tidak ada kebocoran di sana, jadi perlu melakukan apa yang dilakukan benchmark Anda untuk memicu kebocoran.

Terima kasih telah melaporkan masalah tersebut!

Saya sepenuhnya setuju dengan @mpranj di sini, kami perlu mengurangi masalah menjadi minimal, dengan banyak bahasa yang terlibat, sulit untuk menemukan apa pun.

Bisakah Anda menulis program C yang mereproduksi urutan panggilan seperti yang dikeluarkan dari pengikatan go? Setelah kami memilikinya, kami dapat meminimalkan program C untuk kasus uji.

Btw. mencoba menjalankan perintah di atas saya gagal cukup awal: # 3159

Saya akan mencoba mereproduksi ini di C sampai besok

Saya berhasil mereproduksi masalah ini di sini . Jalankan patokan benchmarks/memoryleak.c dan perhatikan memori naik saat mendapatkan kumpulan kunci - dan BUKAN turun saat membebaskan.

Jangan lupa untuk menambahkan sejumlah kunci di DB .. saya mengujinya dengan 100k kunci.

Terima kasih telah membuat contoh! Aku akan melihatnya.

Sekilas, hanya ada satu kebocoran langsung, Anda perlu membebaskan parentKey:
https://github.com/raphi011/libelektra/blob/afcbcf5c8138be8de6ba9b1a9e559bc673ff887f/benchmarks/memoryleak.c#L22

Kebocoran kecil ini mungkin bukan alasan pengamatan Anda.

Sebagai catatan tambahan: menggunakan cache mmap, saya melihat bahkan konsumsi memori LEBIH SEDIKIT daripada tanpa cache. Mungkin saya perlu menambahkan ini ke tesis saya: smile :

Tampaknya kita "membocorkan" memori melalui dlopen() / dlclose() . Berikut ini dapat diamati dengan menggunakan kdb tetapi TIDAK menggunakan kdb-static (hanya kutipan):

valgrind --leak-check=full --show-leak-kinds=all ./bin/kdb ls user
[...]
==48451== 1,192 bytes in 1 blocks are still reachable in loss record 6 of 6
==48451==    at 0x483AB1A: calloc (vg_replace_malloc.c:762)
==48451==    by 0x400BB11: _dl_new_object (in /usr/lib64/ld-2.30.so)
==48451==    by 0x400642F: _dl_map_object_from_fd (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4009315: _dl_map_object (in /usr/lib64/ld-2.30.so)
==48451==    by 0x400DB24: openaux (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48451==    by 0x400DF6A: _dl_map_object_deps (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4013AC3: dl_open_worker (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48451==    by 0x401363D: _dl_open (in /usr/lib64/ld-2.30.so)
==48451==    by 0x496139B: dlopen_doit (in /usr/lib64/libdl-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
[...]

Contoh Anda membuka banyak pegangan lagi, jadi terlihat seperti ini:

==48735== 72,704 bytes in 1 blocks are still reachable in loss record 8 of 8
==48735==    at 0x483880B: malloc (vg_replace_malloc.c:309)
==48735==    by 0x4F860A9: ??? (in /usr/lib64/libstdc++.so.6.0.27)
==48735==    by 0x400FD59: call_init.part.0 (in /usr/lib64/ld-2.30.so)
==48735==    by 0x400FE60: _dl_init (in /usr/lib64/ld-2.30.so)
==48735==    by 0x4013DBD: dl_open_worker (in /usr/lib64/ld-2.30.so)
==48735==    by 0x4A088C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48735==    by 0x401363D: _dl_open (in /usr/lib64/ld-2.30.so)
==48735==    by 0x48C739B: dlopen_doit (in /usr/lib64/libdl-2.30.so)
==48735==    by 0x4A088C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48735==    by 0x4A08962: _dl_catch_error (in /usr/lib64/libc-2.30.so)
==48735==    by 0x48C7B08: _dlerror_run (in /usr/lib64/libdl-2.30.so)
==48735==    by 0x48C7429: dlopen@@GLIBC_2.2.5 (in /usr/lib64/libdl-2.30.so)

Dalam contoh, Anda membuka banyak pegangan KDB, yang menyebabkan efek ini bertambah hingga konsumsi memori yang signifikan. Jika Anda hanya membuka satu pegangan, itu harus lebih kecil. @ raphi011 Saya tidak tahu apakah Anda dapat menggunakan kdb-static untuk benchmark Anda. Saat menautkan tolok ukur ke perpustakaan elektra-static saya tidak dapat mengamati hal yang sama lagi:

==54836== HEAP SUMMARY:
==54836==     in use at exit: 0 bytes in 0 blocks
==54836==   total heap usage: 6,456,631 allocs, 6,456,631 frees, 272,753,180 bytes allocated
==54836== 
==54836== All heap blocks were freed -- no leaks are possible
==54836== 
==54836== For lists of detected and suppressed errors, rerun with: -s
==54836== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Saat ini saya tidak yakin apa yang dapat kami lakukan tentang masalah dengan dlopen() .

Ini adalah simulasi sederhana dari server elektrad . Karena kami ingin mendukung banyak pengguna yang mengonfigurasi sistem pada saat yang sama, kami PERLU mendukung banyak pegangan (penanganan konflik, dll.).

Saya masih menganggap ini bukan akar masalahnya. Masalahnya adalah bahwa Keys / KeySets tidak dibebaskan. Jika Anda mengubah kunci induk menjadi "system" yang memiliki lebih sedikit kunci, Anda akan melihat bahwa konsumsi memori jauh lebih rendah.

@ markus2330 bagaimana menurut Anda?

EDIT: lupakan ini.

Saya tidak berpikir bahwa kami membocorkan memori di sana.
https://gist.github.com/mpranj/bbdf00af308ed3f5b3f0f35bc832756f~~

Saya dapat mengamati hal yang sama yang Anda gambarkan dengan HTOP dan penggunaan memori menggunakan kode dari inti di atas.

Jadi menurut Anda, apakah konsumsi memori elektra tidak pernah menyusut?

Jadi menurut Anda, apakah konsumsi memori elektra tidak pernah menyusut?

Saya tidak mengatakan itu. Saya mengatakan saya mengamati hal yang sama terlepas dari elektra. Saya baru menyadari bahwa pengamatan saya hanya berlaku saat menjalankan valgrind, jadi mungkin valgrind tidak gratis () hingga akhir karena alasan lain.

https://stackoverflow.com/questions/40917024/memory-leak-after-using-calloc-and-free

Sepertinya itu karena Anda mengubah penunjuk sebelum membebaskannya

Sepertinya itu karena Anda mengubah penunjuk sebelum membebaskannya

Saya tidak melakukan itu dan valgrind tidak menunjukkan kebocoran kode di intinya. Tapi lupakan contohnya, ini tidak relevan karena hanya tidak langsung bebas saat dijalankan di dalam valgrind, jika tidak memori akan segera terbebas () d cukup banyak.

Apa yang lebih relevan: Saya mengamati apa yang Anda laporkan hanya dengan menggunakan benchmark_createkeys yang tidak menggunakan KDB sama sekali. Ada sumber daya juga tidak gratis () d segera, tetapi valgrind benar-benar menunjukkan 0 kebocoran. Saya bingung.

@ markus2330 bagaimana menurut Anda?

Dengan KeySets saja, seharusnya tidak ada kebocoran apa pun. (Dengan KDB kami tidak dapat sepenuhnya mengontrol semuanya karena plugin mungkin memuat pustaka lain yang bocor.)

@ raphi011 dapatkah Anda membuat PR atau mengarahkan saya ke contoh minimal yang mereproduksi masalah?

https://github.com/raphi011/libelektra/tree/memoryleak ini dia. Saya telah memperluas contoh saya sebelumnya dengan mencetak berapa banyak kunci yang TIDAK dibebaskan oleh ksDel karena referensi kunci > 0 .

Jika Anda menjalankan benchmark tanpa valgrind, Anda dapat melihat bahwa kunci tidak dibebaskan.

Saya tidak dapat mengkompilasi repo ini, saya mendapatkan kesalahan:

CMake Error: Error processing file: /home/markus/Projekte/Elektra/repos/libelektra/scripts/cmake/ElektraManpage.cmake
make[2]: *** [src/bindings/intercept/env/CMakeFiles/man-kdb-elektrify-getenv.dir/build.make:61: ../doc/man/man1/kdb-elektrify-getenv.1] Fehler 1
make[1]: *** [CMakeFiles/Makefile2:17236: src/bindings/intercept/env/CMakeFiles/man-kdb-elektrify-getenv.dir/all] Fehler 2

Bisakah Anda melakukan rebase ke master utama?

Dan tolong buat PR, jadi lebih mudah melihat perubahannya.

Jika Anda menjalankan benchmark tanpa valgrind, Anda dapat melihat bahwa kunci tidak dibebaskan.

Bisakah Anda menyalin output dari proses di sini?

Saya mengamati apa yang Anda laporkan hanya dengan menggunakan benchmark_createkeys yang tidak menggunakan KDB sama sekali. Ada sumber daya juga tidak gratis () d segera, tetapi valgrind benar-benar menunjukkan 0 kebocoran. Saya bingung.

Saya pikir pertama-tama kita harus mengikuti jejak ini, karena jauh lebih mudah untuk dipahami jika kita hanya memiliki KeySets.

Mungkin kami menambahkan penindasan yang salah ke valgrind?

Sebagai catatan tambahan: menggunakan cache mmap, saya melihat bahkan konsumsi memori LEBIH SEDIKIT daripada tanpa cache. Mungkin saya perlu menambahkan ini ke senyum tesis saya. (cache: ~ 200M vs. tanpa cache: ~ 600M)

Ini pasti berita bagus.

Mungkin kami menambahkan penindasan yang salah ke valgrind?

Valgrind melaporkan tidak menggunakan penekanan ketika saya menjalankan tes, tetapi jarak tempuh Anda bervariasi.

Saya rasa saya menemukan masalah selama benchmark saya. Bagi saya masalahnya adalah alokasi meta KeySet yang bersemangat untuk setiap kunci. Setelah tes kotor di cabang saya, menghapus alokasi meta KeySet yang bersemangat, konsumsi memori dengan cepat turun setelah ksDel (). Mungkin ini diperbaiki oleh # 3142 karena mengubah kode yang saya bicarakan.

Oke, mari kita lihat apakah # 3142 memperbaiki masalah!

@ raphi011 apakah masalah juga terjadi sebelum # 3081?

3142 sekarang digabungkan. @ raphi011 dapatkah Anda memeriksa apakah masalah masih terjadi?

Sayangnya ya

Bagi saya ini tampaknya setidaknya menyelesaikan / mengurangi beberapa masalah. Saya sekarang dapat menjalankan benchmark dengan jutaan kunci menggunakan ~ 2GB memori, sedangkan sebelumnya crash karena menggunakan memori> 20GB untuk benchmark yang sama.

Bagi saya ini tampaknya setidaknya menyelesaikan / mengurangi beberapa masalah. Saya sekarang dapat menjalankan benchmark dengan jutaan kunci menggunakan ~ 2GB memori, sedangkan sebelumnya crash karena menggunakan memori> 20GB untuk benchmark yang sama.

Saya belum membandingkannya dengan versi sebelumnya, tetapi saya cukup yakin bahwa ini menggunakan lebih sedikit memori dari sebelumnya. Akan memeriksa hari ini!

Memori masih belum dibebaskan.

Anda perlu menelepon malloc_trim(0) setelah setiap free (baca: ksDel ). Ini memaksa glibc untuk segera mengembalikan memori ke OS. Ini harus memperbaiki "perilaku aneh" yang kalian lihat. Oh dan bersenang-senanglah membaca dan menggali glibc :-)

Saya membuat # 3183 untuk pengujian / perbaikan lebih lanjut.

Saya menemukan satu memleak (di kdbGet kunci kembali tidak dibebaskan).

Tetapi peningkatan jumlah "ksClose TIDAK membebaskan 532 kunci" mungkin hanya peringatan yang dikumpulkan di parentKey. Jika Anda membuat NUM_RUNS lebih tinggi, misal 100, itu stagnan di beberapa titik, karena jumlah peringatan dibatasi hingga 100. Bagi saya itu pergi ke max. "ksClose TIDAK membebaskan 901 kunci". Memiliki parentKey per pegangan akan memperbaiki masalah ini.

ditutup oleh # 3183

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

mpranj picture mpranj  ·  3Komentar

dmoisej picture dmoisej  ·  3Komentar

markus2330 picture markus2330  ·  4Komentar

markus2330 picture markus2330  ·  4Komentar

markus2330 picture markus2330  ·  4Komentar