Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Travis CI sabis ne na gidan yanar gizo da aka rarraba don gini da software na gwaji wanda ke amfani da GitHub azaman karɓar lambar tushe. Baya ga yanayin aiki na sama, zaku iya ƙara naku godiya ga babban zaɓin daidaitawa. A cikin wannan labarin za mu saita Travis CI don yin aiki tare da PVS-Studio ta amfani da misalin lambar PPSSPP.

Gabatarwar

Travis CI sabis ne na gidan yanar gizo don ginawa da gwada software. Yawancin lokaci ana amfani da shi tare da ci gaba da ayyukan haɗin kai.

PPSSPP - PSP wasan bidiyo emulator. Shirin yana iya yin koyi da ƙaddamar da kowane wasanni daga hotunan diski da aka yi nufin Sony PSP. An fitar da shirin a ranar 1 ga Nuwamba, 2012. PPSSPP tana da lasisi ƙarƙashin GPL v2. Kowa na iya yin gyara ga lambar tushen aikin.

PVS-Studio - mai nazarin lambar a tsaye don neman kurakurai da yuwuwar lahani a lambar shirin. A cikin wannan labarin, don canji, za mu kaddamar da PVS-Studio ba a cikin gida a kan na'ura mai haɓakawa ba, amma a cikin girgije, da kuma neman kurakurai a cikin PPSSPP.

Saita Travis CI

Za mu buƙaci wurin ajiya akan GitHub, inda aikin da muke buƙata yake, da kuma maɓalli na PVS-Studio (zaka iya samun makullin gwaji ko kyauta don ayyukan Buɗewa).

Mu je wurin Travis CI. Bayan izini ta amfani da asusun GitHub ɗin ku, za mu ga jerin ma'ajiyoyi:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Don gwajin, na yi karo na PPSSPP.

Muna kunna ma'ajiyar da muke son tarawa:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
A halin yanzu, Travis CI ba zai iya gina aikinmu ba saboda babu umarnin gini. Don haka lokaci yayi don daidaitawa.

A lokacin bincike, wasu masu canji za su kasance masu amfani a gare mu, misali, maɓalli na PVS-Studio, wanda ba za a so a saka a cikin fayil ɗin sanyi ba. Don haka bari mu ƙara masu canjin yanayi ta amfani da saitunan ginawa a cikin Travis CI:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Za mu buƙaci:

  • PVS_USERNAME - sunan mai amfani
  • PVS_KEY - maɓalli
  • MAIL_USER - imel ɗin da za a yi amfani da shi don aika rahoton
  • MAIL_PASSWORD - kalmar sirri ta imel

Biyu na ƙarshe na zaɓi ne. Za a yi amfani da waɗannan don aika sakamako ta wasiƙa. Idan kuna son rarraba rahoton ta wata hanya, ba kwa buƙatar nuna su.

Don haka, mun ƙara masu canjin yanayi da muke buƙata:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Yanzu bari mu ƙirƙiri fayil .travis.yml kuma sanya shi a cikin tushen aikin. PPSSPP ya riga ya sami fayil ɗin sanyi don Travis CI, duk da haka, ya yi girma da yawa kuma bai dace da misali ba, don haka dole ne mu sauƙaƙe shi sosai kuma mu bar abubuwa masu mahimmanci kawai.

Da farko, bari mu nuna yaren, sigar Ubuntu Linux ɗin da muke son amfani da shi a cikin injin kama-da-wane, da fakitin da ake buƙata don ginin:

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'

Duk fakitin da aka jera ana buƙatar su kawai don PPSSPP.

Yanzu muna nuna matrix taro:

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

Dan karin bayani game da sashin matrix. A cikin Travis CI, akwai hanyoyi guda biyu don ƙirƙirar zaɓuɓɓukan ginawa: na farko shine ƙayyade jerin masu tarawa, nau'ikan tsarin aiki, masu canjin yanayi, da dai sauransu, bayan haka an samar da matrix na duk haɗin haɗin gwiwa; na biyu nuni ne a sarari na matrix. Tabbas, zaku iya haɗa waɗannan hanyoyin guda biyu kuma ku ƙara wani akwati na musamman, ko, akasin haka, cire shi ta amfani da sashin. ware. Kuna iya karanta ƙarin game da wannan a cikin Rahoton da aka ƙayyade na CI.

Abin da ya rage shi ne samar da takamaiman umarnin taro:

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 yana ba ku damar ƙara umarnin ku don matakai daban-daban na rayuwar injin kama-da-wane. Sashe kafin_install kashe kafin installing kunshe-kunshe. Sannan shigar, wanda ya biyo bayan shigar da fakiti daga jerin addons. dacewanda muka nuna a sama. Majalisar da kanta tana faruwa a ciki script. Idan komai ya tafi daidai, to mun sami kanmu a ciki bayan_nasara (a cikin wannan sashe ne za mu gudanar da bincike a tsaye). Waɗannan ba duk matakan da za a iya gyara ba ne, idan kuna buƙatar ƙarin, to ya kamata ku duba Rahoton da aka ƙayyade na CI.

