rr untuk C/C++ – Debugger Rekam dan Putar Ulang
(rr-project.org)- rr memudahkan pelacakan bug C/C++ yang sulit direproduksi dengan merekam satu kali eksekusi yang gagal di Linux, lalu memutar ulang eksekusi yang sama berulang kali dengan gdb
- Selama pemutaran ulang, ruang alamat, register, data system call, dan tata letak memori selalu sama, sehingga petunjuk debugging seperti alamat objek atau urutan event tidak hilang
- Mendukung perintah gdb biasa, scripting, dan integrasi IDE, serta menggabungkan hardware data watchpoint dengan eksekusi mundur agar dapat kembali ke titik saat sebuah nilai berubah
- Dapat menangani Firefox, Chrome, QEMU, LibreOffice, program Go, workload multiproses, hingga container, tetapi memiliki batasan pada emulasi single-core, shared memory, serta cakupan dukungan CPU dan system call
- Berguna untuk menyimpan kegagalan intermiten atau kegagalan dari fuzzer dan injeksi fault acak agar bisa dianalisis berulang kali, sehingga menurunkan biaya perbaikan bug dan meningkatkan kualitas perangkat lunak
Model debugging rr
- rr adalah alat debugging C/C++ untuk Linux yang tidak menggantikan gdb, melainkan memperkuat workflow gdb dengan fitur rekam dan putar ulang
- Setelah eksekusi yang mengalami kegagalan disimpan satu kali, eksekusi yang sama dapat di-debug berulang kali sebanyak yang dibutuhkan
- Pemutaran ulang tidak berubah secara nondeterministik seperti eksekusi live, melainkan berjalan dengan kondisi yang sama setiap kali
- Menyediakan eksekusi mundur yang efisien di bawah gdb
- Dapat memasang breakpoint dan data watchpoint
- Dapat melakukan reverse-execute dengan cepat hingga titik yang diminati
- Digunakan pada aplikasi nyata, dan sudah dimanfaatkan oleh banyak developer untuk memperbaiki bug sungguhan
Fitur yang didukung dan cakupan penerapan
- Fitur rr disesuaikan untuk aplikasi praktik nyata dan alur kerja berbasis gdb
- Overhead lebih rendah daripada alat serupa, terutama pada workload yang sebagian besar single-thread
- Mendukung perekaman dan pemutaran ulang berbagai aplikasi seperti Firefox, Chrome, QEMU, LibreOffice, dan program Go
- Mendukung perekaman, pemutaran ulang, dan debugging workload multiproses, termasuk seluruh container
- Mendukung scripting gdb dan integrasi IDE
- Mendukung trace yang tahan lama, terkompresi, dan dapat dipindahkan antar-mesin
- Menyediakan Chaos mode yang lebih mampu memunculkan bug intermiten
Alur perekaman dan pemutaran ulang
- Aplikasi direkam dengan
rr record /your/application --args - Seluruh eksekusi, termasuk kegagalan, disimpan ke disk, lalu dapat di-debug dengan
rr replay - Selama pemutaran ulang, yang di-debug bukan eksekusi live, melainkan trace yang telah direkam
- Ruang alamat, isi register, dan data system call tetap sama setiap kali diputar ulang
- Sebagian besar perintah gdb biasa dapat digunakan apa adanya
- Saat perlu memulai ulang sesi debugging, gunakan perintah
rundi gdb untuk memutar ulang rekaman dari awal- Setelah dimulai ulang pun, eksekusi yang sama akan diputar ulang lagi
- State debugging dipertahankan antar-restart
- Pointer
thisdari objek yang dialokasikan secara dinamis juga sama di setiap sesi pemutaran ulang- Karena alokasi memori selalu sama, alamat yang akan diawasi dapat di-hardcode
Eksekusi mundur dan watchpoint
- rr memungkinkan debugging dengan cara menelusuri balik dari akibat ke penyebab saat mencari alasan sebuah nilai disetel keliru
- Dalam contoh debugging layout Firefox, setelah terlihat bahwa nilai
mRect.widthkeliru, digunakanwatch -l mRect.widthdanreverse-cont - Dengan menggabungkan hardware watchpoint dan reverse execution, ditemukan titik ketika nilai berubah dari
11220menjadi12000 - Pendekatan ini mengurangi pekerjaan untuk kembali dari saat masalah terlihat ke titik perubahan yang sebenarnya
Instalasi dan mulai menggunakan
- Untuk membangun dari source, ikuti petunjuk Building And Installing
- Jika paket tidak cocok, build dari source disarankan
- Perubahan kernel atau pembaruan OS terkadang mengharuskan perubahan pada rr
- Contoh instalasi Fedora: unduh
rr-5.9.0-Linux-$(uname -m).rpm, lalu pasang dengansudo dnf install - Contoh instalasi Ubuntu: unduh
rr-5.9.0-Linux-$(uname -m).deb, lalu pasang dengansudo dpkg -i
Cara menangani kegagalan intermiten
- Motivasi awal rr adalah mempermudah debugging kegagalan intermiten
- Kegagalan intermiten sulit di-debug karena pada sebagian eksekusi kegagalannya tidak muncul
- Eksekusi test dapat direkam dengan overhead rendah, dan ketika kegagalan terjadi, eksekusi tersebut dapat diputar ulang berulang kali di debugger
- Pemutaran ulang deterministik membuat petunjuk tetap dapat dikumpulkan bahkan dalam debugging bug biasa
- Pada debugger biasa, alamat objek yang menjadi perhatian atau urutan event penting bisa berubah saat dieksekusi ulang, sehingga informasi sebelumnya bisa tidak berguna
- Di rr, pengetahuan tentang eksekusi yang gagal tetap terjaga meskipun pemutaran ulang diulang
- Karena debugging umumnya merupakan proses menelusuri balik dari akibat ke penyebab, kemampuan menjalankan eksekusi mundur dalam waktu membuat pelacakan penyebab menjadi lebih mudah
- rr menyediakan sistem rekam-putar ulang ber-overhead rendah dan backend praktis untuk perintah reverse execution gdb
- Ini adalah alat yang berfungsi dan digunakan secara rutin oleh developer di banyak proyek besar maupun kecil
State yang dijamin oleh pemutaran ulang deterministik
- rr merekam grup proses user-space Linux
- rr menangkap semua input yang diterima proses target dari kernel serta efek CPU nondeterministik
- Pemutaran ulang menjamin preservasi control flow tingkat instruksi, memori, dan isi register
- Tata letak memori selalu sama, alamat objek tidak berubah, nilai register identik, dan system call mengembalikan data yang sama
- Fuzzer dan alat injeksi fault acak menjadi lebih kuat saat digunakan bersama rr
- Alat seperti ini mampu memicu kegagalan intermiten, tetapi mungkin sulit mereproduksi kembali kegagalan yang sama
- rr merekam eksekusi acak, dan jika terjadi kegagalan, rekaman yang disimpan memungkinkan masalah di-debug secara deterministik
Perbedaan dari alat rekam-putar ulang yang sudah ada
- rr mengimplementasikan record-and-replay debugging, sebuah ide lama, sesuai tujuan desain tertentu
- Fokus awalnya adalah Firefox
- Banyak teknik rekam-putar ulang memerlukan bahasa tertentu atau tidak cocok untuk skala Firefox, sehingga sulit menangani Firefox
- Karena Firefox adalah aplikasi yang kompleks, jika berguna untuk debugging Firefox, kemungkinan besar juga berguna secara umum
- Mengutamakan kemudahan distribusi
- rr berjalan di kernel Linux biasa dan hardware umum
- Tidak memerlukan perubahan konfigurasi sistem
- Banyak teknik lain memerlukan perubahan kernel atau menjalankan OS di mesin virtual
- Menargetkan overhead waktu eksekusi yang rendah
- Agar dapat menggantikan workflow gdb, hasil perlu diperoleh dengan kecepatan yang mirip saat menggunakan gdb
- Overhead rendah juga mengurangi gangguan terhadap test
- Mengutamakan kesederhanaan desain
- Menghindari pendekatan yang bergantung pada teknik kompleks seperti instrumentasi biner dinamis
- Kesederhanaan juga berkontribusi pada ketangguhan dan overhead rr yang rendah
Performa dan batasan
- Overhead rr bergantung pada workload aplikasi
- Pada test suite Firefox, performa perekaman berada pada tingkat yang praktis
- Dalam beberapa kasus, slowdown turun hingga ≤ 1.2x
- Test suite yang memerlukan 10 menit kira-kira memerlukan 12 menit saat direkam dengan rr
- Overhead dapat sangat bervariasi tergantung workload
- Pada program yang sebagian besar single-thread, overhead-nya jauh lebih rendah daripada sistem rekam-putar ulang pesaing yang dikenal
- Batasan utamanya adalah sebagai berikut
- Karena mengemulasikan mesin single-core, program paralel mengalami slowdown akibat eksekusi single-core
- Proses yang berbagi memori dengan proses di luar recording tree tidak dapat direkam
- Fitur seperti X shared memory pada proses yang direkam dinonaktifkan otomatis
- Memerlukan CPU x86 yang relatif baru atau CPU ARM tertentu seperti Apple M1+
- Harus mengetahui semua system call yang dijalankan oleh proses yang direkam
- Mendukung cakupan system call yang luas yang dibutuhkan Firefox dan banyak aplikasi, tetapi belum lengkap
- System call yang tidak didukung dapat dilaporkan melalui GitHub issue
- Pembaruan mungkin diperlukan untuk menyesuaikan dengan perubahan kernel, pembaruan pustaka sistem, atau keluarga CPU baru
Referensi dan komunitas
- Extended Technical Report adalah materi ringkasan yang membahas cara kerja dan performa rr
- rr wiki membahas topik teknis terkait rr
- Pertanyaan dapat diajukan di mailing list atau #rr on chat.mozilla.org
- Video demo juga tersedia
1 komentar
Komentar Hacker News
Saya pernah sangat berhasil memakai breakpoint perubahan variabel bersama reverse-continue di rr untuk melakukan rekayasa balik pada codebase besar
Butuh waktu untuk mengekstrak logika inti yang tertanam jauh di dalam, tetapi sangat berguna
Perlu juga disebutkan bahwa seseorang pernah mencoba mem-porting rr ke Rust, menulis sekitar 60 ribu baris, lalu mengarsipkan proyeknya
Ini tampaknya bisa menjadi studi kasus menarik untuk membandingkan dampak, kelebihan, batasan, dan kesulitan saat menulis ulang dari C++ ke Rust
https://github.com/sidkshatriya/rd/
Namun alasan besar mengapa sulit memindahkannya menjadi versi resmi adalah 1) rr punya banyak perbaikan kompleks yang menumpuk selama 10 tahun untuk menangani perilaku kernel/proses yang aneh, sehingga kami khawatir itu hilang saat porting, dan 2) proyek sumber tertutup remix[0] yang dibangun di atas rr juga harus ikut di-porting
[0] https://robert.ocallahan.org/2020/12/rr-remix-efficient-repl...
Saya tidak terlalu paham Rust, tetapi interoperabilitasnya dengan C tampaknya bagus. Kalau perangkat lunaknya sudah berjalan baik, saya penasaran apa manfaat penulisan ulang ke Rust
Dari pengecekan kasar, rr 1.0 memakan waktu 3 tahun, dengan kontribusi besar dari sekitar 3–4 orang, dan tampaknya setidaknya 5 orang berkontribusi. rr yang sekarang adalah hasil dari 10 tahun pekerjaan tambahan di atas itu
Penasaran apakah ini benar-benar khusus C/C++
Sepemahaman saya yang terbatas, debugger memerlukan daftar simbol, sesuatu seperti berkas .pdb di Windows dan padanannya di Linux, pemahaman system call, dan hal-hal serupa lainnya. Saya kira ia tidak terlalu peduli apa yang menghasilkan biner yang sedang di-debug. Tentu dengan asumsi itu kode native
Saya penasaran apakah rr juga tidak berjalan untuk bahasa seperti Rust, Zig, Odin, Nim. Untuk bahasa yang memakai memori terkelola seperti Python, JS, C#, tentu saya tidak berharap
Cukup nyaman jika digunakan dengan allocator Zig. Saat dibebaskan, ia menulis byte 0xaa dan tidak menggunakan ulang alamat, sehingga kemungkinan crash lebih tinggi; setelah itu kita bisa memasang watchpoint pada memori tersebut dan memutar balik sampai titik saat memori itu dibebaskan
https://github.com/python/devguide/issues/1283
https://morepypy.blogspot.com/2016/07/reverse-debugging-for-...
https://github.com/mesalock-linux/mesapy/blob/mesapy2.7/READ...
Memang sistemnya hanya ditampilkan dari sudut pandang GDB, tetapi bisa berjalan baik untuk bahasa interpreter maupun bahasa terkompilasi
Yang tidak berjalan adalah ketika driver secara langsung memperbarui alamat yang dipetakan. CUDA adalah contohnya; untuk memutar ulang, interaksi driver harus dimodelkan, dan itu sudah terjadi bahkan sebelum sampai ke UVM
Hal bagus lainnya adalah RR merekam process tree, sehingga kita bisa dengan mudah memeriksa proses lain yang dijalankan oleh executable tersebut
Namun rr harus membungkus semua system call, sehingga sangat spesifik Linux dan tidak berjalan di Windows. Format informasi simbol di Linux adalah DWARF
rr memang sangat keren, tetapi setiap kali saya memutuskan untuk mengeluarkannya sebagai “senjata berat”, hampir selalu kasusnya adalah bug konkurensi, sehingga rr sering tidak bisa mereproduksinya
Meski begitu, akan sangat bagus jika beberapa bahasa memasukkan rr langsung ke toolchain mereka. Tentu kita selalu bisa “sekadar” memakai rr/gdb, tetapi bayangkan jika pengaturan pemanggilan dan penggunaan rr semudah pdb di Python
Pada dasarnya, ia sering mengganti thread yang sedang berjalan untuk meniru situasi beberapa core berjalan bersamaan. Dalam praktiknya, ia menemukan beberapa race condition untuk saya, meski tentu ada batasannya
Pada pekerjaan MPI, saya hanya memasang rr ke rank 0, lalu menemukan titik tempat urutan send/recv yang berbeda menyebabkan masalah. Selain itu, masalahnya cukup rumit karena berupa model Python yang terhubung dengan banyak pembuatan kode native
Saya ingin memakainya, tetapi sebagian besar pekerjaan saya melibatkan GPU dalam satu atau lain bentuk
Saat benar-benar membutuhkannya, saya sedang di Mac, tetapi sayangnya rr hanya berjalan di Linux
Ada Undodb yang berjalan di Mac dan mungkin juga bisa mendukung multithreading, tetapi sayangnya biayanya sekitar 50 ribu dolar
Meski berbasis rr, https://pernos.co/ juga layak dilihat karena menambahkan seluruh eksekusi program sebagai basis data yang bisa di-query
Hal-hal seperti ini bisa dilakukan
Ada juga debugging terbalik yang bawaan di GDB: https://www.sourceware.org/gdb/wiki/ProcessRecord/Tutorial
Saya melihat rr menawarkan lebih banyak fitur dan fleksibilitas, tetapi bagaimanapun GDB sendiri sudah cukup lama bisa melakukan debugging terbalik
Implementasi GDB lebih nyaman daripada rr karena bisa mulai/berhenti merekam kapan saja, tetapi efisiensinya beberapa orde magnitudo lebih rendah. Hanya layak untuk potongan kode yang sangat kecil; selain itu praktis akan berjalan selamanya atau kehabisan resource
Itu berfungsi dan membantu melacak bug, tetapi sangat lambat sampai menyakitkan. Saya harus mengecilkan ukuran input agar bisa dipakai sama sekali, dan untungnya masalahnya masih bisa direproduksi setelah itu
Jika Anda bisa melakukan pencarian biner ke status program yang diinginkan dengan kurang dari 10000 kali restart, itu membutuhkan waktu mesin lebih sedikit daripada memakai eksekusi terbalik. Dalam praktiknya, karena perlambatannya begitu besar, sering kali cukup dengan memindahkan debugger secara interaktif beberapa kali sampai mendekati status yang benar, lalu me-restart program
Saya bisa memanfaatkan fitur replay gdb karena punya file input yang membuat program crash kurang dari 1 detik setelah mulai. Jadi dari “variabel ini salah” ke “bagaimana variabel ini di-set ke nilai yang salah itu?” bisa ditelusuri mundur hanya dengan menunggu beberapa menit
Di Windows, WinDbg bisa dipakai untuk tujuan yang sama
Dukungannya untuk debugging masalah multithread lebih baik
https://www.forrestthewoods.com/blog/windbg-time-travelling-...
rr memakai implementasi time travel debugging dengan pendekatan record-replay, dan jika diimplementasikan dengan benar bisa punya overhead yang jauh lebih kecil. Terakhir kali saya melihatnya, overhead rr sekitar perlambatan 2 kali, dan kalau ingatan saya benar saya pernah melihat debugger time travel record-replay lain di kisaran 10%
10% itu 100 kali lebih murah daripada WinDbg, dan cukup rendah untuk selalu diaktifkan di production. Ini perbedaan yang mengubah peta permainan
Dulu sekali VMWare Workstation juga mendukung fitur seperti ini
Bukan hanya program user-space, tetapi juga kernel dan driver di dalam VM. Fitur itu dirilis dan ada selama beberapa versi, lalu dihapus karena politik internal
Itu bisa diperluas dengan plugin, dan plugin bisa mengendalikan sepenuhnya CPU yang sedang diemulasikan. Saya penasaran apakah sesuatu yang mirip masih ada di suatu tempat
Saya penasaran apakah masalah CPU Ryzen pada rr sekarang sudah benar-benar menjadi masa lalu
Saya memakai rr sepanjang hari setiap hari untuk merekam eksekusi Firefox di Threadripper Pro 7950 yang cukup baru, dan juga memakainya bersama Pernosco. Wiki rr di GitHub menjelaskan cara membuatnya bekerja. Dengan menerapkan workaround kecil, itu berjalan sangat stabil
Ada juga diskusi-diskusi lama
https://news.ycombinator.com/item?id=31617600 (Juni 2022)
https://news.ycombinator.com/item?id=18388879 (November 2018)