news 2026/9/23 21:45:58

使用 hcdp 调试 Hermes:CDP 调试工具的架构解析与实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 hcdp 调试 Hermes:CDP 调试工具的架构解析与实战指南
  • 语言运行时
  • 编译器
  • 移动开发

【免费下载链接】hermes

A JavaScript engine optimized for running React Native.

项目地址:https://gitcode.com/gh_mirrors/hermes/hermes
点击查看免费下载

导读

hcdp(Hermes CDP)是 Hermes 仓库中一个独立的命令行调试工具,它把 Hermes 引擎的 CDP(Chrome DevTools Protocol)调试能力包装成一个可直接运行的进程:用户既可以用它调试本地的 JavaScript 脚本,也可以借助 Chrome / Edge DevTools 界面进行可视化调试。本文以 tools/hcdp/README.md 为核心,结合 tools/hcdp/hcdp.cpp、tools/hcdp/hcdp.js 等源码,完整讲解 hcdp 的构建、运行方式、双组件架构与 IPC 消息协议,帮助你快速上手并理解 Hermes CDP 调试链路的工作原理。

一、hcdp 是什么

Hermes 是一个针对 React Native 场景优化的 JavaScript 引擎,其调试能力基于 CDP(Chrome DevTools Protocol)实现。hcdp正是围绕这一能力打造的命令行工具,它的核心目标是把“Hermes 运行时 + CDP 调试 API + 调试客户端”串成一条可独立运行的调试链路:

  • 它承载一个真实的 Hermes Runtime,并执行你指定的 JS 脚本;
  • 它对外暴露一个 WebSocket 服务,接收调试客户端(如 Chrome DevTools)发来的 CDP 消息;
  • 它把 CDP 消息转化为内部 IPC 消息,交给 C++ 组件中的 CDP Agent 处理,再将 Agent 产生的响应与通知转发回客户端。

从源码结构看,整个工具由两大部分组成:hcdp.cpp(C++ 组件,负责运行时与调试内核)和hcdp.js(JavaScript 组件,负责网络与交互),二者通过标准输入输出上的行式 IPC 协议通信,详见 tools/hcdp/ 目录。

二、快速上手:构建与运行

1. 构建 hcdp 二进制

进入tools/hcdp目录后,使用 BUCK 或 CMake 构建hcdp可执行文件。CMake 构建入口为 tools/hcdp/CMakeLists.txt,它依赖仓库根目录的 CMakeLists.txt 中定义的add_hermes_toolhermesvm_a目标。

值得注意的是,构建行为与HERMES_ENABLE_DEBUGGER编译选项强相关:

  • 若该选项未开启,构建出的hcdp只是一个打印提示后以失败退出的占位程序(stub),源码见 tools/hcdp/hcdp.cpp:
    hcdp compiled without Hermes debugger enabled.
  • 若该选项开启,才会编译完整的调试实现,并链接hermesvm_a静态库。同时 CMake 会强制为hcdp打开 RTTI 与异常支持(set(HERMES_ENABLE_RTTI ON)set(HERMES_ENABLE_EH ON)),并为JSONHelpers.cpp单独附加-fno-exceptions -fno-rtti(MSVC 下对应/EHs-c- /GR-)编译选项,以隔离差异,见 tools/hcdp/CMakeLists.txt。

构建产物默认通过install(TARGETS hcdp RUNTIME DESTINATION bin)安装到bin目录。

2. 安装 npm 依赖

JavaScript 组件依赖wschrome-launcherchromium-edge-launcher三个包(声明见 tools/hcdp/package.json),在tools/hcdp目录下执行:

npm install

3. 运行

运行hcdp.js,依次传入两个参数:hcdp二进制的路径,以及待调试脚本的路径。例如:

node ./hcdp.js ~/hcdp ~/loop.js

其中:

  • ~/hcdp指向构建好的hcdp二进制;
  • ~/loop.js是你要调试的脚本。

hcdp.js在启动时会做参数与文件存在性校验:缺少参数会打印Usage: node <path to hcdp binary> <path to script to debug>并退出,路径非法会分别报Binary not found at .../Script not found at ...(见 tools/hcdp/hcdp.js)。校验通过后,它会以子进程方式拉起 C++ 二进制,并把脚本内容读入内存备用。

启动成功后终端会打印提示:按下o键会用本机 Chrome(或 Edge)打开 DevTools 界面,按下x键退出。也可以直接访问提示中给出的 URL,其形式为:

