Apa itu schrödinbug?


52

Halaman wiki ini memberi tahu:

Schrödinbug adalah bug yang bermanifestasi hanya setelah seseorang membaca kode sumber atau menggunakan program dengan cara yang tidak biasa, mengetahui bahwa itu tidak seharusnya bekerja sejak awal, di mana program tersebut segera berhenti bekerja untuk semua orang sampai diperbaiki. File Jargon menambahkan: "Meskipun ... ini terdengar mustahil, itu terjadi; beberapa program telah menyembunyikan schrödinbugs laten selama bertahun-tahun."

Apa yang sedang dibicarakan sangat kabur ..

Dapatkah seseorang memberikan contoh bagaimana schrödinbug itu seperti (seperti dengan situasi fiksi / kehidupan nyata)?


15
Perhatikan bahwa kutipan tersebut diceritakan dengan bercanda.

11
Saya pikir Anda akan lebih memahami shrodinbug jika Anda tahu tentang kucing Shrodinger: en.wikipedia.org/wiki/Shrodingers_cat
Eimantas

1
@ Eimantas Saya sebenarnya sekarang lebih bingung tapi itu artikel yang menarik :)

Jawaban:


82

Dalam pengalaman saya polanya adalah ini:

  • Sistem bekerja, seringkali selama bertahun-tahun
  • Kesalahan dilaporkan
  • Pengembang menyelidiki kesalahan dan menemukan sedikit kode yang tampaknya benar-benar cacat dan menyatakan bahwa itu "tidak akan pernah berhasil"
  • Bug diperbaiki dan legenda kode yang tidak akan pernah bisa berfungsi (tetapi tidak selama bertahun-tahun) tumbuh

Mari bersikap logis di sini. Kode yang tidak akan pernah berhasil ... tidak akan pernah bisa berfungsi . Jika melakukan pekerjaan maka pernyataan tersebut adalah palsu.

Jadi saya akan mengatakan bahwa bug persis seperti yang dijelaskan (yang mengamati kode cacat berhenti bekerja) jelas-jelas omong kosong.

Pada kenyataannya apa yang terjadi adalah satu dari dua hal:

1) Pengembang belum sepenuhnya memahami kodenya . Dalam hal ini kode biasanya berantakan dan di suatu tempat di dalamnya memiliki kepekaan besar tetapi tidak jelas untuk beberapa kondisi eksternal (katakanlah versi OS tertentu atau konfigurasi yang mengatur bagaimana beberapa fungsi bekerja dalam beberapa cara kecil namun signifikan). Kondisi eksternal ini diubah (misalnya dengan peningkatan atau perubahan server yang diyakini tidak terkait) dan dengan demikian menyebabkan kode rusak.

Pengembang kemudian melihat kode dan, tidak memahami konteks historis atau memiliki waktu untuk menelusuri setiap dependensi dan skenario yang mungkin, menyatakan bahwa itu tidak akan pernah berhasil dan menulis ulang.

Dalam situasi ini, hal yang perlu dipahami di sini adalah bahwa gagasan "itu tidak akan pernah berhasil" terbukti salah (karena memang benar).

Itu tidak berarti menulis ulang itu adalah hal yang buruk - itu sering tidak, sementara itu baik untuk tahu persis apa yang salah sering menghabiskan waktu dan menulis ulang bagian kode sering lebih cepat dan memungkinkan Anda untuk memastikan bahwa Anda telah memperbaiki hal-hal.

2) Sebenarnya itu tidak pernah berhasil, hanya saja tidak ada yang pernah memperhatikan . Ini sangat umum, terutama dalam sistem besar. Dalam hal ini seseorang baru mulai dan mulai melihat hal-hal dengan cara yang belum pernah dilakukan sebelumnya, atau proses bisnis berubah yang membawa beberapa kasus tepi kecil ke dalam proses utama, dan sesuatu yang tidak pernah benar-benar bekerja (atau bekerja beberapa tetapi tidak semua waktu) ditemukan dan dilaporkan.

Pengembang melihatnya dan menyatakan "itu tidak akan pernah berhasil" tetapi para pengguna mengatakan "omong kosong, kami telah menggunakannya selama bertahun-tahun" dan mereka agak benar tetapi sesuatu yang mereka anggap tidak relevan (dan biasanya gagal disebutkan sampai pengembang menemukan kondisi yang tepat pada titik mana mereka pergi "oh ya, kami melakukan itu sekarang dan tidak sebelumnya") telah berubah.

