1 poin oleh GN⁺ 2024-07-22 | 1 komentar | Bagikan ke WhatsApp
  • 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 run di gdb untuk memutar ulang rekaman dari awal
    • Setelah dimulai ulang pun, eksekusi yang sama akan diputar ulang lagi
    • State debugging dipertahankan antar-restart
  • Pointer this dari 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.width keliru, digunakan watch -l mRect.width dan reverse-cont
  • Dengan menggabungkan hardware watchpoint dan reverse execution, ditemukan titik ketika nilai berubah dari 11220 menjadi 12000
  • 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 dengan sudo dnf install
  • Contoh instalasi Ubuntu: unduh rr-5.9.0-Linux-$(uname -m).deb, lalu pasang dengan sudo 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

1 komentar

 
GN⁺ 2024-07-22
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

    • Kelihatannya menarik; penasaran apakah ada tulisan rangkuman yang bisa dibagikan
    • Penasaran apakah codebase besar itu, meski mungkin bukan ungkapan yang tepat, bisa dianggap ditulis dalam bahasa dynamic typing
  • 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/

    • Dari sudut pandang maintainer rr, pekerjaan Sid bagus dan kami juga mendukungnya
      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 kurang memahami tren “mari tulis ulang X/Y/Z dalam Rust” yang berlangsung beberapa tahun terakhir
      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
    • Itu akan menjadi analisis yang bagus, tetapi sepertinya tidak mudah
      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

    • Saya memakainya bersama Zig
      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
    • Dulu saya pernah menjalankan rr pada build rpython yang sangat spesifik, dan mungkin mengejutkan bahwa minat terkait hal ini masih terus ada
      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...
    • RR banyak dipakai di Julia
      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
    • rr memakai gdb untuk bagian debugger sebenarnya, jadi apa pun yang bisa dilakukan di gdb juga bisa dilakukan di rr
      Namun rr harus membungkus semua system call, sehingga sangat spesifik Linux dan tidak berjalan di Windows. Format informasi simbol di Linux adalah DWARF
    • Juga dipakai di Rust: https://bitshifter.github.io/rr+rust/index.html#1
  • 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

    • Jika menjalankan rr dengan opsi chaos mode, sebagian masalah konkurensi bisa terungkap
      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
    • Ada bug konkurensi yang memang berhasil saya tangkap dengan rr
      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
    • Satu hal lain yang sayangnya tidak didukung rr adalah GPU
      Saya ingin memakainya, tetapi sebagian besar pekerjaan saya melibatkan GPU dalam satu atau lain bentuk
    • Saya juga mirip
      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

    [...] Anda cukup mengklik nilai yang salah. Karena memiliki seluruh riwayat program, Pernosco bisa langsung menjelaskan dari mana nilai ini berasal. Nilai tersebut dilacak mundur melalui peristiwa seperti memcpy atau perpindahan masuk-keluar register, dan setiap langkah sampai titik saat nilai itu “originated” ditampilkan di panel “Dataflow” yang terbuka otomatis. Tanpa perlu membaca atau memahami kode dan menebak apa yang mungkin terjadi, Anda cukup bertanya kepada debugger apa yang sebenarnya terjadi.
    https://pernos.co/examples/race-condition

  • 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

    • Kalau ingin menyebutkan ini, hampir pasti Anda belum benar-benar mencobanya
      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
    • Saya pernah berhasil memakai debugging terbalik bawaan GDB di platform yang saat itu belum didukung rr
      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
    • Implementasi replay bawaan gdb memberi program perlambatan sekitar 10000 kali
      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
    • Kalau ingatan saya benar, rr muncul lebih dulu daripada fitur milik gdb
  • Di Windows, WinDbg bisa dipakai untuk tujuan yang sama
    Dukungannya untuk debugging masalah multithread lebih baik
    https://www.forrestthewoods.com/blog/windbg-time-travelling-...

    • WinDbg memakai implementasi time travel berbasis emulasi tingkat instruksi, sehingga ada perlambatan 10–20 kali yang mengikuti teknik itu
      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

    • Sebelumnya sepertinya ada AMD SimNow
      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

    • Benar
      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)