https://chrome-devtools-frontend.appspot.com/serve_file/@<devtools版本>/js_app.html?ws=127.0.0.1%3A9999

可见调试端口固定为9999,DevTools 前端版本号定义在 tools/hcdp/hcdp.js;浏览器启动依赖chrome-launcher/chromium-edge-launcher,若两者都不可用则抛出“Supported browsers: Google Chrome, Microsoft Edge”的错误。

三、整体架构:双组件协作

原文档明确给出了工具的两个组成部分,这里结合源码进一步展开其职责边界:

组件文件职责
C++ 组件tools/hcdp/hcdp.cpp、tools/hcdp/IPC.cpp、tools/hcdp/JSONHelpers.cpp通过 Hermes API 承载调试会话所需对象(Hermes Runtime、CDP Debug API、CDP Agent);从 stdin 接收 IPC 消息,按需创建/销毁 CDP Agent 或处理消息;把 Agent 产生的响应与通知通过 stdout 以 IPC 消息发出
JavaScript 组件tools/hcdp/hcdp.js启动 WebSocket 服务与调试客户端通信;原样打印流经的 CDP 消息;处理用户键盘输入;在客户端与 C++ 组件之间做消息格式转换

消息流转方向是双向的:

  • 客户端 → JS → C++:调试客户端通过 WebSocket 发送 CDP 消息,hcdp.js将其封装成 IPC 消息写入子进程 stdin,C++ 组件解析后交给对应 CDP Agent 处理;
  • C++ → JS → 客户端:CDP Agent 产生的响应与通知由 C++ 组件写到 stdout,hcdp.js逐行解析、转发回 WebSocket 客户端,并同步打印到终端。

四、C++ 组件:运行时与 CDP Agent 管理

1. RuntimeInstance:承载可调试的 Hermes 运行时

hcdp/hcdp.cpp 中的RuntimeInstance类负责在一个独立线程上运行待调试脚本,其构造过程清晰地展示了 Hermes 调试 API 的标准用法:

  1. fbhermes::makeHermesRuntime(...)创建运行时,配置中显式开启了采样性能分析(withEnableSampleProfiling(true));
  2. 调用cdp::CDPDebugAPI::create(*runtime_)为运行时挂接 CDP 调试接口,这是之后创建 CDP Agent 的前提;
  3. 注入console.log:通过 JSI 的Object/Function::createFromHostFunction构造宿主函数,把参数收集后以cdpDebugAPI->addConsoleMessage(...)上报为 CDP 控制台消息(类型为ConsoleAPIType::kLog),并附带当前调用栈(hermesRt.getDebugger().captureStackTrace()),这样 DevTools 的 Console 面板才能看到脚本输出;
  4. 通过SerialExecutorruntime_->debugJavaScript(source, url, flags)排入运行时线程执行,保证脚本执行与调试任务串行化。

析构顺序同样讲究:先销毁 executor 等待任务结束,再依次销毁 CDP Debug API 与运行时。

2. debugScript:IPC 事件循环与 Agent 生命周期

hcdp/hcdp.cpp 的debugScript是 C++ 侧的主循环,结构为while (std::optional<IPCCommand> ipc = receiveIPC()),对三类 IPC 消息分别处理:

  • C(Connect):为clientID创建新 Agent,调用cdp::CDPAgent::create(...),并传入两个关键回调:
    • 运行时任务回调:把 CDP 内部产生的、需要独占运行时执行的任务通过RuntimeInstance::addTask排到运行时线程,确保“在两次 JS 执行间隙”执行;
    • 出站消息回调:把 Agent 产生的响应/通知封装为M类型 IPC 消息发回 stdout。
  • M(Message):从agents表按clientID查找 Agent,把客户端消息解析为message::Request后交给agent->second->handleCommand(...)处理;若找不到对应 Agent 则抛出No such agent
  • D(Disconnect):从agents表移除并销毁该客户端对应的 Agent。

3. 两个值得注意的实现细节

executionContextCreated通知的拦截注入。为了让 DevTools 的 Console 面板正常工作,运行时必须发出Runtime.executionContextCreated通知。hcdp 的实现方式是:当收到客户端的Runtime.enable命令时,记录其消息id(见 tools/hcdp/hcdp.cpp);随后在出站消息回调里,若发现某条响应的id恰好等于该记录值,就紧接着补发一条Runtime.executionContextCreated通知(desc.name = "main",执行上下文 id 由全局计数器nextExecutionContextId分配),再转发原响应,见 tools/hcdp/hcdp.cpp。响应id的提取由 tools/hcdp/JSONHelpers.cpp 的getResponseId完成,它借助 Hermes 自身的JSONParservalueFromJson<long long>解析。

