[sanitizer] Split Symbolizer/StackTraces from core RTSanitizerCommon
Summary:
Host symbolizer & stacktraces related code in their own RT:
`RTSanitizerCommonSymbolizer`, which is "libcdep" by nature. Symbolizer &
stacktraces specific code that used to live in common files is moved to a new
file `sanitizer_symbolizer_report.cc` as is.
The purpose of this is the enforce a separation between code that relies on
symbolization and code that doesn't. This saves the inclusion of spurious code
due to the interface functions with default visibility, and the extra data
associated.
The following sanitizers makefiles were modified & tested locally:
- dfsan: doesn't require the new symbolizer RT
- esan: requires it
- hwasan: requires it
- lsan: requires it
- msan: requires it
- safestack: doesn't require it
- xray: doesn't require it
- tsan: requires it
- ubsan: requires it
- ubsan_minimal: doesn't require it
- scudo: requires it (but not for Fuchsia that has a minimal runtime)
This was tested locally on Linux, Android, Fuchsia.
Reviewers: alekseyshl, eugenis, dberris, kubamracek, vitalybuka, dvyukov, mcgrathr
Reviewed By: alekseyshl, vitalybuka
Subscribers: srhines, kubamracek, mgorny, krytarowski, delcypher, llvm-commits, #sanitizers
Differential Revision: https://reviews.llvm.org/D45457
llvm-svn: 330131
2018-04-17 00:32:19 +08:00
|
|
|
//===-- sanitizer_coverage_fuchsia.cc -------------------------------------===//
|
2017-08-02 15:51:38 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-08-02 15:51:38 +08:00
|
|
|
//
|
[sanitizer] Split Symbolizer/StackTraces from core RTSanitizerCommon
Summary:
Host symbolizer & stacktraces related code in their own RT:
`RTSanitizerCommonSymbolizer`, which is "libcdep" by nature. Symbolizer &
stacktraces specific code that used to live in common files is moved to a new
file `sanitizer_symbolizer_report.cc` as is.
The purpose of this is the enforce a separation between code that relies on
symbolization and code that doesn't. This saves the inclusion of spurious code
due to the interface functions with default visibility, and the extra data
associated.
The following sanitizers makefiles were modified & tested locally:
- dfsan: doesn't require the new symbolizer RT
- esan: requires it
- hwasan: requires it
- lsan: requires it
- msan: requires it
- safestack: doesn't require it
- xray: doesn't require it
- tsan: requires it
- ubsan: requires it
- ubsan_minimal: doesn't require it
- scudo: requires it (but not for Fuchsia that has a minimal runtime)
This was tested locally on Linux, Android, Fuchsia.
Reviewers: alekseyshl, eugenis, dberris, kubamracek, vitalybuka, dvyukov, mcgrathr
Reviewed By: alekseyshl, vitalybuka
Subscribers: srhines, kubamracek, mgorny, krytarowski, delcypher, llvm-commits, #sanitizers
Differential Revision: https://reviews.llvm.org/D45457
llvm-svn: 330131
2018-04-17 00:32:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2017-08-02 15:51:38 +08:00
|
|
|
//
|
|
|
|
// Sanitizer Coverage Controller for Trace PC Guard, Fuchsia-specific version.
|
|
|
|
//
|
|
|
|
// This Fuchsia-specific implementation uses the same basic scheme and the
|
|
|
|
// same simple '.sancov' file format as the generic implementation. The
|
|
|
|
// difference is that we just produce a single blob of output for the whole
|
|
|
|
// program, not a separate one per DSO. We do not sort the PC table and do
|
|
|
|
// not prune the zeros, so the resulting file is always as large as it
|
|
|
|
// would be to report 100% coverage. Implicit tracing information about
|
|
|
|
// the address ranges of DSOs allows offline tools to split the one big
|
|
|
|
// blob into separate files that the 'sancov' tool can understand.
|
|
|
|
//
|
|
|
|
// Unlike the traditional implementation that uses an atexit hook to write
|
|
|
|
// out data files at the end, the results on Fuchsia do not go into a file
|
|
|
|
// per se. The 'coverage_dir' option is ignored. Instead, they are stored
|
2017-09-13 09:18:15 +08:00
|
|
|
// directly into a shared memory object (a Zircon VMO). At exit, that VMO
|
2017-08-02 15:51:38 +08:00
|
|
|
// is handed over to a system service that's responsible for getting the
|
|
|
|
// data out to somewhere that it can be fed into the sancov tool (where and
|
|
|
|
// how is not our problem).
|
|
|
|
|
|
|
|
#include "sanitizer_platform.h"
|
|
|
|
#if SANITIZER_FUCHSIA
|
|
|
|
#include "sanitizer_atomic.h"
|
|
|
|
#include "sanitizer_common.h"
|
|
|
|
#include "sanitizer_internal_defs.h"
|
2018-11-22 10:00:44 +08:00
|
|
|
#include "sanitizer_symbolizer_fuchsia.h"
|
2017-08-02 15:51:38 +08:00
|
|
|
|
2017-09-13 09:18:15 +08:00
|
|
|
#include <zircon/process.h>
|
|
|
|
#include <zircon/sanitizer.h>
|
|
|
|
#include <zircon/syscalls.h>
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
using namespace __sanitizer; // NOLINT
|
|
|
|
|
|
|
|
namespace __sancov {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// TODO(mcgrathr): Move the constant into a header shared with other impls.
|
|
|
|
constexpr u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
|
|
|
|
static_assert(SANITIZER_WORDSIZE == 64, "Fuchsia is always LP64");
|
|
|
|
|
|
|
|
constexpr const char kSancovSinkName[] = "sancov";
|
|
|
|
|
|
|
|
// Collects trace-pc guard coverage.
|
|
|
|
// This class relies on zero-initialization.
|
2018-01-20 05:14:53 +08:00
|
|
|
class TracePcGuardController final {
|
2017-08-02 15:51:38 +08:00
|
|
|
public:
|
|
|
|
// For each PC location being tracked, there is a u32 reserved in global
|
|
|
|
// data called the "guard". At startup, we assign each guard slot a
|
|
|
|
// unique index into the big results array. Later during runtime, the
|
|
|
|
// first call to TracePcGuard (below) will store the corresponding PC at
|
|
|
|
// that index in the array. (Each later call with the same guard slot is
|
|
|
|
// presumed to be from the same PC.) Then it clears the guard slot back
|
|
|
|
// to zero, which tells the compiler not to bother calling in again. At
|
|
|
|
// the end of the run, we have a big array where each element is either
|
|
|
|
// zero or is a tracked PC location that was hit in the trace.
|
|
|
|
|
|
|
|
// This is called from global constructors. Each translation unit has a
|
|
|
|
// contiguous array of guard slots, and a constructor that calls here
|
|
|
|
// with the bounds of its array. Those constructors are allowed to call
|
|
|
|
// here more than once for the same array. Usually all of these
|
|
|
|
// constructors run in the initial thread, but it's possible that a
|
|
|
|
// dlopen call on a secondary thread will run constructors that get here.
|
|
|
|
void InitTracePcGuard(u32 *start, u32 *end) {
|
|
|
|
if (end > start && *start == 0 && common_flags()->coverage) {
|
|
|
|
// Complete the setup before filling in any guards with indices.
|
|
|
|
// This avoids the possibility of code called from Setup reentering
|
|
|
|
// TracePcGuard.
|
|
|
|
u32 idx = Setup(end - start);
|
|
|
|
for (u32 *p = start; p < end; ++p) {
|
|
|
|
*p = idx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TracePcGuard(u32 *guard, uptr pc) {
|
|
|
|
atomic_uint32_t *guard_ptr = reinterpret_cast<atomic_uint32_t *>(guard);
|
|
|
|
u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed);
|
|
|
|
if (idx > 0) array_[idx] = pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dump() {
|
|
|
|
BlockingMutexLock locked(&setup_lock_);
|
|
|
|
if (array_) {
|
2017-09-13 09:18:15 +08:00
|
|
|
CHECK_NE(vmo_, ZX_HANDLE_INVALID);
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
// Publish the VMO to the system, where it can be collected and
|
|
|
|
// analyzed after this process exits. This always consumes the VMO
|
|
|
|
// handle. Any failure is just logged and not indicated to us.
|
|
|
|
__sanitizer_publish_data(kSancovSinkName, vmo_);
|
2017-09-13 09:18:15 +08:00
|
|
|
vmo_ = ZX_HANDLE_INVALID;
|
2017-08-02 15:51:38 +08:00
|
|
|
|
2017-09-06 08:00:46 +08:00
|
|
|
// This will route to __sanitizer_log_write, which will ensure that
|
|
|
|
// information about shared libraries is written out. This message
|
|
|
|
// uses the `dumpfile` symbolizer markup element to highlight the
|
|
|
|
// dump. See the explanation for this in:
|
2017-09-13 09:18:15 +08:00
|
|
|
// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
|
2018-11-22 10:00:44 +08:00
|
|
|
Printf("SanitizerCoverage: " FORMAT_DUMPFILE " with up to %u PCs\n",
|
2017-09-06 08:00:46 +08:00
|
|
|
kSancovSinkName, vmo_name_, next_index_ - 1);
|
2017-08-02 15:51:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// We map in the largest possible view into the VMO: one word
|
|
|
|
// for every possible 32-bit index value. This avoids the need
|
|
|
|
// to change the mapping when increasing the size of the VMO.
|
|
|
|
// We can always spare the 32G of address space.
|
|
|
|
static constexpr size_t MappingSize = sizeof(uptr) << 32;
|
|
|
|
|
2018-01-20 11:37:47 +08:00
|
|
|
BlockingMutex setup_lock_ = BlockingMutex(LINKER_INITIALIZED);
|
2018-01-20 05:14:53 +08:00
|
|
|
uptr *array_ = nullptr;
|
|
|
|
u32 next_index_ = 0;
|
2018-01-20 11:37:47 +08:00
|
|
|
zx_handle_t vmo_ = {};
|
2018-01-20 05:14:53 +08:00
|
|
|
char vmo_name_[ZX_MAX_NAME_LEN] = {};
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
size_t DataSize() const { return next_index_ * sizeof(uintptr_t); }
|
|
|
|
|
|
|
|
u32 Setup(u32 num_guards) {
|
|
|
|
BlockingMutexLock locked(&setup_lock_);
|
|
|
|
DCHECK(common_flags()->coverage);
|
|
|
|
|
|
|
|
if (next_index_ == 0) {
|
2017-09-13 09:18:15 +08:00
|
|
|
CHECK_EQ(vmo_, ZX_HANDLE_INVALID);
|
2017-08-02 15:51:38 +08:00
|
|
|
CHECK_EQ(array_, nullptr);
|
|
|
|
|
|
|
|
// The first sample goes at [1] to reserve [0] for the magic number.
|
|
|
|
next_index_ = 1 + num_guards;
|
|
|
|
|
2017-09-13 09:18:15 +08:00
|
|
|
zx_status_t status = _zx_vmo_create(DataSize(), 0, &vmo_);
|
|
|
|
CHECK_EQ(status, ZX_OK);
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
// Give the VMO a name including our process KOID so it's easy to spot.
|
|
|
|
internal_snprintf(vmo_name_, sizeof(vmo_name_), "%s.%zu", kSancovSinkName,
|
|
|
|
internal_getpid());
|
2017-09-13 09:18:15 +08:00
|
|
|
_zx_object_set_property(vmo_, ZX_PROP_NAME, vmo_name_,
|
2017-08-02 15:51:38 +08:00
|
|
|
internal_strlen(vmo_name_));
|
|
|
|
|
|
|
|
// Map the largest possible view we might need into the VMO. Later
|
|
|
|
// we might need to increase the VMO's size before we can use larger
|
|
|
|
// indices, but we'll never move the mapping address so we don't have
|
|
|
|
// any multi-thread synchronization issues with that.
|
|
|
|
uintptr_t mapping;
|
2018-08-30 09:27:26 +08:00
|
|
|
status =
|
|
|
|
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
|
|
|
|
0, vmo_, 0, MappingSize, &mapping);
|
2017-09-13 09:18:15 +08:00
|
|
|
CHECK_EQ(status, ZX_OK);
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
// Hereafter other threads are free to start storing into
|
|
|
|
// elements [1, next_index_) of the big array.
|
|
|
|
array_ = reinterpret_cast<uptr *>(mapping);
|
|
|
|
|
|
|
|
// Store the magic number.
|
|
|
|
// Hereafter, the VMO serves as the contents of the '.sancov' file.
|
|
|
|
array_[0] = Magic64;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
// The VMO is already mapped in, but it's not big enough to use the
|
|
|
|
// new indices. So increase the size to cover the new maximum index.
|
|
|
|
|
2017-09-13 09:18:15 +08:00
|
|
|
CHECK_NE(vmo_, ZX_HANDLE_INVALID);
|
2017-08-02 15:51:38 +08:00
|
|
|
CHECK_NE(array_, nullptr);
|
|
|
|
|
|
|
|
uint32_t first_index = next_index_;
|
|
|
|
next_index_ += num_guards;
|
|
|
|
|
2017-09-13 09:18:15 +08:00
|
|
|
zx_status_t status = _zx_vmo_set_size(vmo_, DataSize());
|
|
|
|
CHECK_EQ(status, ZX_OK);
|
2017-08-02 15:51:38 +08:00
|
|
|
|
|
|
|
return first_index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static TracePcGuardController pc_guard_controller;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace __sancov
|
|
|
|
|
|
|
|
namespace __sanitizer {
|
|
|
|
void InitializeCoverage(bool enabled, const char *dir) {
|
|
|
|
CHECK_EQ(enabled, common_flags()->coverage);
|
|
|
|
CHECK_EQ(dir, common_flags()->coverage_dir);
|
|
|
|
|
|
|
|
static bool coverage_enabled = false;
|
|
|
|
if (!coverage_enabled) {
|
|
|
|
coverage_enabled = enabled;
|
|
|
|
Atexit(__sanitizer_cov_dump);
|
|
|
|
AddDieCallback(__sanitizer_cov_dump);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace __sanitizer
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT
|
|
|
|
const uptr *pcs, uptr len) {
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32 *guard) {
|
|
|
|
if (!*guard) return;
|
|
|
|
__sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
|
|
|
|
u32 *start, u32 *end) {
|
|
|
|
if (start == end || *start) return;
|
|
|
|
__sancov::pc_guard_controller.InitTracePcGuard(start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
|
|
|
|
__sancov::pc_guard_controller.Dump();
|
|
|
|
}
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
|
|
|
|
__sanitizer_dump_trace_pc_guard_coverage();
|
|
|
|
}
|
|
|
|
// Default empty implementations (weak). Users should redefine them.
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
|
2017-08-10 22:22:57 +08:00
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
|
2017-08-02 15:51:38 +08:00
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
|
|
|
|
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
|
|
|
|
} // extern "C"
|
|
|
|
|
|
|
|
#endif // !SANITIZER_FUCHSIA
|