PVS-Studio adalah program yang mencari bug dalam kode sumber proyek C++ dan C# yang tidak dapat dilihat oleh compiler namun hampir pasti merupakan kesalahan pemrograman.

Catatan. Artikel ini awalnya "diterbitkan" dalam bahasa Rusia di blog blog.harrix.org. Versi asli dan terjemahan diposting di situs web kami dengan izin dari penulisnya.

Perkenalan

Saya dihubungi oleh orang-orang dari tim PVS-Studio dengan proposal kolaborasi. Saya telah membaca banyak tentang produk mereka di halaman Habrahabr tetapi tidak pernah mencobanya. Jadi, saya menyarankan hal berikut: mereka akan memberi saya lisensi produk, dan saya akan memindai program saya dan menulis ulasan tentang alat tersebut, di mana saya akan mendiskusikan cara saya menggunakan penganalisis, cara memeriksa kode, dan seterusnya. . Mereka menjawab ya.

Jadi, apa yang akan Anda lihat dalam artikel ini adalah, sebagaimana istilah yang sedang tren saat ini, sebuah ulasan jujur ​​​​tanpa bordir apa pun oleh programmer biasa dan rata-rata yang berurusan dengan pemrograman "akademik" daripada pemrograman aplikasi. Dengan kata lain, saya bukan ahli dari perusahaan besar yang mengerjakan proyek kompleks yang mengetahui banyak utilitas dan ahli dalam pengoptimalan kompiler, dan sebagainya.

Hal lain yang harus Anda ketahui adalah bahwa saya adalah penggemar setia pemrograman fungsional beberapa tahun yang lalu. Saya tidak menyukai OOP, tidak pernah menggunakan namespace, sering kali menemukan kembali roda, dan seterusnya. Sekarang saya mengingat periode hidup saya sebagai mimpi buruk dan secara aktif menulis ulang banyak program saya pada masa itu, meskipun program tersebut belum siap untuk analisis statis. Oleh karena itu, saya akan mengambil proyek dari pemrograman fungsional saya sebelumnya (semuanya dapat ditemukan di GitHub) untuk dianalisis. Meskipun fungsionalisme berkuasa di sana, saya sangat berhati-hati dalam pengkodean, pengujian, dan dokumentasi saat menulisnya, jadi menurut saya tidak akan ada banyak bug serius dalam proyek tersebut.

Ini dia.

Instalasi

Instalasi tidak menimbulkan masalah apa pun. Ada tombol besar “Unduh dan coba” di “halaman beranda” situs PVS-Studio, yang akan membawa Anda ke halaman dengan tautan unduh yang tidak akan Anda lewatkan.

Instalasi sepenuhnya standar; bahkan tidak ada opsi khusus untuk dipilih. Namun dalam artikel saya, saya selalu mencoba menjelaskan langkah paling sederhana sekalipun. Nah ini dia screenshotnya:

Proses instalasi PVS-Studio

Langkah 1.

Langkah 2.

Langkah 3.

Langkah 4.

Langkah 5.

Langkah 6.

Langkah 7.

Bagaimana semuanya gagal

Saya langsung mengatakan bahwa saya tidak membaca dokumentasi apa pun pada awalnya. Saya baru saja menginstal programnya dan berpikir, “lalu apa selanjutnya?” Saya menemukan item baru berikut di menu 'Start':

Intuisi memberi tahu saya bahwa item yang saya butuhkan harus memiliki nama yang sama dengan programnya. Klik. Dan di sini saya gagal dan menunjukkan pesan ini:

Sejujurnya, saya menjadi sedikit khawatir. Anda tahu, saya kebanyakan bekerja di Qt dan menggunakan Visual Studio sebagai program tutorial untuk siswa saya.

OKE. Mungkin saya harus mencoba item menu lainnya, Mandiri?

Itu lebih baik. Sekarang, catatan penting. Apa yang saya harapkansebagai algoritme untuk bekerja dengan penganalisis adalah ini: Saya membukanya, memuat file sumber proyek saya, dan ia menemukan bug untuk saya. Asumsi ini ternyata sepenuhnya salah, tapi kita akan membicarakannya nanti.

Jadi, pada awalnya saya mencoba memuat salah satu file saya (saya khawatir karena hal itu memungkinkan saya untuk memilih hanya satu file dalam satu waktu).

Ini dia, tapi apa selanjutnya? Tidak ada lagi tombol besar atau berwarna-warni.

Hanya ada satu item di menu utama yang sepertinya saya perlukan:

Mengkliknya akan membuka jendela berikut.

