Olio-ohjelmointi
Muistivuotojen automaattinen tarkistus GitHub Actionsilla

GitHub Actionsilla voit automatisoida muistivuotojen tarkistamisen jokaisen push-tapahtuman yhteydessä. Alla oleva työnkulku käyttää kahta eri työkalua: AddressSanitizeria (käännösaikainen tarkistus) ja Valgrindia (ajonaikainen analyysi). Lisää projektiisi .github/workflows/-hakemisto ja luo sinne YAML-tiedosto seuraavalla sisällöllä. myapplication on testattavan C++-sovelluksen nimi, joten korvaa se oman projektisi suoritettavan tiedoston nimellä. Huomaa, että GitHub buildaa sovelluksen Linux-koneessa, joten .exe-päätettä ei käytetä, vaikka sovelluksesi olisi tehty Windowsilla.

name: C++ Memory Checks

on:
  push:
    branches:
      - memtest

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y g++ cmake valgrind

      - name: Build project (with AddressSanitizer)
        run: |
          mkdir build-asan
          cd build-asan
          cmake -DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=address,leak -fno-omit-frame-pointer" ..
          make -j

      - name: Run with AddressSanitizer
        run: |
          cd build-asan
          ASAN_OPTIONS=detect_leaks=1 ./myapplication

      - name: Build project (without sanitizers, for Valgrind)
        run: |
          mkdir build-valgrind
          cd build-valgrind
          cmake -DCMAKE_CXX_FLAGS="-g -O1" ..
          make -j

      - name: Run Valgrind memory check
        run: |
          cd build-valgrind
          valgrind \
            --leak-check=full \
            --show-leak-kinds=all \
            --track-origins=yes \
            --error-exitcode=1 \
            ./myapplication

    
Workflow vaihe vaiheelta
name: C++ Memory Checks
Tämä määrittää workflow’n näkyvän nimen GitHubissa.
on:
  push:
    branches:
      - memtest
Workflow käynnistyy ainoastaan kun memtest-haaraan tehdään push. Muut haarat eivät laukaise tätä workflowta.
jobs:
  build-and-test:
    runs-on: ubuntu-latest
Määrittelee yhden työn nimeltä build-and-test, joka ajetaan Ubuntulla. GitHub tarjoaa virtuaalikoneen ilmaiseksi julkisille repositoryille rajoituksetta, privaateille tietyin kuukausittaisin minuuttikiintiöin.
      - name: Checkout repository
        uses: actions/checkout@v4
Kloonaa repositoryn koodin virtuaalikoneelle. Tämä on lähes aina workflows-tiedoston ensimmäinen vaihe.
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y g++ cmake valgrind
Asentaa tarvittavat työkalut: C++-kääntäjän (g++), build-järjestelmän (cmake) sekä Valgrind-muistianalysaattorin.
      - name: Build project (with AddressSanitizer)
        run: |
          mkdir build-asan
          cd build-asan
          cmake -DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=address,leak -fno-omit-frame-pointer" ..
          make -j
Kääntää projektin AddressSanitizer-lipuilla erilliseen build-asan-hakemistoon. Tärkeimmät liput:
  • -fsanitize=address,leak:aktivoi muistivuoto- ja osoitevirhetarkistukset
  • -g:lisää debuggaustiedot, jotta virheilmoituksissa näkyy tiedostonimi ja rivinumero
  • -fno-omit-frame-pointer:säilyttää pinojäljityksen luettavana
      - name: Run with AddressSanitizer
        run: |
          cd build-asan
          ASAN_OPTIONS=detect_leaks=1 ./myapplication
Ajaa käännetyn ohjelman AddressSanizerin kanssa. muistivuoto on buildissa syntyvän suoritettavan tiedoston nimi: se määritellään projektin CMakeLists.txt-tiedostossa. ASAN_OPTIONS=detect_leaks=1 varmistaa, että myös muistivuodot raportoidaan eikä pelkästään virheelliset muistiviittaukset. Jos ohjelma vuotaa muistia, prosessi päättyy virheeseen ja workflow epäonnistuu.
      - name: Build project (without sanitizers, for Valgrind)
        run: |
          mkdir build-valgrind
          cd build-valgrind
          cmake -DCMAKE_CXX_FLAGS="-g -O1" ..
          make -j
Kääntää projektin uudelleen ilman sanitizer-lippuja erilliseen build-valgrind-hakemistoon. Valgrind ei toimi yhteen sanitizereiden kanssa, joten tarvitaan oma käännös.
      - name: Run Valgrind memory check
        run: |
          cd build-valgrind
          valgrind \
            --leak-check=full \
            --show-leak-kinds=all \
            --track-origins=yes \
            --error-exitcode=1 \
            ./myapplication
Ajaa ohjelman Valgrindin läpi. Käytetyt valitsimet:
  • --leak-check=full:raportoi jokaisen yksittäisen vuodon
  • --show-leak-kinds=all:näyttää myös epäsuorat ja mahdolliset vuodot
  • --track-origins=yes:kertoo mistä alustamaton muisti on peräisin
  • --error-exitcode=1:palauttaa virhekoodin, jolloin workflow epäonnistuu automaattisesti jos vuotoja löytyy
Release-automaatio muistitestien jälkeen

Alla oleva workflow laajentaa muistitestauksen niin, että onnistuneiden testien jälkeen sovelluksesta luodaan automaattisesti GitHub Release -julkaisu sekä Linuxille että Windowsille. Workflow käynnistyy ainoastaan kun release-haara päivittyy.

myapplication on testattavan C++-sovelluksen nimi — korvaa se oman projektisi suoritettavan tiedoston nimellä.

