Skip to content

Kcov updated #1

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
Expand Up @@ -2,7 +2,7 @@
"include": [
{
"DOCKER_TAG": "24-03-2022",
"OPERATING_SYSTEM_TAG": "18.04",
"OPERATING_SYSTEM_TAG": "20.04",
"LLVM_VERSION_MAJOR": "10"
}
]
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
[submodule "parallel-hashmap"]
path = submodules/parallel-hashmap
url = https://github.com/greg7mdp/parallel-hashmap
[submodule "submodules/kcov"]
path = submodules/kcov
url = https://github.com/SimonKagstrom/kcov
5 changes: 5 additions & 0 deletions docker/Dockerfile_base
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ RUN chmod +x /usr/local/bin/lit

RUN sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 100

#Install kcov coverage tool and xml parser
RUN apt update && apt install -y kcov
RUN apt install -y libpugixml-dev
RUN apt install -y libpugixml1v5

# Download CLI11 and Rang libs for nice CLI
RUN wget https://github.com/CLIUtils/CLI11/releases/download/v1.9.1/CLI11.hpp -P $UTBOT_ALL/cli
RUN wget https://github.com/agauniyal/rang/releases/download/v3.1.0/rang.hpp -P $UTBOT_ALL/cli
Expand Down
8 changes: 1 addition & 7 deletions docker/utbot_docker_dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,8 @@ CONTAINER_NAME=$USER-utbot-dev
MOUNT_NAME=$USER-utbot
MOUNT_LOCAL_NAME=$MOUNT_NAME-local-mnt

read -e -p "Enter base image tag: " IMAGE_TAG
IMAGE="ghcr.io/unittestbot/utbotcpp/base_env:$IMAGE_TAG"
IMAGE="kcov_image:latest"

echo "Pulling docker image '$IMAGE'"
if ! docker pull $IMAGE ; then
echo "Failed to fetch the image. Aborting.."
exit 1
fi
set +e
docker exec "$CONTAINER_NAME" ls > /dev/null 2>&1
if [ $? -eq 0 ]
Expand Down
1 change: 1 addition & 0 deletions firstmap
Submodule firstmap added at 703bd9
6 changes: 6 additions & 0 deletions server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}")

find_package(run_klee REQUIRED)

#
find_package(pugixml REQUIRED)

option(ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" ON)

if (ENABLE_PRECOMPILED_HEADERS)
Expand All @@ -141,6 +144,7 @@ target_link_libraries(UnitTestBotLib PUBLIC clangTooling clangBasic clangASTMatc
protobuf::libprotobuf
loguru
kleeRunner
pugixml
)
if (ENABLE_PRECOMPILED_HEADERS)
target_precompile_headers(UnitTestBotLib PUBLIC pch.h)
Expand All @@ -153,6 +157,7 @@ add_llvm_executable(utbot main.cpp)
target_link_libraries(utbot PUBLIC
UnitTestBotLib
loguru
pugixml #########

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to link with pugixml again since it is already linked to UnitTestBotLib. The same practice should be applicable to loguru library

)

################################################################################
Expand Down Expand Up @@ -180,6 +185,7 @@ if (ENABLE_UNIT_TESTS)
PUBLIC
gtest
UnitTestBotLib
pugixml
Copy link

@operasfantom operasfantom Jun 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comment related to utbot target

)