Dan di sinilah saya bertindak konyol. Alih-alih membaca teks, saya mulai mengklik tombol-tombolnya. Ketika saya mengklik Pilih, program meminta beberapa file *.suppress, yang jelas bukan yang saya perlukan. Kata Compiler menarik perhatian saya. Oke, jadi saya harus mengklik Mulai Pemantauan.

Saya pikir program tersebut memindai komputer saya untuk mencari kompiler, jadi ini akan memakan waktu cukup lama. Dan itu benar-benar terjadi (saya menunggu beberapa jam), tetapi saya senang melihat akhirnya ia mulai menemukan sesuatu:

Baru beberapa waktu kemudian saya mengetahui alasannya: Saya sedang mengerjakan proyek saya dan menyusunnya saat proses pemantauan sedang berjalan.

Beberapa jam kemudian, saya merasa alat tersebut telah menemukan cukup kompiler dan menghentikannya. Namun, tidak membuahkan hasil apa pun. Lalu apa yang harus saya lakukan? Sial, sekarang saya harus membaca dokumentasinya (

Tautan yang relevan ternyata tidak terlalu terlihat.

Setelah membaca artikel tersebut, saya akhirnya menemukan apa yang harus saya lakukan.

Bagaimana semuanya berjalan lancar

Inilah cara kerja penganalisis sebenarnya.

Anda memulai proses pemantauan di PVS-Studio dan kemudian menjalankan kompiler pada proyek Anda. Ketika kompilasi selesai, hentikan proses pemantauan, dan tunggu beberapa saat hingga program mengeluarkan log analisis.

Saya akan menunjukkan cara kerjanya menggunakan aplikasi pengujian Qt 5.7 dengan MinGW, yang menggunakan perpustakaan Harrix MathLibrary saya, sebagai contoh.

Masuk ke menu analisis.

Mulai pantau peluncuran kompiler.

Proses pemantauan dapat berjalan di latar belakang.

Kompilasi proyek:

PVS-Studio telah mendeteksi instance compiler kami yang diluncurkan.

Hentikan pemantauan.

Dan di sini PVS-Studio menuangkan setumpuk peringatan. Berengsek. Saya mengharapkan hasil yang lebih baik ((

Klik dua kali pada peringatan akan membawa Anda ke file sumber terkait tempat bug ditemukan.

Saat Anda akhirnya memahami ide di balik program ini, mengerjakannya menjadi mudah, namun tidak terlalu intuitif bagi pemula.

Sekarang mari kita lihat bug apa yang kita punya. Apakah mereka benar-benar serangga?

Peringatan. Saat memulai kompiler, bangun kembali seluruh proyek. Baru-baru ini saya merasa sedih dengan 71peringatan, namun setelah saya memperbaikinya dan membangun kembali proyek, alat tersebut mengeluarkan lebih dari1900 peringatan lagi.

Sekarang aku merasa ingin mengutuk.

Menganalisis kesalahan

Kami telah menempuh jalur pendakian saya untuk memahami cara menggunakan program ini. Sekarang mari kita lihat hasil analisisnya.

Saya tidak terlalu tertarik dengan bug yang ditemukan di Qt itu sendiri - bug tersebut merupakan tanggung jawab orang yang mengembangkan compiler.

Bagaimana dengan kesalahan saya sendiri?

Sebagian besar dari lebih dari 1900 peringatan adalah peringatan V550:

V550. Perbandingan yang aneh dan tepat. Mungkin lebih baik menggunakan perbandingan dengan presisi yang ditentukan: fabs(A — B) ‹ Epsilon atau fabs(A — B) › Epsilon

Dan saya setuju dengan peringatan itu dalam banyak kasus. Misalnya, kode berikut dengan (F[i]==F[i+1])mungkin menyebabkan masalah:

//identical elements
//are assigned identical ranks as arithmetic mean
for (i=0;i<VHML_N-1;i++)
{
if (F[i]==F[i+1])
  {
  j=i+1;
  while ((F[i]==F[j])&&(j<VHML_N)) j++;
  Sn=HML_SumOfArithmeticalProgression(i+1,1,j-i);
  Sn/=double(j-i);
  for (k=0;k<VHML_N;k++)
   if (Fitness[k]==F[i]) VHML_ResultVector[k]=Sn;
  i=j-1;
  }
}

Ide yang lebih buruk lagi adalah memeriksa posisi ekstrim roda Maxwell seperti yang dilakukan pada kode mengerikan berikut:

//if the wheel is in extreme positions,
if (((x==R)&&(v<0))||((x==l)&&(v>0))) v=-v*(1.-k);

Dan inilah yang saya dapatkan pada fragmen berikut.

//Calculating arithmetic mean of two samples
xn=HML_Mean(x,VHML_N);
yn=HML_Mean(x,VHML_N);

V656 Variabel 'xn', 'yn' diinisialisasi melalui panggilan ke fungsi yang sama. Ini mungkin kesalahan atau kode yang tidak dioptimalkan. Pertimbangkan untuk memeriksa ekspresi 'HML_Mean(x, VHML_N)'. Periksa baris: 3712, 3713. harrixmathlibrary.h 3713

Ini adalah kesalahan yang cukup mengecewakan. Saya pasti telah menyalin bagian kode tetapi lupa mengubah beberapa token.

Kesalahan konyol lainnya.

int VHML_Result=0;
    if (VHML_N1==VHML_N2)
        for (int i=0;i<VHML_N1;i++)
            if (a[i]!=b[i]) VHML_Result=-1;
            else
                VHML_Result=-1;

V523 Pernyataan ‘then’ setara dengan pernyataan ‘else’. harrixmathlibrary.h 695

Fungsi ini akan selalu memberikan jawaban positif terhadap solusi yang ada. Saya tidak pernah tahu apa yang membuat saya menghentikan semua perhitungan variabel solutionis di akhir fungsi.

double HML_LineTwoPoint(double x, double x1, double y1,
                        double x2, double y2, int *solutionis)
{
/*
This function is a two-point linear equation.
Value of y is returned for given x.
Input parameters:
 x - abscissa of point in question;
 x1 - abscissa of first point;
 y1 - ordinate of first point;
 x2 - abscissa of second point;
 y2 - ordinate of second point;
 solutionis - stores the returned solution:
  0 - no solution;
  1 - solution found;
  2 - any number is a solution (the line is parallel to y-axis).
Return value:
 Value of y for given x.
*/
double y=0;
 
if ((x1==x2)&&(y1==y2))
{
  //this is the same point, so any number is a solution
  y=y1;
  *solutionis=2;
}
else
{
  if (y1==y2)
  {
    //this line is parallel to x-axis
    y=y1;
    *solutionis=1;
  }
  else
  {
    if (x1==x2)
    {
      //this line is parallel to y-axis
      if (x==x1)
      {
        y=y1;
        *solutionis=2;
      }
      else
      {
        y=0;
        *solutionis=0;
      }
    }
    else
    {
      y=(x-x1)*(y2-y1)/(x2-x1)+y1;
    }
  }
}
 
*solutionis=1;
return y;
}

V519 Variabel '* solutionis' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 1788, 1821. harrixmathlibrary.cpp 1821

Peringatan berikutnya berkaitan dengan saya yang terlalu berhati-hati daripada kesalahan nyata: Saya menetapkan variabel ringkasan ke nol terlebih dahulu, untuk berjaga-jaga:

if (VHML_N>0) VHML_Result=0;
 
...
 
//Evaluating real-vector objective function
VHML_Result=VHML_TempFunction(VHML_TempDouble3,RealLength);
 
return VHML_Result;

V519 Variabel 'VHML_Result' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 385, 395. harrixmathlibrary.cpp 395

PVS-Studio juga menemukan dua fungsi serupa dalam kode saya (saat itu saya tidak menyukai std). Selain keduanya, ditemukan juga beberapa fungsi lainnya, yang sangat berguna ketika Anda memiliki proyek besar dengan banyak fungsi dan tidak dapat mengingat apakah Anda sudah menggunakan fungsi ini atau itu atau belum.

template <class T> void HML_Swap(T &a, T &b)
{
/*
This function swaps values of two numbers.
Input parameters:
a - first number;
b - second number.
Return value:
None.
*/
T x;
x = b;
b = a;
a = x;
}
 
template <class T> void HML_NumberInterchange(T &a, T &b)
{
/*
This function swaps values of two numbers.
Input parameters:
a - first number;
b - second number.
Return value:
None.
*/
T x;
x = b;
b = a;
a = x;
}

V524 Sungguh aneh bahwa isi fungsi 'HML_Swap' sepenuhnya setara dengan isi fungsi 'HML_NumberInterchange'. harrixmathlibrary.h 2349

Dan inilah kesalahan klasik yang berhubungan dengan konversi tipe yang hilang.

double HML_TestFunction_HyperEllipsoid(double *x, int VHML_N)
{
/*
Function of multiple variables: Hyperellipsoid.
Test function for real optimization.
Input parameters:
x - pointer to original array;
VHML_N - size of array x.
Return value:
Value of test function at point x.
*/
double VHML_Result=0;
 
for (int i=0;i<VHML_N;i++)
VHML_Result += (i+1)*(i+1)*x[i]*x[i];
 
return VHML_Result;
}

V636 Ekspresi '(i + 1) * (i + 1)' secara implisit diubah dari tipe 'int' ke tipe 'double'. Pertimbangkan untuk menggunakan tipe cast eksplisit untuk menghindari overflow. Contoh: ganda A = (ganda)(X) * Y;. harrixmathlibrary.cpp 10509

Sedangkan untuk kode ini, penganalisis mengeluarkan peringatan palsu, karena HML_ProportionalSelectionV2mengembalikan nilai acak:

NumberOfParent1=HML_ProportionalSelectionV2(....);
NumberOfParent2=HML_ProportionalSelectionV2(....);

V656 Variabel 'NumberOfParent1', 'NumberOfParent2' diinisialisasi melalui panggilan ke fungsi yang sama. Ini mungkin kesalahan atau kode yang tidak dioptimalkan. Periksa baris: 1106, 1107. harrixmathlibrary.cpp 1107

Sejumlah masalah ditemukan di perpustakaan Harrix QtLibrary.

Misalnya, memiliki fungsi untuk membagi string menjadi suku kata. Alat tersebut memberikan tip bagus bahwa saya harus menggabungkan kondisinya.

if ((i>=1)&&(i!=N-1))
{
  if ((HQt_GetTypeCharRus(S.at(i-1))==3) &&
     (HQt_GetTypeCharRus(S.at(i))!=0)    &&
     (HQt_GetTypeCharRus(S.at(i+1))!=0))
    cut=true;
}
 
if ((i>=1)&&(i!=N-1))
{
  if ((HQt_GetTypeCharRus(S.at(i-1))==1) &&
     (HQt_GetTypeCharRus(S.at(i))==1)    &&
     (HQt_GetTypeCharRus(S.at(i+1))!=0))
    cut=true;
}

V581 Ekspresi kondisional dari operator 'jika' yang terletak bersebelahan adalah identik. Periksa baris: 1140, 1147. harrixqtlibrary.cpp 1147

Perulangan pada bagian berikut berisi variabel Boolean, in, yang akan selalu true.

int VHQt_Result = -1;
    bool in=false;
    int i=0;
 
    while ((i<StringList.count())&&(in!=true))
    {
        if (StringList.at(i)==String)
            VHQt_Result=i;
        i++;
    }
   return VHQt_Result;

V560 Bagian dari ekspresi kondisional selalu benar: (dalam != benar). harrixqtlibrary.cpp 2342

Ada juga fragmen dengan kode duplikat saat mengisi model dengan item:

item = new QStandardItem(QString("HML_RealGeneticAlgorith...."));
model->appendRow(item);
 
item = new QStandardItem(QString("HML_RealGeneticAlgorith...."));
model->appendRow(item);

V760 Ditemukan dua blok teks yang identik. Blok kedua dimulai dari baris 86. mainwindow.cpp 83

Dakwaan

Kekurangan:

  • Program ini tidak intuitif; tidak mudah untuk memulainya. Jika saya mengunjungi situs mereka, mengunduh versi demo, dan mencobanya, kemungkinan besar saya akan mencopot pemasangannya, dan itu tidak lebih bijaksana.
  • Desain 'kuno'.
  • Penyorotan sintaks mirip dengan yang ada di Notepad++ (dan itu merupakan nilai tambah), tetapi saya juga terbiasa dengan Notepad++ yang menyorot semua contoh lain dari kata kunci yang dipilih, serta menyorot tanda kurung penutup yang sesuai saat memilih kata kunci pembuka.

Kelebihan:

  • Program ini mengetahui cara melakukan tugasnya, dan ini merupakan hal yang paling penting. Ini dapat menangkap banyak bug atau peringatan tersembunyi yang mungkin tidak Anda sadari.
  • Setelah Anda mengetahui cara menggunakannya, bekerja dengan penganalisis menjadi mudah dan nyaman.
  • Alat ini mendukung beberapa kompiler, termasuk yang digunakan dalam build Qt.

Kesimpulan akhir: program ini tentu saja harus dimiliki. Alat yang sangat berguna untuk mengelola kualitas kode Anda.

P.S. Dan saya berharap tidak akan ada bug (

P.S.S. Lebih dari 1900 peringatan!