summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorMike Aizatsky <aizatsky@chromium.org>2015-11-11 20:58:20 +0000
committerMike Aizatsky <aizatsky@chromium.org>2015-11-11 20:58:20 +0000
commit07261cbac29c07442ff864c982cb4de91db6e123 (patch)
tree375b7ebb6ea451f015671dfc1ed115241ddf787b /tools
parent50085b19f4e9702e8987f68a19bc586653e51e31 (diff)
sancov tool in c++
First batch of sancov.py rewrite in c++. Supports "-print" and "-coveredfns" commands. Followup to http://reviews.llvm.org/D14356 in a better location. Differential Revision: http://reviews.llvm.org/D14579 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252801 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r--tools/sancov/CMakeLists.txt11
-rw-r--r--tools/sancov/Makefile17
-rw-r--r--tools/sancov/sancov.cc264
3 files changed, 292 insertions, 0 deletions
diff --git a/tools/sancov/CMakeLists.txt b/tools/sancov/CMakeLists.txt
new file mode 100644
index 00000000000..f891f779e8c
--- /dev/null
+++ b/tools/sancov/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+ DebugInfoDWARF
+ DebugInfoPDB
+ Object
+ Support
+ Symbolize
+ )
+
+add_llvm_tool(sancov
+ sancov.cc
+ )
diff --git a/tools/sancov/Makefile b/tools/sancov/Makefile
new file mode 100644
index 00000000000..1114fe052d4
--- /dev/null
+++ b/tools/sancov/Makefile
@@ -0,0 +1,17 @@
+##===- tools/sancov/Makefile ----------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL := ../..
+TOOLNAME := sancov
+LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object Support Symbolize
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS := 1
+
+include $(LEVEL)/Makefile.common
diff --git a/tools/sancov/sancov.cc b/tools/sancov/sancov.cc
new file mode 100644
index 00000000000..a611153e468
--- /dev/null
+++ b/tools/sancov/sancov.cc
@@ -0,0 +1,264 @@
+//===-- sancov.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a command-line tool for reading and analyzing sanitizer
+// coverage.
+//===----------------------------------------------------------------------===//
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <set>
+#include <stdio.h>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+// --------- COMMAND LINE FLAGS ---------
+
+enum ActionType { PrintAction, CoveredFunctionsAction };
+
+cl::opt<ActionType> Action(
+ cl::desc("Action (required)"), cl::Required,
+ cl::values(clEnumValN(PrintAction, "print", "Print coverage addresses"),
+ clEnumValN(CoveredFunctionsAction, "covered_functions",
+ "Print all covered funcions."),
+ clEnumValEnd));
+
+static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("<filenames...>"));
+
+static cl::opt<std::string>
+ ClBinaryName("obj", cl::Required,
+ cl::desc("Path to object file to be symbolized"));
+
+static cl::opt<bool>
+ ClDemangle("demangle", cl::init(true),
+ cl::desc("Print demangled function name."));
+
+// --------- FORMAT SPECIFICATION ---------
+
+struct FileHeader {
+ uint32_t Bitness;
+ uint32_t Magic;
+};
+
+static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
+static const uint32_t Bitness32 = 0xFFFFFF32;
+static const uint32_t Bitness64 = 0xFFFFFF64;
+
+// ---------
+
+template <typename T> static void FailIfError(const ErrorOr<T> &E) {
+ if (E)
+ return;
+
+ auto Error = E.getError();
+ errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n";
+ exit(-2);
+}
+
+template <typename T>
+static void readInts(const char *Start, const char *End,
+ std::vector<uint64_t> *V) {
+ const T *S = reinterpret_cast<const T *>(Start);
+ const T *E = reinterpret_cast<const T *>(End);
+ V->reserve(E - S);
+ std::copy(S, E, std::back_inserter(*V));
+}
+
+static std::string CommonPrefix(std::string A, std::string B) {
+ if (A.size() > B.size())
+ return std::string(B.begin(),
+ std::mismatch(B.begin(), B.end(), A.begin()).first);
+ else
+ return std::string(A.begin(),
+ std::mismatch(A.begin(), A.end(), B.begin()).first);
+}
+
+class CoverageData {
+ public:
+ // Read single file coverage data.
+ static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return BufOrErr.getError();
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ errs() << "File too small (<8): " << Buf->getBufferSize();
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+
+ if (Header->Magic != BinCoverageMagic) {
+ errs() << "Wrong magic: " << Header->Magic;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ auto Addrs = make_unique<std::vector<uint64_t>>();
+
+ switch (Header->Bitness) {
+ case Bitness64:
+ readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ case Bitness32:
+ readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ default:
+ errs() << "Unsupported bitness: " << Header->Bitness;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
+ }
+
+ // Merge multiple coverage data together.
+ static std::unique_ptr<CoverageData>
+ merge(const std::vector<std::unique_ptr<CoverageData>> &Covs) {
+ std::set<uint64_t> Addrs;
+
+ for (const auto &Cov : Covs)
+ Addrs.insert(Cov->Addrs->begin(), Cov->Addrs->end());
+
+ auto AddrsVector = make_unique<std::vector<uint64_t>>(
+ Addrs.begin(), Addrs.end());
+ return std::unique_ptr<CoverageData>(
+ new CoverageData(std::move(AddrsVector)));
+ }
+
+ // Read list of files and merges their coverage info.
+ static ErrorOr<std::unique_ptr<CoverageData>>
+ readAndMerge(const std::vector<std::string> &FileNames) {
+ std::vector<std::unique_ptr<CoverageData>> Covs;
+ for (const auto &FileName : FileNames) {
+ auto Cov = read(FileName);
+ if (!Cov)
+ return Cov.getError();
+ Covs.push_back(std::move(Cov.get()));
+ }
+ return merge(Covs);
+ }
+
+ // Print coverage addresses.
+ void printAddrs(raw_ostream &out) {
+ for (auto Addr : *Addrs) {
+ out << "0x";
+ out.write_hex(Addr);
+ out << "\n";
+ }
+ }
+
+ // Print list of covered functions.
+ // Line format: <file_name>:<line> <function_name>
+ void printCoveredFunctions(raw_ostream &out) {
+ if (Addrs->empty())
+ return;
+ symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+ SymbolizerOptions.Demangle = ClDemangle;
+ symbolize::LLVMSymbolizer Symbolizer;
+
+ struct FileLoc {
+ std::string FileName;
+ uint32_t Line;
+ bool operator<(const FileLoc &Rhs) const {
+ return std::tie(FileName, Line) < std::tie(Rhs.FileName, Rhs.Line);
+ }
+ };
+
+ // FileLoc -> FunctionName
+ std::map<FileLoc, std::string> Fns;
+
+ // Fill in Fns map.
+ for (auto Addr : *Addrs) {
+ auto InliningInfo = Symbolizer.symbolizeInlinedCode(ClBinaryName, Addr);
+ FailIfError(InliningInfo);
+ for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
+ auto FrameInfo = InliningInfo->getFrame(i);
+ SmallString<256> FileName(FrameInfo.FileName);
+ sys::path::remove_dots(FileName, /* remove_dot_dot */ true);
+ FileLoc Loc = { FileName.str(), FrameInfo.Line };
+ Fns[Loc] = FrameInfo.FunctionName;
+ }
+ }
+
+ // Compute file names common prefix.
+ std::string FilePrefix = Fns.begin()->first.FileName;
+ for (const auto &P : Fns)
+ FilePrefix = CommonPrefix(FilePrefix, P.first.FileName);
+
+ // Print first function occurence in a file.
+ {
+ std::string LastFileName;
+ std::set<std::string> ProcessedFunctions;
+
+ for (const auto &P : Fns) {
+ std::string FileName = P.first.FileName;
+ std::string FunctionName = P.second;
+ uint32_t Line = P.first.Line;
+
+ if (LastFileName != FileName)
+ ProcessedFunctions.clear();
+ LastFileName = FileName;
+
+ if (!ProcessedFunctions.insert(FunctionName).second)
+ continue;
+
+ out << FileName.substr(FilePrefix.size()) << ":" << Line << " "
+ << FunctionName << "\n";
+ }
+ }
+ }
+
+ private:
+ explicit CoverageData(std::unique_ptr<std::vector<uint64_t>> Addrs)
+ : Addrs(std::move(Addrs)) {}
+
+ std::unique_ptr<std::vector<uint64_t>> Addrs;
+};
+} // namespace
+
+int main(int argc, char **argv) {
+ // Print stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
+
+ auto CovData = CoverageData::readAndMerge(ClInputFiles);
+ FailIfError(CovData);
+
+ switch (Action) {
+ case PrintAction: {
+ CovData.get()->printAddrs(outs());
+ return 0;
+ }
+ case CoveredFunctionsAction: {
+ CovData.get()->printCoveredFunctions(outs());
+ return 0;
+ }
+ }
+}