拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SharpEmu 实时调试服务器(Live Debug Server)完全指南:架构、JSON-Lines 线协议与客户端接入

SharpEmu 实时调试服务器Live Debug Server完全指南架构、JSON-Lines 线协议与客户端接入【免费下载链接】sharpemuAn experimental PlayStation 5 emulator for Windows, Linux and macOS.项目地址: https://gitcode.com/GitHub_Trending/sh/sharpemu导读SharpEmu 是一个面向 Windows、Linux 与 macOS 的实验性 PlayStation 5 模拟器其 CPU 核心直接原生执行 guest x86-64 代码。为了让外部进程能够实时检视并控制正在运行的 guestSharpEmu 内置了一个通过 TCP 暴露的实时调试服务器Live Debug Server服务器运行在模拟器进程内部配套的SharpEmu.DebugClient独立可执行程序是它的一个客户端而其行分隔 JSON 线协议足够简单可以用nc、脚本或任何自定义工具直接驱动。本文以仓库中的 docs/debugger-server.md 为骨架结合SharpEmu.Debugger、SharpEmu.Core、SharpEmu.CLI等源码实现完整讲解调试服务器的分层架构、执行模型、启用方式、线协议规范、协议替换与嵌入方式并给出可直接复制的实战操作步骤。读完本文你将掌握如何用--debug-server启动调试服务器并配合 stop-at-entry 抢占断点窗口、如何使用SharpEmu.DebugClient交互式或脚本化驱动目标、如何用无依赖的 Python 浏览器前端进行图形化调试以及如何基于DebuggerServerHost在自己的代码中嵌入调试服务器。Layering四层装配与单向依赖调试功能被刻意拆分为四个程序集依赖方向非常明确——Core 保持对调试器零感知只发布一个接缝seam程序集角色SharpEmu.Core定义调度器接缝ICpuDebugHook/ICpuDebugFrame命名空间SharpEmu.Core.Cpu.Debugging以及CpuExecutionOptions.DebugHook注入槽。Core不引用调试器。SharpEmu.Debugger调试器本体实现接缝的DebuggerSession、BreakpointStore、TCP 版DebuggerServer、可插拔的IDebugProtocol含 JSON-lines 实现以及一站式装配类DebuggerServerHost。SharpEmu.CLI解析--debug-server参数构建DebuggerServerHost把它的Hook交给SharpEmuRuntimeOptions.DebugHook并管理其生命周期。SharpEmu.DebugClient独立客户端可执行程序只依赖 BCL.NET 基类库。在 src/SharpEmu.Core/Cpu/CpuExecutionOptions.cs 中可以看到这个接缝的形态DebugHook是一个可空的ICpuDebugHook属性默认null且不带来任何运行时开销而 src/SharpEmu.Core/Cpu/Debugging/ICpuDebugHook.cs 定义了三个回调OnFrameEnter(ICpuDebugFrame frame)—— 原生后端开始执行某一帧之前调用OnFrameExit(ICpuDebugFrame frame, OrbisGen2Result result)—— 帧执行完毕无论正常返回还是出错之后调用OnStall(ICpuDebugFrame frame, CpuStallInfo info)—— 后端在运行帧中检测到执行停滞如互斥锁自旋循环时调用。接口文档还特别强调实现必须是线程安全的因为帧可能由专用模拟线程派发而调试服务器在自己的线程上服务客户端。这样一来调试器可以独立演进而不触碰 CPU 核心——任何想观察执行的组件只需要实现ICpuDebugHook并通过 options 注入即可。执行模型帧边界上的停与走CpuDispatcher会为进程入口点以及每个模块初始化器module initializer开启一个全新的帧frame。当DebugHook被挂载后它会在这些边界被通知OnFrameEnter(frame)在原生后端运行帧之前调用。DebuggerSession在此决定是否停止——判断依据依次是暂停请求、入口地址断点、单步请求、以及 stop-at-entry。若要停止它会在调用内部把模拟线程停放在一道闸门gate上此时帧保持存活客户端可以读写寄存器与内存。continue/step会释放这道闸门。OnFrameExit(frame, result)帧完成之后调用。在 src/SharpEmu.Debugger/Session/DebuggerSession.cs 的OnFrameEnter实现中可以看到这一机制的全貌ResolveStopReason依次检查_pausePending返回DebugStopReason.Pause、_stepPending返回Step、Breakpoints.FindExecuteHit(frame.EntryPoint)返回Breakpoint最后是_options.StopAtEntry firstFrame返回EntryPoint一旦确定要停止就把状态置为Paused、抓取寄存器快照构造DebugStopEvent然后调用_resumeGate.Wait()阻塞模拟线程直到客户端Continue()/StepFrame()调用_resumeGate.Set()释放。这里的关键设计是暂停停放的是唯一拥有 guest 上下文的线程因此寄存器与内存访问器只有在会话报告Paused时才会被服务否则一律返回not paused客户端永远不会观察到撕裂torn状态。DebuggerSession中的所有访问器TryGetRegisters、TrySetRegister、TryReadMemory、TryWriteMemory、TryReadXmm都先经过IsPausedWithFrame检查而 src/SharpEmu.Debugger/Server/DebuggerServer.cs 中每个连接共享同一个IDebuggerSession因此多个客户端例如一个 UI 加一个脚本探针观察到的是一致的视图。目前活的与仅在表面层的能力Live已可用attach/握手、运行状态跟踪、寄存器读写、内存读写、断点管理、帧入口执行断点、暂停、帧级单步、继续以及停止/恢复/终止事件。仅表面层armed随后端钩子增长而激活逐指令单步与数据监视点readwatch/writewatch/accesswatch。协议动词与类型已经存在客户端和工具现在就可以先编写好。会话级开关DebuggerSessionOptions从 src/SharpEmu.Debugger/Session/DebuggerSessionOptions.cs 可以看到三个默认全部为true的行为开关选项默认值含义StopAtEntrytrue在观察到的第一帧暂停方便客户端在 guest 运行前挂断点等价于多数调试器的stop at entry行为。BreakOnFaulttrue帧以非 OK 结果结束CPU trap、内存故障或未实现路径时暂停让客户端在帧被销毁前检查故障后的寄存器/内存状态停止原因报告为Fault。BreakOnStalltrue后端检测到执行停滞互斥锁自旋/livelock时暂停停止原因报告为Stall。BreakOnFault的实现在DebuggerSession.OnFrameExit中当result ! OrbisGen2Result.ORBIS_GEN2_OK且状态未终止时同样停放模拟线程并通过BuildFaultStop附带 16 字节 opcode 预览ReadOpcodePreview作为结构化证据。启用服务器CLI 一行启动在模拟器 CLI 中通过--debug-server参数启用SharpEmu --debug-server /path/to/eboot.bin # 127.0.0.1:5714 SharpEmu --debug-server0.0.0.0:5714 /path/to/eboot.bin绑定地址默认是回环地址loopback可路由地址必须显式给出。由于 stop-at-entry 是默认行为guest 会在第一个帧处停住直到有客户端连接并发起continue——这给了你一个在任意 guest 代码运行之前设置断点的窗口。从 src/SharpEmu.CLI/Program.cs 的实现可以看到命令行到服务器的完整接线TryGetDebugServerOptions解析参数并复用DebuggerServerOptions.TryParseEndpoint位于 src/SharpEmu.Debugger/Server/DebuggerServerOptions.cs它支持host:port、裸port、裸 host 三种写法端口校验范围是 1–65535localhost会被当作回环处理然后创建DebuggerServerHost、调用Start()最后通过runtimeOptions with { DebugHook debugHost.Hook }把钩子注入运行时。运行结束后在finally块中依次调用debugHost.NotifyRunCompleted()与DisposeAsync()。DebuggerServerOptions还暴露了三个可配置项BindAddress默认IPAddress.Loopback、Port默认常量5714、MaxClients默认4超出的连接在 accept 队列中等待。浏览器前端零依赖的 Python 调试 UI仓库还附带一个无外部依赖的 Python 浏览器前端tools/SharpEmu.DebuggerFrontend/它只使用 Python 标准库无需安装任何包、无需 JavaScript 构建步骤。它可以选并启动一个eboot.bin、自动 attach 到其调试器并提供了执行控制、寄存器、内存检视、断点管理、进程输出和实时协议活动流./tools/SharpEmu.DebuggerFrontend/run.sh默认连接127.0.0.1:5714并打开http://127.0.0.1:8765/。完整配置与测试选项见 tools/SharpEmu.DebuggerFrontend/README.md常用参数包括--debug-host HOST Debug server host (default 127.0.0.1) --debug-port PORT Debug server port (default 5714) --listen ADDRESS Web UI bind address (default 127.0.0.1) --ui-port PORT Web UI port; 0 chooses a free port (default 8765) --no-connect Do not connect to SharpEmu automatically --no-browser Do not open a browser automatically --verbose Print HTTP request logs前端功能亮点包括连接受控与目标状态显示、本地模拟器启动 自动 attach 进程停止、前台进程实时输出、continue/pause/frame-step 控制含键盘快捷键、寄存器检视与编辑、Hex/ASCII 内存读写、断点与监视点创建/切换/删除、停止原因/帧/结果/opcode/故障详情以及基于证据的停滞诊断给出可能原因、排序后的修复建议与针对性检查。需要提醒的是HTTP 服务默认绑定回环且无认证只有在可信网络上才应使用非回环的--listen地址在 Linux 上Browse按钮依赖zenity或kdialog也可以手动输入完整路径。线协议json-lines/1双向 JSON Lines协议是每行一个 JSON 对象UTF-8 编码以\n结尾双向如此。协议名称为json-lines/1见 src/SharpEmu.Debugger/Protocol/JsonLineDebugProtocol.cs。请求Requests请求由command字符串加命令专属字段组成。数字字段接受 JSON 数字或0x前缀的十六进制字符串。完整动词表如下command字段回复dataping——status别名info—state、breakpoints、lastStop?state—stateregisters别名regs—registersrax..r15, rip, rflags, fs_base, gs_baseset-registerregister、value—read-memoryaddress、length≤ 65536address、length、bytes十六进制write-memoryaddress、bytes十六进制writtenlist-breakpoints别名breakpoints—breakpoints[]add-breakpoint别名breakaddress、kind?、length?breakpointremove-breakpoint别名delete-breakpointid—enable-breakpointid、enabled?默认 true—continue别名cont、c——step别名s——pause——从 src/SharpEmu.Debugger/Protocol/DebugCommandDispatcher.cs 的Dispatch实现可以看到命令语义唯一地集中在这个DebugCommandDispatcher中与线格式无关因此可以被所有连接共享。几个实现细节值得注意read-memory的length被钳制在 1 到MaxMemoryChunk 64 * 102465536之间超出直接报错write-memory的bytes必须是偶数长度的十六进制字符串且同样受 65536 字节上限约束add-breakpoint的kind缺省为execute可选readwatch/writewatch/accesswatchlength缺省为 1set-register支持rip、rflags与 16 个通用寄存器fs_base/gs_base由 TLS 设置逻辑拥有在会话层是只读的TrySetRegister中对它们的分支返回falseenable-breakpoint的enabled字段缺省为true即不带该字段的调用等价于启用。DebuggerServer的每个连接都由DebuggerClientConnection承载走_protocolFactory()创建的协议实例连接关闭时自动从连接字典移除并释放见 src/SharpEmu.Debugger/Server/DebuggerServer.cs。回复Replies成功与失败回复都遵循同一信封结构ok布尔值加上command成功后带data失败后带error{ok:true,command:registers,data:{ registers: { rax:0x…, … } }} {ok:false,command:read-memory,error:Target is not paused.}JsonLineDebugProtocol.WriteResponseAsync会逐字段序列化ok、command、data、error每行写入后立即 Flush保证客户端能及时收到src/SharpEmu.Debugger/Protocol/JsonLineDebugProtocol.cs。协议对畸形请求的处理也值得一提解析失败时不会直接断开客户端而是构造一个command为$parse-errorParseErrorCommand常量的合成请求交给分发器让它回一个带错误信息的失败回复。事件Events主动推送服务器会主动推送以下事件{event:hello,protocol:json-lines/1,state:Paused} {event:stopped,reason:Breakpoint,address:0x…,frameKind:ProcessEntry,frameLabel:eboot.bin,registers:{…},breakpoint:{…}} {event:resumed} {event:terminated}stopped事件的reason取值集合为EntryPoint、Breakpoint、Watchpoint、Step、Pause、Fault、Stall。其中Fault停止还会附带result与opcodeBytes前 16 字节 opcode 的十六进制预览Stall停止则附带完整的结构化证据。Stall 停滞事件的结构化证据停滞停止除了人类可读的细节之外还带有结构化证据。例如 import-loop 证据会标识出 NID、解析到的 HLE 导出、重复的 guest 返回点、派发计数以及前两个 ABI 参数{ event: stopped, reason: Stall, stall: { kind: ImportLoop, nid: 9UK1vLZQft4, instructionPointer: 0x0000000801CE2418, dispatchIndex: 40667904, argument0: 0x0000000812345000, argument1: 0x0000000000000000, resolved: true, library: libKernel, function: scePthreadMutexLock } }Python 前端正是利用这段证据来解释可能的失败类别、并对具体的检查/修复进行排序它的诊断被刻意标注为启发式heuristic——用于定位责任 HLE/调度路径但并不能替代完整跟踪。该事件字段在 src/SharpEmu.Debugger/Protocol/DebugCommandDispatcher.cs 的DescribeStop中被逐字段映射为协议载荷。使用SharpEmu.DebugClient从命令行驱动目标SharpEmu.DebugClient是一个独立、小体积的命令行客户端它不依赖任何模拟器程序集——直接通过 TCP 讲服务器的 JSON-lines 协议因此你完全可以用nc、脚本或自研工具替代它。它的构建方式dotnet build src/SharpEmu.DebugClient/SharpEmu.DebugClient.csproj快速上手启用调试服务器启动模拟器。默认监听127.0.0.1:5714在 stop-at-entry 开启时guest 会在第一个帧处停住直到你继续SharpEmu --debug-server /path/to/game/eboot.bin # 或显式指定端点 SharpEmu --debug-server127.0.0.1:5714 /path/to/game/eboot.bin另开一个终端attach 客户端SharpEmu.DebugClient # 默认 127.0.0.1:5714 SharpEmu.DebugClient 127.0.0.1:5714 # 显式端点驱动目标status regs break 0x00000008801234a0 continue mem 0x00000008802000000 64调用方式SharpEmu.DebugClient [host:port] [--exec command]... [--quiet]选项含义host:port服务器端点。默认127.0.0.1:5714localhost亦可。--exec, -e非交互式地运行一条命令后退出。可重复使用。--quiet抑制连接横幅。--help, -h显示用法与命令列表。可脚本化的非交互示例SharpEmu.DebugClient --exec break 0x8801234a0 --exec continue命令一览地址和值接受十进制或0x前缀十六进制。寄存器与内存命令只在目标处于Paused状态时才会成功。命令服务器动词说明status|infostatus目标状态加最近一次停止。statestate仅运行状态Running/Paused/…。regs|registersregisters转储整数寄存器。setreg reg valueset-register设置rip、rflags或某个通用寄存器。mem addr len|read addr lenread-memory以十六进制读取 guest 内存。write addr hexwrite-memory用十六进制字符串写 guest 内存。break addr [kind] [len]|b …add-breakpoint添加断点。kindexecute默认、readwatch、writewatch、accesswatch。bp|breakpointslist-breakpoints列出断点。del id|rm idremove-breakpoint删除断点。enable id/disable idenable-breakpoint切换断点启用状态。continue|ccontinue恢复暂停的目标。step|sstep恢复并在下一个帧边界停止。pausepause请求运行中的目标在下一个边界停止。pingping往返存活检查。raw json透传发送一条字面 JSON 请求。help|?—显示命令列表本地。quit|exit—断开并退出本地。输出语义客户端按到达顺序打印两类行reply—— 你发出的命令的响应ok以及data或errorevent—— 主动通知连接时的hello、命中断点/入口/单步/暂停时的stopped、继续时的resumed、运行结束时的terminated。因为回复和事件共享同一条流客户端打印的是它收到的一切而不是把回复与请求配对——stopped事件可能夹在你的一条命令和它的回复之间到达。状态栏明确标注infrastructure 阶段停止在帧边界进程入口与每个模块初始化器投递逐指令单步与数据监视点属于协议表面层待 CPU 后端长出对应钩子后激活。替换协议把 GDB stub 或其他封装插进去DebuggerServer的构造函数接受一个FuncIDebugProtocol协议工厂默认工厂返回JsonLineDebugProtocol见 src/SharpEmu.Debugger/Server/DebuggerServer.cs。这意味着一个 GDB remote serial stub或任何其他成帧方式都可以直接替换进来无需改动会话或命令语义——因为命令语义集中在DebugCommandDispatcher协议层只负责行的读取与写入。JsonLineDebugProtocol实现了IDebugProtocol的三个成员Name协议名json-lines/1、ReadRequestAsync逐行读请求空行跳过、坏行转成$parse-error合成请求、WriteResponseAsync/WriteEventAsync把响应/事件序列化并立即 Flush。嵌入服务器在自己的代码里三行接线如果你不想走 CLI也可以在宿主代码中直接构造DebuggerServerHost一条调用链完成建会话、起网络、注入运行时using SharpEmu.Debugger; using SharpEmu.Core.Runtime; await using var host new DebuggerServerHost(); host.Start(); var options new SharpEmuRuntimeOptions { DebugHook host.Hook }; using var runtime SharpEmuRuntime.CreateDefault(options); var result runtime.Run(ebootPath); host.NotifyRunCompleted();从 src/SharpEmu.Debugger/DebuggerServerHost.cs 可以看到这个一站式类的内部结构它同时拥有一个DebuggerSession和一个DebuggerServerHook属性把会话自身DebuggerSession实现了ICpuDebugHook暴露给运行时Start()委托给服务器开始接受客户端NotifyRunCompleted()调用会话的NotifyTerminated()释放任何被停放的模拟线程并把目标标记为终止——这样任何已连接的客户端都会收到通知且 guest 线程绝不会被卡在调试器里DisposeAsync也会先NotifyTerminated再关闭服务器。完整调用链速览把上面的内容串起来一次典型的调试会话沿如下路径流动SharpEmu.CLI解析--debug-server通过DebuggerServerOptions.TryParseEndpoint得到绑定配置构造DebuggerServerHost→ 内部创建DebuggerSession含BreakpointStore与DebuggerServer含默认JsonLineDebugProtocol工厂Start()启动 TCP 监听DebuggerServerHost.Hook通过SharpEmuRuntimeOptions.DebugHook注入运行时最终落到CpuExecutionOptions.DebugHookCpuDispatcher在每个帧边界回调ICpuDebugHookOnFrameEnter/OnFrameExit/OnStallDebuggerSession决定是否停放模拟线程客户端SharpEmu.DebugClient/ Python 前端 / 自研工具连上 TCP 端口DebuggerClientConnection用协议对象读写 JSON-lines 消息DebugCommandDispatcher把动词翻译成会话操作会话在Paused窗口内为客户端服务寄存器/内存访问continue/step释放_resumeGateguest 继续执行运行结束或宿主调用NotifyRunCompleted后所有客户端收到terminated。如需深入了解客户端实现细节可阅读 src/SharpEmu.DebugClient/DEVELOPER_READ.md调试器各组件断点存储、会话、协议、服务器的源码均在 src/SharpEmu.Debugger 目录下其对应测试可参考 tests/SharpEmu.Libs.Tests 中的调试相关用例。【免费下载链接】sharpemuAn experimental PlayStation 5 emulator for Windows, Linux and macOS.项目地址: https://gitcode.com/GitHub_Trending/sh/sharpemu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门