Bagaimana saya bisa tahu bagian mana dalam kode yang tidak pernah digunakan?

312

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?

pengguna63898
sumber
4
Saya pikir bahasa permintaan kode akan memberi Anda pandangan yang lebih baik tentang proyek Anda secara keseluruhan. Saya tidak yakin tentang dunia c ++ tetapi tampaknya ada cppdepend.com (ini tidak gratis), yang terlihat cukup baik. Mungkin sesuatu seperti ini mungkin tersedia secara gratis. Hal lain adalah, sebelum melakukan segala macam refactoring, hal yang waras untuk dilakukan adalah melakukan tes unit jika Anda tidak memilikinya sekarang. Dengan unit test apa yang dapat Anda lakukan adalah memiliki alat profil kode cakupan kode Anda yang di dalamnya sendiri akan membantu untuk menghapus kode mati jika Anda tidak dapat menutupi kode itu.
Biswanath
3
Lihatlah rujukannya di sini: en.wikipedia.org/wiki/Unreachable_code
Martin York
6
Saya menemukan topik serupa. stackoverflow.com/questions/229069/…
UmmaGumma
3
Yup, salah satu hal lucu dari C ++ adalah bahwa menghapus fungsi yang "tidak digunakan" masih dapat mengubah hasil suatu program.
MSalters
1
@ MSalters: Itu yang menarik ... Untuk itu kita harus berbicara tentang fungsi mana dalam set kelebihan yang dipilih untuk panggilan yang diberikan, benar? Sepengetahuan saya, jika ada 2 fungsi yang dinamai f(), dan sebuah panggilan untuk f()secara pasti memutuskan ke yang pertama, maka tidak mungkin untuk membuat panggilan tersebut memutuskan ke yang ke-2 hanya dengan menambahkan fungsi ke-3 bernama f()- 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.
j_random_hacker

Jawaban:

197

Ada dua jenis kode yang tidak digunakan:

  • yang lokal, yaitu, dalam beberapa fungsi beberapa jalur atau variabel tidak digunakan (atau digunakan tetapi tidak berarti, seperti ditulis tetapi tidak pernah dibaca)
  • yang global: fungsi yang tidak pernah dipanggil, objek global yang tidak pernah diakses

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)
  • tidak ada pilihan yang saya tahu untuk memperingatkan tentang catchblok 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:

  • Teoretisnya adalah menggunakan penganalisa statis. Sepotong perangkat lunak yang akan memeriksa seluruh kode sekaligus dengan sangat terperinci dan menemukan semua alur alur. Dalam praktiknya saya tidak tahu ada yang bisa digunakan di sini.
  • Yang pragmatis adalah menggunakan heuristik: gunakan alat cakupan kode (dalam rantai GNU itu 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.

  1. Gunakan perpustakaan Dentang untuk mendapatkan AST (pohon sintaksis abstrak)
  2. Lakukan analisis mark-and-sapuan dari titik masuk dan seterusnya

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.

Matthieu M.
sumber
7
Sangat bagus, +1. Saya suka bahwa Anda membedakan antara kode yang dapat ditentukan secara statis untuk tidak pernah berjalan dalam keadaan apa pun dan kode yang tidak berjalan dalam menjalankan tertentu, tetapi berpotensi bisa. Yang pertama adalah yang penting saya pikir, dan seperti yang Anda katakan analisis jangkauan menggunakan AST dari seluruh program adalah cara untuk mendapatkannya. (Mencegah foo()agar tidak ditandai sebagai "dipanggil" saat itu muncul hanya if (0) { foo(); }akan menjadi bonus tetapi membutuhkan kecerdasan ekstra.)
j_random_hacker
@ j_random_hacker: mungkin menggunakan CFG (Control-Flow Graph) akan lebih baik sekarang karena saya memikirkannya (terima kasih kepada contoh Anda). Saya tahu bahwa Clang sangat ingin berkomentar tentang perbandingan tautologis seperti yang Anda sebutkan dan dengan demikian menggunakan CFG kita mungkin menemukan kode mati sejak awal.
Matthieu M.
@ Matthieu: Ya, mungkin CFG adalah apa yang saya maksud juga, bukan AST :) Yang saya maksud adalah: sebuah digraf di mana simpul berfungsi dan ada tepi dari fungsi x ke fungsi y setiap kali x mungkin bisa memanggil y. (Dan dengan properti penting yang fungsi kelebihannya semua diwakili oleh simpul yang berbeda - terdengar seperti Dentang melakukannya untuk Anda, phew!)
j_random_hacker
1
@ j_random_hacker: sebenarnya CFG lebih rumit dari pada digraf sederhana, karena ini mewakili semua kode yang akan dieksekusi dalam blok dengan tautan dari satu blok ke blok lain berdasarkan pada pernyataan kondisional. Keuntungan utama adalah bahwa itu secara alami cocok untuk kode pemangkasan yang secara statis dapat ditentukan untuk mati (itu menciptakan blok yang tidak dapat dijangkau yang dapat diidentifikasi), jadi akan lebih baik untuk mengeksploitasi CFG daripada AST untuk membuat digraf yang Anda berbicara tentang ... Saya pikir :)
Matthieu M.
1
@ j_random_hacker: sebenarnya AST Clang melakukannya, itu membuat semuanya eksplisit (atau hampir ...) karena dimaksudkan untuk bekerja dengan kode, bukan hanya untuk mengkompilasinya. Sebenarnya ada diskusi saat ini karena ternyata ada masalah dengan daftar penginisialisasi di mana konversi tersirat tidak muncul di AST, tapi saya kira itu akan diperbaiki.
Matthieu M.
35

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-sectionsdan -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-sectionsbagian 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:

  • Fungsi virtual. Tanpa mengetahui subclass mana yang ada dan mana yang sebenarnya dipakai pada saat run time, Anda tidak bisa tahu fungsi virtual mana yang Anda butuhkan untuk ada di program akhir. Linker tidak memiliki informasi yang cukup tentang itu sehingga harus menjaga mereka semua tetap ada.
  • Global dengan konstruktor, dan konstruktor mereka. Secara umum, tautan tidak dapat mengetahui bahwa konstruktor untuk global tidak memiliki efek samping, jadi ia harus menjalankannya. Jelas ini berarti global itu sendiri juga perlu dijaga.

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.