stdout 缓冲处理。main入口调用setbuf(stdout, nullptr)关闭输出缓冲,避免大体积输出(例如性能分析结果)被积压在缓冲区,见 tools/hcdp/hcdp.cpp。

五、IPC 消息协议

C++ 与 JavaScript 组件之间的通信采用行式文本协议,定义于 tools/hcdp/IPC.h 与 tools/hcdp/IPC.cpp。每条消息由三部分组成:

字段说明取值
type消息类型(单个字符)C/M/D
agentId(源码中为clientID目标 Agent 的唯一数字 ID,类型为uint32_t非负整数
message可选的 CDP 消息体(JSON 字符串)CD时留空

三种消息类型的语义如下(沿用原文档定义):

  • C— Connect:连接。agentId表示要创建的新 Agent 的唯一 ID,message不使用。对应源码常量kConnectIPCType = 'C'
  • M— Message:消息。agentId表示此前已创建、应处理message的那个 Agent。对应kMessageIPCType = 'M'
  • D— Disconnect:断开。agentId表示要销毁的 Agent 的 ID,message不使用。对应kDisconnectIPCType = 'D'

线格式示例(C++ 侧发送,见 tools/hcdp/IPC.cpp):

M3{"id":5,"method":"Debugger.enable"}

即:type紧随agentId,随后是消息体(若无消息体则省略),最后以换行符\n结束整条 IPC 消息。

解析侧(tools/hcdp/IPC.cpp)先std::getline读一行,再用istringstream依次提取typeagentId,行尾剩余部分作为message

JavaScript 侧的发送函数与之一一对应:sendConnectIPC/sendMessageIPC/sendDisconnectIPC最终都调用sendIPC(type, clientId, message),拼出<type><clientId><message>\n写入子进程 stdin(见 tools/hcdp/hcdp.js)。

六、JavaScript 组件:WebSocket 服务与消息桥

1. WebSocket 服务与客户端管理

hcdp.js使用ws库在9999端口启动 WebSocket 服务(tools/hcdp/hcdp.js)。每个客户端连接会获得一个自增的数字 ID(clientIdCounter++),并立即向 C++ 组件发送C连接 IPC;收到客户端消息则发送M消息 IPC;连接关闭则发送D断开 IPC,同时清理本地clients表(tools/hcdp/hcdp.js)。因此该服务天然支持多个调试客户端同时连接,每个客户端对应一个独立的 CDP Agent。

2. stdout 行解析与消息转发

C++ 组件通过 stdout 输出两类内容:IPC 消息与脚本自身的输出。hcdp.js按行累积缓冲(lineBuffer),对每一行:

  1. 定位首个{,若无则视为脚本输出,直接打印到终端;
  2. 首字符必须是M(即messageIPCType),否则也视为普通输出打印;
  3. 解析M{之间的数字作为clientID,做合法性校验(非数字、越界、客户端已断开等情况都有对应处理——例如客户端在 CDP 消息到达前就已断开时,会直接跳过该消息);
  4. 对剩余 JSON 做JSON.parse校验,非法消息抛出Malformed message
  5. 转发给对应 WebSocket 客户端,并记录日志(tools/hcdp/hcdp.js)。

3. 本地拦截Debugger.getScriptSource

一个典型的“JS 侧智能”是:当客户端请求Debugger.getScriptSource(获取脚本源码)时,hcdp.js并不把它转发给 C++,而是直接在本地生成响应——因为脚本源码本来就是由hcdp.js自己读入内存的。实现方式为:监听Debugger.scriptParsed通知记录当前脚本的scriptIdinspectScriptParsed),收到getScriptSource请求时,若请求的scriptId匹配则返回result.scriptSource,否则返回-32602(Invalid params)错误(tools/hcdp/hcdp.js)。这既减少了 IPC 往返,也避免把整个脚本源码通过子进程管道传回。

4. 终端交互与事件日志

hcdp.js将进程 stdin 设为 raw 模式,实现两键快捷键:o打开 DevTools、x退出并杀掉子进程(tools/hcdp/hcdp.js)。终端日志以“图标 + 客户端 ID + 消息”的格式打印所有流经事件,图标含义如下:

图标含义
客户端连接(connect)
客户端发来的命令(command from client)
发往客户端的响应/通知(response/notification to client)
客户端断开(disconnect)

七、CDP Agent 与调试 API 的源码依据

hcdp 依赖的调试内核位于 API/hermes/cdp/:

  • CDPDebugAPI:管理运行时级调试状态,CDPDebugAPI::create以 Hermes 运行时为参数创建,hcdp 在RuntimeInstance构造时调用(见 API/hermes/cdp/CDPDebugAPI.h);
  • CDPAgent:处理 Debugger、Runtime、Profiler、HeapProfiler 域的 CDP 消息。其公开接口CDPAgent::create(executionContextID, cdpDebugAPI, enqueueRuntimeTaskFunc, messageCallback, state)与 hcdp 的调用方式完全对应(API/hermes/cdp/CDPAgent.h);注释明确要求集成方维护一个“独占访问运行时”的任务队列,即 hcdp 中SerialExecutor+addTask的职责来源。handleCommand(std::string json)可被任意线程调用,这与 hcdp 在 IPC 主循环中直接调用它相印证。

此外,hcdp 使用的消息类型(如message::Request::fromJsonRuntime.executionContextCreated)由 API/hermes/cdp/MessageTypes.h 等头文件定义,JSON 解析则复用 Hermes 自身的 lib/Parser/JSONParser.cpp。

八、小结与适用场景

通过本文可以梳理出 hcdp 的完整工作链条:

调试客户端(Chrome/Edge DevTools) │ WebSocket (127.0.0.1:9999) ▼ hcdp.js(WebSocket 服务、日志、键盘交互、脚本源码本地应答) │ IPC 行协议(stdin/stdout):C/M/D + agentId + message ▼ hcdp.cpp(Hermes Runtime + CDPDebugAPI + 每客户端一个 CDPAgent) │ debugJavaScript 执行脚本 ▼ 待调试的 JS 脚本

hcdp 的适用场景包括:快速验证 Hermes 的 CDP 调试行为、在没有完整 React Native 宿主环境的情况下调试单个 JS 脚本、以及作为研究 Hermes 调试 API(CDPDebugAPI/CDPAgent/debugJavaScript)如何集成的参考实现。需要特别说明的是,hcdp是仓库tools目录下的独立开发调试工具,若需在应用内集成 CDP 调试,官方路径仍是直接使用 API/hermes/cdp/ 中的公开 API(可参考 API/hermes/DebuggerAPI.h 与 API/hermes/cdp/CDPAgent.h 的接口注释)。

  • 语言运行时
  • 编译器
  • 移动开发

【免费下载链接】hermes

A JavaScript engine optimized for running React Native.

项目地址:https://gitcode.com/gh_mirrors/hermes/hermes
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 21:43:39

SolarWinds NPM 12.0.1 部署调优与维护实战指南

简介&#xff1a;面向网络运维与IT管理人员的SolarWinds网络性能监视器&#xff08;NPM&#xff09;12.0.1及Orion Package 12.1资源包&#xff0c;用于解决大规模网络设备状态监测、性能分析与故障预警&#xff0c;可覆盖10至10000个节点的监控规模。资源为docx格式文档&#…

作者头像 李华
网站建设 2026/9/23 21:43:22

OpenStock开源库存系统搭建指南:Docker部署与核心模块解析

1. 从零认识 OpenStock&#xff1a;它到底是什么&#xff0c;能解决什么问题第一次听到 OpenStock 这个名字&#xff0c;很多人会下意识以为它跟股票行情、量化交易有关。其实不然。OpenStock 是一套面向中小团队和独立开发者的开源库存管理系统&#xff0c;核心定位是“轻量、…

作者头像 李华
网站建设 2026/9/23 21:42:12

JDK 21 ARM64 Linux 安装与生产级部署实践

简介&#xff1a;本资源是面向Linux Arm架构设备&#xff08;如树莓派、国产ARM服务器等&#xff09;的Java开发环境核心组件——JDK 21官方二进制发行版&#xff0c;专为嵌入式开发、边缘计算及国产化信创场景下的Java应用开发与部署提供完整支持。压缩包共386个文件&#xff…

作者头像 李华
网站建设 2026/9/23 21:37:26

机器视觉工业缺陷检测全解析:成像、标定到算法落地

简介&#xff1a;这是一份面向机器视觉初学者与工业检测工程师的技术梳理资料&#xff0c;系统讲解视觉检测系统的组成与硬件选型思路&#xff0c;涵盖光源类型&#xff08;含LED、萤光灯、卤素灯等&#xff09;、相机参数、镜头选择等关键环节&#xff0c;并介绍常用图像处理算…

作者头像 李华