Skip to content

#216 UTBotCpp does not support test generation for 32bit projects #373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/matrix.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"include": [
{
"DOCKER_TAG": "2022.7.0",
"DOCKER_TAG": "2022.8.0",
"OPERATING_SYSTEM_TAG": "18.04",
"LLVM_VERSION_MAJOR": "10"
}
Expand Down
4 changes: 4 additions & 0 deletions docker/Dockerfile_base
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
144 changes: 68 additions & 76 deletions server/src/KleeRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ void KleeRunner::runKlee(const std::vector<tests::TestMethod> &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();
}
Expand Down Expand Up @@ -207,30 +207,18 @@ static void processMethod(MethodKtests &ktestChunk,
}
}

void KleeRunner::processBatchWithoutInteractive(const std::vector<tests::TestMethod> &testMethods,
tests::Tests &tests,
std::vector<tests::MethodKtests> &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<std::vector<std::string>, 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<std::string> argvData = { "klee",
entryPointFlag,
"--entry-point=" + KleeUtils::entryPointFunction(tests, testMethod.methodName, true),
"--libc=klee",
"--utbot",
"--posix-runtime",
Expand All @@ -245,20 +233,49 @@ void KleeRunner::processBatchWithoutInteractive(const std::vector<tests::TestMet
"--check-div-zero=false",
"--check-overshift=false",
"--skip-not-lazy-and-symbolic-pointers",
outputDir };
"--output-dir=" + kleeOut.string()};
if (settingsContext.useDeterministicSearcher) {
argvData.emplace_back("--search=dfs");
}
argvData.push_back(testMethod.bitcodeFilePath);
if (testMethod.is32bits) {
// 32bit project
argvData.emplace_back("--allocate-determ-size=" + std::to_string(1));
argvData.emplace_back("--allocate-determ-start-address=" + std::to_string(0x10000));
}
return {argvData, kleeOut};
}

void KleeRunner::addTailKleeInitParams(std::vector<std::string> &argvData, const std::string &bitcodeFilePath)
{
argvData.emplace_back(bitcodeFilePath);
argvData.emplace_back("--sym-stdin");
argvData.emplace_back(std::to_string(types::Type::symStdinSize));
}

{
std::vector<char *> cargv, cenvp;
std::vector<std::string> 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<tests::TestMethod> &testMethods,
tests::Tests &tests,
std::vector<tests::MethodKtests> &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<char *> cargv, cenvp;
std::vector<std::string> 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();
Expand Down Expand Up @@ -288,51 +305,26 @@ void KleeRunner::processBatchWithInteractive(const std::vector<tests::TestMethod
}
}

TestMethod testMethod = testMethods[0];
std::string entryPoint = KleeUtils::entryPointFunction(tests, testMethod.methodName, true);
std::string entryPointFlag = StringUtils::stringFormat("--entry-point=%s", entryPoint);
auto kleeOut = Paths::kleeOutDirForEntrypoints(projectContext, projectTmpPath, tests.sourceFilePath);
fs::create_directories(kleeOut.parent_path());

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;
}
of.close();
std::string entrypointsArg = "--entrypoints-file=" + entrypoints.string();

std::string outputDir = "--output-dir=" + kleeOut.string();
std::vector<std::string> 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<char *> cargv, cenvp;
std::vector<std::string> tmp;
Expand Down
8 changes: 8 additions & 0 deletions server/src/KleeRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ class KleeRunner {
void processBatchWithInteractive(const std::vector<tests::TestMethod> &testMethods,
tests::Tests &tests,
std::vector<tests::MethodKtests> &ktests);

std::pair<std::vector<std::string>, fs::path>
createKleeParams(const tests::TestMethod &testMethod,
const tests::Tests &tests,
const std::string &methodNameOrEmptyForFolder);

void addTailKleeInitParams(std::vector<std::string> &argvData,
const std::string &bitcodeFilePath);
};


Expand Down
11 changes: 7 additions & 4 deletions server/src/Paths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 4 additions & 2 deletions server/src/Paths.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions server/src/SARIFGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
13 changes: 8 additions & 5 deletions server/src/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion server/src/Tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestMethod, UTBotKTestList, HashUtils::TestMethodHash>;
Expand Down
4 changes: 4 additions & 0 deletions server/src/building/BuildDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
2 changes: 2 additions & 0 deletions server/src/building/BuildDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 6 additions & 2 deletions server/src/building/Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,10 @@ std::vector<tests::TestMethod> 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());
}
}
}
Expand All @@ -294,7 +297,8 @@ std::vector<tests::TestMethod> 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)
Expand Down
5 changes: 4 additions & 1 deletion server/src/utils/HashUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}