16#include "llvm/ADT/STLExtras.h"
17#include "llvm/Support/MathExtras.h"
33 .drop_front(line_offset);
38 "whole line inserted at an unaligned address!");
40 "whole line inserted with a partial buffer!");
42 std::copy(src.begin(), src.end(), line.get());
49 const addr_t end_addr = llvm::SaturatingAdd(addr, size - 1);
50 const uint64_t first_idx =
IndexOf(addr);
51 const uint64_t last_idx =
IndexOf(end_addr);
52 m_lines.remove_if([first_idx, last_idx](
const auto &entry) {
53 return entry.getFirst() >= first_idx && entry.getFirst() <= last_idx;
57ChunkCache::Collection::const_iterator
59 auto pos =
m_chunks.upper_bound(addr);
65 return addr - pos->first < pos->second.size() ? pos :
m_chunks.end();
72 return llvm::ArrayRef(pos->second).drop_front(addr - pos->first);
78 const addr_t last_addr = llvm::SaturatingAdd<addr_t>(addr, src.size() - 1);
79 const uint64_t len = last_addr - addr + 1;
81 for (uint64_t offset = 0; offset < len;) {
82 const addr_t curr_addr = addr + offset;
83 if (
const llvm::ArrayRef<uint8_t> held =
Lookup(curr_addr); !held.empty()) {
84 offset += std::min<uint64_t>(held.size(), len - offset);
88 auto next =
m_chunks.lower_bound(curr_addr);
89 const uint64_t gap_len =
92 : std::min<uint64_t>(next->first - curr_addr, len - offset);
93 const llvm::ArrayRef<uint8_t> gap_bytes = src.slice(offset, gap_len);
94 m_chunks[curr_addr].assign(gap_bytes.begin(), gap_bytes.end());
102 const addr_t end_addr = llvm::SaturatingAdd(addr, size - 1);
104 auto pos =
m_chunks.lower_bound(addr);
107 auto prev = std::prev(pos);
108 if (addr - prev->first < prev->second.size())
111 while (pos !=
m_chunks.end() && pos->first <= end_addr)
124 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
127 if (clear_invalid_ranges)
133 InsertData(addr, {
static_cast<const uint8_t *
>(src), src_len});
137 llvm::ArrayRef<uint8_t> src) {
140 m_L1_cache.EraseRange(line_base_addr, src.size());
144 assert(src.size() <=
m_L2_cache.GetLineByteSize() &&
146 (addr + src.size() - 1) /
m_L2_cache.GetLineByteSize() &&
147 "a partial-line insert must not cross a cache line boundary");
159 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
160 const addr_t last_addr = llvm::SaturatingAdd<addr_t>(addr, src.size() - 1);
161 src = src.take_front(last_addr - addr + 1);
162 const uint32_t line_size =
m_L2_cache.GetLineByteSize();
165 if (
const uint64_t line_offset = addr % line_size) {
166 const uint64_t head_len =
167 std::min<uint64_t>(line_size - line_offset, src.size());
170 src = src.drop_front(head_len);
174 while (src.size() >= line_size) {
177 src = src.drop_front(line_size);
194 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
203 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
213 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
214 const uint32_t idx =
m_invalid_ranges.FindEntryIndexThatContains(base_addr);
227 size_t bytes_filled = 0;
230 const uint64_t space_to_top =
UINT64_MAX - addr;
231 while (bytes_filled < len) {
232 if (bytes_filled > space_to_top)
234 const addr_t curr_addr = addr + bytes_filled;
237 llvm::ArrayRef<uint8_t> cached =
m_L2_cache.Lookup(curr_addr);
243 const size_t to_copy = std::min(cached.size(), len - bytes_filled);
244 memcpy(
static_cast<uint8_t *
>(dst) + bytes_filled, cached.data(), to_copy);
245 bytes_filled += to_copy;
252 size_t bytes_filled)
const {
253 const uint64_t line_size =
m_L2_cache.GetLineByteSize();
254 const addr_t line_base_addr = llvm::alignDown(read_addr, line_size);
256 static constexpr uint32_t kMaxCacheLinesPerRead = 2;
257 const uint64_t grow_span = kMaxCacheLinesPerRead * line_size;
261 if (line_base_addr >
UINT64_MAX - grow_span ||
262 caller_end > line_base_addr + grow_span)
263 return AddrRange(read_addr, caller_end - read_addr);
269 (caller_end <= line_base_addr + line_size || bytes_filled == 0))
270 read_addr = line_base_addr;
274 addr_t last_line_addr = llvm::alignDown(caller_end - 1, line_size);
275 if (last_line_addr > line_base_addr &&
m_L2_cache.Holds(last_line_addr))
276 last_line_addr -= line_size;
277 const addr_t grow_target = last_line_addr + line_size;
280 addr_t read_end = grow_target;
281 if (grow_target > caller_end) {
285 read_end = invalid->GetRangeBase();
287 return AddrRange(read_addr, read_end - read_addr);
292 if (!dst || dst_len == 0)
295 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
300 invalid_addr = invalid->GetRangeBase();
302 "memory read failed for 0x%" PRIx64, invalid_addr);
303 if (invalid_addr <= addr)
305 dst_len = invalid_addr - addr;
309 if (bytes_from_cache == dst_len)
312 addr_t read_addr = addr + bytes_from_cache;
313 addr_t read_end = addr + dst_len;
322 std::vector<uint8_t> read_buf(read_end - read_addr);
323 const size_t bytes_from_inferior =
m_process.ReadMemoryFromInferior(
324 read_addr, read_buf.data(), read_buf.size(),
error);
325 if (bytes_from_inferior == 0)
326 return bytes_from_cache;
328 AddCacheData(read_addr, read_buf.data(), bytes_from_inferior);
332 uint8_t *dst_tail =
static_cast<uint8_t *
>(dst) + bytes_from_cache;
333 return bytes_from_cache +
ReadFromCaches(addr + bytes_from_cache, dst_tail,
334 dst_len - bytes_from_cache);
337llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
339 llvm::MutableArrayRef<uint8_t> buffer) {
342 auto total_ranges_len = llvm::sum_of(
343 llvm::map_range(ranges, [](
auto range) {
return range.size; }));
344 assert(buffer.size() >= total_ranges_len &&
345 "MemoryCache::ReadRanges: provided buffer is too short");
346 if (buffer.size() < total_ranges_len) {
347 llvm::MutableArrayRef<uint8_t> empty;
348 return {ranges.size(), empty};
351 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
353 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> results;
354 results.reserve(ranges.size());
355 llvm::SmallVector<Range<lldb::addr_t, size_t>> missed_ranges;
358 for (
auto range : ranges) {
360 const size_t len = range.GetByteSize();
363 results.push_back(buffer.take_front(0));
368 results.push_back(buffer.take_front(len));
369 buffer = buffer.drop_front(len);
374 results.emplace_back(
nullptr,
nullptr);
375 missed_ranges.push_back(range);
378 if (missed_ranges.empty())
381 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> fetched_buffers_vec =
382 m_process.DoReadMemoryRanges(missed_ranges, buffer);
383 auto fetched_buffers = llvm::ArrayRef(fetched_buffers_vec);
385 for (
auto [missed_range, fetched] : llvm::zip(missed_ranges, fetched_buffers))
389 for (
auto &result : results)
390 if (result.data() ==
nullptr)
391 result = fetched_buffers.consume_front();
397 uint32_t permissions, uint32_t chunk_size)
403 assert(byte_size > chunk_size);
415 for (
size_t i=0; i<free_count; ++i)
418 const lldb::addr_t range_size = free_block.GetByteSize();
419 if (range_size >= size)
424 addr_t addr = free_block.GetRangeBase();
446 free_block.SetRangeBase(reserved_block.
GetRangeEnd());
447 free_block.SetByteSize(bytes_left);
461 bool success =
false;
480 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
481 if (
m_process.IsAlive() && deallocate_memory) {
482 PermissionsToBlockMap::iterator pos, end =
m_memory_map.end();
484 m_process.DoDeallocateMemory(pos->second->GetBaseAddress());
493 const size_t page_size = 4096;
494 const size_t num_pages = (byte_size + page_size - 1) / page_size;
495 const size_t page_byte_size = num_pages * page_size;
501 "Process::DoAllocateMemory (byte_size = 0x%8.8" PRIx32
502 ", permissions = %s) => 0x%16.16" PRIx64,
507 block_sp = std::make_shared<AllocatedBlock>(addr, page_byte_size,
508 permissions, chunk_size);
509 m_memory_map.insert(std::make_pair(permissions, block_sp));
515 uint32_t permissions,
517 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
520 std::pair<PermissionsToBlockMap::iterator, PermissionsToBlockMap::iterator>
523 for (PermissionsToBlockMap::iterator pos = range.first; pos != range.second;
525 addr = (*pos).second->ReserveBlock(byte_size);
534 addr = block_sp->ReserveBlock(byte_size);
538 "AllocatedMemoryCache::AllocateMemory (byte_size = 0x%8.8" PRIx32
539 ", permissions = %s) => 0x%16.16" PRIx64,
546 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
548 PermissionsToBlockMap::iterator pos, end =
m_memory_map.end();
549 bool success =
false;
551 if (pos->second->Contains(addr)) {
552 success = pos->second->FreeBlock(addr);
558 "AllocatedMemoryCache::DeallocateMemory (addr = 0x%16.16" PRIx64
560 (uint64_t)addr, success);
565 std::lock_guard<std::recursive_mutex> guard(
m_mutex);
567 return llvm::any_of(
m_memory_map, [addr](
const auto &block) {
568 return block.second->Contains(addr);
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOGF(log,...)
#define LLDB_LOG_VERBOSE(log,...)
uint32_t CalculateChunksNeededForSize(uint32_t size) const
bool FreeBlock(lldb::addr_t addr)
lldb::addr_t ReserveBlock(uint32_t size)
const uint32_t m_permissions
Range< lldb::addr_t, uint32_t > m_range
AllocatedBlock(lldb::addr_t addr, uint32_t byte_size, uint32_t permissions, uint32_t chunk_size)
RangeVector< lldb::addr_t, uint32_t > m_free_blocks
RangeVector< lldb::addr_t, uint32_t > m_reserved_blocks
const uint32_t m_chunk_size
lldb::addr_t AllocateMemory(size_t byte_size, uint32_t permissions, Status &error)
bool IsInCache(lldb::addr_t addr) const
AllocatedMemoryCache(Process &process)
std::recursive_mutex m_mutex
void Clear(bool deallocate_memory)
bool DeallocateMemory(lldb::addr_t ptr)
std::shared_ptr< AllocatedBlock > AllocatedBlockSP
PermissionsToBlockMap m_memory_map
AllocatedBlockSP AllocatePage(uint32_t byte_size, uint32_t permissions, uint32_t chunk_size, Status &error)
llvm::ArrayRef< uint8_t > Lookup(lldb::addr_t addr) const
The cached bytes from addr to the end of the chunk holding it, empty if no chunk holds it.
Collection::const_iterator FindChunkContaining(lldb::addr_t addr) const
The chunk holding addr, or end().
void EraseRange(lldb::addr_t addr, lldb::addr_t size)
Drop every chunk that intersects [addr, addr+size).
void InsertMissing(lldb::addr_t addr, llvm::ArrayRef< uint8_t > src)
Add the bytes of [addr, addr+src.size()) that no chunk holds yet.
uint64_t IndexOf(lldb::addr_t addr) const
uint32_t m_line_byte_size
llvm::ArrayRef< uint8_t > Lookup(lldb::addr_t addr) const
The cached bytes from addr to the end of the line holding it, empty if that line is not resident.
void Insert(lldb::addr_t addr, llvm::ArrayRef< uint8_t > src)
Add one whole line.
void EraseRange(lldb::addr_t addr, lldb::addr_t size)
Drop every line that intersects [addr, addr+size).
uint32_t GetMemoryCacheLineSize() const
MemoryCache(Process &process)
std::recursive_mutex m_mutex
void AddCacheData(lldb::addr_t addr, const void *src, size_t src_len)
Allow external sources to populate data into the memory cache.
bool RemoveInvalidRange(lldb::addr_t base_addr, lldb::addr_t byte_size)
void Flush(lldb::addr_t addr, size_t size)
void InsertWholeLine(lldb::addr_t line_base_addr, llvm::ArrayRef< uint8_t > src)
llvm::SmallVector< llvm::MutableArrayRef< uint8_t > > ReadRanges(llvm::ArrayRef< Range< lldb::addr_t, size_t > > ranges, llvm::MutableArrayRef< uint8_t > buffer)
Reads memory ranges, serving hits from the cache and batching misses through Process::DoReadMemoryRan...
InvalidRanges m_invalid_ranges
void InsertPartialLine(lldb::addr_t addr, llvm::ArrayRef< uint8_t > src)
void Clear(bool clear_invalid_ranges=false)
size_t ReadFromCaches(lldb::addr_t addr, void *dst, size_t len) const
void InsertData(lldb::addr_t addr, llvm::ArrayRef< uint8_t > src)
AddrRange GrowReadRange(lldb::addr_t read_addr, lldb::addr_t caller_end, size_t bytes_filled) const
size_t Read(lldb::addr_t addr, void *dst, size_t dst_len, Status &error)
void AddInvalidRange(lldb::addr_t base_addr, lldb::addr_t byte_size)
Range< lldb::addr_t, lldb::addr_t > AddrRange
A plug-in interface definition class for debugging a process.
Range< lldb::addr_t, lldb::addr_t > Entry
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
#define LLDB_INVALID_ADDRESS
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
const char * GetPermissionsAsCString(uint32_t permissions)
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
BaseType GetRangeBase() const
SizeType GetByteSize() const
BaseType GetRangeEnd() const
void SetByteSize(SizeType s)