| // 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 |