Saya memiliki kode warisan C ++ yang seharusnya saya hapus dari kode yang tidak digunakan. Masalahnya adalah bahwa basis kode besar.
Bagaimana saya bisa mengetahui kode mana yang tidak pernah dipanggil / tidak pernah digunakan?
c++
optimization
dead-code
pengguna63898
sumber
sumber
f()
, dan sebuah panggilan untukf()
secara pasti memutuskan ke yang pertama, maka tidak mungkin untuk membuat panggilan tersebut memutuskan ke yang ke-2 hanya dengan menambahkan fungsi ke-3 bernamaf()
- yang "terburuk yang dapat Anda lakukan "Dengan menambahkan bahwa fungsi ke-3 adalah untuk menyebabkan panggilan menjadi ambigu dan karena itu mencegah program dari kompilasi. Senang (= ngeri) melihat contoh tandingan.Jawaban:
Ada dua jenis kode yang tidak digunakan:
Untuk jenis pertama, kompiler yang baik dapat membantu:
-Wunused
(GCC, Dentang ) harus memperingatkan tentang variabel yang tidak terpakai, analisis dentang yang tidak terpakai bahkan telah ditambahkan untuk memperingatkan tentang variabel yang tidak pernah dibaca (meskipun digunakan).-Wunreachable-code
(GCC yang lebih lama, dihapus pada 2010 ) harus memperingatkan tentang blok lokal yang tidak pernah diakses (ini terjadi dengan pengembalian awal atau kondisi yang selalu dinilai benar)catch
blok yang tidak digunakan , karena kompiler umumnya tidak dapat membuktikan bahwa tidak ada pengecualian yang akan dilemparkan.Untuk jenis kedua, ini jauh lebih sulit. Secara statis itu memerlukan analisis seluruh program, dan meskipun optimasi waktu tautan sebenarnya dapat menghapus kode mati, dalam praktiknya program ini telah begitu banyak berubah pada saat dilakukan sehingga hampir tidak mungkin untuk menyampaikan informasi yang bermakna kepada pengguna.
Karena itu ada dua pendekatan:
gcov
. Perhatikan bahwa flag tertentu harus dilewati selama kompilasi agar berfungsi dengan baik). Anda menjalankan alat cakupan kode dengan input beragam yang baik (tes unit atau tes non-regresi), kode mati harus dalam kode yang belum terjangkau ... sehingga Anda dapat mulai dari sini.Jika Anda sangat tertarik pada subjek, dan punya waktu dan kecenderungan untuk benar-benar mengerjakan alat sendiri, saya sarankan menggunakan perpustakaan Dentang untuk membangun alat seperti itu.
Karena Dentang akan mem-parsing kode untuk Anda, dan melakukan resolusi kelebihan, Anda tidak perlu berurusan dengan aturan bahasa C ++, dan Anda akan dapat berkonsentrasi pada masalah yang dihadapi.
Namun jenis teknik ini tidak dapat mengidentifikasi penimpaan virtual yang tidak digunakan, karena mereka dapat dipanggil dengan kode pihak ketiga yang tidak dapat Anda alasankan.
sumber
foo()
agar tidak ditandai sebagai "dipanggil" saat itu muncul hanyaif (0) { foo(); }
akan menjadi bonus tetapi membutuhkan kecerdasan ekstra.)Untuk kasus seluruh fungsi yang tidak digunakan (dan variabel global yang tidak digunakan), GCC sebenarnya dapat melakukan sebagian besar pekerjaan untuk Anda asalkan Anda menggunakan GCC dan GNU ld.
Saat mengkompilasi sumber, gunakan
-ffunction-sections
dan-fdata-sections
, lalu saat menautkan gunakan-Wl,--gc-sections,--print-gc-sections
. Linker sekarang akan mendaftar semua fungsi yang bisa dihapus karena mereka tidak pernah dipanggil dan semua global yang tidak pernah direferensikan.(Tentu saja, Anda juga dapat melewati
--print-gc-sections
bagian tersebut dan membiarkan tautan menghapus fungsi secara diam-diam, tetapi menyimpannya di sumbernya.)Catatan: ini hanya akan menemukan fungsi lengkap yang tidak digunakan, itu tidak akan melakukan apa pun tentang kode mati dalam fungsi. Fungsi yang dipanggil dari kode mati dalam fungsi langsung juga akan disimpan.
Beberapa fitur khusus C ++ juga akan menyebabkan masalah, khususnya:
Dalam kedua kasus, apa pun yang digunakan oleh fungsi virtual atau konstruktor variabel global juga harus dijaga.
Peringatan tambahan adalah bahwa jika Anda sedang membangun pustaka bersama, pengaturan default di GCC akan mengekspor setiap fungsi di pustaka bersama, menyebabkannya untuk "digunakan" sejauh menyangkut linker. Untuk memperbaikinya Anda perlu mengatur default untuk menyembunyikan simbol alih-alih mengekspor (menggunakan misalnya
-fvisibility=hidden
), dan kemudian secara eksplisit memilih fungsi yang diekspor yang perlu Anda ekspor.sumber
Nah jika Anda menggunakan g ++ Anda bisa menggunakan flag ini
-Wunused
Dokumentasi menurut:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Sunting : Ini adalah bendera lain yang berguna
-Wunreachable-code
Menurut dokumentasi:Pembaruan : Saya menemukan topik serupa Deteksi kode mati dalam proyek C / C ++ lama
sumber
-Wunused
memperingatkan tentang variabel yang dideklarasikan (atau dideklarasikan dan didefinisikan dalam sekali jalan) tetapi sebenarnya tidak pernah digunakan. Omong-omong, cukup menjengkelkan dengan penjaga ruang lingkup: p Ada implementasi eksperimental di Dentang untuk memilikinya juga memperingatkan untuk variabel non-volatil yang ditulis untuk tetapi tidak pernah dibaca dari (oleh Ted Kremenek).-Wunreachable-code
memperingatkan tentang kode dalam fungsi yang tidak dapat dicapai, dapat kode terletak setelahthrow
ataureturn
pernyataan atau kode cabang yang tidak pernah diambil (yang terjadi dalam kasus perbandingan tautologis) misalnya.Saya pikir Anda sedang mencari alat cakupan kode . Alat cakupan kode akan menganalisis kode Anda saat sedang berjalan, dan itu akan memberi tahu Anda baris kode mana yang dieksekusi dan berapa kali, serta yang mana yang tidak.
Anda bisa mencoba memberikan alat cakupan kode sumber terbuka ini kesempatan: TestCocoon - alat cakupan kode untuk C / C ++ dan C #.
sumber
void func()
di a.cpp, yang digunakan di b.cpp. Bagaimana kompiler dapat memeriksa, apakah func () digunakan dalam program? Ini pekerjaan penghubung.Jawaban sebenarnya di sini adalah: Anda tidak pernah benar-benar tahu pasti.
Paling tidak, untuk kasus nontrivial, Anda tidak bisa memastikan Anda mendapatkan semuanya. Pertimbangkan hal berikut dari artikel Wikipedia tentang kode yang tidak terjangkau :
Seperti yang dicatat Wikipedia dengan benar, kompiler yang cerdik mungkin dapat menangkap sesuatu seperti ini. Tetapi pertimbangkan modifikasi:
Akankah kompiler menangkap ini? Mungkin. Tetapi untuk melakukan itu, perlu melakukan lebih dari menjalankan
sqrt
terhadap nilai skalar yang konstan. Itu harus mencari tahu yang(double)y
akan selalu menjadi bilangan bulat (mudah), dan kemudian memahami kisaran matematikasqrt
untuk himpunan bilangan bulat (keras). Kompiler yang sangat canggih mungkin dapat melakukan ini untuksqrt
fungsi, atau untuk setiap fungsi dalam math.h , atau untuk fungsi input-tetap yang domainnya dapat dicari tahu. Ini menjadi sangat, sangat kompleks, dan kompleksitas pada dasarnya tidak terbatas. Anda dapat terus menambahkan lapisan kecanggihan ke kompiler Anda, tetapi akan selalu ada cara untuk menyelinap dalam beberapa kode yang tidak dapat dijangkau untuk set input yang diberikan.Dan kemudian ada set input yang tidak pernah dimasukkan. Input yang tidak masuk akal dalam kehidupan nyata, atau diblokir oleh logika validasi di tempat lain. Tidak ada cara bagi kompiler untuk mengetahui hal itu.
Hasil akhir dari ini adalah bahwa sementara perangkat lunak yang disebutkan orang lain sangat berguna, Anda tidak akan pernah tahu pasti bahwa Anda telah menangkap semuanya kecuali Anda pergi melalui kode secara manual sesudahnya. Meski begitu, Anda tidak akan pernah yakin bahwa Anda tidak melewatkan apa pun.
Satu-satunya solusi nyata, IMHO, adalah sedapat mungkin waspada, gunakan otomatisasi yang Anda inginkan, refactor di mana Anda bisa, dan terus-menerus mencari cara untuk meningkatkan kode Anda. Tentu saja, itu ide yang baik untuk melakukan itu.
sumber
Saya belum menggunakannya sendiri, tetapi cppcheck , mengklaim menemukan fungsi yang tidak digunakan. Ini mungkin tidak akan menyelesaikan masalah yang lengkap tetapi mungkin ini merupakan permulaan.
sumber
cppcheck --enable=unusedFunction --language=c++ .
untuk menemukan fungsi-fungsi yang tidak terpakai ini.Anda dapat mencoba menggunakan PC-lint / FlexeLint dari Gimple Software . Itu mengklaim
Saya telah menggunakannya untuk analisis statis dan menemukannya sangat baik tetapi saya harus mengakui bahwa saya belum menggunakannya untuk menemukan kode mati secara spesifik.
sumber
Pendekatan normal saya untuk menemukan barang yang tidak digunakan adalah
watch "make 2>&1"
cenderung melakukan trik pada Unix.Ini adalah proses yang agak panjang, tetapi memberikan hasil yang baik.
sumber
Tandai sebanyak mungkin fungsi dan variabel publik sebagai privat atau terlindungi tanpa menyebabkan kesalahan kompilasi, saat melakukan ini, coba juga refactor kodenya. Dengan membuat fungsi-fungsi pribadi dan sampai batas tertentu dilindungi, Anda mengurangi area pencarian Anda karena fungsi-fungsi pribadi hanya dapat dipanggil dari kelas yang sama (kecuali ada makro bodoh atau trik lain untuk menghindari pembatasan akses, dan jika itu masalahnya saya sarankan Anda cari pekerjaan baru). Jauh lebih mudah untuk menentukan bahwa Anda tidak memerlukan fungsi pribadi karena hanya kelas yang sedang Anda kerjakan yang dapat memanggil fungsi ini. Metode ini lebih mudah jika basis kode Anda memiliki kelas-kelas kecil dan secara longgar digabungkan. Jika basis kode Anda tidak memiliki kelas kecil atau memiliki kopling sangat ketat, saya sarankan membersihkannya terlebih dahulu.
Selanjutnya akan menandai semua fungsi publik yang tersisa dan membuat grafik panggilan untuk mencari tahu hubungan antara kelas-kelas. Dari pohon ini, cobalah untuk mencari tahu bagian mana dari cabang yang sepertinya dapat dipangkas.
Keuntungan dari metode ini adalah Anda dapat melakukannya berdasarkan modul, sehingga mudah untuk tetap lulus dari unittest Anda tanpa memiliki periode waktu yang besar ketika Anda mendapatkan basis kode yang rusak.
sumber
Jika Anda menggunakan Linux, Anda mungkin ingin melihat ke dalam
callgrind
, alat analisis program C / C ++ yang merupakan bagian darivalgrind
suite, yang juga berisi alat yang memeriksa kebocoran memori dan kesalahan memori lainnya (yang harus Anda gunakan juga). Ini menganalisis contoh menjalankan program Anda, dan menghasilkan data tentang grafik panggilannya, dan tentang biaya kinerja node pada grafik panggilan. Biasanya digunakan untuk analisis kinerja, tetapi juga menghasilkan grafik panggilan untuk aplikasi Anda, sehingga Anda dapat melihat fungsi apa yang dipanggil, serta peneleponnya.Ini jelas melengkapi metode statis yang disebutkan di tempat lain di halaman, dan itu hanya akan membantu untuk menghilangkan kelas, metode, dan fungsi yang sama sekali tidak digunakan - itu juga tidak membantu menemukan kode mati di dalam metode yang sebenarnya disebut.
sumber
Saya benar-benar belum menggunakan alat apa pun yang melakukan hal seperti itu ... Tapi, sejauh yang saya lihat di semua jawaban, tidak ada yang pernah mengatakan bahwa masalah ini tidak dapat dihitung.
Apa yang saya maksud dengan ini? Bahwa masalah ini tidak dapat diselesaikan dengan algoritma apa pun di komputer. Teorema ini (bahwa algoritma semacam itu tidak ada) adalah akibat wajar dari Turing's Halting Problem.
Semua alat yang akan Anda gunakan bukan algoritma tetapi heuristik (yaitu bukan algoritma yang tepat). Mereka tidak akan memberi Anda persis semua kode yang tidak digunakan.
sumber
Salah satu caranya adalah menggunakan fitur debugger dan kompiler untuk menghilangkan kode mesin yang tidak digunakan selama kompilasi.
Setelah beberapa kode mesin dihilangkan, debugger tidak akan membiarkan Anda meletakkan breakpojnt pada baris kode sumber yang sesuai. Jadi, Anda meletakkan breakpoints di mana-mana dan memulai program dan memeriksa breakpoints - yang ada dalam status "tidak ada kode untuk sumber ini" sesuai dengan kode yang dihilangkan - baik kode yang tidak pernah dipanggil atau telah diuraikan dan Anda harus melakukan beberapa minimum analisis untuk menemukan yang mana dari dua yang terjadi.
Setidaknya begitulah cara kerjanya di Visual Studio dan saya kira toolet lain juga bisa melakukannya.
Itu banyak pekerjaan, tapi saya kira lebih cepat daripada menganalisis semua kode secara manual.
sumber
CppDepend adalah alat komersial yang dapat mendeteksi jenis, metode, dan bidang yang tidak digunakan, dan melakukan lebih banyak lagi. Ini tersedia untuk Windows dan Linux (tetapi saat ini tidak memiliki dukungan 64-bit), dan dilengkapi dengan uji coba 2 minggu.
Penafian: Saya tidak bekerja di sana, tetapi saya memiliki lisensi untuk alat ini (dan juga NDepend , yang merupakan alternatif yang lebih kuat untuk kode .NET).
Bagi mereka yang ingin tahu, berikut adalah contoh aturan bawaan (yang dapat disesuaikan) untuk mendeteksi metode mati, ditulis dalam CQLinq :
sumber
Itu tergantung pada platform yang Anda gunakan untuk membuat aplikasi Anda.
Misalnya, jika Anda menggunakan Visual Studio, Anda bisa menggunakan alat seperti .NET ANTS Profiler yang dapat mengurai dan profil kode Anda. Dengan cara ini, Anda harus segera mengetahui bagian mana dari kode Anda yang benar-benar digunakan. Eclipse juga memiliki plugin yang setara.
Jika tidak, jika Anda perlu tahu apa fungsi aplikasi Anda yang sebenarnya digunakan oleh pengguna akhir Anda, dan jika Anda dapat melepaskan aplikasi Anda dengan mudah, Anda dapat menggunakan file log untuk audit.
Untuk setiap fungsi utama, Anda dapat melacak penggunaannya, dan setelah beberapa hari / minggu baru saja mendapatkan file log itu, dan melihatnya.
sumber
Saya tidak berpikir itu bisa dilakukan secara otomatis.
Bahkan dengan alat cakupan kode, Anda perlu menyediakan data input yang cukup untuk dijalankan.
Mungkin alat analisis statis yang sangat kompleks dan mahal seperti dari Coverity's atau compiler LLVM bisa membantu.
Tapi saya tidak yakin dan saya lebih suka review kode manual.
DIPERBARUI
Yah .. hanya menghapus variabel yang tidak digunakan, fungsi yang tidak digunakan tidak sulit sekalipun.
DIPERBARUI
Setelah membaca jawaban dan komentar lain, saya lebih yakin bahwa itu tidak bisa dilakukan.
Anda harus mengetahui kode untuk memiliki ukuran cakupan kode yang bermakna, dan jika Anda tahu bahwa banyak pengeditan manual akan lebih cepat daripada menyiapkan / menjalankan / meninjau hasil cakupan.
sumber
Saya mempunyai seorang teman yang menanyakan pertanyaan ini kepada saya hari ini, dan saya melihat-lihat beberapa perkembangan Clang yang menjanjikan, misalnya ASTMatcher dan Static Analyzer yang mungkin memiliki visibilitas yang cukup pada kejadian-kejadian selama kompilasi untuk menentukan bagian kode mati, tetapi kemudian saya menemukan ini:
https://blog.flameeyes.eu/2008/01/today-how-to-identify-unused-exported-functions-and-variables
Cukup banyak deskripsi lengkap tentang cara menggunakan beberapa bendera GCC yang tampaknya dirancang untuk tujuan mengidentifikasi simbol yang tidak direferensikan!
sumber
Masalah umum jika beberapa fungsi akan dipanggil adalah NP-Complete. Anda tidak dapat mengetahui sebelumnya secara umum apakah suatu fungsi akan dipanggil karena Anda tidak akan tahu apakah mesin Turing akan pernah berhenti. Anda bisa mendapatkan jika ada beberapa jalur (statis) yang beralih dari main () ke fungsi yang Anda tulis, tetapi itu tidak menjamin Anda bahwa itu akan pernah dipanggil.
sumber
Nah jika Anda menggunakan g ++ Anda bisa menggunakan flag ini -Digunakan
Dokumentasi menurut:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Sunting: Berikut ini adalah flag lain yang berguna -Wunreachable-code Menurut dokumentasi:
sumber