Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
„Travis CI“ yra paskirstyta žiniatinklio paslauga, skirta kurti ir išbandyti programinę įrangą, kuri naudoja „GitHub“ kaip šaltinio kodo prieglobą. Be aukščiau nurodytų veikimo scenarijų, galite pridėti ir savo, naudodamiesi plačiomis konfigūravimo parinktimis. Šiame straipsnyje mes sukonfigūruosime Travis CI dirbti su PVS-Studio, naudodami PPSSPP kodo pavyzdį.

įvedimas

Travisas CI yra žiniatinklio paslauga, skirta kurti ir išbandyti programinę įrangą. Paprastai jis naudojamas kartu su nuolatine integravimo praktika.

PPSSPP - PSP žaidimų konsolės emuliatorius. Programa gali imituoti bet kokių žaidimų paleidimą iš disko vaizdų, skirtų Sony PSP. Programa buvo išleista 1 m. lapkričio 2012 d. PPSSPP licencijuota pagal GPL v2. Kiekvienas gali padaryti patobulinimų projekto šaltinio kodas.

„PVS-Studio“ — statinis kodo analizatorius, skirtas klaidų ir galimų programos kodo spragų paieškai. Šiame straipsnyje, norėdami pakeisti, PVS-Studio paleisime ne lokaliai kūrėjo kompiuteryje, o debesyje ir ieškosime klaidų PPSSPP.

Travis CI nustatymas

Mums reikės saugyklos GitHub, kurioje yra reikalingas projektas, taip pat PVS-Studio rakto (galite gauti bandomasis raktas arba nemokama atvirojo kodo projektams).

Eikime į svetainę Travisas CI. Suteikę prieigos teisę naudodami „GitHub“ paskyrą, pamatysime saugyklų sąrašą:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Bandymui aš sujungiau PPSSPP.

Suaktyviname saugyklą, kurią norime rinkti:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Šiuo metu Travis CI negali sukurti mūsų projekto, nes nėra statybos instrukcijų. Taigi laikas konfigūruoti.

Analizės metu mums bus naudingi kai kurie kintamieji, pavyzdžiui, PVS-Studio raktas, kurio būtų nepageidautina nurodyti konfigūracijos faile. Taigi, pridėkime aplinkos kintamuosius naudodami „Travis CI“ kūrimo nustatymus:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Mums reikės:

  • PVS_USERNAME – vartotojo vardas
  • PVS_KEY – raktas
  • MAIL_USER – el. paštas, kuris bus naudojamas ataskaitai siųsti
  • MAIL_PASSWORD – el. pašto slaptažodis

Paskutiniai du yra neprivalomi. Jie bus naudojami rezultatams siųsti paštu. Jei norite išplatinti ataskaitą kitu būdu, jų nurodyti nereikia.

Taigi, pridėjome mums reikalingus aplinkos kintamuosius:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Dabar sukurkime failą .travis.yml ir įdėkite jį į projekto šaknį. PPSSPP jau turėjo Travis CI konfigūracijos failą, tačiau jis buvo per didelis ir visiškai netinkamas pavyzdžiui, todėl teko jį gerokai supaprastinti ir palikti tik pagrindinius elementus.

Pirmiausia nurodykime kalbą, Ubuntu Linux versiją, kurią norime naudoti virtualioje mašinoje, ir būtinus kūrimo paketus:

language: cpp
dist: xenial

addons:
  apt:
    update: true
    packages:
      - ant
      - aria2
      - build-essential
      - cmake
      - libgl1-mesa-dev
      - libglu1-mesa-dev
      - libsdl2-dev
      - pv
      - sendemail
      - software-properties-common
    sources:
      - sourceline: 'ppa:ubuntu-toolchain-r/test'
      - sourceline: 'ppa:ubuntu-sdk-team/ppa'

Visi išvardyti paketai reikalingi tik PPSSPP.

Dabar nurodome surinkimo matricą:

matrix:
  include:
    - os: linux
      compiler: "gcc"
      env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
    - os: linux
      compiler: "clang"
      env: PPSSPP_BUILD_TYPE=Linux

