Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Travis CI GitHub iturburu-kode ostalari gisa erabiltzen duen softwarea eraikitzeko eta probatzeko banatutako web zerbitzu bat da. Goiko funtzionamendu eszenatokiez gain, zurea gehi dezakezu konfigurazio aukera zabalei esker. Artikulu honetan Travis CI PVS-Studio-rekin lan egiteko konfiguratuko dugu PPSSPP kodearen adibidea erabiliz.

Sarrera

Travis CI softwarea eraikitzeko eta probatzeko web zerbitzu bat da. Normalean, etengabeko integrazio-praktikekin batera erabiltzen da.

PPSSPP - PSP joko kontsolaren emuladorea. Programak Sony PSPrako diseinatutako disko-irudietatik edozein joko abiarazteko gai da. Programa 1ko azaroaren 2012ean kaleratu zen. PPSSPP GPL v2 lizentziapean dago. Edonork egin ditzake hobekuntzak proiektuaren iturburu kodea.

PVS-Studioa β€” Kode-analizzatzaile estatiko bat programa-kodean akatsak eta balizko ahuleziak bilatzeko. Artikulu honetan, aldaketa baterako, PVS-Studio abiaraziko dugu lokalean ez garatzailearen makinan, hodeian baizik, eta PPSSPPn akatsak bilatuko ditugu.

Travis CI konfiguratzea

GitHub-en biltegi bat beharko dugu, non behar dugun proiektua dagoen, baita PVS-Studiorako gako bat ere (lortu dezakezu probako giltza edo Iturburu irekiko proiektuetarako doan).

Goazen gunera Travis CI. Zure GitHub kontua erabiliz baimendu ondoren, biltegien zerrenda ikusiko dugu:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Proba egiteko, PPSSPP bifurkatu nuen.

Bildu nahi dugun biltegia aktibatzen dugu:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Momentuz, Travis CIk ezin du gure proiektua eraiki eraikitzeko argibiderik ez dagoelako. Beraz, konfiguratzeko garaia da.

Azterketan zehar, aldagai batzuk baliagarriak izango zaizkigu, adibidez, PVS-Studiorako gakoa, konfigurazio fitxategian zehaztea komeni ez dena. Beraz, gehitu ditzagun ingurune-aldagaiak Travis CI-ko eraikuntza-ezarpenak erabiliz:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Beharko dugu:

  • PVS_USERNAME - erabiltzaile-izena
  • PVS_KEY - tekla
  • MAIL_USER - txostena bidaltzeko erabiliko den helbide elektronikoa
  • MAIL_PASSWORD - pasahitza elektronikoa

Azken biak aukerakoak dira. Emaitzak postaz bidaltzeko erabiliko dira. Txostena beste modu batean banatu nahi baduzu, ez duzu adierazi beharrik.

Beraz, behar ditugun ingurune-aldagaiak gehitu ditugu:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Orain sortu dezagun fitxategi bat .travis.yml eta kokatu proiektuaren erroan. PPSSPP-k Travis CIrako konfigurazio-fitxategi bat zeukan jada, hala ere, handiegia zen eta guztiz desegokia zen adibiderako, beraz, asko sinplifikatu behar izan genuen eta oinarrizko elementuak soilik utzi.

Lehenik eta behin, adierazi ditzagun hizkuntza, makina birtualean erabili nahi dugun Ubuntu Linux-en bertsioa eta eraikuntzarako beharrezko paketeak:

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'

Zerrendatutako pakete guztiak PPSSPPrako soilik behar dira.

Orain muntaia-matrizea adieraziko dugu:

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

Atalari buruz pixka bat gehiago matrizea. Travis CI-n, bi modu daude eraikitze-aukerak sortzeko: lehena konpiladoreen, sistema eragileen moten, ingurune-aldagaien eta abarren zerrenda zehaztea da, eta ondoren konbinazio posible guztien matrizea sortzen da; bigarrena matrizearen adierazle esplizitua da. Jakina, bi ikuspegi hauek konbina ditzakezu eta kasu bakarra gehitu edo, aitzitik, baztertu atala erabiliz. baztertzeko. Honi buruz gehiago irakur dezakezu Travis CI dokumentazioa.

Proiektuaren berariazko muntaketa argibideak ematea besterik ez da geratzen:

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-k zure komandoak gehi ditzakezu makina birtual baten bizitzako hainbat fasetarako. atala aurretik_instalatu paketeak instalatu aurretik exekutatu. Gero instalatzeko, zerrendako paketeen instalazioari jarraitzen diona gehigarriak.aptgoian adierazi duguna. Batzarra bera bertan egiten da script. Dena ondo joan bada, orduan aurkituko gara arrakasta_ondoren (atal honetan analisi estatikoa egingo dugu). Hauek ez dira alda daitezkeen urrats guztiak, gehiago behar baduzu, begiratu beharko zenuke Travis CI dokumentazioa.

