- 语言运行时
- 嵌入式
- 物联网
- 编译器
【免费下载链接】jerryscript
Ultra-lightweight JavaScript engine for the Internet of Things.
本文以 docs/07.DEBUGGER.md 为主线,系统讲解 JerryScript 远程调试器的整体架构、构建与启动方式、命令行工具用法,以及宿主应用中可用的全部调试器 C-API 与扩展传输层实现。读完本文,你将掌握如何在 IoT 设备上为 JerryScript 启用 WebSocket/串口调试服务、如何用 Python 客户端控制执行流程,以及如何在自己的应用程序中嵌入调试支持。
调试器架构:Server 与 Client 的双组件模型
JerryScript 提供一套远程调试器(remote debugger),用于调试运行在设备上的 JavaScript 程序。整套系统由两个主要组件构成:
- 服务端(Server):内置于 JerryScript 二进制/库中,随目标应用运行在设备端;
- 客户端(Client):独立的调试器应用,运行在开发机等资源更充裕的系统上。
当前仓库内置一个基于 Python 的调试器客户端,位于 jerry-debugger 子目录,入口脚本为 jerry-debugger/jerry_client.py。该客户端实现了完整的交互式命令行界面,可展示客户端与服务端之间的通信协议细节,因此既可以直接用于调试,也可以作为参考实现,被集成开发环境(IDE)复用。
jerry-debugger/README.md 中列出了官方提供的三类调试工具:仓库内的jerry_client.py控制台客户端、IoT.js Code,以及 JerryScript 调试器 Chrome Web 工具。
协议版本:核心头文件 jerry-core/include/jerryscript-debugger.h 中定义了
JERRY_DEBUGGER_VERSION (9),客户端与服务端通过该版本号协商协议兼容性。
可插拔的传输层设计
通信协议的传输层(transport layer)是可插拔的。目前仓库以 JerryScript 扩展(jerry-ext)的形式提供了一套基于 WebSocket 的实现,通过 TCP/IP 网络传输调试消息(源码见 jerry-ext/debugger/debugger-ws.c)。此外仓库还实现了:
- Raw Packet 传输:
jerryx_debugger_rp_create (),见 jerry-ext/debugger/debugger-rp.c; - 串口(Serial)传输:
jerryx_debugger_serial_create (const char *config),见 jerry-ext/debugger/debugger-serial.c; - TCP 传输:
jerryx_debugger_tcp_create (uint16_t port),见 jerry-ext/debugger/debugger-tcp.c。
从源码结构看,只要实现对应的传输接口,任何可靠的流式(stream)或数据报(datagram)协议都可以用来承载调试消息,这为连接 UART、蓝牙、CAN 等物理通道提供了扩展空间。
构建调试器服务端
要让目标二进制包含调试器支持,必须显式开启该特性。在仓库根目录执行构建脚本时传入:
--jerry-debugger=on例如:
python tools/build.py --jerry-debugger=on未开启该选项时,调试器相关代码不会编入产物;这也解释了为什么 jerry-main/arguments/options.c 中的check_feature逻辑会在特性被禁用时输出Ignoring ... option because this feature is disabled!的警告。
启动与连接:调试服务端命令行参数
JerryScript 桌面主程序(jerry-main/main-desktop.c)解析命令行参数并在引擎初始化后启动调试服务。以下是文档给出的三个核心参数:
| 参数 | 作用 |
|---|---|
--start-debug-server | 启动调试服务端,等待客户端连接 |
--debugger-wait-source | 启动后等待客户端下发源代码 |
--log-level 2 | 提升日志级别,以看到Waiting for client connection提示信息 |
组合使用示例:
./build/bin/jerry --start-debug-server --debugger-wait-source --log-level 2除文档列出的参数外,jerry-main/arguments/options.c 还定义了以下可选参数,便于针对不同物理链路定制调试通道:
| 参数 | 默认值 | 说明 |
|---|---|---|
--debug-port NUM | 5001 | 调试服务端口 |
--debug-channel [websocket\|rawpacket] | websocket | 调试消息编码通道 |
--debug-protocol PROTOCOL | tcp | 底层传输协议(tcp或serial) |
--serial-config OPTIONS_STRING | /dev/ttyS0,115200,8,N,1 | 串口配置(设备、波特率、数据位、校验、停止位) |
这些参数最终在 jerry-main/main-desktop.c 的main_init_debugger中按“协议→通道”两级组合完成初始化:先按debug_protocol选择 TCP 或串口传输,再按debug_channel叠加 WebSocket 或 Raw Packet 编码层,最后统一交给jerryx_debugger_after_connect挂接引擎。
客户端连接方式
Python 客户端通过命令行指定服务端地址进行连接:
python jerry-debugger/jerry_client.py <IP地址>:<端口>若服务端与客户端运行在同一台机器上,地址可写为localhost:5001(即默认参数,见 jerry-debugger/jerry_client_main.py 中的parser.add_argument("address", ...)定义)。
连接时序要求:客户端必须在 JavaScript 应用运行之前连上服务端;对于多文件场景,Python 客户端还支持在引擎初始化完成后立即动态挂接(on-the-fly attachment)。
调试信息的内存策略
JerryScript 本身不保留调试信息(例如每个可能断点位置对应的行号索引)。这些信息由资源更充裕的客户端负责存储——客户端在收到调试信息后自行缓存,而 JerryScript 在把调试信息传给客户端后立即释放,以节省设备端宝贵的内存。因此调试能力(如断点行号解析)实际由客户端侧的数据维护能力决定。
断点行为与 help 命令
连接建立后,执行流程受调试器控制。调试器总是停在第一个可能的断点位置,效果等同于执行了stop命令——这允许你在真正有意义的代码执行开始之前,先插入断点。
客户端支持的所有命令可用help命令查询。仓库自带测试用例 tests/debugger 目录下的.cmd/.expected/.js三件套(如 do_break.cmd)覆盖了 break、continue、next、step、backtrace、scope、variables、eval、throw、restart 等交互场景,可作为命令用法的直接参考。
在宿主应用中集成调试支持
当使用扩展提供的 WebSocket 传输层时,宿主应用在调用jerry_init ()之后,通过如下方式启用调试器并阻塞等待客户端连接:
jerryx_debugger_after_connect (jerryx_debugger_tcp_create (debug_port) && jerryx_debugger_ws_create ());jerryx_debugger_tcp_create (debug_port):创建指定端口的 TCP 服务;jerryx_debugger_ws_create ():在 TCP 之上叠加 WebSocket 消息编码;jerryx_debugger_after_connect (success):挂接调试器并等待连接(参数为前述调用的逻辑与结果)。
自定义传输层可以按相同模式实现并初始化。当前扩展还提供:
jerryx_debugger_rp_create ():Raw Packet 传输层;jerryx_debugger_serial_create (const char* config):串口协议,config形如/dev/ttyS0,115200,8,N,1。
上述接口均在扩展头文件 jerry-ext/include/jerryscript-ext/debugger.h 中声明。
源代码名的传递:传递给jerry_parse ()的源名称(source name)会被客户端用于标识源代码来源,通常就是文件名。调试信息与断点位置的对应关系依赖该名称,因此宿主在解析脚本时应传入有意义的文件名而非空串。
JerryScript 调试器 C-API 接口
本节按 docs/07.DEBUGGER.md 的 API 参考框架,完整介绍调试器类型与全部 6 个引擎级函数。这些函数由核心头文件 jerry-core/include/jerryscript-debugger.h 声明,示例代码需同时包含:
#include "jerryscript.h" #include "jerryscript-ext/debugger.h"类型:jerry_debugger_wait_for_source_callback_t
概要
该回调函数由jerry_debugger_wait_for_client_source在成功收到一段源代码时调用。
原型
typedef jerry_value_t (*jerry_debugger_wait_for_source_callback_t) (const jerry_char_t *source_name_p, size_t source_name_size, const jerry_char_t *source_p, size_t source_size, void *user_p);参数
source_name_p- 源代码的源名称(通常为文件名)source_name_size- 源名称的字节大小source_p- 源代码字符数据source_size- 源代码大小user_p- 透传给jerry_debugger_wait_for_client_source的自定义指针
回调的返回值会被jerry_debugger_wait_for_client_source原样回传(存入其return_value输出参数)。
类型:jerry_debugger_wait_for_source_status_t
该枚举定义在 jerry-core/include/jerryscript-debugger.h 中,是jerry_debugger_wait_for_client_source的返回值类型:
| 枚举值 | 含义 |
|---|---|
JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED | 源代码接收失败(如连接中断) |
JERRY_DEBUGGER_SOURCE_RECEIVED | 已成功收到一段源代码 |
JERRY_DEBUGGER_SOURCE_END | 收到“无更多源代码”的信号 |
JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED | 收到客户端发来的上下文重置(context reset)请求 |
其中SOURCE_END与CONTEXT_RESET_RECEIVED正是文档所描述的“客户端可能请求上下文重置或通知无更多源码”两类带外通知。
jerry_debugger_is_connected
概要
返回当前是否已有远程调试器客户端连接。
原型
bool jerry_debugger_is_connected (void);示例
#include <stdio.h> #include "jerryscript.h" #include "jerryscript-ext/debugger.h" int main (void) { jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); if (jerry_debugger_is_connected ()) { printf ("A remote debugger client is connected."); } jerry_cleanup (); }jerry_debugger_stop
概要
若远程调试器已连接且引擎当前未停在断点上,则在下一个可用断点处停止执行。无论该断点是否启用,引擎都会停住。
原型
void jerry_debugger_stop (void)示例
#include "jerryscript.h" #include "jerryscript-ext/debugger.h" int main (void) { jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); jerry_debugger_stop (); jerry_cleanup (); }参见
- jerry_debugger_continue
jerry_debugger_continue
概要
若引擎本会在下一个可用断点处停下,此函数可取消该停靠效果;引擎仍会在已启用的断点处停止。该函数实质上是jerry_debugger_stop ()调用或客户端发出的 stop 请求的“反向操作”。
原型
void jerry_debugger_continue (void)示例
#include "jerryscript.h" #include "jerryscript-ext/debugger.h" int main (void) { jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); jerry_debugger_continue (); jerry_cleanup (); }参见
- jerry_debugger_stop
jerry_debugger_stop_at_breakpoint
概要
启用或禁用“在断点处停止”。当停止被禁用时,所有断点(包括用户启用的断点)都会被忽略,从而实现 ECMAScript 代码的“隐藏执行”(hidden execution)——例如宿主希望不受调试器干扰地运行一段内部逻辑。
原型
void jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint)参数
enable_stop_at_breakpoint- 传true启用断点停靠,传false禁用
示例
#include "jerryscript.h" #include "jerryscript-ext/debugger.h" int main (void) { jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); jerry_debugger_stop_at_breakpoint (true); // Protected execution of JavaScript code. const jerry_char_t script[] = "42"; jerry_eval (script, sizeof (script) - 1, JERRY_PARSE_NO_OPTS); jerry_debugger_stop_at_breakpoint (false); jerry_cleanup (); }jerry_debugger_wait_for_client_source
概要
请求客户端提供下一段源代码,函数会一直等待直到整段源码接收完毕。作为回复,客户端可能请求上下文重置或通知没有更多源码,这些通知通过函数返回值回传。
原型
jerry_debugger_wait_for_source_status_t jerry_debugger_wait_for_client_source (jerry_debugger_wait_for_source_callback_t callback_p, void *user_p, jerry_value_t *return_value)示例(完整的“接收源码→解析→运行→处理重置”循环)
#include "jerryscript.h" #include "jerryscript-ext/debugger.h" /** * Runs the source code received by jerry_debugger_wait_for_client_source. */ static jerry_value_t wait_for_source_callback (const jerry_char_t *source_name_p, /**< source name */ size_t source_name_size, /**< size of source name */ const jerry_char_t *source_p, /**< source code */ size_t source_size, /**< source code size */ void *user_p /**< user pointer */) { (void) user_p; jerry_parse_options_t parse_options; parse_options.options = JERRY_PARSE_HAS_SOURCE_NAME; parse_options.source_name = jerry_string ((const jerry_char_t *) source_name_p, (jerry_size_t) source_name_size, JERRY_ENCODING_UTF8); jerry_value_t ret_val = jerry_parse (source_p, source_size, &parse_options); jerry_value_free (parse_options.source_name); if (!jerry_value_is_exception (ret_val)) { jerry_value_t func_val = ret_val; ret_val = jerry_run (func_val); jerry_value_free (func_val); } return ret_val; } /* wait_for_source_callback */ int main (void) { jerry_debugger_wait_for_source_status_t receive_status; do { /* Create a new JerryScript instance when a context reset is * received. Applications usually registers their core bindings * here as well (e.g. print, setTimeout). */ jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); do { jerry_value_t run_result; receive_status = jerry_debugger_wait_for_client_source (wait_for_source_callback, NULL, &run_result); jerry_value_free (run_result); } while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); jerry_cleanup (); } while (receive_status == JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED); if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) { // Handle the failure (e.g. display an error). } return 0; }实现佐证:桌面主程序在--debugger-wait-source模式下以完全相同的方式循环调用该函数(见 jerry-main/main-desktop.c):SOURCE_RECEIVE_FAILED直接报错退出,SOURCE_END结束循环,CONTEXT_RESET_RECEIVED则执行jerry_cleanup ()后goto restart重新初始化引擎。该示例同时演示了如何把客户端传来的源名称通过JERRY_PARSE_HAS_SOURCE_NAME选项传给jerry_parse (),与文档前述“source name 用于标识源代码来源”的说明相印证。
jerry_debugger_send_output
概要
将程序的输出发送给调试器客户端(例如替代print的输出通道,便于在调试界面中统一展示)。
原型
void jerry_debugger_send_output (const jerry_char_t *buffer, jerry_size_t str_size)示例
#include "jerryscript.h" #include "jerryscript-ext/debugger.h" int main (void) { jerry_init (JERRY_INIT_EMPTY); jerryx_debugger_after_connect (jerryx_debugger_tcp_create (5001) && jerryx_debugger_ws_create ()); jerry_char_t my_output[] = "Hey, this should be sent too!"; jerry_size_t my_output_size = sizeof (my_output); jerry_debugger_send_output (my_output, my_output_size); jerry_cleanup (); }快速上手:从构建到断点调试
综合以上内容,一次完整的调试会话可按下述步骤复现:
- 构建带调试器的二进制:
python tools/build.py --jerry-debugger=on; - 启动服务端:
./build/bin/jerry --start-debug-server --debugger-wait-source --log-level 2,等待Waiting for client connection日志出现; - 连接客户端:
python jerry-debugger/jerry_client.py localhost:5001; - 下发源码并调试:在客户端中使用
break设置断点、continue/next/step控制执行、backtrace/scope检视状态、eval求值表达式,全程可随时用help查询命令;--client-source参数可一次携带多个源码文件,配合--debugger-wait-source实现多文件动态挂接。
延伸阅读
- 调试器协议与命令的测试基线:tests/debugger(
do_*.cmd命令脚本与do_*.expected期望输出一一对应); - 传输层扩展实现:jerry-ext/debugger(
debugger-tcp.c、debugger-ws.c、debugger-rp.c、debugger-serial.c、debugger-common.c); - 调试器核心状态机与等待逻辑:jerry-core/debugger/debugger.c 与 jerry-core/debugger/debugger.h;
- 官方调试器工具清单:jerry-debugger/README.md。
- 语言运行时
- 嵌入式
- 物联网
- 编译器
【免费下载链接】jerryscript
Ultra-lightweight JavaScript engine for the Internet of Things.
相关推荐
JerryScript 调试器传输接口(Debugger Transport Interface)完全解析
JerryScript 调试器传输接口(Debugger Transport Interface)完全解析 JerryScript 是为物联网(IoT)场景设计
语言运行时嵌入式物联网编译器Pomerium项目远程调试指南:从构建到调试全解析
Pomerium项目远程调试指南:从构建到调试全解析 前言 在现代微服务架构中,身份认证和访问控制网关扮演着至关重要的角色。Pomerium作为一款开源的零信任
后端API网关认证鉴权零信任PageSpy API终极指南:从零配置到高级远程调试的完整教程
PageSpy API终极指南:从零配置到高级远程调试的完整教程 PageSpy是一款轻量级跨平台远程调试工具,通过直观的API接口让开发者轻松实现应用监控、问
前端开发工具调试器
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考