Šiek tiek daugiau apie skyrių matrica. „Travis CI“ yra du būdai sukurti kūrimo parinktis: pirmasis – nurodyti kompiliatorių sąrašą, operacinių sistemų tipus, aplinkos kintamuosius ir pan., po to sugeneruojama visų galimų kombinacijų matrica; antrasis yra aiškus matricos nurodymas. Žinoma, galite sujungti šiuos du metodus ir pridėti unikalų atvejį arba, priešingai, jo neįtraukti naudodami skyrių išskirti. Daugiau apie tai galite perskaityti Travis CI dokumentacija.

Belieka pateikti konkrečiam projektui skirtas surinkimo instrukcijas:

before_install:
  - travis_retry bash .travis.sh travis_before_install

install:
  - travis_retry bash .travis.sh travis_install

script:
  - bash .travis.sh travis_script

after_success:
  - bash .travis.sh travis_after_success

„Travis CI“ leidžia pridėti savo komandas įvairiems virtualios mašinos gyvavimo etapams. Skyrius prieš_įdiegimą vykdomas prieš diegiant paketus. Tada įrengti, kuris seka įdiegus paketus iš sąrašo addons.aptkurį nurodėme aukščiau. Pats surinkimas vyksta scenarijus. Jei viskas klostėsi gerai, mes atsiduriame po_sėkmės (šiame skyriuje vykdysime statinę analizę). Tai ne visi žingsniai, kuriuos galima pakeisti, jei reikia daugiau, turėtumėte pasidomėti Travis CI dokumentacija.

Kad būtų lengviau skaityti, komandos buvo įtrauktos į atskirą scenarijų .travis.sh, kuris yra projekto šaknyje.

Taigi turime tokį failą .travis.yml:

language: cpp
dist: xenial

addons:
  apt:
    update: true
    packages:
      - ant
      - aria2
      - build-essential
      - cmake
      - libgl1-mesa-dev
      - libglu1-mesa-dev
      - libsdl2-dev
      - pv
      - sendemail
      - software-properties-common
    sources:
      - sourceline: 'ppa:ubuntu-toolchain-r/test'
      - sourceline: 'ppa:ubuntu-sdk-team/ppa'

matrix:
  include:
    - os: linux
      compiler: "gcc"
      env: PVS_ANALYZE=Yes
    - os: linux
      compiler: "clang"

before_install:
  - travis_retry bash .travis.sh travis_before_install

install:
  - travis_retry bash .travis.sh travis_install

script:
  - bash .travis.sh travis_script

after_success:
  - bash .travis.sh travis_after_success

Prieš diegdami paketus, atnaujinsime submodulius. To reikia norint sukurti PPSSPP. Pridėkime pirmąją funkciją .travis.sh (atkreipkite dėmesį į plėtinį):

travis_before_install() {
  git submodule update --init --recursive
}

Dabar mes pradedame tiesiogiai nustatyti automatinį PVS-Studio paleidimą Travis CI. Pirmiausia sistemoje turime įdiegti PVS-Studio paketą:

travis_install() {
  if [ "$CXX" = "g++" ]; then
    sudo apt-get install -qq g++-4.8
  fi
  
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    wget -q -O - https://files.viva64.com/etc/pubkey.txt 
      | sudo apt-key add -
    sudo wget -O /etc/apt/sources.list.d/viva64.list 
      https://files.viva64.com/etc/viva64.list  
    
    sudo apt-get update -qq
    sudo apt-get install -qq pvs-studio 
                             libio-socket-ssl-perl 
                             libnet-ssleay-perl
  fi
    
  download_extract 
    "https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" 
    cmake-3.6.2-Linux-x86_64.tar.gz
}

Funkcijos pradžioje travis_install mes įdiegiame reikalingus kompiliatorius naudodami aplinkos kintamuosius. Tada, jei kintamasis $PVS_ANALYZE saugo vertę taip (tai nurodėme skyriuje env kūrimo matricos konfigūravimo metu), įdiegiame paketą pvs studija. Be to, nurodomos pakuotės libio-socket-ssl-perl и libnet-ssleay-perl, tačiau jie būtini norint išsiųsti rezultatus, todėl jie nėra būtini, jei pasirinkote kitą ataskaitos pateikimo būdą.

