首页
/ Ghidra 调试器 Agent 开发实战:以 drgn 为例完整走通添加流程

Ghidra 调试器 Agent 开发实战:以 drgn 为例完整走通添加流程

2026-09-06 09:02:22作者:魏侃纯Zoe

本文基于 Ghidra 官方开发者课程文档《Adding a debugger》,系统讲解如何为 Ghidra 调试框架添加一个全新的调试器 Agent(代理)。文章以 Ghidra 仓库中真实存在并持续维护的 drgn Agent 为案例,从 launcher 脚本、对象模型 schema、构建逻辑到 Python 命令/方法/事件回调实现与集成测试,逐层拆解一个 Agent 的四大组成部分、关键调用链与常见陷阱,帮助读者掌握独立开发 Ghidra 调试器 Agent 的完整技术路径。

1. Ghidra 调试器 Agent 架构总览

Ghidra 调试子系统的设计目标是支持跨平台调试:它在 GUI(Java 侧)与各类本地原生调试器之间引入了 Agent 层——即“能够接收原生调试器信息并转交给 Ghidra GUI 的客户端”。当前仓库中内置的 Agent 包括:

  • dbgeng:支持 Windows 调试器(kd/dbgeng 接口);
  • gdb:支持各平台上的 gdb;
  • lldb:面向 macOS 与 Linux;
  • jpda:面向 Java 进程;
  • drgn:本仓库中的“教学级”实例,对接 Meta 开源的 drgn 调试器——一个可对运行中的 Linux 内核、用户态进程以及 core dump 提供(只读)脚本化接口的调试工具。

除 jpda 外,这些 Agent 均用 Python 3 编写,并统一通过一套基于 protobuf 的协议与 GUI 通信,协议定义见 trace-rmi.proto。每个 Agent 在结构上都可以拆成四个部分(文档指出这是“有点武断的划分”,但足够作为心智模型):

组成部分 路径(以 drgn Agent 为例) 作用
debugger-launchers debugger-launchers 一组 .bat/.sh/.py 启动脚本,负责设置环境变量并拉起目标
schema.xml schema.xml 对象模型 schema(注意:虽为 XML 文件,但不是 XML Schema)
src/ghidradrgn src/ghidradrgn Python 包:架构映射、命令、钩子、方法、工具函数
build.gradle build.gradle Gradle 构建逻辑

文档给出了一条重要的工程策略:由于各 Agent 的大量代码相互相同或相似,复制一个现有 Agent 并重命名所有 agent 专属的变量、方法,是成本最低的起点。以 dbgeng 为模板起步通常意味着开发后期会留下大段需要清理的“残骸代码”。仓库佐证了这一点:drgn Agent 的 pyproject.toml 等文件均沿用标准 Agent 骨架,且 commands.py 中大量模式常量与 hooks 结构与 dbgeng 一脉相承。

2. 第一个 launcher:local-drgn.sh

2.1 为什么选 dbgeng 的启动模式而非 gdb/lldb 模式

编写 Agent 最难的一环是确定正确的初始启动模式(initial launch pattern)。gdb 与 lldb 虽然支持 Python 作为脚本语言,但其核心并非 Python:它们的 launcher 先启动原生调试器,再让调试器加载作为插件的 Agent。dbgeng Agent 则反转了这一模式——Agent 本身就是一个 Python 应用,通过 Pybag 包以 COM 方式访问原生 kd 接口。drgn 本身就是纯 Python 编写的,因此天然适合“Agent 主动驱动调试器”这一模式,这也是文档作者从最初复制 lldb Agent 改为基于 dbgeng 开发的原因。

dbgeng 的 debugger-launchers 目录里是 .bat 文件,每个 .bat 再去调用 data/support 下的 .py 文件。由于 drgn 是 Linux 专属调试器,需要把 .bat 转成 .sh,转换相当直接:行注释从 :: 改为 #,环境变量引用从 %VAR% 改为 $VAR

drgn Agent 最终实现了三个 launcher,分别对应用户态、内核态与 core dump 场景:local-drgn.shkernel-drgn.shcore-drgn.sh。其中 local-drgn.sh 是最典型的样例,其关键部分如下:

