十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Delve JSON-RPC 接口完全指南:基于 `service/rpc2` 的 Go 调试协议详解

Delve JSON-RPC 接口完全指南:基于 `service/rpc2` 的 Go 调试协议详解 Delve JSON-RPC 接口完全指南基于service/rpc2的 Go 调试协议详解【免费下载链接】delveDelve is a debugger for the Go programming language.项目地址: https://gitcode.com/gh_mirrors/de/delveDelve 是 Go 语言的调试器除了内置的交互式终端客户端它还对外暴露了 JSON-RPC 与 DAP 两套 API供 IDE、编辑器与自定义前端以编程方式驱动调试。本指南以官方文档 Documentation/api/json-rpc/README.md 为主体结合仓库中 service/rpc2/server.go、service/rpc2/client.go、service/rpccommon/server.go 等源码实现系统讲解 Delve JSON-RPC 接口的版本演进、启动方式、调用约定与典型流程。读完本文你将掌握如何在不借助交互式终端的情况下通过原始 JSON-RPC 报文完成定位函数→创建断点→继续执行的完整调试闭环并理解其底层映射关系。一、接口概览JSON-RPC 与 DAP 并存Delve 为了让交互式终端之外的前端IDE、编辑器等能够以编程方式与调试器交互对外暴露了两套 API 接口JSON-RPC 接口被内置的终端客户端 Documentation/cli/README.md 自身所使用与 Delve 新特性保持同步演进DAP 接口Debug Adapter Protocol一种被众多工具广泛使用的通用调试协议详见 Documentation/api/dap/README.md。两套 API 的架构设计目标参见 Documentation/api/README.mdDelve 将业务逻辑与客户端/服务器实现抽象分离因此可以轻松地新增 API 实现。JSON-RPC 服务端实现在 service/rpccommon/server.go其中serveConnectionDemux会通过连接首个字节判断协议若以CDAP 的Content-Length前缀开头则走 DAP 会话否则走 JSON-RPC codecservice/rpccommon/server.go。1.1 传输方式流式 socket 而非 HTTP特别注意Delve 的 JSON-RPC 接口通过流式 socket 提供服务不是HTTP。这一点在 Documentation/api/json-rpc/README.md 中被明确强调。客户端使用 Go 标准库net/rpc/jsonrpc即可与之通信——service/rpc2/client.go 中的NewClient正是通过jsonrpc.Dial(tcp, addr)建立连接func NewClient(addr string) *RPCClient { client, err : jsonrpc.Dial(tcp, addr) if err ! nil { log.Fatal(dialing:, err) } return newFromRPCClient(client) }1.2 API 版本仅支持 v2Delve 目前只支持 v2 API。v1 支持已在 Delve v1.24.0 中移除。这一点在服务端也有硬性校验ServerImpl.Run中如果配置的APIVersion不等于 2 会直接返回unknown API version错误service/rpccommon/server.goSetApiVersion同样只接受 2service/rpccommon/server.go。客户端在建立连接后也会立即调用SetApiVersion请求 v2service/rpc2/client.go。二、启动无头调试服务--headless模式要使用 JSON-RPC API需要让 Delve 以API 模式无头模式运行。官方文档给出的启动命令如下$ dlv debug --headless --api-version2 --log --log-outputdebugger,dap,rpc --listen127.0.0.1:8181这条命令的含义参数作用--headless以非交互模式启动调试器不进入终端 UI仅监听 API--api-version2指定 API 版本为 2--log启用日志输出--log-outputdebugger,dap,rpc指定日志输出类别其中rpc会打印收发 JSON-RPC 报文便于调试自己的客户端--listen127.0.0.1:8181监听地址与端口其中日志与监听地址都是可选项核心必须的参数是--headless与--api-version2。除debug外标准命令exec、attach、test、trace等同样支持该模式。启动后 Delve 会监听指定 socket等待外部客户端接入。如果希望允许多个 JSON-RPC 或 DAP 客户端同时连接可以额外指定--accept-multiclient标志。这一标志对应服务端配置中的AcceptMulti字段IsMulticlientRPC 方法会直接返回该配置值service/rpc2/server.go而Run在AcceptMulti开启时会持续 accept 新连接否则只服务首个连接service/rpccommon/server.go。远程调试时也可以从 Delve 自身连接无头调试器$ dlv connect 127.0.0.1:81812.1 多协议自动识别值得留意的是无头模式下同一个监听端口既可服务 JSON-RPC 也可服务 DAP。服务端在serveConnectionDemux中通过 peek 连接的首字节判断协议service/rpccommon/server.go首字节为CDAP 的Content-Length头则走 DAP否则按 JSON-RPC 处理。因此--accept-multiclient场景下JSON-RPC 与 DAP 客户端可以共享同一个监听端口。三、调用约定与报文格式3.1 方法注册与RPCServer.前缀service/rpc2.RPCServer类型上的所有方法都可以通过 JSON-RPC 调用其完整文档可在pkg.go.dev上github.com/go-delve/delve/service/rpc2#RPCServer处查看。在 JSON-RPC 报文中方法名必须加上RPCServer.前缀。方法清单由自动生成的 service/rpccommon/suitablemethods.go 定义——例如RPCServer.FindLocation、RPCServer.CreateBreakpoint、RPCServer.Command、RPCServer.Eval等均在其中加上公共方法RPCServer.GetVersion、RPCServer.SetApiVersionservice/rpccommon/suitablemethods.go。从源码结构看方法按同步/异步分为两类大多数方法签名是func (s *RPCServer) Xxx(arg XxxIn, out *XxxOut) error的同步调用少数涉及长时间运行或事件推送的方法如Command、State、Restart、GetEvents等采用service.RPCCallback回调式异步实现service/rpc2/server.go。服务端finishMethodsMapInit通过判断 ReplyType 是否为service.RPCCallback来区分同步与异步service/rpccommon/server.go。3.2 统一参数与返回结构所有暴露的方法都遵循两个约定输入参数是单个结构体通常命名为args例如FindLocationIn、CreateBreakpointIn返回值是一个结构体例如FindLocationOut、CreateBreakpointOut。请求与响应均按 JSON-RPC 1.0/1.1 风格组织请求包含method、params数组形式内含一个参数对象与id响应包含id、result与error成功时为null。一个典型的请求包形如{method:RPCServer.FindLocation,params:[{Scope:{GoroutineID:-1,Frame:0},Loc:main.main}],id:2}服务端在处理请求时会先通过methodMaps反射查找方法再解码参数并调用service/rpccommon/server.go若启用--log-outputrpc还会把收发报文以-/-前缀打印到日志是排查客户端问题的利器。四、实战示例定位函数并设置断点官方文档给出了一个完整的最小示例客户端想要在main.main函数上设置断点。4.1 第一步调用FindLocation解析位置首先调用FindLocation传入Scope api.EvalScope{GoroutineID: -1, Frame: 0}与Loc main.main{method:RPCServer.FindLocation,params:[{Scope:{GoroutineID:-1,Frame:0},Loc:main.main}],id:2}服务端返回不同环境下的地址、路径会不同{id:2,result:{Locations:[{pc:4199019,file:/home/a/temp/callme/callme.go,line:31,function:{name:main.main,value:4198992,type:84,goType:0}}]},error:null}这里几个关键字段的语义依据 service/api/types.go 中Location与Function的定义pc目标位置的程序计数器地址uint64file/line对应的源文件与行号function.name函数名function.value是函数入口地址function.type为 DWARF 类型编码goType为 Go 运行时类型指针0 表示未知。从源码看FindLocation的实现链路是RPCServer.FindLocation→debugger.FindLocationservice/debugger/debugger.go→locspec.Parse解析位置表达式 → 逐个 target 执行位置查找。位置表达式语法由 pkg/locspec/locations.go 定义支持多种形式loc :: filename:line | function[:line] | /regex/ | (|-)offset | line | *addressfilename:line文件名可为完整路径或后缀加行号function[:line]函数名形如pkg.(*T).method、pkg.func、(*T).method等须无歧义可带行号/regex/返回所有匹配正则的函数位置offset/-offset当前行的前/后偏移若干行line当前文件中的某一行*address指定的内存地址。从 pkg/locspec/locations.go 可见位置规范被解析为NormalLocationSpec、RegexLocationSpec、AddrLocationSpec、OffsetLocationSpec、LineLocationSpec、FuncLocationSpec等具体类型。注意FindLocation只解析位置并不会真正设置断点service/rpc2/server.go。此外FindLocation还支持IncludeNonExecutableLines是否包含不可执行行与SubstitutePathRules源码路径替换规则格式为[源路径, 客户端路径]的二元组列表后者可用于跨机器远程调试时路径不一致的场景service/rpc2/server.go。4.2 第二步调用CreateBreakpoint创建断点拿到FindLocation响应中的pc字段值4199019后调用CreateBreakpoint以Breakpoint.addr指定目标地址{method:RPCServer.CreateBreakpoint,params:[{Breakpoint:{addr:4199019}}],id:3}请求成功后服务端返回完整的断点对象{id:3,result:{Breakpoint:{id:1,name:,addr:4199019,file:/home/a/temp/callme/callme.go,line:31,functionName:main.main,Cond:,continue:false,goroutine:false,stacktrace:0,LoadArgs:null,LoadLocals:null,hitCount:{},totalHitCount:0}},error:null}响应中Breakpoint结构体的字段与 service/api/types.go 中api.Breakpoint一一对应含义如下字段含义id断点唯一标识name用户自定义断点名不能是纯数字且只能包含字母与数字校验见ValidBreakpointNameservice/api/types.goaddr断点地址注意该字段已标记 deprecated新增的多地址字段为addrsfile/line断点对应的源文件与行号functionName断点所在函数名Cond断点条件表达式为空表示无条件continue是否 Tracepoint命中后不停止、继续执行对应Tracepoint字段的 JSON 标签goroutine是否在命中时检索 goroutine 信息stacktrace命中时检索的栈帧数量LoadArgs/LoadLocals命中时加载函数参数/局部变量所用的LoadConfighitCount每个 goroutine 的命中次数映射totalHitCount总命中次数从实现看CreateBreakpoint底层调用debugger.CreateBreakpointservice/debugger/debugger.go支持多种断点描述方式按addr设置、按fileline设置Windows 下文件名匹配不区分大小写且忽略斜杠方向、按TraceReturn设置返回断点、按函数名/表达式设置等。同时CreateBreakpointIn还支持LocExpr用于断点被禁用后恢复的位置表达式与Suspended是否先以挂起状态创建。五、围绕断点的完整操作集合v2 API 围绕断点提供了完整的方法族全部定义在 service/rpc2/server.go 中方法作用关键输入RPCServer.CreateBreakpoint创建断点Breakpoint必填RPCServer.ClearBreakpoint按 ID 或名称删除断点Id或NameRPCServer.ToggleBreakpoint启用/禁用断点Id或NameRPCServer.AmendBreakpoint修改已有断点如条件、加载配置Breakpoint须含有效 IDRPCServer.GetBreakpoint按 ID 或名称查询断点Id或NameRPCServer.ListBreakpoints列出全部断点All是否包含内部断点以 ID 还是名称操作由输入参数中Name是否为空字符串决定——非空则按名称查找否则按 IDservice/rpc2/server.go、service/rpc2/server.go。AmendBreakpoint的典型用途是修改命中时的信息收集策略LoadArgs、LoadLocals或增删断点条件service/rpc2/server.go。断点条件支持Cond表达式条件与HitCond命中次数条件形如NUMBER或OP NUMBER如% 3、 5HitCondPerG可改为按每 goroutine 计数service/api/types.go。六、其他常用 RPC 方法速览除断点管理外v2 API 还覆盖了调试器的绝大多数能力方法名与输入输出结构均可在 service/rpc2/server.go 与 service/rpc2/client.go 中对照查看执行控制Command是统一的执行控制入口通过DebuggerCommand.Name区分动作。Name的可选值定义在 service/api/types.go 附近包括continue、step、next、stepout、rewind、halt、switchThread、switchGoroutine、call在指定 goroutine 中调用表达式UnsafeCall可关闭参数逃逸安全检查等。DebuggerCommand还支持WithEvents配合GetEvents实现事件订阅service/api/types.go状态查询State获取当前调试状态NonBlockingtrue时进程运行中也会立即返回、ProcessPid、AttachedToExistingProcess、BuildID、ListTargets、GetVersion栈与变量Stacktrace支持Full加载全部局部变量与参数、Defers读取 deferred 函数、Skip跳过帧、Ancestors、ListLocalVars、ListFunctionArgs、ListPackageVars、Eval在指定作用域求值表达式表达式语法见 Documentation/cli/expr.md、Set修改变量值目前仅支持数值类型与指针线程与 goroutineListThreads、GetThread、ListGoroutines支持分页Start/Count、位置/标签过滤Filters、按条件分组GroupBy详见 service/rpc2/server.go符号与源码ListSources、ListFunctions、ListTypes、ListPackagesBuildInfo、TypeInfo、ListDynamicLibraries、FunctionReturnLocations寄存器与反汇编ListRegisters可用Scope或ThreadID指定上下文、DisassembleStartPC/EndPC都非零时反汇编指定区间否则反汇编包含StartPC的函数内存与记录ExamineMemory单次长度上限为1 16字节即 64 KiB见 service/rpc2/server.go、Recorded、Checkpoint/ListCheckpoints/ClearCheckpoint、StopRecording、FollowExecCore dumpDumpStart、DumpWaitWait为毫秒0 表示立即返回、DumpCancel其他DetachKill为 true 时同时杀死目标进程、Restart支持Rebuild、NewArgs、Rerecord等、DebugInfoDirectories、GuessSubstitutePath通过go list猜测模块与源码目录的映射见 service/rpc2/client.go、GetBufferedTracepoints、CreateEBPFTracepoint、CreateWatchpointWatchRead/WatchWriteservice/api/types.go。客户端侧service/rpc2/client.go 中的RPCClient把这些方法封装成了类型安全的 Go 函数其call方法统一加上RPCServer.前缀service/rpc2/client.goCallAPI则允许调用任意方法。内置终端客户端即通过该RPCClient与调试器交互这也印证了官方文档所述JSON-RPC 接口与终端客户端锁步演进。七、事件推送GetEvents与异步调用执行控制类方法Command、State等在 service/rpc2/server.go 中采用回调式异步实现请求先返回真正的响应由RPCCallback.Return在后续写入连接service/rpccommon/server.go。当Command携带WithEvents: true时调试器产生的proc.Event会写入eventsChan缓冲通道容量 100见 service/rpc2/server.go客户端可通过GetEvents主动拉取事件队列service/rpc2/server.go。RPCClient侧对应的封装是SetEventsFn注册事件回调callWhileDrainingEvents会在发起命令前先排空事件service/rpc2/client.go。对于需要持续监听执行状态的前端如 IDE典型模式是发起Command如continue→ 通过GetEvents循环拉取EventStopped等事件 → 收到停止事件后调用State获取最新状态。这一模式正是终端客户端continueDir实现的依据service/rpc2/client.go。八、调试提示与注意事项开启 RPC 日志启动参数加上--log --log-outputrpc服务端会把收发的 JSON-RPC 报文打印出来是排查参数格式错误最直接的手段服务端日志打印位置见 service/rpccommon/server.go。FindLocation不设断点它只返回位置信息真正设置断点必须调用CreateBreakpoint以addr、file:line或LocExpr等方式。params是数组尽管每个方法只接受一个参数JSON-RPC 报文中params必须写成数组形式例如params:[{Scope:...}]。方法名大小写方法名须与注册表完全一致且带RPCServer.前缀如RPCServer.ListBreakpoints找不到方法时服务端会返回unknown method: xxx错误service/rpccommon/server.go。断点名称约束断点name不能是纯数字且只能包含字母和数字service/api/types.go。远程调试路径问题跨机器调试时源码路径可能不一致可通过SubstitutePathRulesFindLocation/CreateBreakpoint入参或GuessSubstitutePathDebugInfoDirectories配合解决路径替换规则的完整说明见 Documentation/cli/substitutepath.md。九、总结Delve 的 JSON-RPC v2 接口是一套完整、稳定的编程式调试协议以service/rpc2.RPCServer上的方法为服务端能力清单以单结构体入参 单结构体返回 RPCServer.前缀为统一调用约定通过流式 socket 传输。从本文的实战示例可以看到一个最小调试客户端只需两次调用FindLocation→CreateBreakpoint即可在任意函数上设置断点而完整的方法族则覆盖了执行控制、断点管理、栈与变量、goroutine、寄存器、内存、core dump 等全部调试能力。结合 service/rpc2/client.go 中现成的RPCClient封装无论是为 IDE 编写集成插件还是构建自定义调试工具链都可以快速上手。进一步阅读Documentation/api/README.md服务端/客户端 API 总览、Documentation/api/dap/README.mdDAP 接口、Documentation/api/ClientHowto.md客户端接入指南、Documentation/api/json-rpc/README.md本文档原始出处。【免费下载链接】delveDelve is a debugger for the Go programming language.项目地址: https://gitcode.com/gh_mirrors/de/delve创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表