No public description PiperOrigin-RevId: 642052808 Change-Id: I0c9529386d85b5e83fed4e9b26b6cb73fb61ccfd
diff --git a/cheriot/BUILD b/cheriot/BUILD index 4e700d1..17501f1 100644 --- a/cheriot/BUILD +++ b/cheriot/BUILD
@@ -239,6 +239,7 @@ "@com_google_mpact-sim//mpact/sim/generic:instruction", "@com_google_mpact-sim//mpact/sim/proto:component_data_cc_proto", "@com_google_mpact-sim//mpact/sim/util/memory", + "@com_google_mpact-sim//mpact/sim/util/other:instruction_profiler", "@com_google_mpact-sim//mpact/sim/util/other:simple_uart", "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader", "@com_google_protobuf//:protobuf", @@ -280,31 +281,18 @@ name = "instrumentation", srcs = [ "cheriot_instrumentation_control.cc", - "memory_use_profiler.cc", - "profiler.cc", ], hdrs = [ "cheriot_instrumentation_control.h", - "memory_use_profiler.h", - "profiler.h", ], deps = [ ":cheriot_top", ":debug_command_shell", - "//third_party/elfio", - "@com_google_absl//absl/container:btree", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/log", - "@com_google_absl//absl/numeric:bits", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", "@com_google_mpact-riscv//riscv:stoull_wrapper", - "@com_google_mpact-sim//mpact/sim/generic:core", - "@com_google_mpact-sim//mpact/sim/generic:counters", - "@com_google_mpact-sim//mpact/sim/generic:instruction", "@com_google_mpact-sim//mpact/sim/util/memory", - "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader", "@com_googlesource_code_re2//:re2", ], ) @@ -365,6 +353,7 @@ "@com_google_mpact-sim//mpact/sim/generic:type_helpers", "@com_google_mpact-sim//mpact/sim/proto:component_data_cc_proto", "@com_google_mpact-sim//mpact/sim/util/memory", + "@com_google_mpact-sim//mpact/sim/util/other:instruction_profiler", "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader", "@com_google_mpact-sim//mpact/sim/util/renode", "@com_google_mpact-sim//mpact/sim/util/renode:renode_debug_interface",
diff --git a/cheriot/cheriot_instrumentation_control.cc b/cheriot/cheriot_instrumentation_control.cc index 203285f..23aa8ef 100644 --- a/cheriot/cheriot_instrumentation_control.cc +++ b/cheriot/cheriot_instrumentation_control.cc
@@ -25,7 +25,6 @@ #include "absl/strings/string_view.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" -#include "cheriot/memory_use_profiler.h" #include "re2/re2.h" #include "riscv//stoull_wrapper.h"
diff --git a/cheriot/cheriot_instrumentation_control.h b/cheriot/cheriot_instrumentation_control.h index 66dc61a..d9185ca 100644 --- a/cheriot/cheriot_instrumentation_control.h +++ b/cheriot/cheriot_instrumentation_control.h
@@ -22,11 +22,13 @@ #include "absl/strings/string_view.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" -#include "cheriot/memory_use_profiler.h" +#include "mpact/sim/util/memory/memory_use_profiler.h" #include "re2/re2.h" namespace mpact::sim::cheriot { +using ::mpact::sim::util::TaggedMemoryUseProfiler; + class CheriotInstrumentationControl { public: CheriotInstrumentationControl(DebugCommandShell *shell,
diff --git a/cheriot/cheriot_renode.cc b/cheriot/cheriot_renode.cc index 321cadb..aad6ac0 100644 --- a/cheriot/cheriot_renode.cc +++ b/cheriot/cheriot_renode.cc
@@ -41,8 +41,6 @@ #include "cheriot/cheriot_state.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" -#include "cheriot/memory_use_profiler.h" -#include "cheriot/profiler.h" #include "cheriot/riscv_cheriot_minstret.h" #include "mpact/sim/generic/core_debug_interface.h" #include "mpact/sim/generic/type_helpers.h" @@ -275,7 +273,7 @@ } // Add instruction profiler it hasn't already been added. if (inst_profiler_ == nullptr) { - inst_profiler_ = new Profiler(*program_loader_, 2); + inst_profiler_ = new InstructionProfiler(*program_loader_, 2); cheriot_top_->counter_pc()->AddListener(inst_profiler_); cheriot_top_->counter_pc()->SetIsEnabled(false); } else { @@ -455,10 +453,10 @@ if (program_loader_ == nullptr) { // If the program loader is null, assume that it will be added later, // but don't enable the trace until it is. - inst_profiler_ = new Profiler(2); + inst_profiler_ = new InstructionProfiler(2); cheriot_top_->counter_pc()->SetIsEnabled(false); } else { - inst_profiler_ = new Profiler(*program_loader_, 2); + inst_profiler_ = new InstructionProfiler(*program_loader_, 2); cheriot_top_->counter_pc()->SetIsEnabled(true); } cheriot_top_->counter_pc()->AddListener(inst_profiler_);
diff --git a/cheriot/cheriot_renode.h b/cheriot/cheriot_renode.h index d2b98d7..377c01f 100644 --- a/cheriot/cheriot_renode.h +++ b/cheriot/cheriot_renode.h
@@ -28,15 +28,15 @@ #include "cheriot/cheriot_renode_cli_top.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" -#include "cheriot/memory_use_profiler.h" -#include "cheriot/profiler.h" #include "mpact/sim/generic/core_debug_interface.h" #include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/memory_interface.h" +#include "mpact/sim/util/memory/memory_use_profiler.h" #include "mpact/sim/util/memory/single_initiator_router.h" #include "mpact/sim/util/memory/tagged_flat_demand_memory.h" #include "mpact/sim/util/memory/tagged_memory_interface.h" +#include "mpact/sim/util/other/instruction_profiler.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "mpact/sim/util/renode/renode_debug_interface.h" #include "mpact/sim/util/renode/socket_cli.h" @@ -67,10 +67,12 @@ using ::mpact::sim::riscv::RiscVClint; using ::mpact::sim::util::AtomicMemory; using ::mpact::sim::util::ElfProgramLoader; +using ::mpact::sim::util::InstructionProfiler; using ::mpact::sim::util::MemoryInterface; using ::mpact::sim::util::SingleInitiatorRouter; using ::mpact::sim::util::TaggedFlatDemandMemory; using ::mpact::sim::util::TaggedMemoryInterface; +using ::mpact::sim::util::TaggedMemoryUseProfiler; using ::mpact::sim::util::renode::SocketCLI; class CheriotRenode : public util::renode::RenodeDebugInterface { @@ -149,7 +151,7 @@ CheriotCLIForwarder *cheriot_cli_forwarder_ = nullptr; ElfProgramLoader *program_loader_ = nullptr; DebugCommandShell *cmd_shell_ = nullptr; - Profiler *inst_profiler_ = nullptr; + InstructionProfiler *inst_profiler_ = nullptr; TaggedMemoryUseProfiler *mem_profiler_ = nullptr; CheriotInstrumentationControl *instrumentation_control_ = nullptr; };
diff --git a/cheriot/memory_use_profiler.cc b/cheriot/memory_use_profiler.cc deleted file mode 100644 index ac8ed47..0000000 --- a/cheriot/memory_use_profiler.cc +++ /dev/null
@@ -1,225 +0,0 @@ -// 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/memory_use_profiler.h" - -#include <cstdint> -#include <cstring> -#include <ostream> -#include <utility> - -#include "absl/container/btree_map.h" -#include "absl/numeric/bits.h" -#include "absl/strings/str_format.h" -#include "mpact/sim/util/memory/memory_interface.h" -#include "mpact/sim/util/memory/memory_watcher.h" -#include "mpact/sim/util/memory/tagged_memory_interface.h" - -namespace mpact::sim::cheriot { - -namespace internal { - -MemoryUseTracker::~MemoryUseTracker() { - for (auto &[unused, bits] : memory_use_map_) { - delete[] bits; - } - memory_use_map_.clear(); -} - -// Static helper function. -static inline void MarkUsedBits(uint64_t byte_offset, int mask, uint8_t *bits) { - // Shift right by two, so that every byte offset becomes a word offset. - uint64_t word_offset = byte_offset >> 2; - // Byte offs - uint64_t byte_index = word_offset >> 3; - int bit_index = word_offset & 0b111; - - bits[byte_index] |= (mask << bit_index); -} - -void MemoryUseTracker::MarkUsed(uint64_t address, int size) { - // The profiling is done on a word boundary, so word or smaller is marked by - // a single bit, double words by 2 bits. - uint8_t mask = size == 8 ? 0b11 : 0b1; - if ((address >= last_start_) && (address + size - 1 <= last_end_)) { - return MarkUsedBits(address - last_start_, mask, last_used_); - } - auto it = memory_use_map_.find(AddressRange{address, address + size - 1}); - if (it == memory_use_map_.end()) { - // Compute new base and top addresses. - uint64_t start = address & ~kBaseMask; - uint64_t end = start + kSegmentSize - 1; - auto *bits = new uint8_t[kBitsSize]; - std::memset(bits, 0, kBitsSize); - - it = memory_use_map_.insert(std::make_pair(AddressRange{start, end}, bits)) - .first; - } - last_start_ = it->first.start; - last_end_ = it->first.end; - last_used_ = it->second; - MarkUsedBits(address - last_start_, mask, it->second); -} - -// Write out ranges of words that have been used. -void MemoryUseTracker::WriteUseProfile(std::ostream &os) const { - // Current range info. - uint64_t range_start = 0; - uint64_t range_end = 0; - bool range_started = false; - for (auto const &[range, bits] : memory_use_map_) { - auto base = range.start; - int byte_index = 0; - uint8_t byte = bits[byte_index]; - while (byte_index < kBitsSize) { - if (range_started) { - // If we have a range started and the accesses are contiguous, increment - // the byte index and continue. - if (byte == 0xff) { - byte_index++; - if (byte_index < kBitsSize) byte = bits[byte_index]; - continue; - } - // Compute the end of the current range by counting the number of - // consecutive ones starting from the lsb. - int bit_indx = absl::countr_one(byte) - 1; - range_end = base + (byte_index * 8 + bit_indx) * kGranularity; - // Output range. - os << absl::StrFormat("0x%llx,0x%llx,%llu\n", range_start, range_end, - range_end - range_start + 4); - // Clear those bits from the current byte, then we start looking for - // a new range. - byte &= ~((1 << (bit_indx + 1)) - 1); - range_started = false; - } - // If we are here, range_started is false. - if (byte == 0) { - byte_index++; - if (byte_index < kBitsSize) byte = bits[byte_index]; - continue; - } - // At this point byte != 0, and we need to start a new range. - int bit_indx = absl::countr_zero(byte); - range_start = base + (byte_index * 8 + bit_indx) * kGranularity; - // Set the low bits in the current byte so that we can determine the end - // of this range, in case it is contained in this byte. - byte |= ((1 << bit_indx) - 1); - range_started = true; - } - } -} - -} // namespace internal - -MemoryUseProfiler::MemoryUseProfiler() : MemoryUseProfiler(nullptr) {} - -MemoryUseProfiler::MemoryUseProfiler(MemoryInterface *memory) - : memory_(memory) {} - -void MemoryUseProfiler::Load(uint64_t address, DataBuffer *db, - Instruction *inst, ReferenceCount *context) { - if (is_enabled_) tracker_.MarkUsed(address, db->size<uint8_t>()); - if (memory_) memory_->Load(address, db, inst, context); -} - -void MemoryUseProfiler::Load(DataBuffer *address_db, DataBuffer *mask_db, - int el_size, DataBuffer *db, Instruction *inst, - ReferenceCount *context) { - if (is_enabled_) { - for (int i = 0; i < address_db->size<uint64_t>(); ++i) { - if (mask_db->Get<uint8_t>(i)) { - tracker_.MarkUsed(address_db->Get<uint64_t>(i), el_size); - } - } - } - if (memory_) memory_->Load(address_db, mask_db, el_size, db, inst, context); -} - -void MemoryUseProfiler::Store(uint64_t address, DataBuffer *db) { - if (is_enabled_) tracker_.MarkUsed(address, db->size<uint8_t>()); - if (memory_) memory_->Store(address, db); -} - -void MemoryUseProfiler::Store(DataBuffer *address_db, DataBuffer *mask_db, - int el_size, DataBuffer *db) { - if (is_enabled_) { - for (int i = 0; i < address_db->size<uint64_t>(); ++i) { - if (mask_db->Get<uint8_t>(i)) { - tracker_.MarkUsed(address_db->Get<uint64_t>(i), el_size); - } - } - } - if (memory_) memory_->Store(address_db, mask_db, el_size, db); -} - -TaggedMemoryUseProfiler::TaggedMemoryUseProfiler() - : TaggedMemoryUseProfiler(nullptr) {} - -TaggedMemoryUseProfiler::TaggedMemoryUseProfiler( - TaggedMemoryInterface *tagged_memory) - : tagged_memory_(tagged_memory) {} - -void TaggedMemoryUseProfiler::Load(uint64_t address, DataBuffer *db, - Instruction *inst, ReferenceCount *context) { - if (is_enabled_) tracker_.MarkUsed(address, db->size<uint8_t>()); - if (tagged_memory_) tagged_memory_->Load(address, db, inst, context); -} - -void TaggedMemoryUseProfiler::Load(DataBuffer *address_db, DataBuffer *mask_db, - int el_size, DataBuffer *db, - Instruction *inst, ReferenceCount *context) { - if (is_enabled_) { - for (int i = 0; i < address_db->size<uint64_t>(); ++i) { - if (mask_db->Get<uint8_t>(i)) { - tracker_.MarkUsed(address_db->Get<uint64_t>(i), el_size); - } - } - } - if (tagged_memory_) - tagged_memory_->Load(address_db, mask_db, el_size, db, inst, context); -} - -void TaggedMemoryUseProfiler::Load(uint64_t address, DataBuffer *db, - DataBuffer *tags, Instruction *inst, - ReferenceCount *context) { - if ((db != nullptr) && is_enabled_) - tracker_.MarkUsed(address, db->size<uint8_t>()); - if (tagged_memory_) tagged_memory_->Load(address, db, tags, inst, context); -} - -void TaggedMemoryUseProfiler::Store(uint64_t address, DataBuffer *db) { - if (is_enabled_) tracker_.MarkUsed(address, db->size<uint8_t>()); - if (tagged_memory_) tagged_memory_->Store(address, db); -} - -void TaggedMemoryUseProfiler::Store(DataBuffer *address_db, DataBuffer *mask_db, - int el_size, DataBuffer *db) { - if (is_enabled_) { - for (int i = 0; i < address_db->size<uint64_t>(); ++i) { - if (mask_db->Get<uint8_t>(i)) { - tracker_.MarkUsed(address_db->Get<uint64_t>(i), el_size); - } - } - } - if (tagged_memory_) tagged_memory_->Store(address_db, mask_db, el_size, db); -} - -void TaggedMemoryUseProfiler::Store(uint64_t address, DataBuffer *db, - DataBuffer *tags) { - if ((db != nullptr) && is_enabled_) - tracker_.MarkUsed(address, db->size<uint8_t>()); - if (tagged_memory_) tagged_memory_->Store(address, db, tags); -} - -} // namespace mpact::sim::cheriot
diff --git a/cheriot/memory_use_profiler.h b/cheriot/memory_use_profiler.h deleted file mode 100644 index 68e8fd4..0000000 --- a/cheriot/memory_use_profiler.h +++ /dev/null
@@ -1,134 +0,0 @@ -/* - * 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. - */ - -#ifndef MPACT_CHERIOT__MEMORY_USE_PROFILER_H_ -#define MPACT_CHERIOT__MEMORY_USE_PROFILER_H_ - -#include <cstdint> -#include <ostream> - -#include "absl/container/btree_map.h" -#include "mpact/sim/generic/data_buffer.h" -#include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/ref_count.h" -#include "mpact/sim/util/memory/memory_interface.h" -#include "mpact/sim/util/memory/memory_watcher.h" -#include "mpact/sim/util/memory/tagged_memory_interface.h" - -namespace mpact::sim::cheriot { - -using AddressRange = util::MemoryWatcher::AddressRange; -using AddressRangeLess = util::MemoryWatcher::AddressRangeLess; -using generic::DataBuffer; -using generic::Instruction; -using generic::ReferenceCount; -using util::MemoryInterface; -using util::TaggedMemoryInterface; - -namespace internal { - -// This class is used to track the use of word addresses. It dynamically -// allocates tracking memory as needed, and marks a bit for each word that is -// accessed. -class MemoryUseTracker { - public: - MemoryUseTracker() = default; - ~MemoryUseTracker(); - - // Memory use is tracked on word granularity. - static constexpr int kGranularity = sizeof(uint32_t); - // The segment size is the size of the address range for each segment. - static constexpr int kSegmentSize = 128 * 1024; - static constexpr int kBaseMask = kSegmentSize - 1; - // Size of each 'use' bit store. - static constexpr int kBitsSize = kSegmentSize / (kGranularity * 8); - void MarkUsed(uint64_t address, int size); - void WriteUseProfile(std::ostream &os) const; - - private: - uint64_t last_start_ = 0; - uint64_t last_end_ = 0; - uint8_t *last_used_ = nullptr; - absl::btree_map<AddressRange, uint8_t *, AddressRangeLess> memory_use_map_; -}; - -} // namespace internal - -// Use profiler for the MemoryInterface. -class MemoryUseProfiler : public MemoryInterface { - public: - // The default constructor does not set up memory forwarding. - MemoryUseProfiler(); - explicit MemoryUseProfiler(MemoryInterface *memory); - ~MemoryUseProfiler() override = default; - - // Inherited from memory interfaces. - void Load(uint64_t address, DataBuffer *db, Instruction *inst, - ReferenceCount *context) override; - void Load(DataBuffer *address_db, DataBuffer *mask_db, int el_size, - DataBuffer *db, Instruction *inst, - ReferenceCount *context) override; - void Store(uint64_t address, DataBuffer *db) override; - void Store(DataBuffer *address_db, DataBuffer *mask_db, int el_size, - DataBuffer *db) override; - - void WriteProfile(std::ostream &os) const { tracker_.WriteUseProfile(os); } - - // Accessor. - void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; } - bool is_enabled() const { return is_enabled_; } - - private: - bool is_enabled_ = false; - MemoryInterface *memory_; - internal::MemoryUseTracker tracker_; -}; - -// Use profiler for the TaggedMemoryInterface. -class TaggedMemoryUseProfiler : public TaggedMemoryInterface { - public: - // The default constructor does not set up memory forwarding. - TaggedMemoryUseProfiler(); - explicit TaggedMemoryUseProfiler(TaggedMemoryInterface *tagged_memory); - ~TaggedMemoryUseProfiler() override = default; - - // Inherited from memory interfaces. - void Load(uint64_t address, DataBuffer *db, Instruction *inst, - ReferenceCount *context) override; - void Load(DataBuffer *address_db, DataBuffer *mask_db, int el_size, - DataBuffer *db, Instruction *inst, - ReferenceCount *context) override; - void Load(uint64_t address, DataBuffer *db, DataBuffer *tags, - Instruction *inst, ReferenceCount *context) override; - void Store(uint64_t address, DataBuffer *db) override; - void Store(DataBuffer *address_db, DataBuffer *mask_db, int el_size, - DataBuffer *db) override; - void Store(uint64_t address, DataBuffer *db, DataBuffer *tags) override; - - void WriteProfile(std::ostream &os) const { tracker_.WriteUseProfile(os); } - - // Accessor. - void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; } - - private: - bool is_enabled_ = false; - TaggedMemoryInterface *tagged_memory_; - internal::MemoryUseTracker tracker_; -}; - -} // namespace mpact::sim::cheriot - -#endif // MPACT_CHERIOT__MEMORY_USE_PROFILER_H_
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc index 81b7a7f..65dce13 100644 --- a/cheriot/mpact_cheriot.cc +++ b/cheriot/mpact_cheriot.cc
@@ -42,8 +42,6 @@ #include "cheriot/cheriot_instrumentation_control.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" -#include "cheriot/memory_use_profiler.h" -#include "cheriot/profiler.h" #include "cheriot/riscv_cheriot_minstret.h" #include "mpact/sim/generic/core_debug_interface.h" #include "mpact/sim/generic/counters.h" @@ -51,11 +49,13 @@ #include "mpact/sim/proto/component_data.pb.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/memory_interface.h" +#include "mpact/sim/util/memory/memory_use_profiler.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/memory/single_initiator_router.h" #include "mpact/sim/util/memory/tagged_flat_demand_memory.h" #include "mpact/sim/util/memory/tagged_memory_interface.h" #include "mpact/sim/util/memory/tagged_memory_watcher.h" +#include "mpact/sim/util/other/instruction_profiler.h" #include "mpact/sim/util/other/simple_uart.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "re2/re2.h" @@ -66,8 +66,8 @@ using ::mpact::sim::proto::ComponentData; using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange; using ::mpact::sim::cheriot::CheriotInstrumentationControl; -using ::mpact::sim::cheriot::Profiler; -using ::mpact::sim::cheriot::TaggedMemoryUseProfiler; +using ::mpact::sim::util::InstructionProfiler; +using ::mpact::sim::util::TaggedMemoryUseProfiler; // Flags for specifying interactive mode. ABSL_FLAG(bool, i, false, "Interactive mode"); @@ -255,9 +255,9 @@ data_memory, static_cast<MemoryInterface *>(router)); // Enable instruction profiling if the flag is set. - Profiler *inst_profiler = nullptr; + InstructionProfiler *inst_profiler = nullptr; if (absl::GetFlag(FLAGS_inst_profile)) { - inst_profiler = new Profiler(elf_loader, 2); + inst_profiler = new InstructionProfiler(elf_loader, 2); cheriot_top.counter_pc()->AddListener(inst_profiler); } else { cheriot_top.counter_pc()->SetIsEnabled(false);
diff --git a/cheriot/profiler.cc b/cheriot/profiler.cc deleted file mode 100644 index 9e90cec..0000000 --- a/cheriot/profiler.cc +++ /dev/null
@@ -1,130 +0,0 @@ -// 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 "third_party/elfio/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
diff --git a/cheriot/profiler.h b/cheriot/profiler.h deleted file mode 100644 index 8076836..0000000 --- a/cheriot/profiler.h +++ /dev/null
@@ -1,85 +0,0 @@ -/* - * 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. - */ - -#ifndef MPACT_CHERIOT__PROFILER_H_ -#define MPACT_CHERIOT__PROFILER_H_ - -#include <cstdint> -#include <ostream> - -#include "absl/container/btree_map.h" -#include "mpact/sim/generic/counters_base.h" -#include "mpact/sim/util/memory/memory_watcher.h" -#include "mpact/sim/util/program_loader/elf_program_loader.h" - -// This file contains a class definition for a profiler. It connects to a -// counter, and whenever that counter's value is changed, the profiler takes -// the new value using the CounterValueSetInterface SetValue() and adds that -// sample to the profile. Instruction profiling is implemented by connecting -// a profiler instance to a counter that has pc values written to it. - -namespace mpact::sim::cheriot { - -using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange; -using AddressRangeLess = mpact::sim::util::MemoryWatcher::AddressRangeLess; - -using ::mpact::sim::generic::CounterValueSetInterface; -using ::mpact::sim::util::ElfProgramLoader; - -class Profiler : public CounterValueSetInterface<uint64_t> { - public: - // Currently the only constructor works from text ranges in an elf file. - // TODO(torerik): Add constructors for other sets of ranges. - // The granularity is a power of two and determines the value difference - // between two adjacent sample buckets. For instruction profiling this is - // the smallest instruction size in bytes. - Profiler(ElfProgramLoader &elf_loader, unsigned granularity); - explicit Profiler(unsigned granularity); - ~Profiler() override; - - // Inherited from CounterValueSetInterface. This will connect to a counter - // that is assigned the value to profile. The most recently used range is - // cached for performance. If it doesn't match, call AddSampleInternal(). - void SetValue(const uint64_t &value) override { - // See if the previously referenced range applies. - uint64_t sample = value >> shift_; - if ((sample >= last_start_) && (sample <= last_end_)) { - last_profile_range_[sample - last_start_]++; - return; - } - AddSampleInternal(sample); - } - - // Write the profile to the given stream in csv format. - void WriteProfile(std::ostream &os); - - // If the elf loader wasn't set in the constructor, use this method to set - // it once the elf file is available. - void SetElfLoader(ElfProgramLoader *elf_loader); - - private: - void AddSampleInternal(uint64_t sample); - int shift_ = 0; - ElfProgramLoader *elf_loader_ = nullptr; - absl::btree_map<AddressRange, uint64_t *, AddressRangeLess> profile_ranges_; - uint64_t last_start_ = 0xffff'ffff'ffff'ffffULL; - uint64_t last_end_ = 0xffff'ffff'ffff'ffffULL; - uint64_t *last_profile_range_ = nullptr; -}; - -} // namespace mpact::sim::cheriot - -#endif // MPACT_CHERIOT__PROFILER_H_
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD index a7894ae..bcac027 100644 --- a/cheriot/test/BUILD +++ b/cheriot/test/BUILD
@@ -204,19 +204,6 @@ ) cc_test( - name = "memory_use_profiler_test", - size = "small", - srcs = ["memory_use_profiler_test.cc"], - deps = [ - "//cheriot:instrumentation", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_googletest//:gtest_main", - "@com_google_mpact-sim//mpact/sim/generic:core", - ], -) - -cc_test( name = "librenode_mpact_cheriot.so_test", size = "small", srcs = ["librenode_mpact_cheriot_so_test.cc"],
diff --git a/cheriot/test/memory_use_profiler_test.cc b/cheriot/test/memory_use_profiler_test.cc deleted file mode 100644 index abb32c5..0000000 --- a/cheriot/test/memory_use_profiler_test.cc +++ /dev/null
@@ -1,180 +0,0 @@ -// 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/memory_use_profiler.h" - -#include <cstdint> -#include <iostream> - -#include "absl/strings/str_format.h" -#include "googlemock/include/gmock/gmock.h" -#include "mpact/sim/generic/data_buffer.h" - -namespace { - -using ::mpact::sim::cheriot::MemoryUseProfiler; -using ::mpact::sim::cheriot::internal::MemoryUseTracker; -using ::mpact::sim::generic::DataBuffer; -using ::mpact::sim::generic::DataBufferFactory; - -constexpr uint64_t kMemoryBase = 0x1234'5678; - -class MemoryUseProfilerTest : public ::testing::Test { - public: - MemoryUseProfilerTest() { - db1_ = db_factory_.Allocate<uint8_t>(1); - db2_ = db_factory_.Allocate<uint16_t>(1); - db4_ = db_factory_.Allocate<uint32_t>(1); - db8_ = db_factory_.Allocate<uint64_t>(1); - profiler_.set_is_enabled(true); - } - - ~MemoryUseProfilerTest() override { - db1_->DecRef(); - db2_->DecRef(); - db4_->DecRef(); - db8_->DecRef(); - } - - MemoryUseProfiler profiler_; - DataBufferFactory db_factory_; - DataBuffer *db1_; - DataBuffer *db2_; - DataBuffer *db4_; - DataBuffer *db8_; -}; - -// If no references are captured, then there shouldn't be any output. -TEST_F(MemoryUseProfilerTest, NoReferences) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output, ""); -} - -// Test single memory references. -TEST_F(MemoryUseProfilerTest, SingleByteLoad) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Load(kMemoryBase, db1_, nullptr, nullptr); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleHalfLoad) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Load(kMemoryBase, db2_, nullptr, nullptr); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleWordLoad) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Load(kMemoryBase, db4_, nullptr, nullptr); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleDoubleLoad) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Load(kMemoryBase, db8_, nullptr, nullptr); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase + 4))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleByteStore) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Store(kMemoryBase, db1_); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleHalfStore) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Store(kMemoryBase, db2_); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleWordStore) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Store(kMemoryBase, db4_); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SingleDoubleStore) { - testing::internal::CaptureStdout(); - profiler_.WriteProfile(std::cout); - profiler_.Store(kMemoryBase, db8_); - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase + 4))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SpanInSingleRange) { - testing::internal::CaptureStdout(); - for (int i = 0; i < 0x64; i += 4) { - profiler_.Load(kMemoryBase + i, db4_, nullptr, nullptr); - } - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr(absl::StrFormat( - "0x%llx,0x%llx", kMemoryBase, kMemoryBase + 0x60))) - << output; -} - -TEST_F(MemoryUseProfilerTest, SpanInMultipleRanges) { - testing::internal::CaptureStdout(); - auto seg_size = MemoryUseTracker::kSegmentSize; - for (int i = 0; i < seg_size; i += 4) { - profiler_.Load(kMemoryBase + i, db4_, nullptr, nullptr); - } - profiler_.WriteProfile(std::cout); - auto output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, - testing::HasSubstr(absl::StrFormat("0x%llx,0x%llx", kMemoryBase, - kMemoryBase + seg_size - 4))) - << output; -} -} // namespace