Funkcija atsisiuntimo_ištrauka atsisiunčia ir išpakuoja nurodytą archyvą:

download_extract() {
  aria2c -x 16 $1 -o $2
  tar -xf $2
}

Atėjo laikas sujungti projektą. Tai atsitinka skyriuje scenarijus:

travis_script() {
  if [ -d cmake-3.6.2-Linux-x86_64 ]; then
    export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
  fi
  
  CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
  fi
  cmake $CMAKE_ARGS CMakeLists.txt
  make
}

Tiesą sakant, tai yra supaprastinta pradinė konfigūracija, išskyrus šias eilutes:

if [ "$PVS_ANALYZE" = "Yes" ]; then
  CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi

Šiame kodo skyriuje mes nustatėme cmake kompiliavimo komandų eksportavimo vėliavėlė. Tai būtina statiniam kodo analizatoriui. Daugiau apie tai galite perskaityti straipsnyje "Kaip paleisti PVS-Studio „Linux“ ir „MacOS“."

Jei surinkimas buvo sėkmingas, mes turime po_sėkmės, kur atliekame statinę analizę:

travis_after_success() {
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
    pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic 
                                    -o PVS-Studio-${CC}.log 
                                    --disableLicenseExpirationCheck
    
    plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
    sendemail -t [email protected] 
              -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" 
              -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" 
              -s smtp.gmail.com:587 
              -xu $MAIL_USER 
              -xp $MAIL_PASSWORD 
              -o tls=yes 
              -f $MAIL_USER 
              -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
  fi
}

Pažvelkime atidžiau į šias eilutes:

pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic 
                                -o PVS-Studio-${CC}.log 
                                --disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html

Pirmoje eilutėje sugeneruojamas licencijos failas iš vartotojo vardo ir rakto, kuriuos nurodėme pačioje pradžioje nustatydami Travis CI aplinkos kintamuosius.

Antroji eilutė tiesiogiai pradeda analizę. Vėliava -j nustato analizei skirtų gijų skaičių, vėliavėlę -l nurodo licenciją, vėliavą -o apibrėžia žurnalų išvedimo failą ir vėliavėlę - DisableLicenseExpirationCheck reikalinga bandomajai versijai, nes pagal numatytuosius nustatymus pvs-studio-analizatorius įspės vartotoją, kad netrukus baigsis licencijos galiojimas. Kad taip nenutiktų, galite nurodyti šią vėliavėlę.

Žurnalo faile yra neapdorota išvestis, kurios negalima nuskaityti nekonvertavus, todėl pirmiausia turite padaryti failą skaitomą. Perleiskime rąstus plog-keitiklis, o išvestis yra html failas.

Šiame pavyzdyje aš nusprendžiau siųsti ataskaitas paštu naudodamas komandą Siųsti laišką.

Dėl to gavome tokį failą .travis.sh:

#/bin/bash

travis_before_install() {
  git submodule update --init --recursive
}

download_extract() {
  aria2c -x 16 $1 -o $2
  tar -xf $2
}

travis_install() {
  if [ "$CXX" = "g++" ]; then
    sudo apt-get install -qq g++-4.8
  fi
  
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    wget -q -O - https://files.viva64.com/etc/pubkey.txt 
      | sudo apt-key add -
    sudo wget -O /etc/apt/sources.list.d/viva64.list 
      https://files.viva64.com/etc/viva64.list  
    
    sudo apt-get update -qq
    sudo apt-get install -qq pvs-studio 
                             libio-socket-ssl-perl 
                             libnet-ssleay-perl
  fi
    
  download_extract 
    "https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" 
    cmake-3.6.2-Linux-x86_64.tar.gz
}
travis_script() {
  if [ -d cmake-3.6.2-Linux-x86_64 ]; then
    export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
  fi
  
  CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
  fi
  cmake $CMAKE_ARGS CMakeLists.txt
  make
}
travis_after_success() {
  if [ "$PVS_ANALYZE" = "Yes" ]; then
    pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
    pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic 
                                    -o PVS-Studio-${CC}.log 
                                    --disableLicenseExpirationCheck
    
    plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
    sendemail -t [email protected] 
              -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" 
              -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" 
              -s smtp.gmail.com:587 
              -xu $MAIL_USER 
              -xp $MAIL_PASSWORD 
              -o tls=yes 
              -f $MAIL_USER 
              -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
  fi
}
set -e
set -x