#@title drgn
#@desc <html><body width="300px">
#@desc   <h3>Launch with <tt>drgn</tt></h3>
...
#@menu-group drgn
#@icon icon.debugger
#@help drgn#attach
#@depends Debugger-rmi-trace
#@env OPT_TARGET_PID:int=-1 "PID" "The target's process id"

export OPT_TARGET_KIND="user"
# sudo -E drgn -p "$OPT_TARGET_PID" ../support/local-drgn.py
# or 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'
drgn -p "$OPT_TARGET_PID" ../support/local-drgn.py

注意最后一条命令:它直接调用 drgn 命令行并显式传入 Python 入口脚本 local-drgn.py,而不是让 drgn 走默认的 run_interactive(该调用不会返回)。local-drgn.py 随后自己完成 Ghidra 专属初始化,最后才调用 run_interactive(cmd.prog),核心调用链清晰可查:

def main():
    append_paths()

    from ghidradrgn import commands as cmd
    cmd.ghidra_trace_connect(address=os.getenv('GHIDRA_TRACE_RMI_ADDR'))
    cmd.ghidra_trace_create(start_trace=True)
    cmd.ghidra_trace_txstart()
    cmd.ghidra_trace_put_all()
    cmd.ghidra_trace_txcommit()
    cmd.ghidra_trace_activate()
    drgn.cli.run_interactive(cmd.prog)

这里 append_paths() 先把 Debugger-rmi-trace 模块的 data/support 与 Python 包路径加入 sys.path,再通过环境变量 GHIDRA_TRACE_RMI_ADDR 与 GUI 建立的 RMI 通道完成 connect → create → txstart → put_all → txcommit → activate 的握手序列。文档给出的开发建议是:先实现一个尽量调用 connectcreatestart 三个方法的脚本,并让 create 初期只做最少的工作,这样才能先把 arch.pyutil.py 的调试问题暴露出来。

2.2 launcher 元数据注释语法

.sh 脚本本身是标准类 nix Shell,但 launcher 还包含一段元数据头,用于填充 GUI 中的启动器菜单与选项对话框。完整注释集合为:

  • #! 行:指定 Shell;
  • Ghidra 许可声明;
  • #@title:启动器名称;
  • #@desc:HTML 描述,显示在启动对话框中;
  • #@menu-group:启动器分组;
  • #@icon:图标;
  • #@help:帮助文件与锚点;
  • 若干 #@arg 变量(通常只有一个,用于命名可执行镜像);
  • #@args:其余参数(若适用,传递给用户态目标);
  • 若干 #@env 变量:被 Python 代码引用的环境变量。

#@env 的语法形式为:

#@env <Name>:<Type>[!]=<DefaultValue> <Label> <Description>

其中 ! 若出现则表示该选项必填。drgn 的 local-drgn.sh 没有使用 @arg/@args,但 gdb Agent 的 launcher 中示例非常丰富,例如 local-gdb.sh 中同时用到了 #@enum#@arg#@args 与多条 #@env

#@enum StartCmd:str run start starti
#@arg :file "Image" "The target binary executable image, empty for no target"
#@args "Arguments" "Command-line arguments to pass to the target"
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
#@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."

2.3 sudo 与环境变量透传的两个坑

drgn 的大多数目标需要 sudo 权限,这带来两个容易踩坑的问题:

  1. wheel 安装位置混乱sudo 下很容易误把用户级 site-packages 与系统级 site-packages 混用;
  2. 环境变量必须显式透传sudo 默认会重置环境,因此必须加 -E 参数,否则前面 #@env 定义的环境变量到不了 root 环境(见 local-drgn.sh 中被注释掉的 sudo -E drgn ... 一行,以及 ptrace_scope 的替代设置 echo 0 > /proc/sys/kernel/yama/ptrace_scope)。

此外,使用 sudo 时交互 Shell 中打印的第一条消息将是密码提示,Agent 的输出解析逻辑必须容忍这一点。

3. 对象模型 schema:schema.xml 的角色与细节

schema.xml 为 Ghidra 的 Model 视图提供基本结构,让 Ghidra 能识别并定位用于填充 GUI 的各类接口。例如:Memory 接口标识了包含 MemoryRegion 项的容器,MemoryRegion 提供的信息用于填充 Memory 视图。关键接口包括 Process、Thread、Frame、Register、MemoryRegion、Module、Section 等——这些接口是“内置”于 Ghidra 的,使 GUI 能识别哪些对象提供特定信息与命令。