Don sauƙin karantawa, an sanya umarnin a cikin wani rubutun daban .travis.sh, wanda aka sanya a tushen aikin.

Don haka muna da fayil mai zuwa .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

Kafin shigar da fakiti, za mu sabunta submodules. Ana buƙatar wannan don gina PPSSPP. Bari mu ƙara aikin farko zuwa .travis.sh (lura da tsawo):

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

Yanzu mun zo kai tsaye don saita ƙaddamar da atomatik na PVS-Studio a cikin Travis CI. Da farko muna buƙatar shigar da kunshin PVS-Studio akan tsarin:

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
}

A farkon aikin travis_install muna shigar da masu tarawa da muke buƙata ta amfani da masu canjin yanayi. Sa'an nan idan m $PVS_ANALYZE Stores darajar A (mun nuna shi a cikin sashin kimanin a lokacin gina matrix sanyi), mun shigar da kunshin pvs-studio. Baya ga wannan, ana kuma nuna fakitin libio-socket-ssl-perl и libnet-ssleay-perl, duk da haka, ana buƙatar su don sakamakon aikawa, don haka ba lallai ba ne idan kun zaɓi wata hanya don isar da rahoton ku.

aiki download_extract zazzagewa da buɗe fakitin ƙayyadaddun tarihin:

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

Lokaci ya yi da za a haɗa aikin tare. Wannan yana faruwa a cikin sashe 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
}

A haƙiƙa, wannan ƙaƙƙarfan tsari ne na asali, ban da waɗannan layukan:

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

A cikin wannan sashe na code mun saita don kama tuta don fitar da umarnin tattarawa. Wannan ya zama dole don na'urar tantance lambar a tsaye. Kuna iya karanta ƙarin game da wannan a cikin labarin "Yadda ake gudanar da PVS-Studio akan Linux da macOS".

Idan taron ya yi nasara, to mun isa bayan_nasara, inda muke yin nazari a tsaye:

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
}

Bari mu kalli wadannan layukan da kyau:

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

Layin farko yana haifar da fayil ɗin lasisi daga sunan mai amfani da maɓalli wanda muka ƙayyade a farkon lokacin da aka kafa masu canjin yanayi na Travis CI.

Layi na biyu ya fara bincike kai tsaye. Tuta -j yana saita adadin zaren don bincike, tuta -l yana nuna lasisi, tuta -o yana bayyana fayil ɗin don fitar da rajistan ayyukan, da tuta - disableLicenseExpirationCheck da ake buƙata don nau'ikan gwaji, tun ta tsohuwa pvs-studio-analyzer zai gargadi mai amfani cewa lasisin yana gab da ƙarewa. Don hana faruwar hakan, kuna iya tantance wannan tuta.

Fayil ɗin log ɗin yana ƙunshe da ɗanyen kayan aiki wanda ba za a iya karantawa ba tare da juyawa ba, don haka dole ne ka fara sanya fayil ɗin a iya karantawa. Bari mu wuce gungumen azaba plog-converter, kuma fitarwa shine fayil ɗin html.

A cikin wannan misalin, na yanke shawarar aika rahotanni ta wasiƙa ta amfani da umarnin aika saƙon.

A sakamakon haka, mun sami fayil mai zuwa .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;

Yanzu lokaci ya yi da za a tura canje-canje zuwa wurin ajiyar git, bayan haka Travis CI zai gudanar da ginin ta atomatik. Danna "ppsspp" don zuwa ga rahotannin ginawa:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Za mu ga bayyani na ginin na yanzu:

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator
Idan an kammala ginin cikin nasara, za mu sami imel tare da sakamakon binciken a tsaye. Tabbas, ba aikawa da sako ba ita ce kawai hanyar samun rahoto ba. Kuna iya zaɓar kowace hanyar aiwatarwa. Amma yana da mahimmanci a tuna cewa bayan an gama ginin, ba za a iya samun damar yin amfani da fayilolin injin kama-da-wane ba.

Takaitaccen kuskure

Mun samu nasarar kammala mafi wahala. Yanzu bari mu tabbatar da cewa duk kokarinmu ya dace. Bari mu dubi wasu abubuwa masu ban sha'awa daga rahoton bincike na tsaye wanda ya zo mini ta hanyar wasiku (ba don komai ba na nuna shi).

Haɗarin haɓakawa

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

Gargadi na PVS-Studio: V597 Mai tarawa zai iya share kiran aikin 'memset', wanda ake amfani da shi don cire ' jimlar' buffer. Ya kamata a yi amfani da aikin RtlSecureZeroMemory() don goge bayanan sirri. shafi 1.cpp 325