Di sini pengembangnya benar - itu tidak akan pernah berhasil dan tidak pernah berhasil.

Namun dalam kedua kasus tersebut salah satu dari dua hal itu benar:

  • Klaim "itu tidak akan pernah berhasil" adalah benar dan itu tidak pernah berhasil - orang hanya berpikir itu berhasil
  • Itu berhasil dan pernyataan "itu tidak akan pernah berhasil" adalah salah dan turun ke (biasanya masuk akal) kurangnya pemahaman tentang kode dan ketergantungannya

1
Sering terjadi pada saya
genesis

2
Wawasan hebat ke dalam realisme situasi ini
StuperUser

1
Saya kira itu biasanya hasil dari momen "WTF". Saya pernah melakukannya sekali. Saya membaca kembali beberapa kode yang saya tulis dan menyadari bahwa bug yang baru-baru ini diperhatikan seharusnya membuat seluruh aplikasi mogok. Sebenarnya, setelah pemeriksaan lebih lanjut, komponen lain yang saya tulis sangat bagus sehingga mengkompensasi kesalahan.
Thaddee Tyl

1
@Thaddee - Saya pernah melihat itu sebelumnya tetapi saya juga melihat dua bug dalam modul kode yang saling memanggil untuk membatalkan sehingga benar-benar berfungsi. Lihatlah salah satu dari mereka dan mereka hancur tetapi bersama-sama mereka baik-baik saja.
Jon Hopkins

7
@ Jon Hopkins: Saya juga punya kasus 2 bug yang saling membatalkan, dan itu sangat mengejutkan. Saya menemukan bug, mengucapkan pernyataan terkenal "itu tidak akan pernah berhasil", mencari lebih dalam untuk menemukan mengapa itu berhasil, dan menemukan bug lain yang semacam mengoreksi yang pertama, dalam sebagian besar kasus setidaknya. Saya benar-benar terpesona oleh penemuan itu, dan oleh fakta bahwa hanya dengan SATU dari serangga, konsekuensinya akan menjadi bencana besar!
Alexis Dufrenoy

54

Karena semua orang menyebutkan kode yang seharusnya tidak pernah berfungsi, saya akan memberikan Anda contoh yang saya temui, sekitar 8 tahun yang lalu pada proyek VB3 yang sedang sekarat yang sedang dikonversi ke .net. Sayangnya proyek ini harus terus diperbarui hingga versi .net selesai - dan saya adalah satu-satunya yang bahkan memahami VB3 dari jarak jauh.

Ada satu fungsi yang sangat penting yang disebut ratusan kali untuk setiap perhitungan - menghitung bunga bulanan untuk program pensiun jangka panjang. Saya akan mereproduksi bagian yang menarik.

Function CalculateMonthlyInterest([...], IsYearlyInterestMode As Boolean, [...]) As Double
    [about 30 lines of code]
    If IsYearlyInterestMode Then
        [about 30 lines of code]
        If Not IsYearlyInterestMode Then
            [about 30 lines of code (*)]
        End If
    End If
End Function

Bagian yang ditandai dengan bintang memiliki kode paling penting; itu adalah satu-satunya bagian yang melakukan perhitungan aktual. Jelas ini seharusnya tidak berhasil, bukan?

Butuh banyak debugging, tetapi saya akhirnya menemukan penyebabnya: IsYearlyInterestModeadalah True, dan Not IsYearlyInterestModejuga benar. Itu karena di suatu tempat di sepanjang garis seseorang melemparkannya ke integer, maka dalam suatu fungsi yang seharusnya mengaturnya menjadi benar menambahkannya (jika 0 untuk Falseitu akan diatur ke 1, yaitu VB True, jadi saya bisa melihat logika di sana), lalu masukkan kembali ke boolean. Dan saya ditinggalkan dengan kondisi yang tidak pernah bisa terjadi dan belum terjadi setiap saat.


7
Epilog: Saya tidak pernah memperbaiki fungsi itu; Saya hanya menambal situs panggilan gagal untuk mengirim 2 seperti semua yang lain.
konfigurator

jadi maksud Anda digunakan ketika orang salah memasukkan kode?
Pacerier

