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/BreakpointSubscriber.cpp
Views: 1401
// Copyright (c) 2018- 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 "Common/StringUtils.h"18#include "Core/Debugger/Breakpoints.h"19#include "Core/Debugger/DisassemblyManager.h"20#include "Core/Debugger/SymbolMap.h"21#include "Core/Debugger/WebSocket/BreakpointSubscriber.h"22#include "Core/Debugger/WebSocket/WebSocketUtils.h"23#include "Core/MIPS/MIPSDebugInterface.h"2425DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {26// No need to bind or alloc state, these are all global.27map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;28map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;29map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove;30map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList;3132map["memory.breakpoint.add"] = &WebSocketMemoryBreakpointAdd;33map["memory.breakpoint.update"] = &WebSocketMemoryBreakpointUpdate;34map["memory.breakpoint.remove"] = &WebSocketMemoryBreakpointRemove;35map["memory.breakpoint.list"] = &WebSocketMemoryBreakpointList;3637return nullptr;38}3940struct WebSocketCPUBreakpointParams {41uint32_t address = 0;42bool hasEnabled = false;43bool hasLog = false;44bool hasCondition = false;45bool hasLogFormat = false;4647bool enabled;48bool log;49std::string condition;50PostfixExpression compiledCondition;51std::string logFormat;5253bool Parse(DebuggerRequest &req) {54if (!currentDebugMIPS->isAlive()) {55req.Fail("CPU not started");56return false;57}5859if (!req.ParamU32("address", &address))60return false;6162hasEnabled = req.HasParam("enabled");63if (hasEnabled) {64if (!req.ParamBool("enabled", &enabled))65return false;66}67hasLog = req.HasParam("log");68if (hasLog) {69if (!req.ParamBool("log", &log))70return false;71}72hasCondition = req.HasParam("condition");73if (hasCondition) {74if (!req.ParamString("condition", &condition))75return false;76if (!currentDebugMIPS->initExpression(condition.c_str(), compiledCondition)) {77req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError()));78return false;79}80}81hasLogFormat = req.HasParam("logFormat");82if (hasLogFormat) {83if (!req.ParamString("logFormat", &logFormat))84return false;85}8687return true;88}8990void Apply() {91if (hasCondition && !condition.empty()) {92BreakPointCond cond;93cond.debug = currentDebugMIPS;94cond.expressionString = condition;95cond.expression = compiledCondition;96CBreakPoints::ChangeBreakPointAddCond(address, cond);97} else if (hasCondition && condition.empty()) {98CBreakPoints::ChangeBreakPointRemoveCond(address);99}100101if (hasLogFormat) {102CBreakPoints::ChangeBreakPointLogFormat(address, logFormat);103}104105// TODO: Fix this interface.106if (hasLog && !hasEnabled) {107CBreakPoints::IsAddressBreakPoint(address, &enabled);108hasEnabled = true;109}110if (hasLog && hasEnabled) {111BreakAction result = BREAK_ACTION_IGNORE;112if (log)113result |= BREAK_ACTION_LOG;114if (enabled)115result |= BREAK_ACTION_PAUSE;116CBreakPoints::ChangeBreakPoint(address, result);117} else if (hasEnabled) {118CBreakPoints::ChangeBreakPoint(address, enabled);119}120}121};122123// Add a new CPU instruction breakpoint (cpu.breakpoint.add)124//125// Parameters:126// - address: unsigned integer address of instruction to break at.127// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips.128// - log: optional boolean, whether to log when this breakpoint trips.129// - condition: optional string expression to evaluate - breakpoint does not trip if false.130// - logFormat: optional string to log when breakpoint trips, may include {expression} parts.131//132// Response (same event name) with no extra data.133//134// Note: will replace any breakpoint at the same address.135void WebSocketCPUBreakpointAdd(DebuggerRequest &req) {136WebSocketCPUBreakpointParams params;137if (!params.Parse(req))138return;139140CBreakPoints::AddBreakPoint(params.address);141params.Apply();142req.Respond();143}144145// Update a CPU instruction breakpoint (cpu.breakpoint.update)146//147// Parameters:148// - address: unsigned integer address of instruction to break at.149// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips.150// - log: optional boolean, whether to log when this breakpoint trips.151// - condition: optional string expression to evaluate - breakpoint does not trip if false.152// - logFormat: optional string to log when breakpoint trips, may include {expression} parts.153//154// Response (same event name) with no extra data.155void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) {156WebSocketCPUBreakpointParams params;157if (!params.Parse(req))158return;159bool enabled;160if (!CBreakPoints::IsAddressBreakPoint(params.address, &enabled))161return req.Fail("Breakpoint not found");162163params.Apply();164req.Respond();165}166167// Remove a CPU instruction breakpoint (cpu.breakpoint.remove)168//169// Parameters:170// - address: unsigned integer address of instruction to break at.171//172// Response (same event name) with no extra data.173void WebSocketCPUBreakpointRemove(DebuggerRequest &req) {174if (!currentDebugMIPS->isAlive()) {175return req.Fail("CPU not started");176}177178uint32_t address;179if (!req.ParamU32("address", &address))180return;181182CBreakPoints::RemoveBreakPoint(address);183req.Respond();184}185186// List all CPU instruction breakpoints (cpu.breakpoint.list)187//188// No parameters.189//190// Response (same event name):191// - breakpoints: array of objects, each with properties:192// - address: unsigned integer address of instruction to break at.193// - enabled: boolean, whether to actually enter stepping when this breakpoint trips.194// - log: optional boolean, whether to log when this breakpoint trips.195// - condition: null, or string expression to evaluate - breakpoint does not trip if false.196// - logFormat: null, or string to log when breakpoint trips, may include {expression} parts.197// - symbol: null, or string label or symbol at breakpoint address.198// - code: string disassembly of breakpoint address.199void WebSocketCPUBreakpointList(DebuggerRequest &req) {200if (!currentDebugMIPS->isAlive()) {201return req.Fail("CPU not started");202}203204JsonWriter &json = req.Respond();205json.pushArray("breakpoints");206auto bps = CBreakPoints::GetBreakpoints();207for (const auto &bp : bps) {208if (bp.temporary)209continue;210211json.pushDict();212json.writeUint("address", bp.addr);213json.writeBool("enabled", bp.IsEnabled());214json.writeBool("log", (bp.result & BREAK_ACTION_LOG) != 0);215if (bp.hasCond)216json.writeString("condition", bp.cond.expressionString);217else218json.writeNull("condition");219if (!bp.logFormat.empty())220json.writeString("logFormat", bp.logFormat);221else222json.writeNull("logFormat");223std::string symbol = g_symbolMap->GetLabelString(bp.addr);224if (symbol.empty())225json.writeNull("symbol");226else227json.writeString("symbol", symbol);228229DisassemblyManager manager;230DisassemblyLineInfo line;231manager.getLine(manager.getStartAddress(bp.addr), true, line);232json.writeString("code", line.name + " " + line.params);233234json.pop();235}236json.pop();237}238239struct WebSocketMemoryBreakpointParams {240uint32_t address = 0;241uint32_t end = 0;242bool hasEnabled = false;243bool hasLog = false;244bool hasCond = false;245bool hasCondition = false;246bool hasLogFormat = false;247248bool enabled = true;249bool log = true;250MemCheckCondition cond = MEMCHECK_READWRITE;251std::string condition;252PostfixExpression compiledCondition;253std::string logFormat;254255bool Parse(DebuggerRequest &req) {256if (!currentDebugMIPS->isAlive()) {257req.Fail("CPU not started");258return false;259}260261if (!req.ParamU32("address", &address))262return false;263uint32_t size;264if (!req.ParamU32("size", &size))265return false;266if (address + size < address) {267req.Fail("Size is too large");268return false;269}270end = size == 0 ? 0 : address + size;271272hasEnabled = req.HasParam("enabled");273if (hasEnabled) {274if (!req.ParamBool("enabled", &enabled))275return false;276}277hasLog = req.HasParam("log");278if (hasLog) {279if (!req.ParamBool("log", &log))280return false;281}282hasCond = req.HasParam("read") || req.HasParam("write") || req.HasParam("change");283if (hasCond) {284bool read = false, write = false, change = false;285if (!req.ParamBool("read", &read, DebuggerParamType::OPTIONAL) || !req.ParamBool("write", &write, DebuggerParamType::OPTIONAL) || !req.ParamBool("change", &change, DebuggerParamType::OPTIONAL))286return false;287int bits = (read ? MEMCHECK_READ : 0) | (write ? MEMCHECK_WRITE : 0) | (change ? MEMCHECK_WRITE_ONCHANGE : 0);288cond = MemCheckCondition(bits);289}290hasCondition = req.HasParam("condition");291if (hasCondition) {292if (!req.ParamString("condition", &condition))293return false;294if (!currentDebugMIPS->initExpression(condition.c_str(), compiledCondition)) {295req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError()));296return false;297}298}299hasLogFormat = req.HasParam("logFormat");300if (hasLogFormat) {301if (!req.ParamString("logFormat", &logFormat))302return false;303}304305return true;306}307308BreakAction Result(bool adding) {309int bits = MEMCHECK_READWRITE;310if (adding || (hasLog && hasEnabled)) {311bits = (enabled ? BREAK_ACTION_PAUSE : 0) | (log ? BREAK_ACTION_LOG : 0);312} else {313MemCheck prev;314if (CBreakPoints::GetMemCheck(address, end, &prev))315bits = prev.result;316317if (hasEnabled)318bits = (bits & ~BREAK_ACTION_PAUSE) | (enabled ? BREAK_ACTION_PAUSE : 0);319if (hasLog)320bits = (bits & ~BREAK_ACTION_LOG) | (log ? BREAK_ACTION_LOG : 0);321}322323return BreakAction(bits);324}325326void Apply() {327if (hasCondition && !condition.empty()) {328BreakPointCond cond;329cond.debug = currentDebugMIPS;330cond.expressionString = condition;331cond.expression = compiledCondition;332CBreakPoints::ChangeMemCheckAddCond(address, end, cond);333} else if (hasCondition && condition.empty()) {334CBreakPoints::ChangeMemCheckRemoveCond(address, end);335}336if (hasLogFormat) {337CBreakPoints::ChangeMemCheckLogFormat(address, end, logFormat);338}339}340};341342// Add a new memory breakpoint (memory.breakpoint.add)343//344// Parameters:345// - address: unsigned integer address for the start of the memory range.346// - size: unsigned integer specifying size of memory range.347// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips.348// - log: optional boolean, whether to log when this breakpoint trips.349// - read: optional boolean, whether to trip on any read to this address.350// - write: optional boolean, whether to trip on any write to this address.351// - change: optional boolean, whether to trip on a write to this address which modifies data352// (or any write that may modify data.)353// - condition: optional string expression to evaluate - breakpoint does not trip if false.354// - logFormat: optional string to log when breakpoint trips, may include {expression} parts.355//356// Response (same event name) with no extra data.357//358// Note: will replace any breakpoint that has the same start address and size.359void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) {360WebSocketMemoryBreakpointParams params;361if (!params.Parse(req))362return;363364CBreakPoints::AddMemCheck(params.address, params.end, params.cond, params.Result(true));365params.Apply();366req.Respond();367}368369// Update a memory breakpoint (memory.breakpoint.update)370//371// Parameters:372// - address: unsigned integer address for the start of the memory range.373// - size: unsigned integer specifying size of memory range.374// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips.375// - log: optional boolean, whether to log when this breakpoint trips.376// - read: optional boolean, whether to trip on any read to this address.377// - write: optional boolean, whether to trip on any write to this address.378// - change: optional boolean, whether to trip on a write to this address which modifies data379// (or any write that may modify data.)380// - condition: optional string expression to evaluate - breakpoint does not trip if false.381// - logFormat: optional string to log when breakpoint trips, may include {expression} parts.382//383// Response (same event name) with no extra data.384void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) {385WebSocketMemoryBreakpointParams params;386if (!params.Parse(req))387return;388389MemCheck mc;390if (!CBreakPoints::GetMemCheck(params.address, params.end, &mc))391return req.Fail("Breakpoint not found");392393CBreakPoints::ChangeMemCheck(params.address, params.end, params.cond, params.Result(true));394params.Apply();395req.Respond();396}397398// Remove a memory breakpoint (memory.breakpoint.remove)399//400// Parameters:401// - address: unsigned integer address for the start of the memory range.402// - size: unsigned integer specifying size of memory range.403//404// Response (same event name) with no extra data.405void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) {406if (!currentDebugMIPS->isAlive()) {407return req.Fail("CPU not started");408}409410uint32_t address;411if (!req.ParamU32("address", &address))412return;413uint32_t size;414if (!req.ParamU32("size", &size))415return;416417CBreakPoints::RemoveMemCheck(address, size == 0 ? 0 : address + size);418req.Respond();419}420421// List all memory breakpoints (memory.breakpoint.list)422//423// No parameters.424//425// Response (same event name):426// - breakpoints: array of objects, each with properties:427// - address: unsigned integer address for the start of the memory range.428// - size: unsigned integer specifying size of memory range.429// - enabled: boolean, whether to actually enter stepping when this breakpoint trips.430// - log: optional boolean, whether to log when this breakpoint trips.431// - read: optional boolean, whether to trip on any read to this address.432// - write: optional boolean, whether to trip on any write to this address.433// - change: optional boolean, whether to trip on a write to this address which modifies data434// (or any write that may modify data.)435// - condition: null, or string expression to evaluate - breakpoint does not trip if false.436// - logFormat: null, or string to log when breakpoint trips, may include {expression} parts.437// - symbol: null, or string label or symbol at breakpoint address.438void WebSocketMemoryBreakpointList(DebuggerRequest &req) {439if (!currentDebugMIPS->isAlive()) {440return req.Fail("CPU not started");441}442443JsonWriter &json = req.Respond();444json.pushArray("breakpoints");445auto mcs = CBreakPoints::GetMemChecks();446for (const auto &mc : mcs) {447json.pushDict();448json.writeUint("address", mc.start);449json.writeUint("size", mc.end == 0 ? 0 : mc.end - mc.start);450json.writeBool("enabled", mc.IsEnabled());451json.writeBool("log", (mc.result & BREAK_ACTION_LOG) != 0);452json.writeBool("read", (mc.cond & MEMCHECK_READ) != 0);453json.writeBool("write", (mc.cond & MEMCHECK_WRITE) != 0);454json.writeBool("change", (mc.cond & MEMCHECK_WRITE_ONCHANGE) != 0);455json.writeUint("hits", mc.numHits);456if (mc.hasCondition)457json.writeString("condition", mc.condition.expressionString);458else459json.writeNull("condition");460if (!mc.logFormat.empty())461json.writeString("logFormat", mc.logFormat);462else463json.writeNull("logFormat");464std::string symbol = g_symbolMap->GetLabelString(mc.start);465if (symbol.empty())466json.writeNull("symbol");467else468json.writeString("symbol", symbol);469470json.pop();471}472json.pop();473}474475476