From 4c23dc6adf8c833a551e124e71d439bcd397e225 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 1 Jan 2025 15:47:41 +0300 Subject: [PATCH] feat: simple valgrind integration in vctest & AppImage workflow: change build-type to RelWithDebInfo --- .github/workflows/appimage.yml | 7 +++-- vctest/main.cpp | 52 +++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 25ae572f..5d8e1748 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -24,18 +24,19 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ - libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev \ + libcurl4-openssl-dev libgtest-dev cmake squashfs-tools valgrind # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua # install EnTT git clone https://github.com/skypjack/entt.git cd entt/build - cmake -DCMAKE_BUILD_TYPE=Release .. + cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. sudo make install cd ../.. - name: Configure - run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 -DVOXELENGINE_BUILD_TESTS=ON + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVOXELENGINE_BUILD_APPDIR=1 -DVOXELENGINE_BUILD_TESTS=ON - name: Build run: cmake --build build -t install - name: Run tests diff --git a/vctest/main.cpp b/vctest/main.cpp index e871c3b7..530faedb 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -16,6 +16,7 @@ struct Config { fs::path directory; fs::path resDir {"res"}; fs::path workingDir {"."}; + std::string memchecker = "valgrind"; bool outputAlways = false; }; @@ -29,6 +30,7 @@ static bool perform_keyword( std::cout << " --tests , -d = tests directory path\n"; std::cout << " --res , -r = 'res' directory path\n"; std::cout << " --user , -u = user directory path\n"; + std::cout << " --memchecker = path to valgrind\n"; std::cout << " --output-always = always show tests output\n"; std::cout << std::endl; return false; @@ -127,6 +129,37 @@ static void display_test_output( } } +static void display_segfault_valgrind( + const fs::path& path, const fs::path& name, std::ostream& stream +) { + stream << "[MEMCHECK] " << name << std::endl; + if (fs::exists(path)) { + std::ifstream t(path); + while (!t.eof()) { + std::string line; + std::getline(t, line); + // skip until the terminating signal + if (line.find("Process terminating with default action of signal ") != std::string::npos) { + break; + } + } + std::stringstream ss; + while (!t.eof()) { + std::string line; + std::getline(t, line); + size_t pos = line.find("== "); + if (pos == std::string::npos) { + continue; + } + if (line.find("If you") != std::string::npos) { + break; + } + ss << line.substr(pos + 3) << "\n"; + } + stream << ss.str() << std::endl; + } +} + static std::string fix_path(std::string s) { for (char& c : s) { if (c == '\\') { @@ -136,7 +169,7 @@ static std::string fix_path(std::string s) { return s; } -static bool run_test(const Config& config, const fs::path& path) { +static bool run_test(const Config& config, const fs::path& path, bool memcheck = false) { using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::milliseconds; @@ -145,6 +178,9 @@ static bool run_test(const Config& config, const fs::path& path) { auto name = path.stem(); std::stringstream ss; + if (memcheck) { + ss << config.memchecker << " "; + } ss << fs::canonical(config.executable) << " --headless"; ss << " --test " << fix_path(path.string()); ss << " --res " << fix_path(config.resDir.string()); @@ -162,9 +198,17 @@ static bool run_test(const Config& config, const fs::path& path) { .count(); if (code) { - display_test_output(outputFile, name, std::cerr); - std::cerr << "[FAILED] " << name << " in " << testTime << " ms" << std::endl; - fs::remove(outputFile); + if (memcheck) { + // valgrind-specific output + display_segfault_valgrind(outputFile, name, std::cerr); + fs::remove(outputFile); + } else { + display_test_output(outputFile, name, std::cerr); + std::cerr << "[FAILED] " << name << " in " << testTime + << " ms (code=" << code << ")" << std::endl; + fs::remove(outputFile); + run_test(config, path, true); + } return false; } else { if (config.outputAlways) {