1
@Pacerier: Lebih sering ketika kode berantakan sehingga hanya berfungsi dengan benar secara tidak sengaja. Dalam contoh saya, tidak ada pengembang yang dimaksudkan untuk IsYearlyInterestModemengevaluasi baik benar maupun tidak benar; pengembang asli yang menambahkan beberapa baris (termasuk salah satu dari ifmereka tidak benar-benar mengerti cara kerjanya - kebetulan bekerja sehingga cukup baik.
configurator

16

Tidak tahu contoh dunia nyata, tetapi untuk menyederhanakannya dengan contoh situasi:

  • Bug tidak diperhatikan untuk sementara waktu, karena aplikasi tidak menjalankan kode dalam kondisi yang menyebabkannya gagal.
  • Seseorang memperhatikannya dengan melakukan sesuatu di luar penggunaan normal (atau memeriksa sumbernya).
  • Sekarang bug diperhatikan, aplikasi gagal sampai kondisi normal juga, sampai bug diperbaiki.

Ini mungkin terjadi karena bug akan merusak beberapa keadaan aplikasi yang menyebabkan kegagalan dalam kondisi normal sebelumnya.


4
Satu penjelasan adalah bahwa ada kegagalan acak dalam perangkat lunak, bahwa tidak ada yang bisa terhubung secara mental. Dengan demikian, kesalahan-kesalahan itu dianggap sebagai penyebab alami (seperti kegagalan perangkat keras acak). Setelah kode sumber dibaca, orang sekarang dapat menghubungkan semua kesalahan acak sebelumnya dengan penyebab yang satu ini, dan akan menyadari bahwa itu seharusnya tidak pernah bekerja sejak awal.
rwong

4
Penjelasan kedua adalah bahwa ada bagian dalam perangkat lunak yang diimplementasikan dengan pola rantai tanggung jawab. Setiap handler ditulis dengan cara yang kuat, meskipun itu satu handler memiliki bug kritis. Sekarang, penangan pertama akan selalu gagal, tetapi karena penangan kedua (yang memiliki tanggung jawab yang tumpang tindih) mencoba untuk menyelesaikan tugas yang sama, operasi keseluruhan tampaknya telah berhasil. Jika ada perubahan pada modul kedua, seperti perubahan dalam area tanggung jawab, itu akan menyebabkan kegagalan secara keseluruhan, meskipun bug sebenarnya ada di lokasi yang berbeda.
rwong

13

Contoh kehidupan nyata. Saya tidak dapat menampilkan kode, tetapi kebanyakan orang akan terkait dengan ini.

Kami memiliki perpustakaan internal besar fungsi utilitas tempat saya bekerja. Suatu hari saya sedang mencari fungsi untuk melakukan hal tertentu, dan saya menemukan Frobnicate()mencoba menggunakannya. Uh-oh: ternyata Frobnicate()selalu mengembalikan kode kesalahan.

Menggali implementasi, saya menemukan beberapa kesalahan logika dasar Frobnicate()yang membuatnya selalu gagal. Dalam kontrol sumber saya dapat melihat bahwa fungsi tersebut belum dimodifikasi sejak ditulis, artinya fungsi tersebut tidak pernah berfungsi sebagaimana dimaksud. Kenapa tidak ada yang memperhatikan ini? Saya mencari melalui sisa sumber pendaftaran dan menemukan bahwa semua penelepon yang Frobnicate()ada mengabaikan nilai kembali (dan karena itu mengandung bug halus mereka sendiri). Jika saya mengubah fungsi-fungsi itu untuk memeriksa nilai kembali seperti seharusnya, maka mereka mulai gagal juga.

Ini adalah kasus umum kondisi # 2 yang disebutkan Jon Hopkins dalam jawabannya, dan itu sangat umum di perpustakaan internal besar.


... yang merupakan alasan kuat untuk menghindari penulisan pustaka internal di mana pun eksternal dapat digunakan. Ini akan lebih teruji dan dengan demikian memiliki kejutan yang jauh lebih sedikit seperti itu (perpustakaan open-source lebih disukai, karena Anda dapat memperbaikinya jika mereka tetap melakukannya).
Jan Hudec

Ya, tetapi jika programmer mengabaikan kode kembali itu bukan kesalahan perpustakaan. (Omong-omong, kapan terakhir kali Anda memeriksa retcode printf()?)
JensG

Inilah mengapa pengecualian yang diperiksa ditemukan.
Kevin Krumwiede

10

Inilah Schrödinbug nyata yang saya lihat di beberapa kode sistem. Daemon root perlu berkomunikasi dengan modul kernel. Jadi kode kernel membuat beberapa deskriptor file:

int pipeFDs[1];

lalu atur komunikasi melalui pipa yang akan dilampirkan ke pipa bernama:

int pipeResult = pipe(pipeFDs);

Ini seharusnya tidak berhasil. pipe()menulis dua deskriptor file ke dalam array, tetapi hanya ada ruang untuk satu. Tapi selama sekitar tujuh tahun itu melakukan kerja; array kebetulan sebelum beberapa ruang yang tidak terpakai dalam memori yang dikooptasi menjadi deskriptor file.

Kemudian, suatu hari, saya harus mem-port-kan kode ke arsitektur baru. Itu berhenti bekerja, dan bug yang seharusnya tidak berhasil ditemukan.


5

Akibat wajar dari Schrödinbug adalah Heisenbug - yang menggambarkan bug yang hilang (atau kadang-kadang muncul) ketika mencoba untuk menyelidiki dan / atau memperbaikinya.

Heisenbugs adalah blighter pintar dan mitos yang berlari dan bersembunyi saat debugger dimuat, tetapi keluar dari kayu setelah Anda berhenti menonton.

Pada kenyataannya, ini biasanya disebabkan oleh salah satu dari yang berikut ini:

  • dampak optimasi itu, di mana kode yang dikompilasi -DDEBUGdioptimalkan ke tingkat yang berbeda dari rilis rilis
  • perbedaan waktu yang halus karena bus komunikasi dunia nyata atau interupsi yang agak berbeda dari beban tiruan "sempurna" yang disimulasikan

Keduanya menyoroti pentingnya pengujian kode rilis pada peralatan rilis, serta pengujian unit / modul / sistem menggunakan emulator.


Mengapa saya tidak memperhatikan jawaban S.Lote dan komentar delnan sebelum saya memposting ini?
Andrew

Saya memiliki sedikit pengalaman tetapi telah menemukan beberapa dari ini. Saya bekerja di lingkungan Android NDK. Ketika debugger menemukan breakpoint, itu hanya menghentikan thread Java, bukan yang C ++, membuat beberapa panggilan dimungkinkan karena elemen diinisialisasi pada C ++. Jika dibiarkan tanpa debugger, kode Java akan lebih cepat dari C ++ dan mencoba menggunakan nilai yang belum diinisialisasi.
MLProgrammer-CiM

Saya menemukan Heisenbug dalam penggunaan kami API database Django beberapa bulan yang lalu: Ketika DEBUG = True, nama "parameter" berargumen pada perubahan permintaan SQL mentah. Kami telah menggunakannya sebagai argumen kata kunci untuk kejelasan karena lamanya kueri, yang benar-benar rusak ketika tiba saatnya untuk mendorong ke situs beta, di manaDEBUG = False
Izkata

2

Saya telah melihat beberapa Schödinbugs dan selalu karena alasan yang sama:

Kebijakan perusahaan mensyaratkan bahwa setiap orang seharusnya menggunakan program.
Tidak ada yang benar-benar menggunakannya (kebanyakan karena tidak ada pelatihan untuk itu.)
Tapi mereka tidak bisa memberi tahu manajemen ini. Jadi semua orang harus mengatakan "Saya telah menggunakan program ini selama 2 tahun dan tidak pernah menemukan bug ini sampai hari ini."
Program ini tidak pernah benar-benar berfungsi, kecuali sebagian kecil pengguna (termasuk pengembang yang menulisnya.)

Dalam satu kasus, program tersebut telah mengalami banyak pengujian, tetapi tidak pada database nyata (yang dianggap terlalu sensitif, sehingga versi palsu digunakan.)


1

Saya punya contoh dari sejarah saya sendiri, ini sekitar 25 tahun yang lalu. Saya adalah seorang anak yang melakukan pemrograman grafis dasar di Turbo Pascal. TP memiliki pustaka yang disebut BGI yang mencakup beberapa fungsi yang memungkinkan Anda menyalin wilayah layar ke dalam blok memori berbasis-pointer, dan kemudian memutarnya di tempat lain. Dikombinasikan dengan xor-blitting pada layar hitam-putih itu dapat digunakan untuk melakukan animasi sederhana.

Saya ingin melangkah lebih jauh dan membuat sprite. Saya menulis sebuah program yang menggambar blok besar dan kontrol untuk mewarnainya, seperti yang Anda buat itu mereproduksi ini sebagai piksel, menghasilkan program menggambar sederhana untuk membuat sprite, yang kemudian dapat menyalin ke memori. Hanya ada satu masalah, untuk menggunakan sprite blitted ini, mereka harus disimpan ke file sehingga program lain bisa membacanya. Tapi TP tidak punya cara mengalokasikan alokasi memori berbasis penunjuk. Buku pedoman menyatakan bahwa mereka tidak dapat ditulis ke file.

Saya datang dengan sepotong kode yang, berhasil, menulis ke file. Dan mulai menulis program pengujian yang menghapus sprite dari program menggambar saya di latar belakang - dalam perjalanan saya membuat game. Dan itu berhasil, dengan indah. Namun keesokan harinya, itu berhenti bekerja. Itu tidak menunjukkan apa-apa selain kekacauan berantakan. Tidak pernah berhasil lagi. Saya membuat sprite baru, dan berhasil, sempurna - sampai tidak, dan itu berantakan lagi.

Butuh waktu lama tetapi akhirnya saya menemukan apa yang terjadi. Program menggambar itu, seperti yang saya pikir, menyimpan data piksel yang disalin ke file - itu menyimpan pointer itu sendiri. Ketika program berikutnya membaca file, itu berakhir dengan pointer ke blok memori yang sama - yang masih berisi apa yang ditulis oleh program terakhir di sana (ini di MS-DOS, manajemen memori tidak ada). Tapi itu berhasil ... sampai Anda mem-boot ulang, atau telah menjalankan apa pun yang menggunakan kembali area memori yang sama, dan kemudian Anda mendapatkan kekacauan yang kacau karena Anda memuntahkan sekelompok data yang sama sekali tidak terkait ke blok video-memory.

Seharusnya tidak pernah bekerja, seharusnya tidak pernah bahkan tampaknya berfungsi (dan pada OS nyata tidak akan) tetapi tetap saja, dan setelah itu rusak - itu tetap rusak.


0

Ini terjadi setiap saat ketika orang menggunakan debugger.

Lingkungan debugging berbeda dari lingkungan produksi sebenarnya - tanpa debugger - produksi.

Menjalankan dengan debugger dapat menutupi hal-hal seperti stack overflows karena tumpukan debugger menutupi bug.


Saya tidak berpikir itu merujuk pada perbedaan antara kode yang berjalan di debugger dan ketika dikompilasi.
Jon Hopkins

26
Itu bukan schrödinbug, itu heisenbug .

@ Darnan: Ini di tepi, IMO. Saya menemukan itu menjadi hal yang tidak pasti karena ada derajat kebebasan yang tidak diketahui. Saya suka mencadangkan heisenbug untuk hal-hal di mana pengukuran satu hal benar-benar mengganggu yang lain (yaitu, kondisi balapan, pengaturan pengoptimal, batasan bandwidth jaringan, dll.)
S.Lott

@ S.Lott: Situasi yang Anda gambarkan memang melibatkan pengamatan mengubah hal-hal dengan mengacaukan frame tumpukan atau sejenisnya. (Contoh terburuk yang pernah saya lihat adalah debugger akan secara damai dan "benar" mengeksekusi banyak nilai register segmen yang tidak valid dalam mode langkah tunggal. Hasilnya adalah beberapa rutinitas dalam RTL yang dikirimkan meskipun memuat penunjuk mode nyata saat dalam mode terlindungi Karena hanya disalin dan tidak ditinjau, ia berperilaku sempurna.)
Loren Pechtel

0

Saya belum pernah melihat schrodinbug sejati dan saya tidak berpikir mereka bisa ada - menemukan itu tidak akan merusak segalanya.

Sebaliknya, sesuatu berubah yang mengekspos bug yang sudah lama bersembunyi. Apa pun yang diubah masih berubah dan dengan demikian bug terus muncul sementara pada saat yang sama seseorang menemukan bug.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.