name: Memory Check and Release

on:
  push:
    branches:
      - release

permissions:
  contents: write

jobs:
  memory-check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y g++ cmake valgrind

      - name: Build with AddressSanitizer
        run: |
          mkdir build-asan
          cd build-asan
          cmake -DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=address,leak -fno-omit-frame-pointer" ..
          make -j

      - name: Run AddressSanitizer
        run: |
          cd build-asan
          ASAN_OPTIONS=detect_leaks=1 ./myapplication

      - name: Build for Valgrind
        run: |
          mkdir build-valgrind
          cd build-valgrind
          cmake -DCMAKE_CXX_FLAGS="-g -O1" ..
          make -j

      - name: Run Valgrind
        run: |
          cd build-valgrind
          valgrind \
            --leak-check=full \
            --show-leak-kinds=all \
            --track-origins=yes \
            --error-exitcode=1 \
            ./myapplication

  build-linux:
    needs: memory-check
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y g++ cmake

      - name: Build release
        run: |
          mkdir build-release
          cd build-release
          cmake -DCMAKE_BUILD_TYPE=Release ..
          make -j

      - name: Upload Linux artifact
        uses: actions/upload-artifact@v4
        with:
          name: myapplication-linux
          path: build-release/myapplication

  build-windows:
    needs: memory-check
    runs-on: windows-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install MinGW
        run: choco install mingw -y

      - name: Build release
        run: |
          mkdir build-release
          cd build-release
          cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ..
          cmake --build .

      - name: Upload Windows artifact
        uses: actions/upload-artifact@v4
        with:
          name: myapplication-windows
          path: build-release/myapplication.exe

  create-release:
    needs: [build-linux, build-windows]
    runs-on: ubuntu-latest
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: build-${{ github.run_number }}
          name: Release ${{ github.run_number }}
          files: |
            myapplication-linux/myapplication
            myapplication-windows/myapplication.exe
Release-työnkulku vaihe vaiheelta
on:
  push:
    branches:
      - release
Workflow käynnistyy ainoastaan kun release-haaraan tehdään push. Muut haarat eivät laukaise tätä workflowta.
permissions:
  contents: write
Antaa workflowlle oikeuden luoda GitHub Release -julkaisuja repositoryyn. Ilman tätä release-vaihe epäonnistuu lupapuutteeseen.

Workflow koostuu neljästä työstä, jotka suoritetaan seuraavassa järjestyksessä:

  1. memory-check: muistitestit (AddressSanitizer + Valgrind)
  2. build-linux ja build-windows: ajetaan rinnakkain vasta kun testit ovat läpäisseet
  3. create-release: luodaan julkaisu vasta kun molemmat buildit ovat valmiit
  build-linux:
    needs: memory-check
  build-windows:
    needs: memory-check
needs määrittää riippuvuuden: nämä kaksi työtä käynnistyvät vain jos memory-check onnistui. Koska molemmat viittaavat samaan edeltäjään, GitHub ajaa ne rinnakkain — Windows- ja Linux-buildit tapahtuvat samanaikaisesti.
      - name: Upload Linux artifact
        uses: actions/upload-artifact@v4
        with:
          name: myapplication-linux
          path: build-release/myapplication
Tallentaa käännetyn suoritettavan tiedoston väliaikaiseen GitHub-varastoon työnimen alle. Artifaktit ovat saatavilla muille töille saman workflow-ajon sisällä.
      - name: Install MinGW
        run: choco install mingw -y

      - name: Build release
        run: |
          cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ..
          cmake --build .
Windows-build ajetaan Windows-virtuaalikoneella, mutta MSVC-kääntäjän sijaan käytetään MinGW:tä. Syy: MSVC ei tunne unistd.h-headeria, joka on POSIX-standardin osa eikä kuulu Windowsin omaan C-kirjastoon. MinGW sisältää GCC-kääntäjän Windows-versiona ja tukee POSIX-headereita, joten koodi kääntyy muuttamatta. choco on Windowsin paketinhallinta, joka on valmiiksi asennettuna GitHub-virtuaalikoneissa. Suoritettava tiedosto syntyy suoraan build-release/myapplication.exe-polkuun.

Huomaa: jos sovellus käyttää Qt Widget -kirjastoa, MinGW ei välttämättä toimi GitHub Actionsissa. Qt:n Windows-binäärit jaetaan erillisinä MSVC- ja MinGW-versioina, ja asennustyökalut hakevat oletuksena MSVC-version. Tällöin MinGW-käännetty koodi yrittää linkittyä MSVC-Qt-kirjastoihin, mikä epäonnistuu ABI-yhteensopimattomuuden vuoksi. Qt Widget -sovelluksille kannattaa käyttää MSVC:tä.
  create-release:
    needs: [build-linux, build-windows]
Tämä työ odottaa, että molemmat build-työt ovat onnistuneet. Jos kumpi tahansa epäonnistuu, release jää luomatta.
      - name: Download artifacts
        uses: actions/download-artifact@v4
Lataa molemmat aiemmin tallennetut artifaktit. Ilman parametreja lataa kaikki saman ajon artifaktit omiin alikansioihinsa (myapplication-linux/ ja myapplication-windows/).
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: build-${{ github.run_number }}
          name: Release ${{ github.run_number }}
          files: |
            myapplication-linux/myapplication
            myapplication-windows/myapplication.exe
Luo GitHub Release -julkaisun ja liittää siihen molemmat binäärit ladattaviksi. github.run_number on automaattisesti kasvava juokseva numero (1, 2, 3, …), joka toimii releasen versiotunnisteena.



Toggle Menu