diff --git a/.github/workflows/matrix.json b/.github/workflows/matrix.json index 18f9de77..ff610027 100644 --- a/.github/workflows/matrix.json +++ b/.github/workflows/matrix.json @@ -1,7 +1,7 @@ { "include": [ { - "DOCKER_TAG": "2022.7.0", + "DOCKER_TAG": "2022.8.0", "OPERATING_SYSTEM_TAG": "18.04", "LLVM_VERSION_MAJOR": "10" } diff --git a/docker/Dockerfile_base b/docker/Dockerfile_base index 1effe623..8fee4ab6 100644 --- a/docker/Dockerfile_base +++ b/docker/Dockerfile_base @@ -24,6 +24,7 @@ RUN echo "check_certificate = off" > /etc/wgetrc # We use C++ 17 for UnitTestBot, it is available in gcc-9; default gcc for ubuntu:18.04 is gcc-7 RUN add-apt-repository ppa:ubuntu-toolchain-r/test RUN apt update && apt install -y --no-install-recommends gcc-9 g++-9 +# Skip 32bits libs installation before LLVM compilation RUN sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 100 RUN sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 100 RUN sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-9 100 @@ -209,6 +210,9 @@ RUN git clone -b klee_uclibc_v1.2 https://github.com/klee/klee-uclibc.git $UTBOT WORKDIR $UTBOT_ALL/klee-uclibc RUN ./configure --make-llvm-lib && make -j`nproc` +# Install 32bits libs AFTER LLVM compilation +RUN apt update && apt install -y --no-install-recommends gcc-multilib g++-multilib gcc-9-multilib g++-9-multilib + # Download library for access private members RUN git clone https://github.com/martong/access_private.git $UTBOT_ALL/access_private diff --git a/server/src/KleeRunner.cpp b/server/src/KleeRunner.cpp index 4394a276..6060857c 100644 --- a/server/src/KleeRunner.cpp +++ b/server/src/KleeRunner.cpp @@ -95,8 +95,8 @@ void KleeRunner::runKlee(const std::vector &testMethods, std::stringstream logStream; if (LogUtils::isMaxVerbosity()) { logStream << "Processing batch: "; - for (const auto &[methodName, bitcodeFile, sourceFilepath] : batch) { - logStream << methodName << ", "; + for (const auto &method : batch) { + logStream << method.methodName << ", "; } LOG_S(MAX) << logStream.str(); } @@ -207,30 +207,18 @@ static void processMethod(MethodKtests &ktestChunk, } } -void KleeRunner::processBatchWithoutInteractive(const std::vector &testMethods, - tests::Tests &tests, - std::vector &ktests) { - if (!tests.isFilePresentedInArtifact || testMethods.empty()) { - return; - } - - for (const auto &testMethod : testMethods) { - if (testMethod.sourceFilePath != tests.sourceFilePath) { - std::string message = StringUtils::stringFormat( - "While generating tests for source file: %s tried to generate tests for method %s " - "from another source file: %s. This can cause invalid generation.\n", - tests.sourceFilePath, testMethod.methodName, testMethod.sourceFilePath); - LOG_S(WARNING) << message; - } - - std::string entryPoint = KleeUtils::entryPointFunction(tests, testMethod.methodName, true); - std::string entryPointFlag = StringUtils::stringFormat("--entry-point=%s", entryPoint); - auto kleeOut = Paths::kleeOutDirForEntrypoints(projectContext, projectTmpPath, testMethod.sourceFilePath, - testMethod.methodName); +std::pair, fs::path> +KleeRunner::createKleeParams(const tests::TestMethod &testMethod, + const tests::Tests &tests, + const std::string &methodNameOrEmptyForFolder) { + fs::path kleeOut = Paths::kleeOutDirForEntrypoints(projectContext, + projectTmpPath, + tests.sourceFilePath, + methodNameOrEmptyForFolder); fs::create_directories(kleeOut.parent_path()); - std::string outputDir = "--output-dir=" + kleeOut.string(); + std::vector argvData = { "klee", - entryPointFlag, + "--entry-point=" + KleeUtils::entryPointFunction(tests, testMethod.methodName, true), "--libc=klee", "--utbot", "--posix-runtime", @@ -245,20 +233,49 @@ void KleeRunner::processBatchWithoutInteractive(const std::vector cargv, cenvp; - std::vector tmp; - ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false); - LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " "); - MEASURE_FUNCTION_EXECUTION_TIME +void KleeRunner::processBatchWithoutInteractive(const std::vector &testMethods, + tests::Tests &tests, + std::vector &ktests) { + if (!tests.isFilePresentedInArtifact || testMethods.empty()) { + return; + } + + for (const auto &testMethod : testMethods) { + if (testMethod.sourceFilePath != tests.sourceFilePath) { + std::string message = StringUtils::stringFormat( + "While generating tests for source file: %s tried to generate tests for method %s " + "from another source file: %s. This can cause invalid generation.\n", + tests.sourceFilePath, testMethod.methodName, testMethod.sourceFilePath); + LOG_S(WARNING) << message; + } + + auto [argvData, kleeOut] = createKleeParams(testMethod, tests, testMethod.methodName); + addTailKleeInitParams(argvData, testMethod.bitcodeFilePath); + { + std::vector cargv, cenvp; + std::vector tmp; + ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false); + LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " "); + MEASURE_FUNCTION_EXECUTION_TIME RunKleeTask task(cargv.size(), cargv.data(), settingsContext.timeoutPerFunction); ExecUtils::ExecutionResult result __attribute__((unused)) = task.run(); @@ -288,51 +305,26 @@ void KleeRunner::processBatchWithInteractive(const std::vector argvData = { "klee", - entryPointFlag, - "--libc=klee", - "--utbot", - "--posix-runtime", - "--fp-runtime", - "--only-output-states-covering-new", - "--allocate-determ", - "--external-calls=all", - "--timer-interval=1000ms", - "--bcov-check-interval=6s", - "-istats-write-interval=5s", - "--disable-verify", - "--check-div-zero=false", - "--check-overshift=false", - "--skip-not-lazy-and-symbolic-pointers", - "--interactive", - KleeUtils::processNumberOption(), - entrypointsArg, - outputDir }; - if (settingsContext.timeoutPerFunction.has_value()) { - argvData.push_back(StringUtils::stringFormat("--timeout-per-function=%d", settingsContext.timeoutPerFunction.value())); - } - if (settingsContext.useDeterministicSearcher) { - argvData.emplace_back("--search=dfs"); + auto [argvData, kleeOut] = createKleeParams(testMethods[0], tests, ""); + { + // additional KLEE arguments + argvData.emplace_back("--interactive"); + argvData.emplace_back(KleeUtils::processNumberOption()); + { + // entrypoints + fs::path entrypoints = kleeOut.parent_path() / "entrypoints.txt"; + std::ofstream of(entrypoints); + for (const auto &method : testMethods) { + of << KleeUtils::entryPointFunction(tests, method.methodName, true) << std::endl; + } + argvData.emplace_back("--entrypoints-file=" + entrypoints.string()); + } + if (settingsContext.timeoutPerFunction.has_value()) { + argvData.emplace_back(StringUtils::stringFormat( + "--timeout-per-function=%d", settingsContext.timeoutPerFunction.value())); + } + addTailKleeInitParams(argvData, testMethods[0].bitcodeFilePath); } - argvData.push_back(testMethod.bitcodeFilePath); - argvData.emplace_back("--sym-stdin"); - argvData.emplace_back(std::to_string(types::Type::symStdinSize)); - { std::vector cargv, cenvp; std::vector tmp; diff --git a/server/src/KleeRunner.h b/server/src/KleeRunner.h index ee615b28..39347f81 100644 --- a/server/src/KleeRunner.h +++ b/server/src/KleeRunner.h @@ -48,6 +48,14 @@ class KleeRunner { void processBatchWithInteractive(const std::vector &testMethods, tests::Tests &tests, std::vector &ktests); + + std::pair, fs::path> + createKleeParams(const tests::TestMethod &testMethod, + const tests::Tests &tests, + const std::string &methodNameOrEmptyForFolder); + + void addTailKleeInitParams(std::vector &argvData, + const std::string &bitcodeFilePath); }; diff --git a/server/src/Paths.cpp b/server/src/Paths.cpp index 23609d2d..1c824f46 100644 --- a/server/src/Paths.cpp +++ b/server/src/Paths.cpp @@ -168,11 +168,14 @@ namespace Paths { return kleeOutDir / relative; } - fs::path kleeOutDirForEntrypoints(const utbot::ProjectContext &projectContext, const fs::path &projectTmpPath, - const fs::path &srcFilePath, const std::string &methodName) { + fs::path kleeOutDirForEntrypoints(const utbot::ProjectContext &projectContext, + const fs::path &projectTmpPath, + const fs::path &srcFilePath, + const std::string &methodNameOrEmptyForFolder) { auto kleeOutDirForFile = kleeOutDirForFilePath(projectContext, projectTmpPath, srcFilePath); - std::string suffix = methodName.empty() ? - addOrigExtensionAsSuffixAndAddNew(srcFilePath, "").filename().string() : methodName; + std::string suffix = methodNameOrEmptyForFolder.empty() + ? addOrigExtensionAsSuffixAndAddNew(srcFilePath, "").filename().string() + : methodNameOrEmptyForFolder; return kleeOutDirForFile / ("klee_out_" + suffix); } diff --git a/server/src/Paths.h b/server/src/Paths.h index 1ed35b22..c4742f48 100644 --- a/server/src/Paths.h +++ b/server/src/Paths.h @@ -242,8 +242,10 @@ namespace Paths { fs::path kleeOutDirForFilePath(const utbot::ProjectContext &projectContext, const fs::path &projectTmpPath, const fs::path &filePath); - fs::path kleeOutDirForEntrypoints(const utbot::ProjectContext &projectContext, const fs::path &projectTmpPath, - const fs::path &srcFilePath, const std::string &methodName = ""); + fs::path kleeOutDirForEntrypoints(const utbot::ProjectContext &projectContext, + const fs::path &projectTmpPath, + const fs::path &srcFilePath, + const std::string &methodNameOrEmptyForFolder); //endregion diff --git a/server/src/SARIFGenerator.cpp b/server/src/SARIFGenerator.cpp index 629b3691..f01c063e 100644 --- a/server/src/SARIFGenerator.cpp +++ b/server/src/SARIFGenerator.cpp @@ -76,8 +76,8 @@ namespace sarif { const fs::path &srcPath = fs::path(stack_match[3]); const fs::path &relPathInProject = getInProjectPath(projectContext.projectPath, srcPath); const fs::path &fullPathInProject = projectContext.projectPath / relPathInProject; - if (Paths::isSubPathOf(projectContext.buildDir(), fullPathInProject)) { - LOG_S(DEBUG) << "Full path " << fullPathInProject << " is in build - skip it"; + if (Paths::isSubPathOf(Paths::getUtbotBuildDir(projectContext), fullPathInProject)) { + LOG_S(WARNING) << "Full path " << fullPathInProject << " is in build - skip it"; continue; } if (!relPathInProject.empty() && fs::exists(fullPathInProject)) { diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 6a0347ac..888530d4 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -1137,13 +1137,16 @@ UnionValueView::UnionValueView( entryValue(PrinterUtils::convertBytesToUnion(typeName, rawDataView->getEntryValue(nullptr))) { } -TestMethod::TestMethod(std::string methodName, fs::path bitcodeFile, fs::path sourceFilename) - : methodName(std::move(methodName)), bitcodeFilePath(std::move(bitcodeFile)), - sourceFilePath(std::move(sourceFilename)) { -} +TestMethod::TestMethod(std::string methodName, fs::path bitcodeFile, fs::path sourceFilename, bool is32) + : methodName(std::move(methodName)) + , bitcodeFilePath(std::move(bitcodeFile)) + , sourceFilePath(std::move(sourceFilename)) + , is32bits(is32) +{} bool TestMethod::operator==(const TestMethod &rhs) const { - return std::tie(methodName, bitcodeFilePath, sourceFilePath) == std::tie(rhs.methodName, rhs.bitcodeFilePath, rhs.sourceFilePath); + return std::tie( methodName, bitcodeFilePath, sourceFilePath, is32bits) + == std::tie(rhs.methodName, rhs.bitcodeFilePath, rhs.sourceFilePath, rhs.is32bits); } bool TestMethod::operator!=(const TestMethod &rhs) const { return !(rhs == *this); diff --git a/server/src/Tests.h b/server/src/Tests.h index 046db7da..124523ce 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -557,10 +557,12 @@ namespace tests { std::string methodName; fs::path bitcodeFilePath; fs::path sourceFilePath; + bool is32bits; + bool operator==(const TestMethod &rhs) const; bool operator!=(const TestMethod &rhs) const; - TestMethod(std::string methodName, fs::path bitcodeFile, fs::path sourceFilename); + TestMethod(std::string methodName, fs::path bitcodeFile, fs::path sourceFilename, bool is32); }; using MethodKtests = std::unordered_map; diff --git a/server/src/building/BuildDatabase.cpp b/server/src/building/BuildDatabase.cpp index ec403591..14d1bb89 100644 --- a/server/src/building/BuildDatabase.cpp +++ b/server/src/building/BuildDatabase.cpp @@ -567,6 +567,10 @@ void BuildDatabase::ObjectFileInfo::addFile(fs::path file) { files.insert(std::move(file)); } +bool BuildDatabase::ObjectFileInfo::is32bits() const { + return CollectionUtils::contains(command.getCommandLine(), "-m32"); +} + void BuildDatabase::TargetInfo::addFile(fs::path file) { files.insert(std::move(file)); } diff --git a/server/src/building/BuildDatabase.h b/server/src/building/BuildDatabase.h index 5e4036cd..3015284c 100644 --- a/server/src/building/BuildDatabase.h +++ b/server/src/building/BuildDatabase.h @@ -71,6 +71,8 @@ class BuildDatabase { // User object file [[nodiscard]] fs::path getOutputFile() const; + [[nodiscard]] bool is32bits() const; + void setOutputFile(const fs::path &file); void addFile(fs::path file) override; diff --git a/server/src/building/Linker.cpp b/server/src/building/Linker.cpp index dfccc47b..15041d39 100644 --- a/server/src/building/Linker.cpp +++ b/server/src/building/Linker.cpp @@ -268,7 +268,10 @@ std::vector Linker::getTestMethods() { auto compilationUnitInfo = testGen.buildDatabase->getClientCompilationUnitInfo(fileName); if (compilationUnitInfo->kleeFilesInfo->isCorrectMethod(methodName)) { - testMethods.emplace_back(methodName, bitcodePath, fileName); + testMethods.emplace_back(methodName, + bitcodePath, + fileName, + compilationUnitInfo->is32bits()); } } } @@ -294,7 +297,8 @@ std::vector Linker::getTestMethods() { if (compilationUnitInfo->kleeFilesInfo->isCorrectMethod(methodName)) { tests::TestMethod testMethod{ methodName, bitcodeFileName.at(lineInfo->filePath), - fileName }; + fileName, + compilationUnitInfo->is32bits()}; testMethods.emplace_back(testMethod); } if (!lineInfo->forClass) diff --git a/server/src/utils/HashUtils.cpp b/server/src/utils/HashUtils.cpp index fd4fcfc6..7e1b7952 100644 --- a/server/src/utils/HashUtils.cpp +++ b/server/src/utils/HashUtils.cpp @@ -17,7 +17,10 @@ namespace HashUtils { std::size_t TestMethodHash::operator()(const tests::TestMethod &testMethod) const { size_t seed = 0; - hashCombine(seed, testMethod.methodName, testMethod.bitcodeFilePath, testMethod.sourceFilePath); + hashCombine(seed, testMethod.methodName, + testMethod.bitcodeFilePath, + testMethod.sourceFilePath, + testMethod.is32bits); return seed; } }