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