入门的最省事做法是克隆 dbgeng 的 schema 再按需修改;虽然后期要大量清理,但 schema 错误往往微妙且难以定位,先跑通再回头收拾是更稳妥的路径。同时需要修改 MANIFEST.in(drgn 中为 MANIFEST.in)以反映 schema 路径。

从 drgn 实际生成的 schema.xml 可以看到文件结构(其头部注释标明该文件由 build_xml 自动生成):

<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
    <attribute name="Id" schema="STRING" fixed="yes"/>
    <interface name="Process"/>
    <interface name="Aggregate"/>
    <interface name="ExecutionStateful"/>
    <element schema="VOID"/>
    <attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes"/>
    <attribute name="Memory" schema="Memory" required="yes" fixed="yes"/>
    <attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes"/>
    ...
</schema>

文档中列出的 schema 编写规则,均可在此文件中找到对应物:

  • 接口名必须正确:Process、Thread、Frame、Module、Section、Memory 等元素的名字可以任意取,但 <interface> 名必须与内置接口一致;
  • canonical 标记:如果某元素需要作为默认搜索流程的一部分被遍历,其容器必须标记 canonical(如 ProcessContainerThreadContainerModuleContainer);
  • Aggregate 接口:若属性(attributes)需要被遍历,其父对象应带有 Aggregate 接口;
  • elementsattributes:每个条目可以包含按 key 排序的同类型 elements 与任意类型的 attributeselement 条目描述所有元素的统一 schema,属性 schema 可用命名 attribute 显式声明,也可以用匿名条目给出默认值(通常是 <attribute schema="VOID"><attribute schema="ANY">);
  • hidden=yes:隐藏条目,效果不言自明;
  • 特殊属性名_display 定义 Model 树中条目的可见 ID;ADDRESSRANGE schema 的属性标记为“可导航(navigable)”(如 MemoryRegionRange 属性、ModuleRange 属性)。

一个实用技巧:用鼠标悬停(hover)即可查看 Model 视图中任意元素的 schema,这对定位 schema 遍历错误非常有帮助。

4. 构建逻辑:build.gradlepyproject.toml

build.gradle 基本可以从 dbgeng 克隆,只需相应修改 eclipse.project.name。drgn 的 build.gradle 印证了文档的说法——主体只需要套用两个脚本:

apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
apply from: "$rootProject.projectDir/gradle/hasPythonPackage.gradle"

apply plugin: 'eclipse'
eclipse.project.name = 'Debug Debugger-agent-drgn'

dependencies {
	// Only for Help :/
	api project(':Debugger-rmi-trace')
}

(此外它还 apply 了 javaProject.gradlehelpProject.gradle,后者纯粹因为“必须是一个 Help project”。)若需进一步定制,可参考 Ghidra 项目内的其他示例以及 Gradle 官方文档。

还有一个不直接属于构建逻辑但必须同步的项:pyproject.toml 中的版本号——按惯例使用 Ghidra 的版本号(见 pyproject.toml)。

5. Python 实现层:arch、util、commands、methods、hooks

5.1 arch.py:架构映射是难点

arch.py 是好的起点,因为大量初始逻辑依赖它。其“难就难在知道什么映射到什么”:

  • language_map调试器自报的架构转换为 Ghidra 的语言集;
  • Ghidra 的语言再映射到“语言→编译器”映射表,进而把调试器自报的语言映射到 Ghidra 编译器;
  • 某些组合不被允许,因为 Ghidra 对相应“语言-编译器”组合没有概念——例如 x86 语言永远不会映射到 default,所以必须存在 x86_compiler_map 并回退到其他编译器(drgn 中是 gcc)。

对照 arch.py 源码可以完整看到上述设计:

# NOTE: This map is derived from the ldefs using a script
language_map: Dict[str, List[str]] = {
    'AARCH64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'],
    'ARM': ['ARM:BE:32:v8', 'ARM:BE:32:v8T', 'ARM:LE:32:v8', 'ARM:LE:32:v8T'],
    ...
    'X86_64': ['x86:LE:64:default'],
    'UNKNOWN': ['DATA:LE:64:default', 'DATA:LE:64:default'],
}

x86_compiler_map: Dict[Optional[str], str] = {
    'Language.C': 'gcc',
}

