Bug bertopeng di embedd

Steker tidak bisa dihindari ketika mengembangkan perangkat lunak apa pun. Dalam sebuah embedd, lima sen murah hati mereka juga dapat menimbulkan masalah perangkat keras, tetapi ini adalah lagu yang terpisah. Tapi penyergapan yang murni terprogram, ketika Anda terjebak di tempat yang tampaknya kosong ... Bagi saya ada tiga jenis penyergapan.

Cara termudah adalah ketika manual, standar, atau, katakanlah, prosedur untuk mengonfigurasi perpustakaan untuk besi tidak sepenuhnya dipahami. Jelas di sini: tidak semua gerakan telah habis, kesabaran dan kerja, lima atau dua percobaan lagi, dan itu akan hidup kembali. Osiloskop dan tyk ilmiah untuk membantu.


Memilih pembagi frekuensi untuk mengkonfigurasi bus CAN

Lebih buruk lagi, ketika masalahnya adalah kesalahan ketik atau kesalahan dalam logika bahwa Anda tidak dapat melihat titik kosong sampai Anda berjalan melalui tempat ini dua puluh kali dengan mata Anda dan debugging langkah-demi-langkah. Kemudian baru sadar, pukulan keras ke dahi, sebuah seruan, "Yah, kau semacam babai!", Editing. Itu bekerja.

Dan pandangan ketiga yang suram: sebuah kesalahan bercokol di perpustakaan asing dan merangkak keluar di persimpangan dengan besi. Gairah Shakespeare menimbulkan cahaya yang stabil dari sebuah monitor. β€œKenapa, tidak bisa, sistem tidak bisa berperilaku seperti ini, karena tidak pernah bisa! Ya benar! Ah?! ” Tidak. Terima, tanda tangani.

Akibatnya, kenyataan lebih luas, lebih luas, dan lebih luas dari yang diharapkan. Beberapa contoh:

Sejarah No. 1. MicroSD flash drive dan DMA berfungsi


Anamnesis


Anda perlu membuang data ke file di kartu SD. Tentu saja, saya tidak punya waktu atau keinginan untuk menulis sistem file dan driver SDIO sendiri, jadi saya mengambil perpustakaan yang sudah selesai. Saya mengaturnya untuk besi, dan semuanya berfungsi dengan baik. Pada awalnya. Dan kemudian ternyata data itu direkam dengan liar: volumenya akurat, tetapi dalam file itu sendiri, pasangan byte yang terpisah digandakan, kemudian menghilang, tanpa keteraturan. Tidak bagus!

Eksperimen dimulai. Saya menulis data uji - semuanya baik-baik saja. Saya menulis pertarungan - semacam iblis. Saya mengubah ukuran buffer data, frekuensi pembilasan, templat data tidak berguna. Dalam buffer itu sendiri, semuanya selalu sangat baik, data dalam memori ada di mana-mana yang Anda butuhkan. Dan, bagaimanapun, gangguan pada flash drive - ini dia.

Butuh beberapa hari untuk menggali anjing itu.

Diagnosis


Masalahnya adalah dalam interaksi perpustakaan dengan peralatan DMA .

Kartu SD memiliki kekhasan: kartu hanya ditulis dalam blok 512 byte. Untuk melakukan ini, pustaka buffer data dalam array 512-byte, dan setelah mengisinya mengalir dari sana melalui DMA ke flash. Tapi!

Jika saya mentransfer ke catatan sebuah fragmen yang lebih besar dari <512xN + ruang kosong di buffer perpustakaan> byte, maka perpustakaan (jelas, agar tidak mendorong memori bolak-balik) melakukan ini: ia mengisi ulang buffernya, menulisnya ke flash , dan byte 512xN berikutnya dilemparkan langsung ke DMA saya dari buffer saya! Nah, jika sesuatu dibiarkan belum selesai - itu lagi salinan untuk sendiri, sampai waktu berikutnya.

Dan semua akan baik-baik saja, tetapi kontroler DMA mensyaratkan bahwa data ditempatkan di memori selaras pada batas 4-byte. Buffer perpustakaan selalu sangat selaras, bahasa menjamin ini. Tetapi dengan alamat apa, setelah menyalin bagian dari data, yang tersisa 512xN dengan byte kecil dimulai dari saya - Tuhan tahu. Dan perpustakaan tidak memeriksa ini sama sekali: alamat, seperti apa adanya, diteruskan ke pengontrol DMA.

"Mereka mengirim sesuatu yang canggung ... Seekor anjing bersamanya." Controller diam-diam mereset 2 bit lebih rendah dari alamat yang ditransmisikan. Dan mulai transfer.


Alamat, awalnya bukan kelipatan 4, digantikan oleh beberapa - voila, hingga tiga byte terakhir dari buffer perpustakaan ditulis ulang ke file dari tambang, dan jumlah byte yang sama dari buffer saya hilang tanpa jejak. Akibatnya, jumlah total data benar, operasi berjalan dengan lancar, tetapi disk tidak masuk akal.

Perawatan


Saya harus menambahkan buffer lain segera sebelum memanggil fungsi perekaman perangkat keras. Jika alamat tulis bukan kelipatan 4, data pertama kali disalin ke alamat itu. Pada saat yang sama, kecepatan rata-rata meningkat karena pilihan ukuran buffer yang masuk akal. Tentu saja, itu membutuhkan memori, tetapi berapa 4 kilobyte untuk tujuan yang baik, ketika Anda siap membantu - tanpa batas 192!

Sejarah No. 2. Rantime dan banyak


Prolog


Setelah perubahan berikutnya, program mulai jatuh, dan entah bagaimana itu jatuh sangat keras, melempar prosesor ke dalam pengendali Hard Fault . Dan dia melemparkannya di sana setelah dimulainya, bahkan sebelum eksekusi sampai ke main (), yaitu, tidak satu baris kode saya punya waktu untuk mengeksekusi.