$1;

Dabar atėjo laikas perkelti pakeitimus į „git“ saugyklą, o po to „Travis CI“ automatiškai paleis kūrimą. Spustelėkite „ppsspp“, kad patektumėte į kūrimo ataskaitas:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Pamatysime dabartinės konstrukcijos apžvalgą:

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį
Jei statyba bus sėkmingai baigta, gausime el. laišką su statinės analizės rezultatais. Žinoma, el. paštu nėra vienintelis būdas gauti ataskaitą. Galite pasirinkti bet kurį įgyvendinimo būdą. Tačiau svarbu atsiminti, kad užbaigus kūrimą nebus galima pasiekti virtualios mašinos failų.

Klaidų suvestinė

Sėkmingai įveikėme sunkiausią dalį. Dabar įsitikinkime, kad visos mūsų pastangos to vertos. Pažvelkime į keletą įdomių punktų iš statinės analizės ataskaitos, kuri man atėjo paštu (tai ne veltui nurodžiau).

Pavojingas optimizavimas

void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
  sha1_context ctx;

  sha1_starts( &ctx );
  sha1_update( &ctx, input, ilen );
  sha1_finish( &ctx, output );

  memset( &ctx, 0, sizeof( sha1_context ) );
}

PVS-Studio įspėjimas: V597 Kompiliatorius gali ištrinti funkcijos „memset“ iškvietimą, kuris naudojamas „sum“ buferiui išplauti. Funkcija RtlSecureZeroMemory() turėtų būti naudojama privatiems duomenims ištrinti. sha1.cpp 325

Ši kodo dalis yra saugios maišos modulyje, tačiau joje yra rimta saugos klaida (CWE-14). Pažvelkime į surinkimo sąrašą, kuris sugeneruojamas kuriant derinimo versiją:

; Line 355
  mov r8d, 20
  xor edx, edx
  lea rcx, QWORD PTR sum$[rsp]
  call memset
; Line 356

Viskas tvarkoje ir veikia memeset yra vykdomas, taip perrašant svarbius duomenis RAM, tačiau kol kas nesidžiaukite. Pažvelkime į leidimo versijos su optimizavimu surinkimo sąrašą:

; 354  :
; 355  :  memset( sum, 0, sizeof( sum ) );
; 356  :}

Kaip matyti iš sąrašo, kompiliatorius ignoravo kvietimą memeset. Taip yra dėl to, kad funkcijoje sha1 po skambučio memeset nebėra nuorodų į struktūrą ctx. Todėl kompiliatorius nemato prasmės gaišti procesoriaus laiko perrašydamas nenaudojamą atmintį ateityje. Tai galite išspręsti naudodami funkciją RtlSecureZeroMemory arba panašus jai.

Teisingai:

void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
  sha1_context ctx;

  sha1_starts( &ctx );
  sha1_update( &ctx, input, ilen );
  sha1_finish( &ctx, output );

  RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) );
} 

Nereikalingas palyginimas

static u32 sceAudioOutputPannedBlocking
             (u32 chan, int leftvol, int rightvol, u32 samplePtr) {
  int result = 0;
  // For some reason, this is the only one that checks for negative.
  if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
    ....
  } else {
    if (leftvol >= 0) {
      chans[chan].leftVolume = leftvol;
    }
    if (rightvol >= 0) {
      chans[chan].rightVolume = rightvol;
    }
    chans[chan].sampleAddress = samplePtr;
    result = __AudioEnqueue(chans[chan], chan, true);
  }
}

PVS-Studio įspėjimas: V547 Išraiška 'leftvol >= 0' visada teisinga. sceAudio.cpp 120

