blob: 493663b0d760d81d602890be615fd05a6f5f9fbd [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 <lib/version.h>
#include <stdio.h>
#include <string.h>
#include <ktl/iterator.h>
#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 Kind {
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;
constexpr size_t content_size() const { return end - start; }
} 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.profraw",
// 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"},
// NOTE! This element must be last. This file contains logging text
// with symbolizer markup that describes all the other data files.
{{}, {}, "data/symbolizer.log", {}, {}, {}, {}, {}},
};
void PrintDumpfile(FILE* f, const Kind& k) {
fprintf(f, "%s: {{{dumpfile:%s:%s}}} maximum %zu %s.\n", k.announce, k.sink_name, k.vmo_name,
(k.end - k.start) / k.scale, k.units);
}
} // 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_, kKinds[which()].content_size(),
VmObjectDispatcher::InitialMutability::kMutable,
&new_handle, &rights);
if (status == ZX_OK) {
*handle = Handle::Make(ktl::move(new_handle), rights & ~ZX_RIGHT_WRITE).release();
}
return status;
}
bool InstrumentationData::Publish(FILE* symbolizer) {
if (vmo_->size() == 0) {
return false;
}
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));
if (symbolizer) {
// Log the name that goes with the VMO.
PrintDumpfile(stdout, k);
PrintDumpfile(symbolizer, k);
}
return true;
}
zx_status_t InstrumentationData::GetVmos(Handle* handles[]) {
// This object facilitates doing fprintf directly into the VMO representing
// the symbolizer markup data file. This gets the symbolizer context for the
// kernel and then a dumpfile element for each VMO published.
struct SymbolizerFile {
fbl::RefPtr<VmObjectPaged>& vmo_ = instances_[kSymbolizer].vmo_;
size_t pos_ = 0;
FILE stream_{this};
zx_status_t Create() {
return VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, VmObjectPaged::kResizable, PAGE_SIZE, &vmo_);
}
int Write(ktl::string_view str) {
zx_status_t status = vmo_->Write(str.data(), pos_, str.size());
ZX_ASSERT(status == ZX_OK);
pos_ += str.size();
return static_cast<int>(str.size());
}
} symbolizer;
zx_status_t status = symbolizer.Create();
if (status != ZX_OK) {
return status;
}
PrintSymbolizerContext(&symbolizer.stream_);
bool any_published = false;
for (auto& instance : instances_) {
bool published = false;
if (instance.which() == kSymbolizer) {
// This is the last iteration, so everything has been published now.
static_assert(kSymbolizer == ktl::size(instances_) - 1);
if (any_published) {
// Publish the symbolizer file.
published = instance.Publish(nullptr);
} else {
// Nothing to publish, so zero out the symbolizer file VMO so it won't
// be published either.
status = instance.vmo_->Resize(0);
ZX_ASSERT(status == ZX_OK);
}
} else {
status = instance.Create();
published = instance.Publish(&symbolizer.stream_);
}
if (status == ZX_OK) {
status = instance.GetVmo(&handles[instance.which()]);
}
if (published) {
any_published = true;
} else {
// The empty VMO doesn't need to be kept alive.
instance.vmo_.reset();
}
if (status != ZX_OK) {
return status;
}
}
if (any_published) {
// Finalize the official size of the symbolizer file.
auto* dispatcher = handles[kSymbolizer]->dispatcher().get();
auto vmo = DownCastDispatcher<VmObjectDispatcher>(dispatcher);
vmo->SetContentSize(symbolizer.pos_);
}
// There's no need to keep the symbolizer file VMO alive if userland drops
// it. Its memory is not special and isn't used by the kernel directly.
symbolizer.vmo_.reset();
return ZX_OK;
}