blob: 6cf1074529296eaa78d92149dfedb0ae88eb7b08 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/instrumentation/vmo.h>
#include <cstring>
#include <object/vm_object_dispatcher.h>
#include <vm/vm_object_paged.h>
namespace {
// These are defined by the linker script. When there is no such
// instrumentation data in this kernel build, they're equal.
extern "C" const uint8_t __llvm_profile_start[], __llvm_profile_end[];
extern "C" const uint8_t __llvm_profile_vmo_end[];
extern "C" const uint8_t __sancov_pc_table[], __sancov_pc_table_end[];
extern "C" const uint8_t __sancov_pc_table_vmo_end[];
extern "C" const uint8_t __sancov_pc_counts[], __sancov_pc_counts_end[];
extern "C" const uint8_t __sancov_pc_counts_vmo_end[];
constexpr struct {
const char* announce;
const char* sink_name;
const char* vmo_name;
const uint8_t* start;
const uint8_t* end;
const uint8_t* vmo_end;
size_t scale;
const char* units;
} kKinds[] = {
// LLVM profile data. When not compiled in, this will be a zero-length
// anonymous VMO and userland will just ignore it. But it's simpler to
// keep the number of VMOs fixed in the ABI with userboot because the
// way the build works, the userboot build is independent of different
// kernel variants that might have things enabled or disabled.
{"LLVM Profile", "llvm-profile", "data/zircon.elf.profdata",
// Linker-generated symbols.
__llvm_profile_start, __llvm_profile_end, __llvm_profile_vmo_end,
// Units.
1, "bytes"},
// -fsanitizer-coverage=trace-pc-guard data. Same story.
{"SanitizerCoverage", "sancov",
// The sancov tool matches "<binaryname>" to "<binaryname>.%u.sancov".
"data/zircon.elf.1.sancov",
// Linker-generated symbols.
__sancov_pc_table, __sancov_pc_table_end, __sancov_pc_table_vmo_end,
// Units.
sizeof(uintptr_t), "PCs"},
{"SanitizerCoverage Counts", "sancov-counts",
// This follows the sancov PCs file name just for consistency.
"data/zircon.elf.1.sancov-counts",
// Linker-generated symbols.
__sancov_pc_counts, __sancov_pc_counts_end, __sancov_pc_counts_vmo_end,
// Units.
sizeof(uint64_t), "counters"},
};
} // namespace
decltype(InstrumentationData::instances_) InstrumentationData::instances_;
zx_status_t InstrumentationData::Create() {
const auto& k = kKinds[which()];
return VmObjectPaged::CreateFromWiredPages(k.start, k.vmo_end - k.start, false, &vmo_);
}
zx_status_t InstrumentationData::GetVmo(Handle** handle) {
zx_rights_t rights;
KernelHandle<VmObjectDispatcher> new_handle;
zx_status_t status = VmObjectDispatcher::Create(vmo_, &new_handle, &rights);
if (status == ZX_OK) {
*handle = Handle::Make(ktl::move(new_handle), rights & ~ZX_RIGHT_WRITE).release();
}
return status;
}
void InstrumentationData::Publish() {
if (vmo_->size() == 0) {
// The empty VMO doesn't need to be kept alive.
vmo_.reset();
} else {
const auto& k = kKinds[which()];
// Set the name to expose the meaning of the VMO to userland.
vmo_->set_name(k.vmo_name, strlen(k.vmo_name));
// Log the name that goes with the VMO.
printf("%s: {{{dumpfile:%s:%s}}} maximum %zu %s.\n", k.announce, k.sink_name, k.vmo_name,
(k.end - k.start) / k.scale, k.units);
}
}
zx_status_t InstrumentationData::GetVmos(Handle* handles[]) {
for (auto& instance : instances_) {
zx_status_t status = instance.Create();
if (status == ZX_OK) {
status = instance.GetVmo(&handles[instance.which()]);
}
if (status != ZX_OK) {
return status;
}
instance.Publish();
}
return ZX_OK;
}