Pirmiausia atkreipkite dėmesį į kitą šaką if. Kodas bus vykdomas tik esant visoms sąlygoms leftvol > 0xFFFF || dešinysis tūris > 0xFFFF || leftvol < 0 || dešinė < 0 pasirodys netikra. Todėl gauname tokius teiginius, kurie bus teisingi ir kitai šakai: leftvol <= 0xFFFF, rightvol <= 0xFFFF, kairysis tūris >= 0 и dešinysis tūris >= 0. Atkreipkite dėmesį į du paskutinius teiginius. Ar prasminga patikrinti, kokia yra būtina sąlyga, kad ši kodo dalis būtų vykdoma?

Taigi galime saugiai pašalinti šiuos sąlyginius teiginius:

static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
  int result = 0;
  // For some reason, this is the only one that checks for negative.
  if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
    ....
  } else {
    chans[chan].leftVolume = leftvol;
    chans[chan].rightVolume = rightvol;

    chans[chan].sampleAddress = samplePtr;
    result = __AudioEnqueue(chans[chan], chan, true);
  }
}

Kitas scenarijus. Už šių perteklinių sąlygų slypi kažkokia klaida. Galbūt jie nepatikrino, ko reikia.

Ctrl+C Ctrl+V atmuša

static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
  if (!Memory::IsValidAddress(psmfData) ||
      !Memory::IsValidAddress(psmfData)) {
    return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
  }
  ....
}

V501 Kairėje ir dešinėje nuo '||' yra identiški antriniai posakiai '!Memory::IsValidAddress(psmfData)' operatorius. scePsmf.cpp 703

Atkreipkite dėmesį į čekį viduje if. Ar jums neatrodo keista, kad tikriname, ar adresas galioja? psmfData, dvigubai daugiau? Taigi man tai atrodo keista... Tiesą sakant, tai, žinoma, yra rašybos klaida, ir buvo mintis patikrinti abu įvesties parametrus.

Teisingas variantas:

static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
  if (!Memory::IsValidAddress(psmfStruct) ||
      !Memory::IsValidAddress(psmfData)) {
    return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
  }
  ....
}

Pamirštas kintamasis

extern void ud_translate_att(
  int size = 0;
  ....
  if (size == 8) {
    ud_asmprintf(u, "b");
  } else if (size == 16) {
    ud_asmprintf(u, "w");
  } else if (size == 64) {
    ud_asmprintf(u, "q");
  }
  ....
}

PVS-Studio įspėjimas: V547 Išraiška 'dydis == 8' visada klaidinga. syn-att.c 195

Ši klaida yra aplanke ext, tad nelabai aktualu projektui, bet klaida buvo rasta anksčiau nei aš ją pastebėjau, todėl nusprendžiau ją palikti. Juk šiame straipsnyje kalbama ne apie klaidų peržiūrą, o apie integraciją su Travis CI, o jokia analizatoriaus konfigūracija nebuvo atlikta.

Kintamas Dydis inicijuojamas konstanta, tačiau kode ji visai nenaudojama iki pat operatoriaus if, kuris, žinoma, suteikia klaidingas tikrinant sąlygas, nes, kaip prisimename, Dydis lygus nuliui. Vėlesni patikrinimai taip pat neturi prasmės.

Matyt, kodo fragmento autorius pamiršo perrašyti kintamąjį Dydis prieš tai.

Stop

Čia tikriausiai ir baigsime klaidas. Šio straipsnio tikslas – pademonstruoti PVS-Studio darbą kartu su Travis CI, o ne kuo nuodugniau analizuoti projektą. Jei nori didesnių ir gražesnių klaidų, visada gali jomis žavėtis čia :).

išvada

Naudojant žiniatinklio paslaugas kuriant projektus kartu su laipsniškos analizės praktika, galima rasti daug problemų iškart po kodo sujungimo. Tačiau vienos versijos gali nepakakti, todėl testavimo nustatymas kartu su statine analize žymiai pagerins kodo kokybę.

Naudingos nuorodos

Kaip sukonfigūruoti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį

Jei norite pasidalinti šiuo straipsniu su angliškai kalbančia auditorija, naudokite vertimo nuorodą: Maksimas Zvyagintsevas. Kaip nustatyti PVS-Studio Travis CI naudojant PSP žaidimų konsolės emuliatoriaus pavyzdį.

Šaltinis: www.habr.com

Добавить комментарий