Irakurtzeko erraztasuna lortzeko, komandoak gidoi berezi batean jarri ziren .travis.sh, proiektuaren erroan kokatzen dena.

Beraz, honako fitxategia dugu .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

Paketeak instalatu aurretik, azpimoduluak eguneratuko ditugu. PPSSPP eraikitzeko beharrezkoa da. Gehitu dezagun lehenengo funtzioa .travis.sh (kontuan izan luzapena):

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

Orain Travis CI-n PVS-Studio abiarazte automatikoa konfiguratzera gatoz. Lehenik eta behin PVS-Studio paketea instalatu behar dugu sisteman:

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
}

Funtzioaren hasieran travis_install behar ditugun konpilatzaileak ingurune-aldagaiak erabiliz instalatzen ditugu. Orduan aldagaia bada $PVS_ANALYZE balioa gordetzen du Bai (atalean adierazi dugu env eraikitze matrizearen konfigurazioan), paketea instalatzen dugu pvs-estudioa. Honetaz gain, paketeak ere adierazten dira libio-socket-ssl-perl ΠΈ libnet-ssleay-perl, hala ere, emaitzak postaz bidaltzeko beharrezkoak dira, beraz, ez dira beharrezkoak zure txostena bidaltzeko beste metodo bat aukeratu baduzu.

Funtzioa deskargatu_atera zehaztutako artxiboa deskargatu eta deskargatzen du:

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

Proiektua osatzeko garaia da. Hau atalean gertatzen da script:

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
}

Izan ere, jatorrizko konfigurazio sinplifikatu bat da, lerro hauek izan ezik:

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

ezarri dugun kodearen atal honetan cmake konpilazio komandoak esportatzeko bandera. Hau beharrezkoa da kode-analisi estatiko baterako. Honi buruz gehiago irakur dezakezu artikuluan "Nola exekutatu PVS-Studio Linux eta macOS-en".

Batzarrak arrakasta izan badu, lortuko dugu arrakasta_ondoren, non analisi estatikoa egiten dugu:

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
}

Ikus ditzagun ondoko lerro hauek:

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

Lehenengo lerroak Travis CI ingurune-aldagaiak konfiguratzean hasieran zehaztu genuen erabiltzaile-izenaren eta gakoaren lizentzia-fitxategi bat sortzen du.

Bigarren lerroak zuzenean hasten du analisia. Bandera -j analisirako hari kopurua ezartzen du, bandera -l lizentzia adierazten du, bandera -o erregistroak ateratzeko fitxategia eta bandera definitzen ditu -disableLicenseExpirationCheck probako bertsioetarako beharrezkoa da, lehenespenez geroztik pvs-estudio-analizatzailea lizentzia iraungitzear dagoela ohartaraziko dio erabiltzaileari. Hori gerta ez dadin, bandera hau zehaztu dezakezu.

Erregistro fitxategiak bihurketarik gabe irakurri ezin den irteera gordina dauka, beraz, lehenik fitxategia irakurgarri egin behar duzu. Pasa ditzagun erregistroak plog-bihurgailua, eta irteera html fitxategi bat da.

Adibide honetan, txostenak postaz bidaltzea erabaki nuen komandoa erabiliz bidali posta elektronikoa.

Ondorioz, hurrengo fitxategia lortu dugu .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;

Orain git biltegian aldaketak bultzatzeko garaia da, eta ondoren Travis CI-k automatikoki exekutatu egingo du eraikuntza. Egin klik "ppsspp"-en eraikuntza-txostenetara joateko:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Gaur egungo eraikuntzaren ikuspegi orokorra ikusiko dugu:

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz
Eraikuntza behar bezala amaitzen bada, mezu elektroniko bat jasoko dugu analisi estatikoaren emaitzekin. Jakina, posta bidaltzea ez da txosten bat jasotzeko modu bakarra. Edozein ezarpen-metodo aukeratu dezakezu. Baina garrantzitsua da gogoratzea eraikuntza amaitu ondoren ezin izango dela makina birtualeko fitxategietara sartu.

Errorearen laburpena

Zailena arrakastaz bete dugu. Orain ziurtatu dezagun gure ahalegin guztiak merezi duela. Azter ditzagun postaz heldu zaidan analisi estatikoko txostenetik puntu interesgarri batzuk (ez zen alferrik adierazi nuena).

Optimizazio arriskutsua

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-Studioko abisua: V597 Konpilatzaileak 'memset' funtzio-deia ezabatu dezake, hau da, 'sum' bufferra garbitzeko erabiltzen dena. RtlSecureZeroMemory() funtzioa erabili behar da datu pribatuak ezabatzeko. sha1.cpp 325

Kode zati hau hashing modulu seguruan dago, baina segurtasun akats larri bat dauka (CWE-14). Ikus dezagun Debug bertsioa konpilatzean sortzen den muntaia-zerrenda:

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

