Debugger: Add API to read/write chunks of memory.

Using base64 at this point to keep JSON.
This commit is contained in:
Unknown W. Brackets 2020-12-28 10:30:58 -08:00
parent f19d957c42
commit 7e5052bc9e
4 changed files with 132 additions and 0 deletions

View file

@ -33,3 +33,48 @@ std::string Base64Encode(const uint8_t *p, size_t sz) {
return result;
}
std::vector<uint8_t> Base64Decode(const char *s, size_t sz) {
const uint8_t lookup[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 63, 62, 255, 63,
// '0' starts here.
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
// 'A' after an invalid.
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
// 'a' after an invalid.
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
};
std::vector<uint8_t> result;
result.reserve(3 * sz / 4);
for (size_t i = 0; i < sz; i += 4) {
uint8_t quad[4] = {
lookup[s[i]],
i + 1 < sz ? lookup[s[i + 1]] : 255,
i + 2 < sz ? lookup[s[i + 2]] : 255,
i + 3 < sz ? lookup[s[i + 3]] : 255,
};
// First: ABCDEF GHXXXX XXXXXX XXXXXX. Neither 6-bit value should be invalid.
result.push_back((quad[0] << 2) | ((quad[1] & 0x30) >> 4));
// Next: XXXXXX XXABCD EFGHXX XXXXXX. Invalid if quad[2] is invalid.
if (quad[2] == 255) {
continue;
}
result.push_back(((quad[1] & 0x0F) << 4) | ((quad[2] & 0x3C) >> 2));
// Last: XXXXXX XXXXXX XXXXAB CDEFGH. Invalid only if quad[3] is.
if (quad[3] == 255) {
continue;
}
result.push_back(((quad[2] & 0x03) << 6) | (quad[3] & 0x3F));
}
return result;
}

View file

@ -1,5 +1,8 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
std::string Base64Encode(const uint8_t *p, size_t sz);
std::vector<uint8_t> Base64Decode(const char *s, size_t sz);

View file

@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/Data/Encoding/Base64.h"
#include "Common/StringUtils.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSDebugInterface.h"
@ -27,9 +29,11 @@ DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map) {
map["memory.read_u8"] = &WebSocketMemoryReadU8;
map["memory.read_u16"] = &WebSocketMemoryReadU16;
map["memory.read_u32"] = &WebSocketMemoryReadU32;
map["memory.read"] = &WebSocketMemoryRead;
map["memory.write_u8"] = &WebSocketMemoryWriteU8;
map["memory.write_u16"] = &WebSocketMemoryWriteU16;
map["memory.write_u32"] = &WebSocketMemoryWriteU32;
map["memory.write"] = &WebSocketMemoryWrite;
return nullptr;
}
@ -112,6 +116,50 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) {
json.writeUint("value", Memory::Read_U32(addr));
}
// Read bytes from memory (memory.read)
//
// Parameters:
// - address: unsigned integer address for the start of the memory range.
// - size: unsigned integer specifying size of memory range.
//
// Response (same event name):
// - base64: base64 encode of binary data.
void WebSocketMemoryRead(DebuggerRequest &req) {
auto memLock = Memory::Lock();
if (!currentDebugMIPS->isAlive() || !Memory::IsActive())
return req.Fail("CPU not started");
uint32_t addr;
if (!req.ParamU32("address", &addr)) {
return;
}
uint32_t size;
if (!req.ParamU32("size", &size)) {
return;
}
if (!Memory::IsValidAddress(addr)) {
return req.Fail("Invalid address");
} else if (!Memory::IsValidRange(addr, size)) {
return req.Fail("Invalid size");
}
JsonWriter &json = req.Respond();
// Start a value without any actual data yet...
json.writeRaw("base64", "");
req.Flush();
// Now we'll write it directly to the stream.
req.ws->AddFragment(false, "\"");
// 65535 is an "even" number of base64 characters.
static const size_t CHUNK_SIZE = 65535;
for (size_t i = 0; i < size; i += CHUNK_SIZE) {
size_t left = std::min(size - i, CHUNK_SIZE);
req.ws->AddFragment(false, Base64Encode(Memory::GetPointerUnchecked(addr) + i, left));
}
req.ws->AddFragment(false, "\"");
}
// Write a byte to memory (memory.write_u8)
//
// Parameters:
@ -204,3 +252,37 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) {
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U32(addr));
}
// Write bytes to memory (memory.write)
//
// Parameters:
// - address: unsigned integer address for the start of the memory range.
// - base64: data to write, encoded as base64 string
//
// Response (same event name) with no extra data.
void WebSocketMemoryWrite(DebuggerRequest &req) {
auto memLock = Memory::Lock();
if (!currentDebugMIPS->isAlive() || !Memory::IsActive())
return req.Fail("CPU not started");
uint32_t addr;
if (!req.ParamU32("address", &addr)) {
return;
}
std::string encoded;
if (!req.ParamString("base64", &encoded)) {
return;
}
std::vector<uint8_t> value = Base64Decode(&encoded[0], encoded.size());
uint32_t size = (uint32_t)value.size();
if (!Memory::IsValidAddress(addr)) {
return req.Fail("Invalid address");
} else if (value.size() != (size_t)size || !Memory::IsValidRange(addr, size)) {
return req.Fail("Invalid size");
}
Memory::MemcpyUnchecked(addr, &value[0], size);
req.Respond();
}

View file

@ -24,6 +24,8 @@ DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map);
void WebSocketMemoryReadU8(DebuggerRequest &req);
void WebSocketMemoryReadU16(DebuggerRequest &req);
void WebSocketMemoryReadU32(DebuggerRequest &req);
void WebSocketMemoryRead(DebuggerRequest &req);
void WebSocketMemoryWriteU8(DebuggerRequest &req);
void WebSocketMemoryWriteU16(DebuggerRequest &req);
void WebSocketMemoryWriteU32(DebuggerRequest &req);
void WebSocketMemoryWrite(DebuggerRequest &req);