Index: include/sanitizer/coverage_interface.h =================================================================== --- include/sanitizer/coverage_interface.h +++ include/sanitizer/coverage_interface.h @@ -23,6 +23,11 @@ void __sanitizer_cov_init(); // Record and dump coverage info. void __sanitizer_cov_dump(); + + // Dump collected coverage info. Sorts pcs by module into individual + // .sancov files. + void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len); + // Open .sancov.packed in the coverage directory and return the file // descriptor. Returns -1 on failure, or if coverage dumping is disabled. // This is intended for use by sandboxing code. Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -53,6 +53,7 @@ set(SANITIZER_LIBCDEP_SOURCES sanitizer_common_libcdep.cc sanitizer_coverage_libcdep.cc + sanitizer_coverage_libcdep_new.cc sanitizer_coverage_mapping_libcdep.cc sanitizer_linux_libcdep.cc sanitizer_posix_libcdep.cc Index: lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -954,6 +954,7 @@ } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { coverage_data.DumpAll(); + __sanitizer_dump_trace_pc_guard_coverage(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, @@ -1036,10 +1037,6 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_cov_trace_gep() {} SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_cov_trace_pc_guard() {} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_cov_trace_pc_indir() {} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_cov_trace_pc_guard_init() {} #endif // !SANITIZER_WINDOWS } // extern "C" Index: lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc @@ -0,0 +1,189 @@ +//===-- sanitizer_coverage_libcdep_new.cc ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Sanitizer Coverage. + +#include "sanitizer_addrhashmap.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" + +using namespace __sanitizer; + +using AddressRange = LoadedModule::AddressRange; + +namespace { + +static const u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL; +static const u64 Magic32 = 0xC0BFFFFFFFFFFF32ULL; +static const u64 Magic = SANITIZER_WORDSIZE == 64 ? Magic64 : Magic32; + +struct ModuleAndRange { + const LoadedModule* module; + const AddressRange* range; +}; + +struct CovFile { + CovFile() : fd(-1), num_offsets(0), file_path(0) {} + + fd_t fd; + uptr num_offsets; + char* file_path; +}; + +static fd_t OpenFile(const char* path) { + error_t err; + fd_t fd = OpenFile(path, WrOnly, &err); + if (fd == kInvalidFd) + Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", + path, err); + return fd; +} + +static void GetCoverageFilename(char* path, const char* name, + const char* extension) { + CHECK(name); + internal_snprintf(path, kMaxPathLength, "%s/%s.%zd.%s", + common_flags()->coverage_dir, name, internal_getpid(), + extension); +} + +static void SanitizerDumpCoverage(const uptr* pcs, uptr len) { + auto symbolizer = Symbolizer::GetOrInit(); + if (!symbolizer) return; + + using CovFilesMap = AddrHashMap; + CovFilesMap cov_files; + + ListOfModules modules; + modules.init(); + InternalMmapVector ranges(modules.size()); + for (const LoadedModule& module : modules) { + for (const AddressRange& r : module.ranges()) { + ranges.push_back(ModuleAndRange {&module, &r}); + } + } + InternalSort(&ranges, ranges.size(), + [](const ModuleAndRange& m1, const ModuleAndRange& m2) { + return m1.range->end < m2.range->end; + }); + + for (uptr i = 0; i < len; ++i) { + const uptr pc = pcs[i]; + if (!pc) continue; + + uptr idx = InternalLowerBound( + ranges, 0, ranges.size(), pc, + [](const ModuleAndRange& m, uptr pc) { return pc >= m.range->end; }); + if (idx >= ranges.size()) { + Printf("ERROR: bad pc %x\n", pc); + continue; + } + + const ModuleAndRange& module_and_range = ranges[idx]; + const LoadedModule* module = module_and_range.module; + auto range = module_and_range.range; + + if (range->beg > pc || range->end <= pc) { + Printf("ERROR: bad pc %x\n", pc); + continue; + } + + CovFilesMap::Handle h(&cov_files, (uptr)module); + if (h.created()) { + h->file_path = static_cast(InternalAlloc(kMaxPathLength)); + GetCoverageFilename(h->file_path, StripModuleName(module->full_name()), + "sancov"); + h->fd = OpenFile(h->file_path); + WriteToFile(h->fd, &Magic, sizeof(Magic)); + } + + const uptr offset = pc - module->base_address(); + h->num_offsets++; + WriteToFile(h->fd, &offset, sizeof(offset)); + } + + for (const LoadedModule& module : modules) { + CovFilesMap::Handle h(&cov_files, (uptr)&module); + if (h.created()) { + continue; + } + CloseFile(h->fd); + Printf("SanitizerCoverage: %s %zd PCs written\n", h->file_path, + h->num_offsets); + InternalFree(h->file_path); + } +} + +class TracePcGuardController { + public: + void InitTracePcGuard(u32* start, u32* end) { + CHECK(!enabled); + CHECK(!pc_array); + CHECK(start != end); + CHECK(!*start); + + enabled = true; + pc_array = reinterpret_cast( + MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); + atomic_store(&pc_array_index, 0, memory_order_relaxed); + + for (u32* p = start; p < end; p++) *p = 1; + } + + void TracePcGuard(u32* guard, uptr pc) { + *guard = 0; + uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + pc_array[idx] = pc; + } + + void Dump() { + if (!enabled) return; + uptr size = atomic_load(&pc_array_index, memory_order_relaxed); + __sanitizer_dump_coverage(pc_array, size); + } + + private: + // Maximal size pc array may ever grow. + // We MmapNoReserve this space to ensure that the array is contiguous. + static const uptr kPcArrayMaxSize = + FIRST_32_SECOND_64(1 << (SANITIZER_ANDROID ? 24 : 26), 1 << 27); + + bool enabled; + uptr* pc_array; + + // Index of the first available pc_array slot. + atomic_uintptr_t pc_array_index; +}; + +static TracePcGuardController pc_guard_controller; + +}; // namespace + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT + const uptr* pcs, uptr len) { + return SanitizerDumpCoverage(pcs, len); +} + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void +__sanitizer_cov_trace_pc_guard(u32* guard) { + if (!*guard) return; + pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC()); +} + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void +__sanitizer_cov_trace_pc_guard_init(u32* start, u32* end) { + if (start == end || *start) return; + pc_guard_controller.InitTracePcGuard(start, end); +} + +SANITIZER_INTERFACE_ATTRIBUTE void +__sanitizer_dump_trace_pc_guard_coverage() { + pc_guard_controller.Dump(); +} +} // extern "C" Index: lib/sanitizer_common/sanitizer_interface_internal.h =================================================================== --- lib/sanitizer_common/sanitizer_interface_internal.h +++ lib/sanitizer_common/sanitizer_interface_internal.h @@ -46,8 +46,12 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( + const __sanitizer::uptr *pcs, const __sanitizer::uptr len); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_annotate_contiguous_container(const void *beg, Index: test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc @@ -0,0 +1,31 @@ +// Tests trace pc guard coverage collection. +// +// REQUIRES: has_sancovcc +// XFAIL: tsan +// +// RUN: rm -rf *.sancov +// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// +// TODO(aizatsky): sancov tool can't process trace-pc-guard binaries. +// RUN: not %sancovcc -covered-functions *.sancov %t 2>&1 | FileCheck --check-prefix=CHECK-SANCOV %s + +#include + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + foo(); +} + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: foo +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov 2 PCs written +// +// CHECK-SANCOV: Error: Coverage points in binary and .sancov file do not match.