Skip to content

Commit

Permalink
Only discard the debugger state cache after the target has stopped. Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
xusheng6 committed Aug 15, 2024
1 parent 8edc178 commit 51f3b38
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 30 deletions.
6 changes: 2 additions & 4 deletions core/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1509,11 +1509,11 @@ void DebuggerController::EventHandler(const DebuggerEvent& event)
// Todo: this is just a temporary workaround. Otherwise, the connection status would not be set properly
m_state->SetConnectionStatus(DebugAdapterConnectedStatus);
m_state->SetExecutionStatus(DebugAdapterRunningStatus);
m_state->MarkDirty();
break;
}
case TargetExitedEventType:
m_exitCode = event.data.exitData.exitCode;
m_state->MarkDirty();
case QuitDebuggingEventType:
case DetachedEventType:
case LaunchFailureEventType:
Expand All @@ -1534,6 +1534,7 @@ void DebuggerController::EventHandler(const DebuggerEvent& event)
}
case TargetStoppedEventType:
{
m_state->MarkDirty();
m_state->UpdateCaches();
m_state->SetConnectionStatus(DebugAdapterConnectedStatus);
m_state->SetExecutionStatus(DebugAdapterPausedStatus);
Expand Down Expand Up @@ -1705,9 +1706,6 @@ DataBuffer DebuggerController::ReadMemory(std::uintptr_t address, std::size_t si
if (!m_state->IsConnected())
return DataBuffer {};

if (m_state->IsRunning())
return DataBuffer {};

DebuggerMemory* memory = m_state->GetMemory();
if (!memory)
return DataBuffer {};
Expand Down
82 changes: 58 additions & 24 deletions core/debuggerstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,62 @@ DebuggerMemory::DebuggerMemory(DebuggerState* state) : m_state(state) {}
void DebuggerMemory::MarkDirty()
{
std::unique_lock<std::recursive_mutex> memoryLock(m_memoryMutex);
m_valueCache.clear();
m_errorCache.clear();
for (auto& it: m_valueCache)
{
if (it.second.status == UpToDateStatus)
it.second.status = OutOfDateStatus;
else
it.second.status = DefaultStatus;
}
}


DataBuffer DebuggerMemory::ReadBlock(uint64_t block)
{
auto iter = m_valueCache.find(block);
if (iter != m_valueCache.end())
{
switch (iter->second.status)
{
case FailedToReadStatus:
return {};
case OutOfDateStatus:
{
if (m_state->IsConnected() && m_state->IsRunning())
{
// The cache is old but the target is running, return old value
return iter->second.value;
}
// Break out and try to read the new value
break;
}
case UpToDateStatus:
{
// Cache is up-to-date, return the value
return iter->second.value;
}
case DefaultStatus:
// There is no useful information about the status, break out and try to read it
break;
}
}

// Try to read the memory value from the backend
if (m_state->IsConnected() && !m_state->IsRunning())
{
// The cache is old and the target is stopped, try to update the cache value
DataBuffer buffer = m_state->GetAdapter()->ReadMemory(block, 0x100);
if (buffer.GetLength() > 0)
{
// Successfully updated
m_valueCache[block] = {buffer, UpToDateStatus};
return buffer;
}
}

// Update failed
m_valueCache[block] = {{}, FailedToReadStatus};
return {};
}


Expand All @@ -639,30 +693,10 @@ DataBuffer DebuggerMemory::ReadMemory(uint64_t offset, size_t len)
// List of 256-byte block addresses to read into the cache to fully cover this region
for (uint64_t block = cacheStart; block < cacheEnd; block += 0x100)
{
// If any block cannot be read, then return false
if (m_errorCache.find(block) != m_errorCache.end())
{
auto cached = ReadBlock(block);
if (cached.GetLength() == 0)
return result;
}

auto iter = m_valueCache.find(block);
if (iter == m_valueCache.end())
{
// The ReadMemory() function should return the number of bytes read
DataBuffer buffer = m_state->GetAdapter()->ReadMemory(block, 0x100);
// TODO: what if the buffer's size is smaller than 0x100
if (buffer.GetLength() > 0)
{
m_valueCache[block] = buffer;
}
else
{
m_errorCache.insert(block);
return result;
}
}

DataBuffer cached = m_valueCache[block];
if (offset + len < block + cached.GetLength())
{
// Last block
Expand Down
19 changes: 17 additions & 2 deletions core/debuggerstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,33 @@ namespace BinaryNinjaDebugger {
bool ResumeThread(std::uint32_t tid);
};

enum MemoryByteCacheStatus
{
DefaultStatus,
UpToDateStatus,
OutOfDateStatus,
FailedToReadStatus
};


struct MemoryBytesCache
{
DataBuffer value;
MemoryByteCacheStatus status;
};


class DebuggerMemory
{
DebuggerState* m_state;
std::map<uint64_t, DataBuffer> m_valueCache;
std::set<uint64_t> m_errorCache;
std::map<uint64_t, MemoryBytesCache> m_valueCache;
std::recursive_mutex m_memoryMutex;

public:
DebuggerMemory(DebuggerState* state);

void MarkDirty();
DataBuffer ReadBlock(uint64_t block);
DataBuffer ReadMemory(uint64_t offset, size_t len);
bool WriteMemory(std::uintptr_t address, const DataBuffer& buffer);
};
Expand Down

0 comments on commit 51f3b38

Please sign in to comment.