olsner
sumber
Nasihat praktis yang bagus. Hanya mendapatkan daftar fungsi yang diketahui tidak dapat digunakan di mana saja (bahkan jika, seperti yang Anda katakan, daftar ini tidak lengkap) akan mendapatkan banyak buah yang tergantung rendah saya pikir.
j_random_hacker
Saya rasa semua ini tidak berfungsi untuk templat yang tidak diverifikasi .
Jakub Klinkovský
25

Nah jika Anda menggunakan g ++ Anda bisa menggunakan flag ini -Wunused

Dokumentasi menurut:

Memperingatkan kapan saja suatu variabel tidak digunakan selain dari deklarasi, setiap kali suatu fungsi dinyatakan statis tetapi tidak pernah didefinisikan, setiap kali label dideklarasikan tetapi tidak digunakan, dan setiap kali pernyataan menghitung hasil yang secara eksplisit tidak digunakan.

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Sunting : Ini adalah bendera lain yang berguna -Wunreachable-code Menurut dokumentasi:

Opsi ini dimaksudkan untuk memperingatkan ketika kompiler mendeteksi bahwa setidaknya seluruh baris kode sumber tidak akan pernah dieksekusi, karena beberapa kondisi tidak pernah puas atau karena itu setelah prosedur yang tidak pernah kembali.

Pembaruan : Saya menemukan topik serupa Deteksi kode mati dalam proyek C / C ++ lama

UmmaGumma
sumber
4
Ini tidak akan menangkap header bahwa fungsi prototipe yang tidak pernah dipanggil. Atau metode kelas publik yang tidak dipanggil. Itu hanya dapat memeriksa apakah variabel cakupan lokal digunakan dalam lingkup itu.
Falmarri
@ Falmarri Saya tidak pernah menggunakan bendera ini. Saya mencoba mencari tahu sendiri kode mati macam apa yang dapat saya temukan dengannya.
UmmaGumma
-Wunusedmemperingatkan 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-codememperingatkan tentang kode dalam fungsi yang tidak dapat dicapai, dapat kode terletak setelah throwatau returnpernyataan atau kode cabang yang tidak pernah diambil (yang terjadi dalam kasus perbandingan tautologis) misalnya.
Matthieu M.
18

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 #.

Carlos V
sumber
7
Kuncinya di sini adalah "saat berjalan" - jika data input Anda tidak menjalankan beberapa jalur kode, jalur itu tidak akan dikenali seperti yang digunakan, bukan?
sharptooth
1
Itu betul. Tanpa menjalankan kode, tidak ada cara untuk mengetahui baris apa yang tidak tercapai. Saya bertanya-tanya seberapa sulit untuk mengatur beberapa Tes Unit untuk meniru beberapa berjalan normal.
Carlos V
1
@drhishch Saya pikir, sebagian besar kode yang tidak terpakai tersebut harus menemukan linker dan bukan compiler.
UmmaGumma
1
@drhirsch Benar, kompiler dapat menangani beberapa kode yang tidak dapat dijangkau, seperti fungsi yang dideklarasikan tetapi tidak dipanggil dan beberapa evaluasi hubung singkat, tetapi bagaimana dengan kode yang bergantung pada tindakan pengguna, atau menjalankan variabel waktu?
Carlos V
1
@ golcarcol Ok, mari kita fungsi void func()di a.cpp, yang digunakan di b.cpp. Bagaimana kompiler dapat memeriksa, apakah func () digunakan dalam program? Ini pekerjaan penghubung.
UmmaGumma
15

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 :

