blob: c20bdc8d249583c4c8afbd4689d91508e8a5db8d [file] [log] [blame]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cheriot/profiler.h"
#include <cstdint>
#include <cstring>
#include <ostream>
#include <utility>
#include "absl/log/log.h"
#include "absl/numeric/bits.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "elfio/elf_types.hpp"
namespace mpact::sim::cheriot {
Profiler::Profiler(ElfProgramLoader &elf_loader, unsigned granularity)
: elf_loader_(&elf_loader) {
if (!absl::has_single_bit(granularity)) {
LOG(ERROR) << absl::StrCat("Invalid granularity: ", granularity,
", must be a power of 2");
}
shift_ = absl::bit_width(granularity) - 1;
SetElfLoader(&elf_loader);
}
Profiler::Profiler(unsigned granularity) : elf_loader_(nullptr) {
if (!absl::has_single_bit(granularity)) {
LOG(ERROR) << absl::StrCat("Invalid granularity: ", granularity,
", must be a power of 2");
}
shift_ = absl::bit_width(granularity) - 1;
}
Profiler::~Profiler() {
for (auto const &[unused, counters] : profile_ranges_) {
delete[] counters;
}
profile_ranges_.clear();
}
void Profiler::AddSampleInternal(uint64_t sample) {
if (elf_loader_ == nullptr) return;
// Look up a new range.
auto it = profile_ranges_.find({sample, sample});
if (it == profile_ranges_.end()) {
LOG(WARNING) << absl::StrCat("Profile sample out of range: ",
absl::Hex(sample << shift_));
return;
}
// Save the range info and increment the counter.
last_profile_range_ = it->second;
last_start_ = it->first.start;
last_end_ = it->first.end;
last_profile_range_[sample - last_start_]++;
}
void Profiler::WriteProfile(std::ostream &os) {
os << "Address,Count" << "\n";
for (auto const &[range, counters] : profile_ranges_) {
uint64_t size = range.end - range.start;
for (auto i = 0; i < size; ++i) {
if (counters[i] == 0) continue;
os << absl::StrFormat("0x%llx,%llu\n", (range.start + i) << shift_,
counters[i]);
}
}
}
void Profiler::SetElfLoader(ElfProgramLoader *elf_loader) {
elf_loader_ = elf_loader;
uint64_t begin = 0;
uint64_t end = 0;
// Iterate through the elf segments (assumes they are in order), and
// coalesces ranges that are spaced by less than 0x1'000 units of granularity.
// This reduces the number of ranges in the map and improves performance
// during simulation.
for (auto const &segment : elf_loader_->elf_reader()->segments) {
// Only consider segments that are loaded, executable, and with size > 0.
if (segment->get_type() != PT_LOAD) continue;
if ((segment->get_flags() & PF_X) == 0) continue;
uint64_t size = segment->get_memory_size() >> shift_;
if (size == 0) continue;
uint64_t vaddr_begin = segment->get_virtual_address() >> shift_;
// If it's the first time we see a segment, just get the start and end
// values.
if (begin == 0 && end == 0) {
begin = vaddr_begin;
end = vaddr_begin + size;
continue;
};
// If the segment is close enough to the current, just coalesce.
if (vaddr_begin - end < 0x1000) {
end = vaddr_begin + size;
continue;
}
// Otherwise, create a entry from the previously accumulated ranges, and
// start a new range.
size = end - begin - 1;
uint64_t *counters = new uint64_t[size];
::memset(counters, 0, size * sizeof(uint64_t));
profile_ranges_.insert(std::make_pair(AddressRange{begin, end}, counters));
begin = vaddr_begin;
end = vaddr_begin + size;
}
// Make the last entry.
if (begin != 0 || end != 0) {
uint64_t size = end - begin - 1;
uint64_t *counters = new uint64_t[size];
::memset(counters, 0, size * sizeof(uint64_t));
profile_ranges_.insert(
std::make_pair(AddressRange{begin, end - 1}, counters));
}
}
} // namespace mpact::sim::cheriot