compiler_map: Dict[str, Dict[Optional[str], str]] = {
    'DATA:BE:64:': data64_compiler_map,
    'DATA:LE:64:': data64_compiler_map,
    'x86:LE:32:': x86_compiler_map,
    'x86:LE:64:': x86_compiler_map,
    ...
}

compute_ghidra_language() 的选取逻辑也值得注意:先检查便利变量(auto 表示自动推导),然后取该架构的候选语言列表,按字节序过滤,并用“优先 :default 后缀、其次变体 ID 最短”的启发式排序选出第一个;若无匹配则回退到 DATA 语言。compute_ghidra_compiler() 则按前缀匹配 compiler_map 的语言键,最后对 x86 特判回退到 gcc

5.2 util.py:版本信息与“当前选中”状态

arch.py 之后应尽早给 util.py 打一个初稿,因为版本信息在启动早期就会被用到。与当前项目无关的代码可以先不管,但至少要实现(或先伪造)selected_processselected_threadselected_frame 等方法。drgn 场景下通常只有一个 session、一个进程,最终还要决定 schema 里是否保留 Session。文档记录的初始做法是:session 与 process 默认 0,thread 默认 1(因为 0 在内核调试中是非法值);后来发现用户态调试用“attach 的 pid 与 prog.main_thread().tid”更合理,崩溃转储调试则用 prog.crashed_thread().tidutil.py 中的实现正是模块级全局变量配合 select_*/selected_* 访问器:

selected_pid = -1
selected_tid = -1
selected_level = -1

def selected_process() -> int:
    return selected_pid

def select_thread(id: int) -> int:
    global selected_tid
    selected_tid = id
    return selected_tid

5.3 commands.py:从树根向下实现 put 命令

arch.pyutil.py 达到初稿水准后,通常从 Model 视图树根开始,向下逐层实现 commands.py 中各对象的 put 方法。SessionProcess 定义较模糊(drgn 中各留一个即可),一般先做 Threads

对调试器 API 中的每个迭代器,通常要实现两条命令:

  • 一个内部方法做实际工作,如 put_threads();它由其他 Python 代码调用,调用方负责建立事务;
  • 一个可调用方法,如 ghidra_trace_put_threads(),把内部方法包裹进(可批量的)事务中;带 ghidra_trace 前缀的方法属于用户可自定义 CLI 命令集的一部分,因此必须自行建立事务。

内部方法的典型流程是:用容器 pattern、单条 key pattern 与组合 pattern(如 THREADS_PATTERNTHREAD_KEY_PATTERNTHREAD_PATTERN)拼出对象路径 → 从路径创建对应调试器对象的 trace 对象 → 插入 trace 数据库。pattern 由更底层的 pattern 逐级拼装,最终回到树根。drgn 的 commands.py 中可以看到这种拼装方式:

PROCESS_PATTERN = PROCESSES_PATH + PROCESS_KEY_PATTERN
THREADS_PATTERN = PROCESS_PATTERN + '.Threads'
THREAD_KEY_PATTERN = '[{tnum}]'
THREAD_PATTERN = THREADS_PATTERN + THREAD_KEY_PATTERN

测试通过后,就可以用 set_value 给基对象追加属性;非原子的属性则采用 create-populate-insert 三步:用 create_object 在路径扩展处创建对象、填充其子节点、再 insert。若填充子节点代价很高(很常见),可以选择延迟填充——先建一个占位节点、按需再生成,代价是要为这些节点额外编写 refresh 方法。

这里文档专门澄清了 create_objectinsert 的语义区别,值得原文保留:trace 中的对象维护在一棵目录树里,允许链接(与反向链接),其可视化体现就是 Model 视图;树上操作遵循常规图操作语义。create_object 只创建节点而不创建任何边——连隐含的“父到子规范(canonical)边”也不创建;insert 才创建这条规范边。在那条边存在之前,对象不被视为“存活”,因此这条边的生命周期实际上编码了对象的生命周期。遵循 create-populate-insert 模式可以最小化需要处理的事件数量。

5.4 methods.py:refresh 方法与焦点语义