double x = sqrt(2);
if (x > 5)
{
  doStuff();
}

Seperti yang dicatat Wikipedia dengan benar, kompiler yang cerdik mungkin dapat menangkap sesuatu seperti ini. Tetapi pertimbangkan modifikasi:

int y;
cin >> y;
double x = sqrt((double)y);

if (x != 0 && x < 1)
{
  doStuff();
}

Akankah kompiler menangkap ini? Mungkin. Tetapi untuk melakukan itu, perlu melakukan lebih dari menjalankan sqrtterhadap nilai skalar yang konstan. Itu harus mencari tahu yang (double)yakan selalu menjadi bilangan bulat (mudah), dan kemudian memahami kisaran matematika sqrtuntuk himpunan bilangan bulat (keras). Kompiler yang sangat canggih mungkin dapat melakukan ini untuk sqrtfungsi, 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.

Justin Morgan
sumber
1
Benar dan jangan tinggalkan kode mati! Jika Anda menghapus fitur, bunuh kode mati. Membiarkannya di sana "berjaga-jaga" hanya menyebabkan mengasapi yang (seperti telah Anda bahas) sulit ditemukan nanti. Biarkan kontrol versi melakukan penimbunan untukmu.
Lightness Races dalam Orbit
12

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.

Tuan Shark
sumber
Ya, ia dapat menemukan variabel dan fungsi lokal yang tidak dirujuk.
Chugaister
Yap digunakan cppcheck --enable=unusedFunction --language=c++ .untuk menemukan fungsi-fungsi yang tidak terpakai ini.
Jason Harris
9

Anda dapat mencoba menggunakan PC-lint / FlexeLint dari Gimple Software . Itu mengklaim

temukan makro yang tidak digunakan, typedef, kelas, anggota, deklarasi, dll. di seluruh proyek

Saya telah menggunakannya untuk analisis statis dan menemukannya sangat baik tetapi saya harus mengakui bahwa saya belum menggunakannya untuk menemukan kode mati secara spesifik.

Tony
sumber
5

Pendekatan normal saya untuk menemukan barang yang tidak digunakan adalah

  1. pastikan sistem build menangani pelacakan ketergantungan dengan benar
  2. mengatur monitor kedua, dengan jendela terminal layar penuh, menjalankan build berulang dan menampilkan screenful pertama dari output. watch "make 2>&1"cenderung melakukan trik pada Unix.
  3. menjalankan operasi temukan-dan-ganti pada seluruh pohon sumber, tambahkan "//?" di awal setiap baris
  4. perbaiki kesalahan pertama yang ditandai oleh kompiler, dengan menghapus "//?" di baris yang sesuai.
  5. Ulangi sampai tidak ada kesalahan yang tersisa.

Ini adalah proses yang agak panjang, tetapi memberikan hasil yang baik.

Simon Richter
sumber
2
Memiliki prestasi, tetapi sangat padat karya. Anda juga harus memastikan untuk menghapus komentar semua kelebihan fungsi pada saat yang sama - jika ada lebih dari satu yang berlaku, membatalkan komentar yang kurang disukai mungkin memungkinkan kompilasi untuk berhasil tetapi menghasilkan perilaku program yang salah (dan gagasan yang salah tentang hal itu) fungsi digunakan).
j_random_hacker
Saya hanya membatalkan pernyataan deklarasi pada langkah pertama (semua kelebihan), dan di iterasi berikutnya kemudian melihat definisi yang hilang; dengan begitu, saya bisa melihat mana kelebihan yang sebenarnya digunakan.
Simon Richter
@Simon: Yang menarik dalam komentar pada pertanyaan utama, MSalters menunjukkan bahwa bahkan ada / tidaknya deklarasi untuk suatu fungsi yang tidak pernah dipanggil dapat memengaruhi 2 fungsi lain mana yang ditemukan oleh resolusi kelebihan beban. Memang ini membutuhkan pengaturan yang sangat aneh dan dibuat-buat, sehingga tidak mungkin menjadi masalah dalam praktiknya.
j_random_hacker
4

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.

Lie Ryan
sumber
3

Jika Anda menggunakan Linux, Anda mungkin ingin melihat ke dalam callgrind, alat analisis program C / C ++ yang merupakan bagian dari valgrindsuite, 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.

Adam Higuera
sumber
3

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.

