blob: fb8fc5e9c73a2ed41908416ca43ec1dd57da5c77 [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 <align.h>
#include <lib/llvm-profdata/llvm-profdata.h>
#include <lib/version.h>
#include <stdint.h>
#include <string.h>
#include <zircon/assert.h>
#include <ktl/byte.h>
#include <ktl/move.h>
#include <ktl/span.h>
#include <ktl/string_view.h>
#include <vm/vm_object_paged.h>
#include "kernel-mapped-vmo.h"
#include "private.h"
#include <ktl/enforce.h>
namespace {
constexpr ktl::string_view kVmoName = "data/zircon.elf.profraw";
// This holds the pinned mapping of the live-updated counters.
KernelMappedVmo gProfdataCounters;
} // namespace
InstrumentationDataVmo LlvmProfdataVmo() {
LlvmProfdata profdata;
profdata.Init(ElfBuildId());
if (profdata.size_bytes() == 0) {
return {};
}
// Create a VMO to hold the whole profdata dump.
fbl::RefPtr<VmObjectPaged> vmo;
zx_status_t status = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, 0, profdata.size_bytes(), &vmo);
ZX_ASSERT(status == ZX_OK);
// First fill in just the fixed data, by mapping the whole VMO into the
// kernel address space. Then let the mapping and pinning be cleaned up,
// since we don't need the whole thing mapped into the kernel at runtime.
{
KernelMappedVmo mapped_vmo;
status = mapped_vmo.Init(vmo, 0, profdata.size_bytes(), "llvm-profdata-setup");
ZX_ASSERT(status == ZX_OK);
ktl::span<ktl::byte> mapped_data{
reinterpret_cast<ktl::byte*>(mapped_vmo.base()),
mapped_vmo.size(),
};
profdata.WriteFixedData(mapped_data);
}
// Now map in just the pages holding the counters. This mapping will be kept
// alive permanently so the live counters can be updated through it.
const uint64_t map_offset = ROUNDDOWN(profdata.counters_offset(), PAGE_SIZE);
const size_t map_size =
ROUNDUP_PAGE_SIZE(profdata.counters_offset() + profdata.counters_size_bytes()) - map_offset;
status = gProfdataCounters.Init(ktl::move(vmo), map_offset, map_size, "llvm-profdata-counters");
ZX_ASSERT(status == ZX_OK);
ktl::span<ktl::byte> counters{
reinterpret_cast<ktl::byte*>(profdata.counters_offset() - map_offset +
gProfdataCounters.base()),
profdata.counters_size_bytes(),
};
// Counts up to this point have collected in global variable space.
// Copy those counters into the mapped VMO data.
profdata.CopyCounters(counters);
// Switch instrumented code over to updating the mapped VMO data in place.
// From this point on, the kernel's VMO mapping is used by all instrumented
// code and must be kept valid and pinned.
//
// TODO(mcgrathr): We could theoretically decommit the global data pages
// after this to recover that RAM. That part of the kernel's global data
// area should never be accessed again.
LlvmProfdata::UseCounters(counters);
return {
.announce = LlvmProfdata::kAnnounce,
.sink_name = LlvmProfdata::kDataSinkName,
.handle = gProfdataCounters.Publish(kVmoName, profdata.size_bytes()),
};
}