CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp
Views: 1401
// Copyright (c) 2021- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include "Core/MIPS/MIPS.h"19#include "Core/MIPS/MIPSDebugInterface.h"20#include "Core/Debugger/MemBlockInfo.h"21#include "Core/Debugger/WebSocket/MemoryInfoSubscriber.h"22#include "Core/Debugger/WebSocket/WebSocketUtils.h"23#include "Core/MemMap.h"2425class WebSocketMemoryInfoState : public DebuggerSubscriber {26public:27WebSocketMemoryInfoState() {28}29~WebSocketMemoryInfoState() {30UpdateOverride(false);31}3233void Mapping(DebuggerRequest &req);34void Config(DebuggerRequest &req);35void Set(DebuggerRequest &req);36void List(DebuggerRequest &req);37void Search(DebuggerRequest &req);3839protected:40void UpdateOverride(bool flag);4142bool detailOverride_ = false;43};4445DebuggerSubscriber *WebSocketMemoryInfoInit(DebuggerEventHandlerMap &map) {46auto p = new WebSocketMemoryInfoState();47map["memory.mapping"] = std::bind(&WebSocketMemoryInfoState::Mapping, p, std::placeholders::_1);48map["memory.info.config"] = std::bind(&WebSocketMemoryInfoState::Config, p, std::placeholders::_1);49map["memory.info.set"] = std::bind(&WebSocketMemoryInfoState::Set, p, std::placeholders::_1);50map["memory.info.list"] = std::bind(&WebSocketMemoryInfoState::List, p, std::placeholders::_1);51map["memory.info.search"] = std::bind(&WebSocketMemoryInfoState::Search, p, std::placeholders::_1);5253return p;54}5556void WebSocketMemoryInfoState::UpdateOverride(bool flag) {57if (detailOverride_ && !flag)58MemBlockReleaseDetailed();59if (!detailOverride_ && flag)60MemBlockOverrideDetailed();61detailOverride_ = flag;62}6364// List memory map data (memory.mapping)65//66// No parameters.67//68// Response (same event name):69// - ranges: array of objects:70// - type: one of "ram", "vram", "sram".71// - subtype: "primary" or "mirror".72// - name: string, friendly name.73// - address: number, start address of range.74// - size: number, in bytes.75//76// Note: Even if you set false, may stay enabled if set by user or another debug session.77void WebSocketMemoryInfoState::Mapping(DebuggerRequest &req) {78struct MemRange {79const char *type;80const char *subtype;81const char *name;82const uint32_t address;83const uint32_t size;84};85constexpr uint32_t kernelMemorySize = PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase();86constexpr uint32_t volatileMemorySize = PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart();87static constexpr MemRange ranges[] = {88{ "sram", "primary", "Scratchpad", PSP_GetScratchpadMemoryBase(), Memory::SCRATCHPAD_SIZE },89{ "vram", "primary", "VRAM", PSP_GetVidMemBase(), Memory::VRAM_SIZE },90{ "vram", "mirror", "VRAM (Swizzled)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 1, Memory::VRAM_SIZE },91{ "vram", "mirror", "VRAM (Mirror)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 2, Memory::VRAM_SIZE },92{ "vram", "mirror", "VRAM (Swizzled + Interleaved)", PSP_GetVidMemBase() + Memory::VRAM_SIZE * 3, Memory::VRAM_SIZE },93{ "ram", "primary", "Kernel Memory", 0x80000000 | PSP_GetKernelMemoryBase(), kernelMemorySize },94{ "ram", "primary", "Volatile Memory", PSP_GetVolatileMemoryStart(), volatileMemorySize },95// Size is specially calculated.96{ "ram", "primary", "User Memory", PSP_GetUserMemoryBase(), 0 },97};9899JsonWriter &json = req.Respond();100json.pushArray("ranges");101for (auto range : ranges) {102uint32_t size = range.size;103if (size == 0) {104size = Memory::g_MemorySize;105if (size == 0) {106size = Memory::RAM_NORMAL_SIZE;107}108size -= kernelMemorySize + volatileMemorySize;109}110json.pushDict();111json.writeString("type", range.type);112json.writeString("subtype", range.subtype);113json.writeString("name", range.name);114json.writeUint("address", range.address);115json.writeUint("size", size);116json.pop();117118// Also write the uncached range.119json.pushDict();120json.writeString("type", range.type);121json.writeString("subtype", "mirror");122json.writeString("name", std::string("Uncached ") + range.name);123json.writeUint("address", 0x40000000 | range.address);124json.writeUint("size", size);125json.pop();126}127json.pop();128}129130// Update memory info tracking config (memory.info.config)131//132// Parameters:133// - detailed: optional, boolean to force enable detailed tracking (perf impact.)134//135// Response (same event name):136// - detailed: boolean state of tracking before any changes.137//138// Note: Even if you set false, may stay enabled if set by user or another debug session.139void WebSocketMemoryInfoState::Config(DebuggerRequest &req) {140bool setDetailed = req.HasParam("detailed");141bool detailed = false;142if (!req.ParamBool("detailed", &detailed, DebuggerParamType::OPTIONAL))143return;144145JsonWriter &json = req.Respond();146json.writeBool("detailed", MemBlockInfoDetailed());147148if (setDetailed)149UpdateOverride(detailed);150}151152static MemBlockFlags FlagFromType(const std::string &type) {153if (type == "write")154return MemBlockFlags::WRITE;155if (type == "texture")156return MemBlockFlags::TEXTURE;157if (type == "alloc")158return MemBlockFlags::ALLOC;159if (type == "suballoc")160return MemBlockFlags::SUB_ALLOC;161if (type == "free")162return MemBlockFlags::FREE;163if (type == "subfree")164return MemBlockFlags::SUB_FREE;165return MemBlockFlags::SKIP_MEMCHECK;166}167168static std::string TypeFromFlag(const MemBlockFlags &flag) {169if (flag & MemBlockFlags::WRITE)170return "write";171else if (flag & MemBlockFlags::TEXTURE)172return "texture";173else if (flag & MemBlockFlags::ALLOC)174return "alloc";175else if (flag & MemBlockFlags::SUB_ALLOC)176return "suballoc";177return "error";178}179180// Update memory info tagging (memory.info.set)181//182// Parameters:183// - address: number representing start address of the range to modify.184// - size: number, bytes from start address.185// - type: string, one of:186// - "write" for last modification information.187// - "texture" for last texture usage information.188// - "alloc" for allocation information.189// - "suballoc" for allocations within an existing allocation.190// - "free" to mark a previous allocation and its suballocations freed (ignores tag.)191// - "subfree" to mark a previous suballocation freed (ignores tag.)192// - tag: string label to give the memory. Optional if type if free or subfree.193// - pc: optional, number indicating PC address for this tag.194//195// Response (same event name) with no extra data.196//197// Note: Only one tag per type is maintained for any given memory address.198// Small extent info may be ignored unless detailed tracking enabled (see memory.info.config.)199void WebSocketMemoryInfoState::Set(DebuggerRequest &req) {200if (!currentDebugMIPS->isAlive() || !Memory::IsActive())201return req.Fail("CPU not started");202203std::string type;204if (!req.ParamString("type", &type))205return;206std::string tag;207if (type != "free" && type != "subfree") {208if (!req.ParamString("tag", &tag))209return;210}211uint32_t addr;212if (!req.ParamU32("address", &addr))213return;214uint32_t size;215if (!req.ParamU32("size", &size))216return;217uint32_t pc = currentMIPS->pc;218if (!req.ParamU32("pc", &pc, false, DebuggerParamType::OPTIONAL))219return;220221MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type);222if (flags == MemBlockFlags::SKIP_MEMCHECK)223return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree");224225if (!Memory::IsValidAddress(addr))226return req.Fail("Invalid address");227else if (!Memory::IsValidRange(addr, size))228return req.Fail("Invalid size");229230NotifyMemInfoPC(flags, addr, size, pc, tag.c_str(), tag.size());231req.Respond();232}233234// List memory info tags for address range (memory.info.list)235//236// Parameters:237// - address: number representing start address of the range.238// - size: number, bytes from start address.239// - type: optional string to limit information to one of:240// - "write" for last modification information.241// - "texture" for last texture usage information.242// - "alloc" for allocation information.243// - "suballoc" for allocations within an existing allocation.244//245// Response (same event name):246// - extents: array of objects:247// - type: one of the above type string values.248// - address: number (may be outside requested range if overlapping.)249// - size: number (may be outside requested range if overlapping.)250// - ticks: number indicating tick counter as of last tag.251// - pc: number address of last tag.252// - tag: string tag for this memory extent.253// - allocated: boolean, if this extent is marked as allocated (for alloc/suballoc types.)254void WebSocketMemoryInfoState::List(DebuggerRequest &req) {255if (!currentDebugMIPS->isAlive() || !Memory::IsActive())256return req.Fail("CPU not started");257258std::string type;259if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL))260return;261uint32_t addr;262if (!req.ParamU32("address", &addr))263return;264uint32_t size;265if (!req.ParamU32("size", &size))266return;267268// Allow type to be omitted.269MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type);270if (flags == MemBlockFlags::SKIP_MEMCHECK && req.HasParam("type"))271return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree");272273if (!Memory::IsValidAddress(addr))274return req.Fail("Invalid address");275else if (!Memory::IsValidRange(addr, size))276return req.Fail("Invalid size");277278std::vector<MemBlockInfo> results;279if (flags == MemBlockFlags::SKIP_MEMCHECK)280results = FindMemInfo(addr, size);281else282results = FindMemInfoByFlag(flags, addr, size);283284JsonWriter &json = req.Respond();285json.pushArray("extents");286for (const auto &result : results) {287json.pushDict();288json.writeString("type", TypeFromFlag(result.flags));289json.writeUint("address", result.start);290json.writeUint("size", result.size);291json.writeFloat("ticks", result.ticks);292json.writeUint("pc", result.pc);293json.writeString("tag", result.tag);294json.writeBool("allocated", result.allocated);295json.pop();296}297json.pop();298}299300// Search memory info tags for a string (memory.info.search)301//302// Parameters:303// - address: optional number representing start address of the range.304// - end: optional end address as a number (otherwise uses start address.)305// - match: string to search for within tag.306// - type: optional string to limit information to one of:307// - "write" for last modification information.308// - "texture" for last texture usage information.309// - "alloc" for allocation information.310// - "suballoc" for allocations within an existing allocation.311//312// Response (same event name):313// - extent: null, or matching object containing:314// - type: one of the above type string values.315// - address: number (may be outside requested range if overlapping.)316// - size: number (may be outside requested range if overlapping.)317// - ticks: number indicating tick counter as of last tag.318// - pc: number address of last tag.319// - tag: string tag for this memory extent.320// - allocated: boolean, if this extent is marked as allocated (for alloc/suballoc types.)321//322// Note: may not be fast.323void WebSocketMemoryInfoState::Search(DebuggerRequest &req) {324if (!currentDebugMIPS->isAlive() || !Memory::IsActive())325return req.Fail("CPU not started");326327uint32_t start = 0;328if (!req.ParamU32("address", &start, false, DebuggerParamType::OPTIONAL))329return;330uint32_t end = start;331if (!req.ParamU32("end", &end, false, DebuggerParamType::OPTIONAL))332return;333std::string type;334if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL))335return;336std::string match;337if (!req.ParamString("match", &match))338return;339340// Allow type to be omitted.341MemBlockFlags flags = MemBlockFlags::SKIP_MEMCHECK | FlagFromType(type);342if (flags == MemBlockFlags::SKIP_MEMCHECK && req.HasParam("type"))343return req.Fail("Invaid type - expecting write, texture, alloc, suballoc, free, or subfree");344345start = RoundMemAddressUp(start);346end = RoundMemAddressUp(end);347std::transform(match.begin(), match.end(), match.begin(), ::tolower);348349bool found = false;350MemBlockInfo foundResult;351352uint32_t addr = start;353constexpr uint32_t CHUNK_SIZE = 0x1000;354do {355uint32_t chunk_end = addr + CHUNK_SIZE;356if (addr < end && chunk_end >= end) {357chunk_end = end;358}359360std::vector<MemBlockInfo> results;361if (flags == MemBlockFlags::SKIP_MEMCHECK)362results = FindMemInfo(addr, chunk_end - addr);363else364results = FindMemInfoByFlag(flags, addr, chunk_end - addr);365366for (const auto &result : results) {367std::string lowercase = result.tag;368std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), ::tolower);369370if (lowercase.find(match) != lowercase.npos) {371found = true;372foundResult = result;373break;374}375}376addr = RoundMemAddressUp(chunk_end);377} while (!found && addr != end);378379JsonWriter &json = req.Respond();380if (found) {381json.pushDict("extent");382json.writeString("type", TypeFromFlag(foundResult.flags));383json.writeUint("address", foundResult.start);384json.writeUint("size", foundResult.size);385json.writeFloat("ticks", foundResult.ticks);386json.writeUint("pc", foundResult.pc);387json.writeString("tag", foundResult.tag);388json.writeBool("allocated", foundResult.allocated);389json.pop();390} else {391json.writeNull("extent");392}393}394395396