xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_coverage_fuchsia.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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