| // 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; |
| } |