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