完成一条命令后有两个推进方向:继续实现树中其他对象的命令,或为已完成对象实现 methods.py 中对应的 refresh 方法。methods.py 同样依赖 pattern(通常通过 find_x_by_pattern 系列方法)来把路径匹配到 trace 对象;refresh 方法是否依赖 find_by 系列方法,取决于匹配命令是否需要参数——例如可以假设 selected_thread 与视图中的当前对象一致并借此定位节点;若 trace 对象能轻易对应到调试器对象,则可以强制方法匹配该节点;也可以反过来用该节点去设置 selected_thread

焦点(focus)语义是调试器中一个复杂且极易混淆的概念,文档的界定值得逐字掌握:

  • selected(选中):表示 GUI 当前的焦点,通常是用户在 Model 或关联视图中选中的节点,代表用户关心的进程/线程/帧。它还可能与*高亮(highlighted)*节点不同——单击产生高亮,双击才设置选中;
  • current(当前):原生调试器自己对焦点的认知。这一概念还被 event 对象(如调试器断在哪个线程)与 current 对象(如正在检视哪个线程)的区分进一步复杂化;
  • 数据流向:current 值从原生调试器向上推(push up)到 Ghidra GUI;selected 值从 Ghidra 向下推(push down)到原生调试器。尽可能同步这两类值:新的 selected 应促使改变 current 对象集合,current 变化的事件也应改变 GUI 的 selected 对象集合——当然要注意避免形成往返循环。

refresh 方法(及其他方法)常带多种注解:

  • @REGISTRY.method:把方法暴露给 GUI,指定将执行的 action 与 GUI 弹出菜单中的 display 名称。Actions 可以是纯描述性的,也可以对应 GUI 的内置动作,如 refresh 以及 step_into 等控制方法;
  • sch.Schema(惯例标注在第一个参数上):指明方法适用的节点;
  • ParamDesc:描述参数类型与弹窗对话框中的标签。

取回所需参数后,refresh 方法把 commands.py 中的方法包裹在事务中调用。

drgn 的具体实现顺序是:线程、帧、寄存器(putreg)、局部变量,然后模块与段、内存与区域、环境,最后是进程;并用 drgn 的 read API 实现了 putmemSymbols 也曾是候选,但整体填充符号代价过高,于是改为提供 retrieve_symbols 支持按 pattern 逐个添加符号——遗憾的是 drgn API 不支持通配符,文档作者也指出最终需要换一种策略。

5.5 hooks.py:事件回调与推送式架构

hooks.py 是原生调试器发出各类事件时的回调集合。drgn 目前没有事件系统,所以 hooks.py 中保留的是一组骨架方法——一方面可以把 GUI 的单步按钮当作“更新状态”的替代手段,另一方面 drgn 社区正在讨论实现更多控制功能。对真正实现 hooks.py 的开发者,挑战集中在事件循环上,尤其是需要在调试器与 repl 之间来回切换的场景;还要区分等待事件的控制命令依赖回调但立即完成的命令

经验法则是:向 Ghidra 推送(push)——Ghidra 异步发出请求,Agent 负责更新 trace 数据库。drgn 的 hooks.pyProcessState.record() 就是一个标准的“停止时增量刷新”实现:首次停止时全量 put_processes()put_environment(),之后按 threads/regions/modules 等脏标志位增量刷新,并对访问过的线程与帧去重(visited 集合),每个钩子(on_stopon_conton_thread_selected 等)都在 trace.open_tx(...) 事务中批量提交。install_hooks()/remove_hooks() 则只是一对以 HOOK_STATE.installed 布尔量守卫的占位实现——这正是文档所说“骨架方法”的体现。

5.6 回头修订 schema

进入实现阶段后可能需要回头编辑 schema。drgn 的实例:既然永远不可能有多个 session,把 Processes 直接嵌到树根更干净(对照生成的 schema,根 DrgnRoot 的唯一必填属性就是 Processes);又因为不支持断点,断点相关条目可以安全删除。这类结构性调整会连锁要求修改 commands.pymethods.py 中的 pattern。

5.7 扩展功能

其他都完成后,可以考虑调试器专属的附加功能,放在 commands.pymethods.py 中均可。drgn 增加了 attach 方法,允许用户附加到额外的程序。

6. 单元测试