Wannan yanki na lambar yana cikin amintaccen tsarin hashing, duk da haka, yana ƙunshe da babban lahani na tsaro (Saukewa: CWE-14). Bari mu kalli lissafin taron da aka ƙirƙira lokacin tattara sigar Debug:

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

Komai yana cikin tsari da aikin tuna An kashe shi, don haka sake rubuta mahimman bayanai a cikin RAM, duk da haka, kada ku yi murna tukuna. Mu duba lissafin taron sigar Sakin tare da ingantawa:

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

Kamar yadda ake iya gani daga lissafin, mai tarawa yayi watsi da kiran tuna. Wannan shi ne saboda gaskiyar cewa a cikin aikin sha1 bayan kiran tuna babu sauran magana ga tsari ctx. Don haka, mai tarawa bai ga wata fa'ida ba wajen ɓata lokacin aikin na'ura mai sarrafa rubutu da sake rubuta ƙwaƙwalwar da ba a yi amfani da ita nan gaba ba. Kuna iya gyara wannan ta amfani da aikin RtlSecureZeroMemory ko kama zuwa gareta.

Daidai:

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

Kwatancen da ba dole ba

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

Gargadi na PVS-Studio: V547 Maganar 'hagu> = 0' gaskiya ne koyaushe. sceAudio.cpp 120

Kula da sauran reshe na farko if. Za a aiwatar da lambar ne kawai idan duk sharuɗɗa hannun hagu > 0xFFFF || rightvol> 0xFFFF || hannun hagu <0 || daidaici <0 zai juya ya zama ƙarya. Don haka, muna samun maganganu masu zuwa, waɗanda za su kasance gaskiya ga wani reshe: hannun hagu <= 0xFFFF, hannun dama <= 0xFFFF, hagu >> 0 и Dama > = 0. Ka lura da maganganun biyu na ƙarshe. Shin yana da ma'ana don bincika abin da yake wajibi ne don aiwatar da wannan lambar?

Don haka za mu iya cire waɗannan sharuddan kalamai lafiya:

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

Wani labari. Akwai wani nau'i na kuskure da ke ɓoye a bayan waɗannan sharuɗɗan da yawa. Wataƙila ba su bincika abin da ake buƙata ba.

Ctrl+C Ctrl+V Yana Buge Baya

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 Akwai makamantan ƙananan bayanan '!Memory::IsValidAddress(psmfData)' zuwa hagu da dama na'||' ma'aikaci. scePsmf.cpp 703

Kula da dubawa a ciki if. Ba ku ganin yana da ban mamaki mu bincika ko adireshin yana aiki? psmfData, sau biyu? Don haka wannan yana da ban mamaki a gare ni ... A gaskiya, wannan, ba shakka, rubutun rubutu ne, kuma ra'ayin shine duba sigogin shigarwa guda biyu.

Madaidaicin zaɓi:

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

Canjin da aka manta

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

Gargadi na PVS-Studio: V547 Maganar 'girman == 8' koyaushe karya ce. syn-at.c 195

Wannan kuskuren yana cikin babban fayil ɗin Ex, Don haka bai dace da aikin ba sosai, amma an samo kwaro kafin in lura da shi, don haka na yanke shawarar barin shi. Bayan haka, wannan labarin ba game da sake duba kurakurai ba ne, amma game da haɗin kai tare da Travis CI, kuma ba a gudanar da wani tsari na mai nazari ba.

Mai canzawa size An fara farawa ta akai-akai, duk da haka, ba a yi amfani da shi kwata-kwata a cikin lambar, har zuwa ga mai aiki if, wanda, ba shakka, yana ba da arya yayin duba yanayin, saboda, kamar yadda muke tunawa, size daidai da sifili. Binciken da ya biyo baya kuma ba shi da ma'ana.

A bayyane yake, marubucin guntun lambar ya manta ya sake rubuta canjin size kafin haka.

Tsaya

Anan ne tabbas zamu ƙare da kurakurai. Manufar wannan labarin shine don nuna aikin PVS-Studio tare da Travis CI, kuma ba don nazarin aikin ba kamar yadda zai yiwu. Idan kuna son manyan kurakurai masu kyau da kyau, koyaushe kuna iya sha'awar su a nan :).

ƙarshe

Yin amfani da sabis na yanar gizo don gina ayyuka tare da aikin bincike na haɓaka yana ba ku damar samun matsaloli da yawa nan da nan bayan haɗa lambar. Koyaya, ginin ɗaya bazai isa ba, don haka kafa gwaji tare da bincike na tsaye zai inganta ingantaccen lambar.

hanyoyi masu amfani

Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator

Idan kuna son raba wannan labarin tare da masu sauraron Ingilishi, da fatan za a yi amfani da hanyar haɗin fassarar: Maxim Zvyagintsev. Yadda ake saita PVS-Studio a cikin Travis CI ta amfani da misalin PSP game console emulator.

source: www.habr.com

Add a comment