diff --git a/README.md b/README.md index 6e5e2d05..5556dfff 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,14 @@ kcov --merge /tmp/merged-output /tmp/kcov-output1 /tmp/kcov-output2 kcov --merge /tmp/merged-output /tmp/kcov-output* # With a wildcard ``` -Use from continuous integration systems ---------------------------------------- +Integration with other systems +------------------------------ kcov is easy to integrate with [travis-ci](http://travis-ci.org) together with [coveralls.io](http://coveralls.io) or [codecov.io](http://codecov.io). It can also be used from Jenkins, [SonarQube](http://sonarqube.org) and [GitLab CI](http://gitlab.com). Refer to +* [vscode](doc/vscode.md) for details about vscode + coverage gutters * [coveralls](doc/coveralls.md) for details about travis-ci + coveralls, or * [codecov](doc/codecov.md) for details about travis-ci + codecov * [jenkins](doc/jenkins.md) for details about how to integrate in Jenkins diff --git a/doc/kcov.1 b/doc/kcov.1 index 840323f9..e8591959 100644 --- a/doc/kcov.1 +++ b/doc/kcov.1 @@ -67,6 +67,9 @@ Don't accumulate data from multiple runs. \fB\-\-collect\-only Only collect coverage data, don't produce HTML/Cobertura output. .TP +\fB\-\-dump\-summary +Dump a brief coverage summary on stdout after execution +.TP \fB\-\-report\-only Only report HTML/Cobertura output, don't collect data. .TP @@ -121,6 +124,10 @@ want this behavior, this option turns that off. \fB\-\-bash\-parse\-files\-in\-dir\fP=\fIP1\fP[\fI,P2\fP...] Parse directories for bash scripts. .TP +\fB\-\-bash\-tracefd\-closeexec +Force children to not be traced by configuring the trace fd as non-cloneable with +LD_PRELOAD. Buggy on some systems. +.TP \fB\-\-replace\-src\-path\fP=\fIP1\fP:\fIP2\fP Replace source file path P1 with P2, if found. .TP @@ -130,6 +137,18 @@ for more information on full-system instrumentation. .TP \fB\-\-system\-report Produce coverage output for a full-system coverage run. +.TP +\fB\-\-debug\fP=\fIlevel\fP +Set the debug level, 0...31 (as a mask). Default is 0 for no debug output. +.TP +\fB\-\-output\-interval\fP=\fIMS\fP +Set the output writing interval in milliseconds. The default is 5000. +.TP +\fB\-\-configure\fP=\fIKEY=VAL\fP +Configure key/value pairs of internal options. See -\-help \-\-uncommon\-options for possible configuration options. +.TP +\fB\-\-patchelf\fP=\fPCMD\fP +For system mode, set the patchelf command (default patchelf). .RE .SH EXAMPLES .PP diff --git a/doc/vscode.md b/doc/vscode.md new file mode 100644 index 00000000..f46fc2b6 --- /dev/null +++ b/doc/vscode.md @@ -0,0 +1,17 @@ +Integration with vscode +----------------------- +It's easy to produce output for parsing with the [coverage gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) +extension to vscode. The `--cobertura-only` option is well-suited for this, which only outputs a +`cov.xml` file and the coverage database in the output directory. Coverage information can then +be directly shown in the "gutter" beside the editor, and is automatically updated by the +extension when the coverage changes. + +To produce this output, just run + +``` +kcov --cobertura-only [other options] /path/to/your/folder/.vscode /path/to/the/binary +``` + +The coverage gutters extension looks for `cov.xml` files in the workspace, but for performance +reasons, it's good to configure it to only look where the kcov output is (`.vscode`) in this +example. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91a8b0d1..592ba865 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -169,7 +169,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") find_package (LibElf) find_package (ElfUtils) set (ELF_SRCS - engines/clang-coverage-engine.cc engines/ptrace.cc engines/ptrace_linux.cc engines/kernel-engine.cc @@ -187,7 +186,6 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") find_package (LibElf) find_package (ElfUtils) set (ELF_SRCS - engines/clang-coverage-engine.cc engines/ptrace.cc engines/ptrace_freebsd.cc parsers/elf.cc @@ -230,7 +228,6 @@ set (${KCOV}_SRCS engines/system-mode-file-format.cc engines/python-engine.cc filter.cc - gcov.cc main.cc merge-file-parser.cc output-handler.cc @@ -249,7 +246,6 @@ set (${KCOV}_SRCS ${ELF_SRCS} ${MACHO_SRCS} include/capabilities.hh - include/gcov.hh include/reporter.hh include/collector.hh include/generated-data-base.hh @@ -276,14 +272,12 @@ set (KCOV_SYSTEM_MODE_SRCS engines/ptrace.cc engines/ptrace_linux.cc filter.cc - gcov.cc include/capabilities.hh include/collector.hh include/configuration.hh include/engine.hh include/file-parser.hh include/filter.hh - include/gcov.hh include/generated-data-base.hh include/lineid.hh include/manager.hh diff --git a/src/configuration.cc b/src/configuration.cc index 084b2186..10964de4 100644 --- a/src/configuration.cc +++ b/src/configuration.cc @@ -125,7 +125,6 @@ class Configuration : public IConfiguration { "path-strip-level", required_argument, 0, 'S' }, { "skip-solibs", no_argument, 0, 'L' }, { "exit-first-process", no_argument, 0, 'F' }, - { "gcov", no_argument, 0, 'g' }, { "clang", no_argument, 0, 'c' }, { "configure", required_argument, 0, 'M' }, { "clean", no_argument, 0, 'E' }, @@ -217,12 +216,6 @@ class Configuration : public IConfiguration case 'F': setKey("daemonize-on-first-process-exit", 1); break; - case 'g': - setKey("gcov", 1); - break; - case 'c': - setKey("clang-sanitizer", 1); - break; case 'E': setKey("clean-output", 1); break; @@ -572,8 +565,6 @@ class Configuration : public IConfiguration setKey("path-strip-level", 2); setKey("attach-pid", 0); setKey("parse-solibs", 1); - setKey("gcov", 0); - setKey("clang-sanitizer", 0); setKey("clean-output", 0); setKey("low-limit", 25); setKey("high-limit", 75); @@ -707,8 +698,6 @@ class Configuration : public IConfiguration " --path-strip-level=num path levels to show for common paths (default: %d)\n" "\n" " --cobertura-only Only produce cobertura output in the output dir\n" - " --gcov use gcov parser instead of DWARF debugging info\n" - " --clang use Clang Sanitizer-coverage parser\n" " --dump-summary dump a summary of coverage on stdout\n" " --system-record perform full-system instrumentation\n" " --system-report report full-system coverage data\n" diff --git a/src/engines/clang-coverage-engine.cc b/src/engines/clang-coverage-engine.cc deleted file mode 100644 index 66bad9e2..00000000 --- a/src/engines/clang-coverage-engine.cc +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include // std::pair - -#include "../parsers/dwarf.hh" -#include "script-engine-base.hh" - -using namespace kcov; - -class ClangEngine : public ScriptEngineBase, IFileParser::ILineListener -{ -public: - ClangEngine() : - ScriptEngineBase(), m_child(-1), m_checksum(0) - { - } - - bool start(IEventListener &listener, const std::string &executable) - { - IConfiguration &conf = IConfiguration::getInstance(); - char * const *argv = (char * const *) conf.getArgv(); - - m_listener = &listener; - - // Run the program until completion - m_child = fork(); - if (m_child == 0) - { - std::string env = fmt("ASAN_OPTIONS=coverage=1:coverage_dir=%s", conf.keyAsString("target-directory").c_str()); - - char *cpy = xstrdup(env.c_str()); - - putenv(cpy); - unsetenv("LD_PRELOAD"); - execv(argv[0], argv); - } - else if (m_child < 0) - { - perror("fork"); - - return false; - } - - return true; - } - - bool continueExecution() - { - if (m_child < 0) - return false; - - int status = 0; - - // Wait for the child - waitpid(m_child, &status, 0); - - m_child = -1; - - // All coverage collection is done after the program has been run - DIR *dir; - struct dirent *de; - - std::string targetDir = IConfiguration::getInstance().keyAsString("target-directory"); - dir = opendir(targetDir.c_str()); - panic_if(!dir, "Can't open directory\n"); - - for (de = readdir(dir); de; de = readdir(dir)) - { - std::string cur = fmt("%s/%s", targetDir.c_str(), de->d_name); - - // ... except for the current coveree - if (cur.find(".sancov") == std::string::npos) - continue; - - parseCoverageFile(cur); - } - closedir(dir); - - Event ev(ev_exit, WEXITSTATUS(status)); - - // Report the exit status - m_listener->onEvent(ev); - - return false; - } - - std::string getParserType() - { - return "clang-sanitizer"; - } - - virtual uint64_t getChecksum() - { - return m_checksum; - } - - unsigned int matchParser(const std::string &filename, uint8_t *data, size_t dataSize) - { - if (IConfiguration::getInstance().keyAsInt("clang-sanitizer")) - return match_perfect; - - return match_none; - } - - virtual enum IFileParser::PossibleHits maxPossibleHits() - { - return IFileParser::HITS_SINGLE; - } - - void kill(int signal) - { - } - - bool addFile(const std::string &filename, struct phdr_data_entry *phdr_data) - { - IElf *elf = IElf::create(filename); - - if (!elf) - return false; - - const std::vector &segs = elf->getSegments(); - for (std::vector::const_iterator it = segs.begin(); it != segs.end(); ++it) - IDisassembler::getInstance().addSection(it->getData(), it->getSize(), it->getBase()); - - bool rv = m_dwarfParser.open(filename); - - // Get a list of all possible source lines - if (rv) - m_dwarfParser.forEachLine(*this); - - size_t sz; - uint8_t *p = (uint8_t *) read_file(&sz, "%s", filename.c_str()); - - m_checksum = hash_block(p, sz); - - free(p); - - return rv; - } - -private: - typedef std::vector FileList_t; - - void onLine(const std::string &file, unsigned int lineNr, uint64_t addr) - { - - for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) - (*it)->onLine(get_real_path(file), lineNr, addr); - } - - void parseCoverageFile(const std::string &name) - { - size_t sz; - void *p; - - p = read_file(&sz, "%s", name.c_str()); - if (!p) - { - return; - } - - // Remove the coverage file for the next round - unlink(name.c_str()); - - // Only header? - if (sz < 8) - { - free(p); - return; - } - - uint64_t header = *(uint64_t *) p; - // Assume native-endianness (?) - if (header == 0xC0BFFFFFFFFFFF64ULL) - { - size_t nEntries = (sz - sizeof(uint64_t)) / sizeof(uint64_t); - uint64_t *entries = &((uint64_t *) p)[1]; - - for (size_t i = 0; i < nEntries; i++) - { - m_dwarfParser.forAddress(*this, entries[i] + 1); - reportBreakpoint(entries[i] + 1); - } - } - else if (header == 0xC0BFFFFFFFFFFF32ULL) - { - size_t nEntries = (sz - sizeof(uint64_t)) / sizeof(uint32_t); - uint32_t *entries = &((uint32_t *) p)[1]; - - for (size_t i = 0; i < nEntries; i++) - { - m_dwarfParser.forAddress(*this, entries[i] + 1); - reportBreakpoint(entries[i] + 1); - } - } - - free(p); - } - - void reportBreakpoint(uint64_t address) - { - std::vector bb = IDisassembler::getInstance().getBasicBlock(address); - - for (std::vector::iterator it = bb.begin(); it != bb.end(); ++it) - reportEvent(ev_breakpoint, 0, *it); - - // Fallback in case kcov is broken - if (bb.empty()) - { - kcov_debug(ENGINE_MSG, "Address 0x%llx not in a basic block\n", (long long) address); - reportEvent(ev_breakpoint, 0, address); - } - } - - pid_t m_child; - DwarfParser m_dwarfParser; - uint64_t m_checksum; -}; - -static ClangEngine *g_clangEngine; -class ClangCtor -{ -public: - ClangCtor() - { - g_clangEngine = new ClangEngine(); - } -}; -static ClangCtor g_clangCtor; - -class ClangEngineCreator: public IEngineFactory::IEngineCreator -{ -public: - virtual ~ClangEngineCreator() - { - } - - virtual IEngine *create() - { - return g_clangEngine; - } - - unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) - { - if (IConfiguration::getInstance().keyAsInt("clang-sanitizer")) - return match_perfect; - - return match_none; - } -}; - -static ClangEngineCreator g_clangEngineCreator; diff --git a/src/engines/gcov-engine.cc b/src/engines/gcov-engine.cc deleted file mode 100644 index e2492c8d..00000000 --- a/src/engines/gcov-engine.cc +++ /dev/null @@ -1,205 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include // std::pair - -using namespace kcov; - -class GcovEngine : public IEngine, IFileParser::IFileListener -{ -public: - GcovEngine(IFileParser &parser) : - m_listener(NULL), m_child(-1) - { - parser.registerFileListener(*this); - } - - int registerBreakpoint(unsigned long addr) - { - return 0; - } - - bool start(IEventListener &listener, const std::string &executable) - { - char * const *argv = (char * const *) IConfiguration::getInstance().getArgv(); - - m_listener = &listener; - - // Run the program until completion - m_child = fork(); - if (m_child == 0) - { - execv(argv[0], argv); - } - else if (m_child < 0) - { - perror("fork"); - - return false; - } - - return true; - } - - void kill(int sig) - { - } - - bool continueExecution() - { - if (m_child < 0) - return false; - - int status; - - // Wait for the child - waitpid(m_child, &status, 0); - - m_child = -1; - - // All coverage collection is done after the program has been run - for (FileList_t::const_iterator it = m_gcdaFiles.begin(); it != m_gcdaFiles.end(); ++it) - { - const std::string &gcda = *it; - std::string gcno = gcda; - - size_t sz = gcno.size(); - - // .gcda -> .gcno - gcno[sz - 2] = 'n'; - gcno[sz - 1] = 'o'; - - // Need a pair - if (!file_exists(gcno) || !file_exists(gcda)) - continue; - - parseGcovFiles(gcno, gcda); - } - - Event ev(ev_exit, WEXITSTATUS(status)); - - // Report the exit status - m_listener->onEvent(ev); - - return false; - } - -private: - typedef std::vector FileList_t; - - void parseGcovFiles(const std::string &gcnoFile, const std::string gcdaFile) - { - size_t sz; - void *d = read_file(&sz, "%s", gcnoFile.c_str()); - - if (!d) - return; - GcnoParser gcno((uint8_t *) d, sz); - - void *d2 = read_file(&sz, "%s", gcdaFile.c_str()); - - if (!d2) - return; - GcdaParser gcda((uint8_t *) d2, sz); - - gcno.parse(); - gcda.parse(); - - std::unordered_map bbsByNumber; - - const GcnoParser::BasicBlockList_t &bbs = gcno.getBasicBlocks(); - const GcnoParser::ArcList_t &arcs = gcno.getArcs(); - - for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) - { - const GcnoParser::BasicBlockMapping &cur = *it; - - bbsByNumber[cur.m_basicBlock].push_back(cur); - } - - std::unordered_map counterByFunction; - for (GcnoParser::ArcList_t::const_iterator it = arcs.begin(); it != arcs.end(); ++it) - { - const GcnoParser::Arc &cur = *it; - - int64_t counter = gcda.getCounter(cur.m_function, counterByFunction[cur.m_function]); - - counterByFunction[cur.m_function]++; - - // Not found? - if (counter < 0) - { - warning("Arc %u but no counter\n", counterByFunction[cur.m_function]); - continue; - } - - // No hit, so ignore - if (counter == 0) - continue; - - reportBasicBlockHit(bbsByNumber[cur.m_dstBlock], counter); - reportBasicBlockHit(bbsByNumber[cur.m_srcBlock], counter); - } - } - - void reportBasicBlockHit(const GcnoParser::BasicBlockList_t &bbs, int64_t counter) - { - for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) - { - const GcnoParser::BasicBlockMapping &bb = *it; - - uint64_t addr = gcovGetAddress(bb.m_file, bb.m_function, bb.m_basicBlock, bb.m_index); - Event ev(ev_breakpoint, counter, addr); - - m_listener->onEvent(ev); - } - } - - // From IFileParser::IFileListener - void onFile(const IFileParser::File &file) - { - if (!(file.m_flags & IFileParser::FLG_TYPE_COVERAGE_DATA)) - return; - - m_gcdaFiles.push_back(file.m_filename); - } - - FileList_t m_gcdaFiles; - IEventListener *m_listener; - pid_t m_child; -}; - -class GcovEngineCreator : public IEngineFactory::IEngineCreator -{ -public: - virtual ~GcovEngineCreator() - { - } - - virtual IEngine *create(IFileParser &parser) - { - return new GcovEngine(parser); - } - - unsigned int matchFile(const std::string &filename, uint8_t *data, size_t dataSize) - { - if (!IConfiguration::getInstance().keyAsInt("gcov")) - return match_none; - - return match_perfect; - } -}; - -static GcovEngineCreator g_gcovEngineCreator; diff --git a/src/gcov.cc b/src/gcov.cc deleted file mode 100644 index eeed7863..00000000 --- a/src/gcov.cc +++ /dev/null @@ -1,409 +0,0 @@ -#include -#include - -using namespace kcov; - -/* - * From gcov-io.h: - * - * int32: byte3 byte2 byte1 byte0 | byte0 byte1 byte2 byte3 - * int64: int32:low int32:high - * string: int32:0 | int32:length char* char:0 padding - * padding: | char:0 | char:0 char:0 | char:0 char:0 char:0 - * item: int32 | int64 | string - * - * File: - * file : int32:magic int32:version int32:stamp record* - * - * Record: - * record: header data - * header: int32:tag int32:length - * data: item* - * - * gcno file records: - * note: unit function-graph* - * unit: header int32:checksum string:source - * function-graph: announce_function basic_blocks {arcs | lines}* - * announce_function: header int32:ident - * int32:lineno_checksum int32:cfg_checksum - * string:name string:source int32:lineno - * basic_block: header int32:flags* - * arcs: header int32:block_no arc* - * arc: int32:dest_block int32:flags - * lines: header int32:block_no line* - * int32:0 string:NULL - * line: int32:line_no | int32:0 string:filename - * - * gcda file records: - * data: {unit summary:object summary:program* function-data*}* - * unit: header int32:checksum - * function-data: announce_function present counts - * announce_function: header int32:ident - * int32:lineno_checksum int32:cfg_checksum - * present: header int32:present - * counts: header int64:count* - * summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE - * count-summary: int32:num int32:runs int64:sum - * int64:max int64:sum_max histogram - * histogram: {int32:bitvector}8 histogram-buckets* - * histogram-buckets: int32:num int64:min int64:sum - */ -#define GCOV_DATA_MAGIC ((uint32_t)0x67636461) /* "gcda" */ -#define GCOV_NOTE_MAGIC ((uint32_t)0x67636e6f) /* "gcno" */ - -#define GCOV_TAG_FUNCTION ((uint32_t)0x01000000) -#define GCOV_TAG_FUNCTION_LENGTH (3) -#define GCOV_TAG_BLOCKS ((uint32_t)0x01410000) -#define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM) -#define GCOV_TAG_BLOCKS_NUM(LENGTH) (LENGTH) -#define GCOV_TAG_ARCS ((uint32_t)0x01430000) -#define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2) -#define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH) - 1) / 2) -#define GCOV_TAG_LINES ((uint32_t)0x01450000) -#define GCOV_TAG_COUNTER_BASE ((uint32_t)0x01a10000) -#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2) -#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) -#define GCOV_TAG_OBJECT_SUMMARY ((uint32_t)0xa1000000) /* Obsolete */ -#define GCOV_TAG_PROGRAM_SUMMARY ((uint32_t)0xa3000000) -#define GCOV_TAG_SUMMARY_LENGTH(NUM) \ - (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5) -#define GCOV_TAG_AFDO_FILE_NAMES ((uint32_t)0xaa000000) -#define GCOV_TAG_AFDO_FUNCTION ((uint32_t)0xac000000) -#define GCOV_TAG_AFDO_WORKING_SET ((uint32_t)0xaf000000) - -/* Arc flags */ -#define GCOV_ARC_ON_TREE (1 << 0) -#define GCOV_ARC_FAKE (1 << 1) -#define GCOV_ARC_FALLTHROUGH (1 << 2) - -struct file_header -{ - int32_t magic; - int32_t version; - int32_t stamp; -}; - -struct header -{ - int32_t tag; - int32_t length; -}; - -GcovParser::GcovParser(const uint8_t *data, size_t dataSize) : - m_data(data), m_dataSize(dataSize) -{ -} - -GcovParser::~GcovParser() -{ - free((void *) m_data); -} - -bool GcovParser::parse() -{ - // Not a gcov file? - if (!verify()) - return false; - - const uint8_t *cur = m_data + sizeof(struct file_header); - ssize_t left = (ssize_t) (m_dataSize - sizeof(struct file_header)); - - // Iterate through the headers - while (1) - { - const struct header *header = (struct header *) cur; - - if (!onRecord(header, cur + sizeof(*header))) - return false; - - size_t curLen = sizeof(struct header) + header->length * 4; - left -= curLen; - - if (left <= 0) - break; - - cur += curLen; - } - - return true; -} - -bool GcovParser::verify() -{ - const struct file_header *header = (const struct file_header *) m_data; - - if (header->magic != GCOV_DATA_MAGIC && header->magic != GCOV_NOTE_MAGIC) - return false; - - return true; -} - -const uint8_t *GcovParser::readString(const uint8_t *p, std::string &out) -{ - int32_t length = *(const int32_t*) p; - const char *c_str = (const char *) &p[4]; - - out = std::string(c_str); - - return padPointer(p + length * 4 + 4); // Including the length field -} - -// Convenience when using 32-bit pointers -const int32_t *GcovParser::readString(const int32_t *p, std::string &out) -{ - return (const int32_t *) readString((const uint8_t *) p, out); -} - -const uint8_t *GcovParser::padPointer(const uint8_t *p) -{ - unsigned long addr = (unsigned long) p; - - if ((addr & 3) != 0) - p += 4 - (addr & 3); - - return p; -} - -GcnoParser::BasicBlockMapping::BasicBlockMapping(const BasicBlockMapping &other) : - m_function(other.m_function), m_basicBlock(other.m_basicBlock), - m_file(other.m_file), m_line(other.m_line), m_index(other.m_index) -{ -} - -GcnoParser::BasicBlockMapping::BasicBlockMapping(int32_t function, - int32_t basicBlock, const std::string &file, int32_t line, - int32_t index) : - m_function(function), m_basicBlock(basicBlock), m_file(file), m_line(line), m_index(index) -{ -} - -GcnoParser::Arc::Arc(const Arc &other) : - m_function(other.m_function), m_srcBlock(other.m_srcBlock), m_dstBlock( - other.m_dstBlock) -{ -} - -GcnoParser::Arc::Arc(int32_t function, int32_t srcBlock, int32_t dstBlock) : - m_function(function), m_srcBlock(srcBlock), m_dstBlock(dstBlock) -{ -} - -GcnoParser::GcnoParser(const uint8_t *data, size_t dataSize) : - GcovParser(data, dataSize), m_functionId(-1) -{ -} - -const GcnoParser::BasicBlockList_t &GcnoParser::getBasicBlocks() -{ - return m_basicBlocks; -} - -const GcnoParser::FunctionList_t &GcnoParser::getFunctions() -{ - return m_functions; -} - -const GcnoParser::ArcList_t &GcnoParser::getArcs() -{ - return m_arcs; -} - -bool GcnoParser::onRecord(const struct header *header, const uint8_t *data) -{ - kcov_debug(ENGINE_MSG, "GCNO record: 0x%08x (%d bytes)\n", header->tag, - header->length * 4); - - switch (header->tag) - { - case GCOV_TAG_FUNCTION: - onAnnounceFunction(header, data); - break; - case GCOV_TAG_BLOCKS: - onBlocks(header, data); - break; - case GCOV_TAG_LINES: - onLines(header, data); - break; - case GCOV_TAG_ARCS: - onArcs(header, data); - break; - default: - break; - } - - return true; -} - -void GcnoParser::onAnnounceFunction(const struct header *header, - const uint8_t *data) -{ - const int32_t *p32 = (const int32_t *) data; - const uint8_t *p8 = data; - int32_t ident = p32[0]; - - p8 = readString(p8 + 3 * 4, m_function); - p8 = readString(p8, m_file); - m_functionId = ident; - - m_functions.push_back(m_functionId); - - kcov_debug(ENGINE_MSG, "GCNO function %d: %s\n", m_functionId, - m_file.c_str()); - // The first line of this function comes next, but let's ignore that -} - -void GcnoParser::onBlocks(const struct header *header, const uint8_t *data) -{ - // Not used by kcov -} - -void GcnoParser::onLines(const struct header *header, const uint8_t *data) -{ - const int32_t *p32 = (const int32_t *) data; - int32_t blockNo = p32[0]; - const int32_t *last = &p32[header->length]; - int32_t n = 0; // index - - p32++; // Skip blockNo - - // Iterate through lines - while (p32 < last) - { - int32_t line = *p32; - - // File name - if (line == 0) - { - std::string name; - - // Setup current file name - p32 = readString(p32 + 1, name); - if (name != "") - m_file = name; - - continue; - } - - p32++; - - kcov_debug(ENGINE_MSG, "GCNO basic block in function %d, nr %d %s:%d\n", - m_functionId, blockNo, m_file.c_str(), line); - m_basicBlocks.push_back( - BasicBlockMapping(m_functionId, blockNo, m_file, line, n)); - - n++; - } -} - -void GcnoParser::onArcs(const struct header *header, const uint8_t *data) -{ - const int32_t *p32 = (const int32_t *) data; - int32_t blockNo = p32[0]; - const int32_t *last = &p32[header->length]; - unsigned int arc = 0; - - p32++; // Skip blockNo - - // Iterate through lines - while (p32 < last) - { - int32_t destBlock = p32[0]; - int32_t flags = p32[1]; - - // Report non-on-tree arcs - if (!(flags & GCOV_ARC_ON_TREE)) - m_arcs.push_back(Arc(m_functionId, blockNo, destBlock)); - - kcov_debug(ENGINE_MSG, "GCNO arc in function %d, %d->%d (flags %d%s)\n", - m_functionId, blockNo, destBlock, flags, - flags & GCOV_ARC_ON_TREE ? " OT" : ""); - - p32 += 2; - arc++; - } -} - -GcdaParser::GcdaParser(const uint8_t *data, size_t dataSize) : - GcovParser(data, dataSize), m_functionId(-1) -{ -} - -size_t GcdaParser::countersForFunction(int32_t function) -{ - if (function < 0) - panic("Garbage in!"); - - if (m_functionToCounters.find(function) == m_functionToCounters.end()) - return 0; - - // Simply the size of the vector - return m_functionToCounters[function].size(); -} - -int64_t GcdaParser::getCounter(int32_t function, int32_t counter) -{ - if (function < 0 || counter < 0) - panic("Garbage in!"); - - if (m_functionToCounters.find(function) == m_functionToCounters.end()) - return -1; - - // List of counters - CounterList_t &cur = m_functionToCounters[function]; - if ((size_t) counter >= cur.size()) - return -1; - - return cur[counter]; -} - -bool GcdaParser::onRecord(const struct header *header, const uint8_t *data) -{ - kcov_debug(ENGINE_MSG, "GCDA record: 0x%08x (%d bytes)\n", header->tag, - header->length * 4); - - switch (header->tag) - { - case GCOV_TAG_FUNCTION: - onAnnounceFunction(header, data); - break; - case GCOV_TAG_COUNTER_BASE: - onCounterBase(header, data); - break; - default: - break; - } - - return true; -} - -void GcdaParser::onAnnounceFunction(const struct header *header, - const uint8_t *data) -{ - const int32_t *p32 = (const int32_t *) data; - int32_t ident = p32[0]; - - // FIXME! Handle checksums after this - m_functionId = ident; - - kcov_debug(ENGINE_MSG, "GCDA function %d\n", ident); -} - -void GcdaParser::onCounterBase(const struct header *header, const uint8_t *data) -{ - const int32_t *p32 = (const int32_t *) data; - int32_t count = header->length; // 32-bit data with 64-bit values - - // Store all counters in a list - CounterList_t counters; - for (int32_t i = 0; i < count; i += 2) - { - uint64_t v64 = (uint64_t) p32[i] | ((uint64_t) p32[i + 1] << 32ULL); - - counters.push_back(v64); - - kcov_debug(ENGINE_MSG, "GCDA counter %d %lld\n", i, - (long long) counters.back()); - } - - m_functionToCounters[m_functionId] = counters; -} diff --git a/src/include/elf.hh b/src/include/elf.hh index 0fdd4fdf..651d376b 100644 --- a/src/include/elf.hh +++ b/src/include/elf.hh @@ -107,10 +107,6 @@ namespace kcov { } - virtual const std::vector &getGcovGcnoFiles() = 0; - - virtual const std::vector &getGcovGcdaFiles() = 0; - virtual const std::string &getBuildId() = 0; /** diff --git a/src/include/gcov.hh b/src/include/gcov.hh deleted file mode 100644 index f7ac9d38..00000000 --- a/src/include/gcov.hh +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include -#include - -struct header; - -namespace kcov -{ - /** - * Combine basic block/file info into an "address" - * - * @param filename the source filename - * @param function the function to map for - * @param basicBlock the basic block number - * @param lineIndex the index number in the basic block map - */ - static inline uint64_t gcovGetAddress(const std::string &filename, int32_t function, - int32_t basicBlock, int32_t lineIndex) - { - uint32_t fn16 = ((uint32_t)function) & 0xffff; - uint32_t bb16 = ((uint32_t)basicBlock) & 0xffff; - uint64_t fnAndBb = (fn16 << 16) | bb16; - uint64_t fileNameHash = (((uint64_t)std::hash()(filename)) & 0xffffff00ULL); - uint64_t lineIndexMask = lineIndex & 0xffULL; - - return (fileNameHash << 32ULL) | (lineIndexMask << 32ULL) | fnAndBb; - } - - class GcovParser - { - public: - /** - * Parse the gcov file. - * - * @return true if the file was OK, false otherwise - */ - bool parse(); - - protected: - GcovParser(const uint8_t *data, size_t dataSize); - - virtual ~GcovParser(); - - virtual bool onRecord(const struct header *header, const uint8_t *data) = 0; - - bool verify(); - - const uint8_t *readString(const uint8_t *p, std::string &out); - - // Convenience when using 32-bit pointers - const int32_t *readString(const int32_t *p, std::string &out); - - - const uint8_t *padPointer(const uint8_t *p); - - private: - const uint8_t *m_data; - size_t m_dataSize; - }; - - // Specialized parser for gcno files - class GcnoParser : public GcovParser - { - public: - // Holder-class for fn/bb -> file/line - class BasicBlockMapping - { - public: - int32_t m_function; - int32_t m_basicBlock; - std::string m_file; - int32_t m_line; - int32_t m_index; - - BasicBlockMapping(const BasicBlockMapping &other); - - BasicBlockMapping(int32_t function, int32_t basicBlock, - const std::string &file, int32_t line, int32_t index); - }; - - // Holder-class for arcs between blocks - class Arc - { - public: - int32_t m_function; - int32_t m_srcBlock; - int32_t m_dstBlock; - - Arc(const Arc &other); - - Arc(int32_t function, int32_t srcBlock, int32_t dstBlock); - }; - - typedef std::vector BasicBlockList_t; - typedef std::vector FunctionList_t; - typedef std::vector ArcList_t; - - - - GcnoParser(const uint8_t *data, size_t dataSize); - - /* Return a reference to the basic blocks to file/line in the file. - * Empty if parse() hasn't been called. - * - * @return the basic blocks - */ - const BasicBlockList_t &getBasicBlocks(); - - /** - * Return a reference to the arcs in the file. Empty if parse() hasn't - * been called. - * - * @return the arcs - */ - const ArcList_t &getArcs(); - - /** - * Return a reference to the function IDs in the file. Empty if parse() - * hasn't been called. - * - * @return the functions - */ - const FunctionList_t &getFunctions(); - - protected: - bool onRecord(const struct header *header, const uint8_t *data); - - private: - // Handler for record types - void onAnnounceFunction(const struct header *header, const uint8_t *data); - void onBlocks(const struct header *header, const uint8_t *data); - void onLines(const struct header *header, const uint8_t *data); - void onArcs(const struct header *header, const uint8_t *data); - - std::string m_file; - std::string m_function; - int32_t m_functionId; - FunctionList_t m_functions; - BasicBlockList_t m_basicBlocks; - ArcList_t m_arcs; - }; - - class GcdaParser : public GcovParser - { - public: - GcdaParser(const uint8_t *data, size_t dataSize); - - /** - * Return the number of counters for a particular function. - * - * @param function the function - * - * @return the number of counters, or 0 for invalid functions - */ - size_t countersForFunction(int32_t function); - - /** - * Return the counter value for a function. - * - * @param function the function - * @param counter the counter number - * - * @return the counter value, or -1 for invalid function/counters - */ - int64_t getCounter(int32_t function, int32_t counter); - - protected: - bool onRecord(const struct header *header, const uint8_t *data); - - void onAnnounceFunction(const struct header *header, const uint8_t *data); - - void onCounterBase(const struct header *header, const uint8_t *data); - - typedef std::vector CounterList_t; - typedef std::unordered_map FunctionToCountersMap_t; - - int32_t m_functionId; - FunctionToCountersMap_t m_functionToCounters; - }; -} diff --git a/src/parsers/elf-parser.cc b/src/parsers/elf-parser.cc index 283d5570..9722c596 100644 --- a/src/parsers/elf-parser.cc +++ b/src/parsers/elf-parser.cc @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -221,11 +220,7 @@ class ElfInstance : public IFileParser, IFileParser::ILineListener setupSections(); - // Gcov data? - if (IConfiguration::getInstance().keyAsInt("gcov") && !m_gcnoFiles.empty()) - parseGcnoFiles(relocation); - else - parseOneDwarf(relocation); + parseOneDwarf(relocation); return true; } @@ -253,50 +248,6 @@ class ElfInstance : public IFileParser, IFileParser::ILineListener return true; } - void parseGcnoFiles(unsigned long relocation) - { - for (FileList_t::const_iterator it = m_gcnoFiles.begin(); it != m_gcnoFiles.end(); ++it) - { - const std::string &cur = *it; - - parseOneGcno(cur, relocation); - } - } - - void parseOneGcno(const std::string &filename, unsigned long relocation) - { - size_t sz; - void *data; - - // The data is freed by the parser - data = read_file(&sz, "%s", filename.c_str()); - if (!data) - return; - - // Parse this gcno file! - GcnoParser parser((uint8_t *) data, sz); - - // Parsing error? - if (!parser.parse()) - { - warning("Can't parse %s\n", filename.c_str()); - - return; - } - - const GcnoParser::BasicBlockList_t &bbs = parser.getBasicBlocks(); - - for (GcnoParser::BasicBlockList_t::const_iterator it = bbs.begin(); it != bbs.end(); ++it) - { - const GcnoParser::BasicBlockMapping &cur = *it; - - // Report a generated address - for (LineListenerList_t::const_iterator it = m_lineListeners.begin(); it != m_lineListeners.end(); ++it) - (*it)->onLine(cur.m_file, cur.m_line, - gcovGetAddress(cur.m_file, cur.m_function, cur.m_basicBlock, cur.m_index) + relocation); - } - } - void setupSections() { for (SegmentList_t::const_iterator it = m_executableSegments.begin(); it != m_executableSegments.end(); ++it) @@ -378,15 +329,6 @@ class ElfInstance : public IFileParser, IFileParser::ILineListener for (SegmentList_t::iterator it = segments.begin(); it != segments.end(); ++it) m_executableSegments.push_back(*it); - // Gcov data - std::vector gcdaFiles = m_elf->getGcovGcdaFiles(); - for (std::vector::iterator it = gcdaFiles.begin(); it != gcdaFiles.end(); ++it) - { - for (FileListenerList_t::const_iterator lit = m_fileListeners.begin(); lit != m_fileListeners.end(); ++lit) - (*lit)->onFile(File(*it, IFileParser::FLG_TYPE_COVERAGE_DATA)); - } - - m_gcnoFiles = m_elf->getGcovGcnoFiles(); m_buildId = m_elf->getBuildId(); const std::pair *dbgLink = m_elf->getDebugLink(); @@ -556,7 +498,6 @@ class ElfInstance : public IFileParser, IFileParser::ILineListener SegmentList_t m_curSegments; SegmentList_t m_executableSegments; - FileList_t m_gcnoFiles; IDisassembler &m_addressVerifier; bool m_verifyAddresses; diff --git a/src/parsers/elf.cc b/src/parsers/elf.cc index 4faa3d8c..6a9ad3a4 100644 --- a/src/parsers/elf.cc +++ b/src/parsers/elf.cc @@ -19,16 +19,6 @@ class ElfImpl : public IElf { } - virtual const std::vector &getGcovGcdaFiles() - { - return m_gcdaFiles; - } - - virtual const std::vector &getGcovGcnoFiles() - { - return m_gcnoFiles; - } - virtual const std::string &getBuildId() { return m_buildId; @@ -64,7 +54,6 @@ class ElfImpl : public IElf Elf_Scn *scn = NULL; size_t shstrndx; bool ret = false; - bool doScanForGcda = IConfiguration::getInstance().keyAsInt("gcov"); unsigned int i; char *raw; size_t sz; @@ -139,34 +128,6 @@ class ElfImpl : public IElf goto out_elf_begin; } - // Parse rodata to find gcda files - if (doScanForGcda && strcmp(name, ".rodata") == 0) - { - const char *dataPtr = (const char *) data->d_buf; - - for (size_t i = 0; i < data->d_size - 5; i++) - { - const char *p = &dataPtr[i]; - - if (memcmp(p, (const void *) "gcda\0", 5) != 0) - continue; - - const char *gcda = p; - - // Rewind to start of string - while (gcda != dataPtr && *gcda != '\0') - gcda--; - - // Rewound until start of rodata? - if (gcda == dataPtr) - continue; - - std::string file(gcda + 1); - - m_gcdaFiles.push_back(file); - } - } - if (sh_type == SHT_NOTE) { if (elfIs32Bit) @@ -228,20 +189,6 @@ class ElfImpl : public IElf m_segments.push_back(seg); } - // If we have gcda files, try to find the corresponding gcno dittos - for (FileList_t::iterator it = m_gcdaFiles.begin(); it != m_gcdaFiles.end(); ++it) - { - std::string &gcno = *it; // Modify in-place - size_t sz = gcno.size(); - - // .gcda -> .gcno - gcno[sz - 2] = 'n'; - gcno[sz - 1] = 'o'; - - if (file_exists(gcno)) - m_gcnoFiles.push_back(gcno); - } - ret = true; out_elf_begin: @@ -250,8 +197,6 @@ class ElfImpl : public IElf return ret; } - std::vector m_gcnoFiles; - std::vector m_gcdaFiles; std::string m_buildId; bool m_debugLinkValid; std::pair m_debugLink; diff --git a/src/solib-handler.cc b/src/solib-handler.cc index ecdd47ce..5114e8eb 100644 --- a/src/solib-handler.cc +++ b/src/solib-handler.cc @@ -36,8 +36,7 @@ class SolibHandler : public ISolibHandler, ICollector::IEventTickListener memset(&m_solibThread, 0, sizeof(m_solibThread)); // Only useful for ELF binaries - if (parser.getParserType() == "ELF" && !IConfiguration::getInstance().keyAsInt("gcov") - && !IConfiguration::getInstance().keyAsInt("clang-sanitizer")) + if (parser.getParserType() == "ELF") collector.registerEventTickListener(*this); } diff --git a/tests/unit-tests/CMakeLists.txt b/tests/unit-tests/CMakeLists.txt index 08278806..fa105747 100644 --- a/tests/unit-tests/CMakeLists.txt +++ b/tests/unit-tests/CMakeLists.txt @@ -41,7 +41,6 @@ set (${TGT}_SRCS ../../src/collector.cc ../../src/engine-factory.cc ../../src/engines/system-mode-file-format.cc - ../../src/gcov.cc ../../src/output-handler.cc ../../src/parser-manager.cc ../../src/source-file-cache.cc diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index b3801417..93f79dd7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -40,7 +40,6 @@ set (${LINE2ADDR}_SRCS ../src/capabilities.cc ../src/configuration.cc ../src/filter.cc - ../src/gcov.cc ../src/parsers/dwarf.cc ../src/parsers/elf-parser.cc ../src/parsers/elf.cc