写单元测试最难的部分几乎总是让第一个测试跑起来;与 Python 文件一样,最容易的是 commands.py 的测试。drgn 依旧以 dbgeng 为模板,但必须修改 AbstractDrgnTraceRmiTest(位于 AbstractDrgnTraceRmiTest.java)中的 runThrowError(更具体地说是 execInPython)逻辑:由于 launcher 执行的是一个脚本,需要改成以脚本为参数ProcessBuilder 调用,而不是把脚本写入 stdin。顺路还可以从所有测试类中裁掉断点、watchpoint 等无关的辅助逻辑。

methods.py 的 JUnit 测试遵循类似模式,同样“跑通第一个最难”。drgn 上需要覆盖(override)waitForPasswaitForCondition 的超时;测试目标先用硬编码路径起步,之后还要在 execInDrgn 中动态改写 PREAMBLE。没有真正的 hooks.py 逻辑,自然也不需要 DrgnHooksTest。仓库中的 drgn 集成测试目录 agent/drgn 下现有 DrgnCommandsTestDrgnConnectorsTestDrgnMethodsTestDrgnVersionTest 四个测试类,与文档描述一一对应。

两个实践细节值得记住:

  • 测试用 gdb 的 gcore 命令生成 core dump,作为测试输入;
  • 用户态与内核态调试都要求特权,测试环境里这不理想——这也是 drgn 用 core dump 路径做集成测试的动机。同时,IntegrationTest 的 build.gradle 需要修改以包含新的调试器 Python 包依赖。

7. 文档与扩展特性

所有新调试器的首要文档是 launcher 说明:目前这些信息集中存放于 TraceRmiLauncherServicePlugin.htmlDebug/Debugger-rmi-trace 模块内)。细节要求:launcher 中的 #@help 位置必须与该 HTML 文件中的锚点标签一致,launcher 名称也必须一致(drgn 的 #@help drgn#attach 即对应 drgn 帮助页中的 attach 锚点)。

最后,当其余工作全部完成后,再考虑添加调试器专属的扩展功能。对 drgn,最终落地的就是允许附加到额外程序的 attach 方法——这印证了文档“扩展功能放在最后”的节奏建议:先把 connect/create/put/refresh 主干跑通,再谈特色功能。

8. 小结:新增一个 Agent 的检查清单

按文档脉络与 drgn 实例归纳,新增 Ghidra 调试器 Agent 的完整检查清单如下:

  1. 选型:目标调试器若本身是 Python 应用,走 dbgeng 式“Agent 主动驱动”模式;否则(gdb/lldb)走“启动调试器 + 加载插件”模式;
  2. launcher:从现有 Agent 的 .bat/.sh 转换起步,补齐 #@title/#@desc/#@menu-group/#@icon/#@help/#@arg(s)/#@env 元数据;注意 sudo -E 与环境变量透传;
  3. schema:克隆 dbgeng 的 schema.xml,确认内置接口名(Process/Thread/Frame/Register/MemoryRegion/Module/Section)、canonical 容器与 Aggregate 接口,同步修改 MANIFEST.in
  4. 构建:套用 distributableGhidraModule.gradlehasPythonPackage.gradle,改 eclipse.project.name,同步 pyproject.toml 版本号为 Ghidra 版本号;
  5. Python 主干arch.py(语言/编译器映射,注意 x86 不可映射 default)→ util.py(版本信息、selected_* 状态)→ commands.py(自树根向下 put_*ghidra_trace_* 前缀方法自管事务,遵循 create-populate-insert)→ methods.py(refresh 方法与 @REGISTRY.method/sch.Schema/ParamDesc 注解,同步 selected/current 双向焦点)→ hooks.py(推送式事件回调);
  6. 回头修订 schema(删除用不到的断点条目、调整层级并同步 pattern);
  7. 测试:修改 execInPython 为脚本式 ProcessBuilder 调用、覆盖等待超时、必要时用 gcore 生成 core dump 规避特权要求,并在 IntegrationTest 的 build.gradle 中加入新包依赖;
  8. 文档:更新 launcher 帮助页,保持 #@help 锚点与 launcher 名称一致;
  9. 扩展:最后再实现调试器专属功能(如 drgn 的 attach)。

掌握上述路径后,读者即可参照仓库中 Debugger-agent-drgn 的完整实现与 DebuggerIntegrationTest 的测试骨架,独立完成一个新调试器 Agent 的开发与验证。

登录后查看全文
热门项目推荐
相关项目推荐