Kesan pertama adalah "berang-berang sudah mati, chip untuk penggantian." Dan kemudian programmer memberi ek. Tapi tidak, versi lama dari firmware bekerja dengan stabil, tetapi yang baru jatuh dengan mantap di beberapa kedalaman perakitan yang tidak jelas antara peluncuran dan kode saya. Saya tidak punya asumsi bidat macam apa ini.

Bab 1


Membantu Internet mengawasi cara mendapatkan setidaknya beberapa informasi tambahan. Prosedur untuk mem-parsing konsekuensi dari hard default di-Google-kan: status register, dump stack. Dopilil. Menggunakannya

Ternyata macet karena kesalahan operasi di bus. Saya memutuskan bahwa ini lagi-lagi akses yang tidak seimbang - masalah dengan tipe yang sama seperti di cerita pertama, tetapi dari perspektif yang berbeda. Tetapi yang paling berlawanan adalah di mana kesalahan terjadi. Dan itu muncul di dalam perpustakaan runtime, yaitu, dalam kode, yang, secara teori, dijilat seperti memar kucing pada hari yang cerah.

Kelanjutan dari analisis menunjukkan bahwa kesalahan adalah konsekuensi dari upaya untuk menginisialisasi variabel statis lokal.

Penyimpangan liris
Ngomong-ngomong, mengingat kode yang dibongkar, saya secara bersamaan menemukan jawaban untuk pertanyaan yang kadang-kadang saya tanyakan pada diri saya sendiri, tetapi terlalu malas untuk langsung mencari tahu: bagaimana situasinya diselesaikan ketika 2 atau lebih utas dapat mencoba menginisialisasi variabel seperti itu pada saat yang sama. Ternyata dalam kasus ini, kompiler mengatur inisialisasi dengan semafor, menjamin bahwa hanya satu utas pada satu waktu akan melewati seluruh prosedur, dan sisanya akan menunggu sampai yang pertama selesai. Perilaku ini telah distandarisasi sejak C ++ 11. Tahukah anda? Saya tidak.

Bab 2


Setelah runtime terlibat dalam pembangunan variabel, itu juga baginya untuk memanggil destruktor setelah menyelesaikan program (bahkan jika program tidak pernah benar-benar menyelesaikan pekerjaan, yang merupakan norma absolut untuk mikrokontroler). Untuk melakukan ini, ia perlu tempat untuk menyimpan informasi tentang semua variabel yang berhasil diinisialisasi.

Itu tepat di tempat di mana informasi tersebut disimpan dalam semacam daftar internal, runtime juga turun. Karena fungsi malloc (), yang melaluinya memori dialokasikan untuk elemen-elemen dari daftar ini dan yang, menurut standar, menghasilkan blok-blok yang dijamin akan disejajarkan setidaknya pada batas 8 byte , setelah sejumlah panggilan yang berhasil, ia menghasilkan bagian yang tidak selaras pada batas ini.



Perubahan kode firmware baru rusak malloc ?! Tetapi bagaimana ini mungkin? Saya tidak mendefinisikan ulang malloc; saya sendiri tidak membutuhkannya di tempat lain!

Berguna dalam opsi kompiler, untuk mencari beberapa kata kunci, bantuan, tetapi jelas dikatakan di mana-mana: malloc () menjamin output memori selaras di sepanjang batas mendasar. Atau null pointer jika tidak ada cukup memori .

Bab 3


Untuk waktu yang lama saya memasukkan kode dengan tidak masuk akal, mengatur breakpoints, menderita dan tidak mengerti apa-apa, sampai pada titik tertentu itu tidak muncul dan saya melihat alamat yang dikembalikan oleh malloc dengan hati-hati. Sebelum ini, seluruh analisis adalah untuk melihat apakah digit terakhir dari alamat adalah 0x4. Dan sekarang dia mulai membandingkan sepenuhnya antara satu sama lain alamat yang dikeluarkan oleh panggilan berturut-turut ke malloc.

Dan oh, keajaiban!

Semua panggilan berhasil mengeluarkan alamat dari ruang RAM (0x20000000 dan lebih lama untuk batu ini), secara berurutan meningkat dari panggilan ke panggilan. Dan yang gagal pertama mengembalikan 0x00000036. Artinya, alamat tidak hanya itu tidak selaras, tetapi juga tidak ada di ruang alamat RAM sama sekali! Prosesor mencoba menulis sesuatu di sana dan secara alami jatuh.

Dan, yang mengejutkan, bahkan jika malloc () bertindak sesuai dengan standar dan mengembalikan 0 jika tidak ada ruang yang cukup, ini tidak akan mengubah apa pun dalam arti crash program (kecuali jika penyebab bug akan diklarifikasi sebelumnya). Nilai yang dikembalikan oleh malloc masih belum diperiksa dengan cara apa pun, tetapi segera mulai berlaku. Ini sedang runtime.

Epilog


Menambah ukuran tumpukan di file konfigurasi, dan semuanya sudah diperbaiki.

Tetapi sebelum saat itu saya bahkan tidak memikirkan volumenya. Apakah neraka itu menyerah padaku, pikirku. Lagi pula, saya memiliki semua variabel dan objek baik statis atau di stack. Jadi, hanya dengan inersia, saya meninggalkan 0x300 byte di bawahnya, karena beberapa volume di bawah tumpukan dialokasikan di semua proyek template. Tapi tidak, runtime C ++ masih membutuhkan memori yang dialokasikan secara dinamis, dan dalam jumlah yang cukup nyata, dengan standar pengendali.

Hidup dan belajar.

Source: https://habr.com/ru/post/id453944/


All Articles