if (ENABLE_PRECOMPILED_HEADERS)
Expand Down
4 changes: 4 additions & 0 deletions server/src/Paths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ namespace Paths {
fs::path getGccCoverageDir(const utbot::ProjectContext &projectContext) {
return getCoverageDir(projectContext) / "gcov";
}
///////////////////////////////////////////////////////
fs::path getKcovReportDir(const utbot::ProjectContext &projectContext) {
return getBuildDir(projectContext) / "report";
}
fs::path getTestExecutable(const utbot::ProjectContext &projectContext,
const fs::path &filePath) {
return getTestExecDir(projectContext) / filePath.stem();
Expand Down
2 changes: 2 additions & 0 deletions server/src/Paths.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ namespace Paths {

fs::path getGccCoverageDir(const utbot::ProjectContext &projectContext);

fs::path getKcovReportDir(const utbot::ProjectContext &projectContext);

fs::path getTestExecutable(const utbot::ProjectContext &projectContext, const fs::path &filePath);

fs::path getGeneratedHeaderPath(const utbot::ProjectContext &projectContext, const fs::path &sourceFilePath);
Expand Down
27 changes: 1 addition & 26 deletions server/src/coverage/CoverageAndResultsGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ grpc::Status CoverageAndResultsGenerator::generate(bool withCoverage,
utbot::SettingsContext &settingsContext) {
MEASURE_FUNCTION_EXECUTION_TIME
try {
init(withCoverage);
init(withCoverage, true);
runTests(withCoverage, settingsContext.timeoutPerTest);
if (withCoverage) {
collectCoverage();
Expand Down Expand Up @@ -82,30 +82,5 @@ nlohmann::json const &CoverageAndResultsGenerator::getTotals() {

void CoverageAndResultsGenerator::collectCoverage() {
MEASURE_FUNCTION_EXECUTION_TIME
if (testsToLaunch.empty()) {
return;
}
std::vector<ShellExecTask> coverageCommands = coverageTool->getCoverageCommands(
CollectionUtils::filterToVector(testsToLaunch, [this](const UnitTest &testToLaunch) {
return testStatusMap[testToLaunch.testFilePath][testToLaunch.testname] !=
testsgen::TEST_INTERRUPTED;
}));
if (coverageCommands.empty()) {
return;
}
ExecUtils::doWorkWithProgress(
coverageCommands, coverageAndResultsWriter, "Collecting coverage",
[this](ShellExecTask &task) {
auto [out, status, path] = task.run();
if (status != 0) {
exceptions.emplace_back(
StringUtils::stringFormat("Command: %s\nOutput: %s", task.toString(), out),
path.value());
}
});

LOG_S(DEBUG) << "All coverage commands were executed";

coverageMap = coverageTool->getCoverageInfo();
totals = coverageTool->getTotals();
}
8 changes: 7 additions & 1 deletion server/src/coverage/CoverageTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "exceptions/CoverageGenerationException.h"
#include "utils/CompilationUtils.h"
#include "utils/StringUtils.h"
#include "KcovCoverageTool.h"

using namespace CompilationUtils;

Expand All @@ -17,7 +18,12 @@ CoverageTool::CoverageTool(ProgressWriter const *progressWriter) : progressWrite

std::unique_ptr<CoverageTool> getCoverageTool(const std::string &compileCommandsJsonPath,
utbot::ProjectContext projectContext,
ProgressWriter const *progressWriter) {
ProgressWriter const *progressWriter,
bool withKcov) {
if (withKcov) {
return std::make_unique<KcovCoverageTool>(projectContext, progressWriter);
}

auto compilationDatabase = CompilationUtils::getCompilationDatabase(compileCommandsJsonPath);
fs::path compilerPath = compilationDatabase->getBuildCompilerPath();
CompilerName compilerName = CompilationUtils::getCompilerName(compilerPath);
Expand Down
3 changes: 2 additions & 1 deletion server/src/coverage/CoverageTool.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class CoverageTool {

std::unique_ptr<CoverageTool> getCoverageTool(const std::string &compileCommandsJsonPath,
utbot::ProjectContext projectContext,
const ProgressWriter *progressWriter);
const ProgressWriter *progressWriter,
bool withKcov);

#endif // UNITTESTBOT_COVERAGETOOL_H
131 changes: 131 additions & 0 deletions server/src/coverage/KcovCoverageTool.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// Created by andrey on 05.06.2022.
//

#include "KcovCoverageTool.h"

#include "Coverage.h"
#include "Paths.h"
#include "TimeExecStatistics.h"
#include "environment/EnvironmentPaths.h"
#include "exceptions/CoverageGenerationException.h"
#include "utils/ArgumentsUtils.h"
#include "utils/CollectionUtils.h"
#include "utils/FileSystemUtils.h"
#include "utils/JsonUtils.h"
#include "utils/MakefileUtils.h"
#include "utils/StringUtils.h"
#include "utils/path/FileSystemPath.h"
#include "pugixml.hpp"
#include <regex>

#include "loguru.h"

using Coverage::CoverageMap;
using Coverage::FileCoverage;


KcovCoverageTool::KcovCoverageTool(utbot::ProjectContext projectContext,
ProgressWriter const *progressWriter)
: CoverageTool(progressWriter), projectContext(projectContext) {
}

std::vector<BuildRunCommand>
KcovCoverageTool::getBuildRunCommands(const std::vector<UnitTest> &testsToLaunch, bool withCoverage) {
ExecUtils::throwIfCancelled();

std::vector<BuildRunCommand> result;
ExecUtils::doWorkWithProgress(
testsToLaunch, progressWriter, "Collecting build and run commands",
[&](UnitTest const &testToLaunch) {
auto makefile = Paths::getMakefilePathFromSourceFilePath(
projectContext,
Paths::testPathToSourcePath(projectContext, testToLaunch.testFilePath));
auto gtestFlags = getTestFilter(testToLaunch);
auto buildCommand =
MakefileUtils::makefileCommand(projectContext, makefile, "build", gtestFlags);
auto runCommand =
MakefileUtils::makefileCommand(projectContext, makefile, "run", gtestFlags);
result.push_back({testToLaunch, buildCommand, runCommand});
});
return result;
}

std::vector<ShellExecTask>
KcovCoverageTool::getCoverageCommands(const std::vector<UnitTest> &testsToLaunch) {
return {};
}


static void addLine(uint32_t lineNumber, bool covered, FileCoverage &fileCoverage) {
assert(lineNumber > 0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than calling the assert function which causes server to crash, please throw an exception

if (covered) {
fileCoverage.fullCoverageLines.insert({lineNumber});
} else {
fileCoverage.noCoverageLines.insert({lineNumber});
}
}

static void setLineNumbers(pugi::xml_node lines, FileCoverage &fileCoverage) {
for (pugi::xml_node line: lines.children("line")) {
uint32_t lineNumber = line.attribute("number").as_int();
bool covered = line.attribute("hits").as_int() > 0;
addLine(lineNumber, covered, fileCoverage);
}
}

Coverage::CoverageMap KcovCoverageTool::getCoverageInfo() const {
ExecUtils::throwIfCancelled();

CoverageMap coverageMap;

auto coverageReportDirPath = Paths::getKcovReportDir(projectContext);
if (!fs::exists(coverageReportDirPath)) {
std::string message = "Couldn't find coverage directory at " + coverageReportDirPath.string();
LOG_S(ERROR) << message;
throw CoverageGenerationException(message);
}
LOG_S(INFO) << "Reading coverage file";


std::string pathToReport = "";
for (const auto &entry: fs::directory_iterator(coverageReportDirPath.string())) {
std::regex reg("(calc.).*$");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid building std::regex object every single time and construct the corresponding object just once per program, say, declaring it as a global const static variable

if (std::regex_search(entry.path().string(), reg)) {
pathToReport = entry.path().string() + "/cov.xml";
break;
}
}
Comment on lines +91 to +98
Copy link

@operasfantom operasfantom Jun 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's extract this block to private method getPathToReport


auto path = pathToReport.c_str();

pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(path);
auto tools = doc.child("coverage").child("packages").child("package").child("classes");


for (pugi::xml_node fileIter = tools.child("class"); fileIter; fileIter = fileIter.next_sibling("class")) {
std::string filePathString = fileIter.attribute("filename").as_string();
fs::path filePath(filePathString);

pugi::xml_node lines = fileIter.child("lines");

setLineNumbers(lines, coverageMap[filePath]);
//setFunctionBorders(lines, coverageMap[filePath]);
}

return coverageMap;
}


nlohmann::json KcovCoverageTool::getTotals() const {
return nlohmann::json();
}

void KcovCoverageTool::cleanCoverage() const {

}

fs::path KcovCoverageTool::getKcovReportFile() const {
return nullptr;
}
30 changes: 30 additions & 0 deletions server/src/coverage/KcovCoverageTool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Created by andrey on 05.06.2022.
//

#ifndef UNITTESTBOT_KCOVCOVERAGETOOL_H
#define UNITTESTBOT_KCOVCOVERAGETOOL_H

#include "CoverageAndResultsGenerator.h"
#include "CoverageTool.h"

class KcovCoverageTool : public CoverageTool {
public:
KcovCoverageTool(utbot::ProjectContext projectContext, const ProgressWriter *progressWriter);

std::vector<BuildRunCommand> getBuildRunCommands(const std::vector<UnitTest> &testsToLaunch,
bool withCoverage) override;

std::vector<ShellExecTask> getCoverageCommands(const std::vector<UnitTest> &testFilePath) override;

[[nodiscard]] Coverage::CoverageMap getCoverageInfo() const override;
[[nodiscard]] nlohmann::json getTotals() const override;
void cleanCoverage() const override;
private:
const utbot::ProjectContext projectContext;

fs::path getKcovReportFile() const;
};


#endif //UNITTESTBOT_KCOVCOVERAGETOOL_H
4 changes: 2 additions & 2 deletions server/src/coverage/TestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ grpc::Status TestRunner::runTests(bool withCoverage, const std::optional<std::ch
return Status::OK;
}

void TestRunner::init(bool withCoverage) {
void TestRunner::init(bool withCoverage, bool withKcov) {
MEASURE_FUNCTION_EXECUTION_TIME
fs::path ccJson = CompilationUtils::substituteRemotePathToCompileCommandsJsonPath(
projectContext.projectPath, projectContext.buildDirRelativePath);
coverageTool = getCoverageTool(ccJson, projectContext, progressWriter);
coverageTool = getCoverageTool(ccJson, projectContext, progressWriter, withKcov);
if (withCoverage) {
cleanCoverage();
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/coverage/TestRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class TestRunner {
std::string testSuite,
std::string testName);

void init(bool withCoverage);
void init(bool withCoverage, bool withKcov);

std::vector<UnitTest> getTestsToLaunch();

Expand Down
4 changes: 3 additions & 1 deletion server/src/printers/NativeMakefilePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ namespace printer {

declareTarget("bin", { FORCE }, { stringFormat("echo %s", coverageInfoBinary) });

utbot::RunCommand testRunCommand{ { testExecutablePath.string(), "$(GTEST_FLAGS)" },
std::string kcovRunCommand = "kcov " + (projectContext.buildDir.string() + "/report ") + testExecutablePath.string();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reuse getKcovReportDir here


utbot::RunCommand testRunCommand{ { kcovRunCommand, "$(GTEST_FLAGS)" },
buildDirectory };
testRunCommand.addEnvironmentVariable("PATH", "$$PATH:$(pwd)");
if (primaryCompilerName == CompilationUtils::CompilerName::GCC) {
Expand Down
4 changes: 2 additions & 2 deletions server/src/utils/ArgumentsUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ namespace CompilationUtils {
switch (compilerName) {
case CompilerName::GCC:
case CompilerName::GXX:
return { "--coverage" };
return { "-g", "--coverage" };
case CompilerName::CLANG:
case CompilerName::CLANGXX:
return { "-fprofile-instr-generate", "-fcoverage-mapping" };
return { "-g", "-fprofile-instr-generate", "-fcoverage-mapping" };
Comment on lines +85 to +88

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add "-g" option since it is already added in Makefile

default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions submodules/kcov
Submodule kcov added at e615f9