13cab2bb3Spatrick //===-- sanitizer_coverage_fuchsia.cpp ------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // Sanitizer Coverage Controller for Trace PC Guard, Fuchsia-specific version.
103cab2bb3Spatrick //
113cab2bb3Spatrick // This Fuchsia-specific implementation uses the same basic scheme and the
123cab2bb3Spatrick // same simple '.sancov' file format as the generic implementation. The
133cab2bb3Spatrick // difference is that we just produce a single blob of output for the whole
143cab2bb3Spatrick // program, not a separate one per DSO. We do not sort the PC table and do
153cab2bb3Spatrick // not prune the zeros, so the resulting file is always as large as it
163cab2bb3Spatrick // would be to report 100% coverage. Implicit tracing information about
173cab2bb3Spatrick // the address ranges of DSOs allows offline tools to split the one big
183cab2bb3Spatrick // blob into separate files that the 'sancov' tool can understand.
193cab2bb3Spatrick //
203cab2bb3Spatrick // Unlike the traditional implementation that uses an atexit hook to write
213cab2bb3Spatrick // out data files at the end, the results on Fuchsia do not go into a file
223cab2bb3Spatrick // per se. The 'coverage_dir' option is ignored. Instead, they are stored
233cab2bb3Spatrick // directly into a shared memory object (a Zircon VMO). At exit, that VMO
243cab2bb3Spatrick // is handed over to a system service that's responsible for getting the
253cab2bb3Spatrick // data out to somewhere that it can be fed into the sancov tool (where and
263cab2bb3Spatrick // how is not our problem).
273cab2bb3Spatrick
283cab2bb3Spatrick #include "sanitizer_platform.h"
293cab2bb3Spatrick #if SANITIZER_FUCHSIA
301f9cb04fSpatrick #include <zircon/process.h>
311f9cb04fSpatrick #include <zircon/sanitizer.h>
321f9cb04fSpatrick #include <zircon/syscalls.h>
331f9cb04fSpatrick
343cab2bb3Spatrick #include "sanitizer_atomic.h"
353cab2bb3Spatrick #include "sanitizer_common.h"
36*810390e3Srobert #include "sanitizer_interface_internal.h"
373cab2bb3Spatrick #include "sanitizer_internal_defs.h"
383cab2bb3Spatrick #include "sanitizer_symbolizer_fuchsia.h"
393cab2bb3Spatrick
403cab2bb3Spatrick using namespace __sanitizer;
413cab2bb3Spatrick
423cab2bb3Spatrick namespace __sancov {
433cab2bb3Spatrick namespace {
443cab2bb3Spatrick
453cab2bb3Spatrick // TODO(mcgrathr): Move the constant into a header shared with other impls.
463cab2bb3Spatrick constexpr u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
473cab2bb3Spatrick static_assert(SANITIZER_WORDSIZE == 64, "Fuchsia is always LP64");
483cab2bb3Spatrick
493cab2bb3Spatrick constexpr const char kSancovSinkName[] = "sancov";
503cab2bb3Spatrick
513cab2bb3Spatrick // Collects trace-pc guard coverage.
523cab2bb3Spatrick // This class relies on zero-initialization.
533cab2bb3Spatrick class TracePcGuardController final {
543cab2bb3Spatrick public:
TracePcGuardController()55*810390e3Srobert constexpr TracePcGuardController() {}
56*810390e3Srobert
573cab2bb3Spatrick // For each PC location being tracked, there is a u32 reserved in global
583cab2bb3Spatrick // data called the "guard". At startup, we assign each guard slot a
593cab2bb3Spatrick // unique index into the big results array. Later during runtime, the
603cab2bb3Spatrick // first call to TracePcGuard (below) will store the corresponding PC at
613cab2bb3Spatrick // that index in the array. (Each later call with the same guard slot is
623cab2bb3Spatrick // presumed to be from the same PC.) Then it clears the guard slot back
633cab2bb3Spatrick // to zero, which tells the compiler not to bother calling in again. At
643cab2bb3Spatrick // the end of the run, we have a big array where each element is either
653cab2bb3Spatrick // zero or is a tracked PC location that was hit in the trace.
663cab2bb3Spatrick
673cab2bb3Spatrick // This is called from global constructors. Each translation unit has a
683cab2bb3Spatrick // contiguous array of guard slots, and a constructor that calls here
693cab2bb3Spatrick // with the bounds of its array. Those constructors are allowed to call
703cab2bb3Spatrick // here more than once for the same array. Usually all of these
713cab2bb3Spatrick // constructors run in the initial thread, but it's possible that a
723cab2bb3Spatrick // dlopen call on a secondary thread will run constructors that get here.
InitTracePcGuard(u32 * start,u32 * end)733cab2bb3Spatrick void InitTracePcGuard(u32 *start, u32 *end) {
743cab2bb3Spatrick if (end > start && *start == 0 && common_flags()->coverage) {
753cab2bb3Spatrick // Complete the setup before filling in any guards with indices.
763cab2bb3Spatrick // This avoids the possibility of code called from Setup reentering
773cab2bb3Spatrick // TracePcGuard.
783cab2bb3Spatrick u32 idx = Setup(end - start);
793cab2bb3Spatrick for (u32 *p = start; p < end; ++p) {
803cab2bb3Spatrick *p = idx++;
813cab2bb3Spatrick }
823cab2bb3Spatrick }
833cab2bb3Spatrick }
843cab2bb3Spatrick
TracePcGuard(u32 * guard,uptr pc)853cab2bb3Spatrick void TracePcGuard(u32 *guard, uptr pc) {
863cab2bb3Spatrick atomic_uint32_t *guard_ptr = reinterpret_cast<atomic_uint32_t *>(guard);
873cab2bb3Spatrick u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed);
881f9cb04fSpatrick if (idx > 0)
891f9cb04fSpatrick array_[idx] = pc;
903cab2bb3Spatrick }
913cab2bb3Spatrick
Dump()923cab2bb3Spatrick void Dump() {
93*810390e3Srobert Lock locked(&setup_lock_);
943cab2bb3Spatrick if (array_) {
953cab2bb3Spatrick CHECK_NE(vmo_, ZX_HANDLE_INVALID);
963cab2bb3Spatrick
973cab2bb3Spatrick // Publish the VMO to the system, where it can be collected and
983cab2bb3Spatrick // analyzed after this process exits. This always consumes the VMO
993cab2bb3Spatrick // handle. Any failure is just logged and not indicated to us.
1003cab2bb3Spatrick __sanitizer_publish_data(kSancovSinkName, vmo_);
1013cab2bb3Spatrick vmo_ = ZX_HANDLE_INVALID;
1023cab2bb3Spatrick
1033cab2bb3Spatrick // This will route to __sanitizer_log_write, which will ensure that
1043cab2bb3Spatrick // information about shared libraries is written out. This message
1053cab2bb3Spatrick // uses the `dumpfile` symbolizer markup element to highlight the
1063cab2bb3Spatrick // dump. See the explanation for this in:
1073cab2bb3Spatrick // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
1083cab2bb3Spatrick Printf("SanitizerCoverage: " FORMAT_DUMPFILE " with up to %u PCs\n",
1093cab2bb3Spatrick kSancovSinkName, vmo_name_, next_index_ - 1);
1103cab2bb3Spatrick }
1113cab2bb3Spatrick }
1123cab2bb3Spatrick
1133cab2bb3Spatrick private:
1143cab2bb3Spatrick // We map in the largest possible view into the VMO: one word
1153cab2bb3Spatrick // for every possible 32-bit index value. This avoids the need
1163cab2bb3Spatrick // to change the mapping when increasing the size of the VMO.
1173cab2bb3Spatrick // We can always spare the 32G of address space.
1183cab2bb3Spatrick static constexpr size_t MappingSize = sizeof(uptr) << 32;
1193cab2bb3Spatrick
120*810390e3Srobert Mutex setup_lock_;
1213cab2bb3Spatrick uptr *array_ = nullptr;
1223cab2bb3Spatrick u32 next_index_ = 0;
1233cab2bb3Spatrick zx_handle_t vmo_ = {};
1243cab2bb3Spatrick char vmo_name_[ZX_MAX_NAME_LEN] = {};
1253cab2bb3Spatrick
DataSize() const1263cab2bb3Spatrick size_t DataSize() const { return next_index_ * sizeof(uintptr_t); }
1273cab2bb3Spatrick
Setup(u32 num_guards)1283cab2bb3Spatrick u32 Setup(u32 num_guards) {
129*810390e3Srobert Lock locked(&setup_lock_);
1303cab2bb3Spatrick DCHECK(common_flags()->coverage);
1313cab2bb3Spatrick
1323cab2bb3Spatrick if (next_index_ == 0) {
1333cab2bb3Spatrick CHECK_EQ(vmo_, ZX_HANDLE_INVALID);
1343cab2bb3Spatrick CHECK_EQ(array_, nullptr);
1353cab2bb3Spatrick
1363cab2bb3Spatrick // The first sample goes at [1] to reserve [0] for the magic number.
1373cab2bb3Spatrick next_index_ = 1 + num_guards;
1383cab2bb3Spatrick
1393cab2bb3Spatrick zx_status_t status = _zx_vmo_create(DataSize(), ZX_VMO_RESIZABLE, &vmo_);
1403cab2bb3Spatrick CHECK_EQ(status, ZX_OK);
1413cab2bb3Spatrick
1423cab2bb3Spatrick // Give the VMO a name including our process KOID so it's easy to spot.
1433cab2bb3Spatrick internal_snprintf(vmo_name_, sizeof(vmo_name_), "%s.%zu", kSancovSinkName,
1443cab2bb3Spatrick internal_getpid());
1453cab2bb3Spatrick _zx_object_set_property(vmo_, ZX_PROP_NAME, vmo_name_,
1463cab2bb3Spatrick internal_strlen(vmo_name_));
1471f9cb04fSpatrick uint64_t size = DataSize();
1481f9cb04fSpatrick status = _zx_object_set_property(vmo_, ZX_PROP_VMO_CONTENT_SIZE, &size,
1491f9cb04fSpatrick sizeof(size));
1501f9cb04fSpatrick CHECK_EQ(status, ZX_OK);
1513cab2bb3Spatrick
1523cab2bb3Spatrick // Map the largest possible view we might need into the VMO. Later
1533cab2bb3Spatrick // we might need to increase the VMO's size before we can use larger
1543cab2bb3Spatrick // indices, but we'll never move the mapping address so we don't have
1553cab2bb3Spatrick // any multi-thread synchronization issues with that.
1563cab2bb3Spatrick uintptr_t mapping;
1573cab2bb3Spatrick status =
1583cab2bb3Spatrick _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
1593cab2bb3Spatrick 0, vmo_, 0, MappingSize, &mapping);
1603cab2bb3Spatrick CHECK_EQ(status, ZX_OK);
1613cab2bb3Spatrick
1623cab2bb3Spatrick // Hereafter other threads are free to start storing into
1633cab2bb3Spatrick // elements [1, next_index_) of the big array.
1643cab2bb3Spatrick array_ = reinterpret_cast<uptr *>(mapping);
1653cab2bb3Spatrick
1663cab2bb3Spatrick // Store the magic number.
1673cab2bb3Spatrick // Hereafter, the VMO serves as the contents of the '.sancov' file.
1683cab2bb3Spatrick array_[0] = Magic64;
1693cab2bb3Spatrick
1703cab2bb3Spatrick return 1;
1713cab2bb3Spatrick } else {
1723cab2bb3Spatrick // The VMO is already mapped in, but it's not big enough to use the
1733cab2bb3Spatrick // new indices. So increase the size to cover the new maximum index.
1743cab2bb3Spatrick
1753cab2bb3Spatrick CHECK_NE(vmo_, ZX_HANDLE_INVALID);
1763cab2bb3Spatrick CHECK_NE(array_, nullptr);
1773cab2bb3Spatrick
1783cab2bb3Spatrick uint32_t first_index = next_index_;
1793cab2bb3Spatrick next_index_ += num_guards;
1803cab2bb3Spatrick
1813cab2bb3Spatrick zx_status_t status = _zx_vmo_set_size(vmo_, DataSize());
1823cab2bb3Spatrick CHECK_EQ(status, ZX_OK);
1831f9cb04fSpatrick uint64_t size = DataSize();
1841f9cb04fSpatrick status = _zx_object_set_property(vmo_, ZX_PROP_VMO_CONTENT_SIZE, &size,
1851f9cb04fSpatrick sizeof(size));
1861f9cb04fSpatrick CHECK_EQ(status, ZX_OK);
1873cab2bb3Spatrick
1883cab2bb3Spatrick return first_index;
1893cab2bb3Spatrick }
1903cab2bb3Spatrick }
1913cab2bb3Spatrick };
1923cab2bb3Spatrick
1933cab2bb3Spatrick static TracePcGuardController pc_guard_controller;
1943cab2bb3Spatrick
1953cab2bb3Spatrick } // namespace
1963cab2bb3Spatrick } // namespace __sancov
1973cab2bb3Spatrick
1983cab2bb3Spatrick namespace __sanitizer {
InitializeCoverage(bool enabled,const char * dir)1993cab2bb3Spatrick void InitializeCoverage(bool enabled, const char *dir) {
2003cab2bb3Spatrick CHECK_EQ(enabled, common_flags()->coverage);
2013cab2bb3Spatrick CHECK_EQ(dir, common_flags()->coverage_dir);
2023cab2bb3Spatrick
2033cab2bb3Spatrick static bool coverage_enabled = false;
2043cab2bb3Spatrick if (!coverage_enabled) {
2053cab2bb3Spatrick coverage_enabled = enabled;
2063cab2bb3Spatrick Atexit(__sanitizer_cov_dump);
2073cab2bb3Spatrick AddDieCallback(__sanitizer_cov_dump);
2083cab2bb3Spatrick }
2093cab2bb3Spatrick }
2103cab2bb3Spatrick } // namespace __sanitizer
2113cab2bb3Spatrick
2123cab2bb3Spatrick extern "C" {
__sanitizer_dump_coverage(const uptr * pcs,uptr len)2133cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage(const uptr *pcs,
2143cab2bb3Spatrick uptr len) {
2153cab2bb3Spatrick UNIMPLEMENTED();
2163cab2bb3Spatrick }
2173cab2bb3Spatrick
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_pc_guard,u32 * guard)2183cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32 *guard) {
2191f9cb04fSpatrick if (!*guard)
2201f9cb04fSpatrick return;
2213cab2bb3Spatrick __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
2223cab2bb3Spatrick }
2233cab2bb3Spatrick
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_pc_guard_init,u32 * start,u32 * end)2243cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
2253cab2bb3Spatrick u32 *start, u32 *end) {
2261f9cb04fSpatrick if (start == end || *start)
2271f9cb04fSpatrick return;
2283cab2bb3Spatrick __sancov::pc_guard_controller.InitTracePcGuard(start, end);
2293cab2bb3Spatrick }
2303cab2bb3Spatrick
__sanitizer_dump_trace_pc_guard_coverage()2313cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
2323cab2bb3Spatrick __sancov::pc_guard_controller.Dump();
2333cab2bb3Spatrick }
__sanitizer_cov_dump()2343cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
2353cab2bb3Spatrick __sanitizer_dump_trace_pc_guard_coverage();
2363cab2bb3Spatrick }
2373cab2bb3Spatrick // Default empty implementations (weak). Users should redefine them.
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_cmp,void)2383cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_cmp1,void)2393cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_cmp2,void)2403cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_cmp4,void)2413cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_cmp8,void)2423cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_const_cmp1,void)2433cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_const_cmp2,void)2443cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_const_cmp4,void)2453cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_const_cmp8,void)2463cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_switch,void)2473cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_div4,void)2483cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_div8,void)2493cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_gep,void)2503cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_cov_trace_pc_indir,void)2513cab2bb3Spatrick SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
2523cab2bb3Spatrick } // extern "C"
2533cab2bb3Spatrick
2543cab2bb3Spatrick #endif // !SANITIZER_FUCHSIA
255