geekazoid
sumber
1
Saya pikir OP terutama ingin menemukan fungsi yang tidak dipanggil dari mana saja, yang tentu saja tidak dapat dihitung - sebagian besar penghubung modern dapat melakukannya! Ini hanya masalah mengekstraksi info itu dengan paling sedikit rasa sakit dan membosankan.
j_random_hacker
Anda benar, saya tidak melihat komentar terakhir untuk pertanyaan utama. Omong-omong, mungkin ada fungsi yang dirujuk dalam kode yang sebenarnya tidak digunakan. Hal-hal semacam itu mungkin tidak terdeteksi.
geekazoid
2

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.

sharptooth
sumber
4
Saya pikir pertanyaan OP adalah tentang bagaimana menemukan subset kode sumber yang lebih kecil dan lebih mudah dikelola, tidak begitu banyak memastikan biner yang dikompilasi efisien.
j_random_hacker
@ j_random_hacker Saya memberikannya - dan ternyata penghapusan kode bahkan dapat digunakan untuk melacak kembali ke kode sumber asli.
sharptooth
Anda harus beberapa flag compiler tertentu di studio visual untuk mencapainya? dan apakah itu hanya berfungsi dalam mode rilis atau apakah akan bekerja dalam debug juga?
Naveen
Bagaimana dengan garis yang digunakan tetapi dioptimalkan-keluar oleh kompiler?
Itamar Katz
@Naveen: Dalam Visual C ++ 9 Anda harus mengaktifkan optimasi dan menggunakan / OPT: ICF
sharptooth
2

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 :

// <Name>Potentially dead Methods</Name>
warnif count > 0
// Filter procedure for methods that should'nt be considered as dead
let canMethodBeConsideredAsDeadProc = new Func<IMethod, bool>(
    m => !m.IsPublic &&       // Public methods might be used by client applications of your Projects.
         !m.IsEntryPoint &&            // Main() method is not used by-design.
         !m.IsClassConstructor &&      
         !m.IsVirtual &&               // Only check for non virtual method that are not seen as used in IL.
         !(m.IsConstructor &&          // Don't take account of protected ctor that might be call by a derived ctors.
           m.IsProtected) &&
         !m.IsGeneratedByCompiler
)

// Get methods unused
let methodsUnused = 
   from m in JustMyCode.Methods where 
   m.NbMethodsCallingMe == 0 && 
   canMethodBeConsideredAsDeadProc(m)
   select m

// Dead methods = methods used only by unused methods (recursive)
let deadMethodsMetric = methodsUnused.FillIterative(
   methods => // Unique loop, just to let a chance to build the hashset.
              from o in new[] { new object() }
              // Use a hashet to make Intersect calls much faster!
              let hashset = methods.ToHashSet()
              from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods)
              where canMethodBeConsideredAsDeadProc(m) &&
                    // Select methods called only by methods already considered as dead
                    hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe
              select m)

from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain)
select new { m, m.MethodsCallingMe, depth = deadMethodsMetric[m] }
Roman Boiko
sumber
Pembaruan: Dukungan 64-bit untuk Linux telah ditambahkan di versi 3.1.
Roman Boiko
1

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.

AUS
sumber
1
.net ANTS Profiler sepertinya itu untuk C # - apakah Anda yakin itu juga berfungsi untuk C ++?
j_random_hacker
@ j_random_hacker: selama saya tahu, ini berfungsi dengan kode yang dikelola. Jadi. NET ANTS tentunya tidak akan dapat menganalisis kode C ++ 'standar' (yaitu dikompilasi dengan gcc, ...).
AUS
0

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.

9dan
sumber
2
kata-kata dari jawaban Anda menyesatkan, tidak ada yang istimewa dari LLVM ... gratis!
Matthieu M.
pengeditan manual tidak akan membantu Anda dengan menjalankan variabel waktu yang melewati cabang logika di program Anda. Bagaimana jika kode Anda tidak pernah memenuhi kriteria tertentu dan karena itu selalu mengikuti jalur yang sama?
Carlos V
0

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!

Steven Lu
sumber
0

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.

Luis Colorado
sumber
-3

Nah jika Anda menggunakan g ++ Anda bisa menggunakan flag ini -Digunakan

Dokumentasi menurut:

Warn whenever a variable is unused aside from its declaration, whenever a function is declared static but never defined, whenever a label is declared but not used, and whenever a statement computes a result that is explicitly not used.

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Sunting: Berikut ini adalah flag lain yang berguna -Wunreachable-code Menurut dokumentasi:

This option is intended to warn when the compiler detects that at least a whole line of source code will never be executed, because some condition is never satisfied or because it is after a procedure that never returns.
ram singh
sumber
6
Informasi persis ini telah disebutkan dalam jawaban berperingkat teratas saat ini. Harap baca jawaban yang ada untuk menghindari duplikasi yang tidak perlu.
j_random_hacker
1
Sekarang Anda bisa mendapatkan lencana Tekanan Teman Anda!
Andrew Grimm