Index: include/sanitizer/coverage_interface.h =================================================================== --- include/sanitizer/coverage_interface.h +++ include/sanitizer/coverage_interface.h @@ -41,6 +41,13 @@ // Some of the entries in *data will be zero. uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); + // Set *data to the growing buffer with covered PCs and return the size + // of the buffer. The entries are never zero. + // When only unique pcs are collected, the size is equal to + // __sanitizer_get_total_unique_coverage. + // WARNING: EXPERIMENTAL API. + uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data); + // The coverage instrumentation may optionally provide imprecise counters. // Rather than exposing the counter values to the user we instead map // the counters to a bitset. Index: lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -108,6 +108,7 @@ uptr *data(); uptr size(); + uptr *buffer() const { return pc_buffer; } private: void DirectOpen(); @@ -133,6 +134,8 @@ // Descriptor of the file mapped pc array. fd_t pc_fd; + uptr *pc_buffer; + // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor guard_array_vec; @@ -209,6 +212,12 @@ atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); } + if (common_flags()->coverage_pc_buffer) + pc_buffer = reinterpret_cast(MmapNoReserveOrDie( + sizeof(uptr) * kPcArrayMaxSize, "CovInit::pc_buffer")); + else + pc_buffer = nullptr; + cc_array = reinterpret_cast(MmapNoReserveOrDie( sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array")); atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); @@ -246,6 +255,10 @@ UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); cc_array = nullptr; } + if (pc_buffer) { + UnmapOrDie(pc_buffer, sizeof(uptr) * kPcArrayMaxSize); + pc_buffer = nullptr; + } if (tr_event_array) { UnmapOrDie(tr_event_array, sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + @@ -414,6 +427,7 @@ atomic_load(&pc_array_size, memory_order_acquire)); uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); pc_array[idx] = BundlePcAndCounter(pc, counter); + if (pc_buffer) pc_buffer[counter] = pc; } // Registers a pair caller=>callee. @@ -944,6 +958,12 @@ } SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_coverage_pc_buffer(uptr **data) { + *data = coverage_data.buffer(); + return __sanitizer_get_total_unique_coverage(); +} + +SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_number_of_counters() { return coverage_data.GetNumberOf8bitCounters(); } Index: lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- lib/sanitizer_common/sanitizer_flags.inc +++ lib/sanitizer_common/sanitizer_flags.inc @@ -144,6 +144,9 @@ COMMON_FLAG(const char *, coverage_dir, ".", "Target directory for coverage dumps. Defaults to the current " "directory.") +COMMON_FLAG(bool, coverage_pc_buffer, true, + "If set (and if 'coverage' is set too), the pcs would be collected " + "in a buffer.") COMMON_FLAG(bool, full_address_space, false, "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized") Index: test/asan/TestCases/coverage-pc-buffer.cc =================================================================== --- /dev/null +++ test/asan/TestCases/coverage-pc-buffer.cc @@ -0,0 +1,48 @@ +// Test __sanitizer_coverage_pc_buffer(). + +// RUN: %clangxx_asan -fsanitize-coverage=edge %s -o %t && %run %t + +// UNSUPPORTED: android + +#include +#include +#include + +static volatile int sink; +__attribute__((noinline)) void bar() { sink = 2; } +__attribute__((noinline)) void foo() { sink = 1; } + +void assertNotZeroPcs(uintptr_t *buf, uintptr_t size) { + assert(buf); + for (uintptr_t i = 0; i < size; ++i) + assert(buf[i]); +} + +int main() { + uintptr_t *buf = NULL; + uintptr_t sz = __sanitizer_get_coverage_pc_buffer(&buf); + assertNotZeroPcs(buf, sz); + assert(sz); + + foo(); + bar(); + uintptr_t *buf1 = NULL; + uintptr_t sz1 = __sanitizer_get_coverage_pc_buffer(&buf1); + assertNotZeroPcs(buf1, sz1); + assert(buf1 == buf); + assert(sz1 > sz); + + bar(); + uintptr_t *buf2 = NULL; + uintptr_t sz2 = __sanitizer_get_coverage_pc_buffer(&buf2); + assertNotZeroPcs(buf2, sz2); + assert(buf2 == buf); + assert(sz2 > sz1); + + __sanitizer_reset_coverage(); + uintptr_t *buf3 = NULL; + uintptr_t sz3 = __sanitizer_get_coverage_pc_buffer(&buf3); + assertNotZeroPcs(buf3, sz3); + assert(buf3 == buf); + assert(sz3 < sz2); +}