Dena dago ordena eta funtzioa memeset exekutatzen da, eta horrela RAM datu garrantzitsuak gainidatziz, hala ere, ez poztu oraindik. Ikus dezagun Oharra bertsioaren muntaia-zerrenda optimizazioarekin:

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

Zerrendatik ikus daitekeenez, konpilatzaileak deiari ez zion jaramonik egin memeset. Funtzioan dagoelako da hori sha1 deialdiaren ostean memeset egiturari erreferentzia gehiagorik ez ctx. Horregatik, konpilatzaileak ez du zentzurik ikusten etorkizunean erabiltzen ez den memoria gainidatziz prozesadorearen denbora galtzea. Hau konpondu dezakezu funtzioa erabiliz RtlSecureZeroMemory edo antzeko berari.

zuzentzeko:

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 ) );
} 

Alferrikako konparaketa

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-Studioko abisua: V547 'Leftvol >= 0' adierazpena beti da egia. sceAudio.cpp 120

Erreparatu beste adarrari lehenengoari if. Baldintza guztiak baldin badira bakarrik exekutatuko da kodea leftvol > 0xFFFF || rightvol > 0xFFFF || ezkervol < 0 || eskuineko bola < 0 faltsua izango da. Beraz, baieztapen hauek lortuko ditugu, beste adarrentzat egia izango direnak: ezkerreko vol <= 0xFFFF, eskuineko vol <= 0xFFFF, ezkerreko bola >= 0 ΠΈ eskuineko bol >= 0. Kontuan izan azken bi adierazpenak. Zentzurik al du kode zati hau exekutatzeko beharrezko baldintza zein den egiaztatzeak?

Beraz, segurtasunez ken ditzakegu baldintzazko adierazpen hauek:

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);
  }
}

Beste eszenatoki bat. Baldintza erredundante horien atzean errore motaren bat ezkutatzen da. Beharbada ez zuten egiaztatu zer eskatzen zen.

Ctrl+C Ctrl+V-k atzera jotzen du

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 '!Memory::IsValidAddress(psmfData)' azpi-esamolde berdinak daude '||'-ren ezkerraldean eta eskuinean. operadorea. scePsmf.cpp 703

Erreparatu barruko txekeari if. Ez al zaizu arraroa iruditzen helbideak balio duen egiaztatzea? psmfData, bi aldiz gehiago? Beraz, hau arraroa iruditzen zait... Izan ere, hau, noski, akatsa da, eta ideia sarrerako bi parametroak egiaztatzea zen.

Aukera zuzena:

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

Ahaztutako aldagaia

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-Studioko abisua: V547 'Tamaina == 8' adierazpena beti da faltsua. syn-att.c 195

Errore hau karpetan dago ext, beraz, ez da proiekturako oso garrantzitsua, baina akatsa konturatu baino lehen aurkitu zen, beraz, uztea erabaki nuen. Azken finean, artikulu hau ez da akatsak berrikustea, Travis CI-rekin integratzeari buruzkoa baizik, eta ez zen analizatzailearen konfiguraziorik egin.

Aldakorra tamaina konstante baten bidez hasieratzen da, hala ere, ez da batere erabiltzen kodean, operadoreari arte if, eta horrek, noski, ematen du false baldintzak egiaztatzen diren bitartean, zeren, gogoratzen dugunez, tamaina zeroren berdina. Ondorengo kontrolek ere ez dute zentzurik.

Dirudienez, kode zatiaren egileari aldagaia gainidaztea ahaztu zaio tamaina horren aurretik.

Gelditu

Hemen amaituko dugu ziurrenik akatsekin. Artikulu honen helburua PVS-Studio-k Travis CIrekin batera egindako lana erakustea da, eta ez proiektua ahalik eta ondoen aztertzea. Akats handiagoak eta ederragoak nahi badituzu, beti miretsi ditzakezu Hemen :).

Ondorioa

Web-zerbitzuak erabiltzeak proiektuak eraikitzeko analisi inkrementalaren praktikarekin batera, kodea batu eta berehala arazo asko aurki ditzakezu. Hala ere, baliteke eraikuntza bat nahikoa ez izatea, beraz, azterketa estatikoarekin batera probak konfiguratzeak kodearen kalitatea nabarmen hobetuko du.

Esteka interesgarriak

Nola konfiguratu PVS-Studio Travis CI-n PSP joko-kontsolaren emuladore baten adibidea erabiliz

Artikulu hau ingelesez hitz egiten duen publiko batekin partekatu nahi baduzu, mesedez erabili itzulpen-esteka: Maxim Zvyagintsev. Nola konfiguratu PVS-Studio Travis CIn PSP joko-kontsolaren emuladorearen adibidea erabiliz.

Iturria: www.habr.com

Gehitu iruzkin berria