
语言运行时嵌入式解释器【免费下载链接】duktapeDuktape - embeddable Javascript engine with a focus on portability and compact footprint项目地址https://gitcode.com/gh_mirrors/du/duktape点击查看免费下载导读本文基于 Duktape 仓库 debugger/README.rst 编写系统介绍 Duktape 调试器的两大配套工具基于 Node.js 的 Web 调试界面duk_debug.js与 JSON 调试代理Node.js / DukLuv 两种实现。读者将掌握从编译带调试支持的duk命令行工具、启动调试目标、连接浏览器调试界面到使用 telnet 通过 JSON 协议直接与目标交互的完整流程并理解调试传输transport与调试协议protocol的分层架构以及如何在自定义设备上实现自己的调试传输。总览Web 调试 UI 与 JSON 调试代理debugger/目录下的这套调试器配套工具包含两个核心部件Web 调试 UIduk_debug.js一个 Node.js 程序同时承担调试客户端debug client与 Web 服务器两种角色。它连接到 Duktape 命令行工具或其他支持示例 TCP 传输examples/debug-trans-socket的调试目标并在浏览器中提供可交互的调试界面。JSON 调试代理把 Duktape 的二进制调试协议映射为更易读写的 JSON 协议。仓库提供两种实现——一个运行在 Node.js 上的duk_debug.js通过--json-proxy选项启用另一个是单文件的duk_debug_proxy.js运行在 DukLuv 环境。关于调试器内部机制协议格式、dvalue 编码、断点与步进语义等的详细说明见 doc/debugger.rst本文侧重于这两套工具的实际使用。环境准备与编译带调试支持的 duk 命令行工具前提条件Node.js v0.10.x 或更新版本Web 调试 UI 依赖express、socket.io、bluebird等 npm 包见 debugger/package.json旧版本 Node.js 无法支撑这些依赖。调试目标如duk命令行工具与调试客户端之间需要能建立 TCP 连接。编译支持调试的 Duktape要在 Duktape 中启用调试功能需要同时满足两个层面的配置Duktape 库层面通过 tools/configure.py 开启DUK_USE_DEBUGGER_SUPPORT与DUK_USE_INTERRUPT_COUNTER两个配置项。其中中断计数器interrupt counter是调试器轮询的基础——只有启用了它字节码执行器才能在每条指令间检查断点与调试请求。源码层面这两个选项分别对应 config/config-options/DUK_USE_DEBUGGER_SUPPORT.yaml 与 config/config-options/DUK_USE_INTERRUPT_COUNTER.yaml可选的还有DUK_USE_DEBUGGER_DUMPHEAP堆转储与DUK_USE_DEBUGGER_INSPECT对象检视。命令行工具层面编译duk时在编译器命令行上定义DUK_CMDLINE_DEBUGGER_SUPPORT用于开启--debugger选项。源码发布包方式进入发布目录后直接使用自带的 Makefile 构建$ cd duktape dist directory $ make -f Makefile.dukdebugdist-files/Makefile.dukdebug 中展示了这一过程的完整细节它先调用tools/configure.py生成配置头与合并源码--source-directory src-input --output-directory prep并注入-DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER -DDUK_USE_DEBUGGER_DUMPHEAP -DDUK_USE_DEBUGGER_INSPECT然后在编译duk_cmdline.c时加上-DDUK_CMDLINE_DEBUGGER_SUPPORT。Unix 平台使用 examples/debug-trans-socket/duk_trans_socket_unix.c 作为示例 TCP 传输WindowsMinGW则应换用duk_trans_socket_windows.c并链接-lws2_32。Git 仓库方式仓库根目录的duk目标默认就带有调试支持直接构建即可$ make clean duk使用 Web 调试 UI启动调试目标启动duk命令行工具并让它等待调试器连接。注意此时需要停留在包含被调试源码的目录中以保证函数对象的fileName属性与调试客户端侧的搜索路径一致这直接决定断点能否命中# 源码发布包方式 $ cd duktape dist directory $ ./duk --debugger mandel.js # Git 仓库方式 $ cd duktape checkout/tests/ecmascript/ $ ../../duk --debugger test-dev-mandel2-func.js仓库中的实际测试用例位于 tests/ecmascript/dev/test-dev-mandel2-func.jsREADME 中示例使用的正是该文件。启动 Web 调试 UIduk_debug.js需要从debugger/目录启动Makefile 中的搜索路径均以此相对目录计算$ cd debugger/ $ make # 等价于运行 node duk_debug.jsdebugger/Makefile 中的run目标会先执行npm install并下载前端静态资源socket.io、jQuery 等再以node duk_debug.js --source-dirs自动探测的源码目录启动。其默认端口约定为9091调试目标监听端口duk --debugger在此等待连接9092Web UI 的 HTTP 端口浏览器访问9093JSON 调试代理端口见下文。随后在浏览器中打开 http://localhost:9092/ 即可开始调试。调试客户端会在启动时自动连接调试目标如果目标启动得较晚需要点击 Web UI 中的 Attach 按钮手动连接。duk_debug.js的命令行选项解析逻辑见 debugger/duk_debug.js 的main()函数汇总如下选项默认值说明--target-host127.0.0.1调试目标主机地址--target-port9091调试目标 TCP 端口--http-port9092Web UI HTTP 服务端口--json-proxy关闭以 JSON 代理模式运行替代 Web UI 模式--json-proxy-port9093JSON 代理监听端口--source-dirs../tests/ecmascript源码搜索路径路径分隔符分隔可多个--dump-debug-read无将接收到的二进制调试流转储到文件--dump-debug-write无将发送的二进制调试流转储到文件--dump-debug-pretty无以可读格式转储调试流--log-messages关闭记录调试消息日志make rundebug目标提供了带完整诊断输出的启动方式--verbose --log-messages /tmp/dukdebug-messages --dump-debug-pretty /tmp/dukdebug-pretty便于排查连接与协议问题。使用 JSON 调试代理JSON 调试代理的价值在于实现一个调试客户端时不必处理 Duktape 的二进制调试协议——JSON 协议与二进制协议大致呈 1:1 映射但语法更直观。代理在内部完成 JSON ↔ 二进制 dvalue 的双向转换。仓库为此提供了两种实现。DukLuv JSON 代理DukLuv 是一个基于 LibUV 与 Duktape 构建的小型可移植事件循环与 Duktape 同为 MIT 许可体积远小于 Node.js因此适合内嵌到自定义调试客户端中——只需把 DukLuv 可执行文件与duk_debug_proxy.js一并携带即可。安装 DukLuv# 确保已安装 cmake $ git clone https://github.com/creationix/dukluv.git $ cd dukluv $ git submodule init; git submodule update $ make构建产物位置Linux 下为./build/dukluvWindows 下为.\build\Debug\dukluv.exe。启动代理# 使用 Makefile 自动生成 duk_debug_meta.json 并启动 # 如 DukLuv 不在 PATH 中需编辑 Makefile 中的 DUKLUV 变量 $ make runproxydukluv # 手动启动可运行 dukluv duk_debug_proxy.js --help 查看帮助 $ .../path/to/dukluv duk_debug_proxy.jsduk_debug_proxy.js的配置位于文件头部debugger/duk_debug_proxy.js监听0.0.0.0:9093连接目标127.0.0.1:9091。它需要一份元数据文件把调试命令编号、opcode 编号、错误码等映射为可读名称该文件由 tools/merge_debug_meta.py 合并四个 YAML 源文件生成debugger/duk_classnames.yaml、debugger/duk_debugcommands.yaml、debugger/duk_debugerrors.yaml、debugger/duk_opcodes.yaml。启动调试目标与 Web UI 场景相同$ cd duktape checkout/tests/ecmascript/ $ ../../duk --debugger test-dev-mandel2-func.js然后用 telnet 连接代理并开始发命令。代理会自动连接目标并先把握手与状态通知推送给客户端$ telnet localhost 9093 Trying 127.0.0.1... Connected to localhost. Escape character is ^]. {notify:_TargetConnecting,args:[127.0.0.1,9091]} {notify:_TargetConnected,args:[1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo]} {notify:Status,command:1,args:[1,test-dev-mandel2-func.js,global,58,0]} {request:BasicInfo} {reply:true,args:[10499,v1.4.0-140-gc9a6c7c,duk command built from Duktape repo,1]} {request:Eval,args:[print(Hello world!); 123;]} {notify:Print,command:2,args:[Hello world!\n]} {reply:true,args:[0,{type:number,data:405ec00000000000}]} [...]代理日志非常有利于开发调试——它同时输出 JSON 与二进制 dvalue 两种流量可以清晰地看到每条 JSON 命令如何被翻译成对应的二进制字节流$ make runproxydukluv Running Dukluv based debug proxy dukluv duk_debug_proxy.js --log-level 2 --metadata duk_debug_meta.json 2016-02-17T13:59:42.308Z INF Proxy: Read proxy metadata from duk_debug_meta.json 2016-02-17T13:59:42.325Z INF Proxy: Listening for incoming JSON debug connection on 0.0.0.0:9093, target is 127.0.0.1:9091 2016-02-17T13:59:47.994Z INF Proxy: PROXY -- TARGET: |04| 2016-02-17T13:59:47.994Z INF Proxy: PROXY -- TARGET: |81| ... 2016-02-17T13:59:48.160Z INF Proxy: PROXY -- CLIENT: {notify:Status,command:1,args:[1,test-dev-mandel2-func.js,global,58,0]} 2016-02-17T13:59:51.289Z INF Proxy: PROXY -- CLIENT: {request:BasicInfo} 2016-02-17T13:59:51.289Z INF Proxy: PROXY -- TARGET: |01| 2016-02-17T13:59:51.291Z INF Proxy: PROXY -- TARGET: |02| 2016-02-17T13:59:51.291Z INF Proxy: PROXY -- TARGET: |e903| ... 2016-02-17T13:59:51.293Z INF Proxy: PROXY -- CLIENT: {reply:true,args:[10499,v1.4.0-140-gc9a6c7c,duk command built from Duktape repo,1]} 2016-02-17T14:00:06.105Z INF Proxy: PROXY -- CLIENT: {request:Eval,args:[print(Hello world!); 123;]} 2016-02-17T14:00:06.105Z INF Proxy: PROXY -- TARGET: |01| 2016-02-17T14:00:06.105Z INF Proxy: PROXY -- TARGET: |9e| 2016-02-17T14:00:06.105Z INF Proxy: PROXY -- TARGET: |7b7072696e74282748656c6c6f20776f726c642127293b203132333b| ... 2016-02-17T14:00:06.174Z INF Proxy: PROXY -- CLIENT: {reply:true,args:[0,{type:number,data:405ec00000000000}]}从上例可以直观地看到客户端发送的Eval请求被编码为目标侧能识别的二进制命令|01|为请求帧起始、|9e|为 Eval 命令编号其后为 UTF-8 编码的 eval 源码字节目标的Print通知与数值回复再被转换回 JSON。这种人可读的 JSON ↔ 二进制协议的双向日志是理解 Duktape 调试协议最直接的教材。Node.js JSON 代理duk_debug.js本身也内置了 JSON 代理模式前提条件与运行调试客户端一致$ make runproxynodejs启动调试目标后连接 localhost:9093 即可交互。下面是一次使用 telnet 手工输入命令的示例会话--表示发送方向、--表示接收方向仅为可读性标注并非协议内容$ telnet localhost 9093 Trying 127.0.0.1... Connected to localhost. Escape character is ^]. -- {notify:_TargetConnected,args:[1 10199 v1.1.0-275-gbd4d610-dirty duk command built from Duktape repo]} -- {notify:Status,command:1,args:[1,test-dev-mandel2-func.js,global,58,0]} -- {request:BasicInfo} -- {reply:true,args:[10199,v1.1.0-275-gbd4d610-dirty,duk command built from Duktape repo,1]} -- {request:Eval, args:[ print(Math.PI) ]} -- {notify:Print,command:2,args:[3.141592653589793\n]} -- {reply:true,args:[0,{type:undefined}]} -- {request:Resume} -- {reply:true,args:[]} -- {notify:Status,command:1,args:[0,test-dev-mandel2-func.js,global,58,0]} -- {notify:Status,command:1,args:[0,test-dev-mandel2-func.js,global,58,0]} -- {notify:Print,command:2,args:[................................................................................\n]} [...] -- {notify:_Disconnecting}telnet 方式的另一大价值是即使你最终打算直接实现二进制协议也可以先用 telnet 会话粘贴调试命令快速试验命令语义再把这些理解迁移到二进制实现中。代理连接的调试目标地址同样由duk_debug.js的--target-host/--target-port等命令行选项控制。源码搜索路径机制Node.js 调试客户端必须能找到与目标上运行的代码相匹配的源文件。目标侧与客户端侧的 fileName 必须精确一致——因为断点正是基于 Function 对象的fileName属性来定位的文件名不匹配意味着断点无法命中。搜索路径通过--source-dirs选项设置默认只包含../tests/ecmascript/。搜索路径的语义如下均相对于duk_debug.js的工作目录即debugger/若目标上某函数的 fileName 为foo/bar.js客户端将尝试从../tests/ecmascript/foo/bar.js加载源码若文件系统中存在../tests/ecmascript/baz/quux.jsWeb UI 的下拉框会显示baz/quux.js选择该文件并添加断点时发送给调试目标的断点 fileName 就是baz/quux.js。debugger/Makefile 对默认路径做了自动探测若仓库的../tests/ecmascript/*.js不存在即处于源码发布包目录则回退为--source-dirs../避免在完整仓库中扫描到 test262 等大量无关文件。值得注意的是README 也坦承搜索路径机制仍有很多可改进之处例如支持加入某个路径但按路径和模式排除文件等说明这套工具是够用但非完备的参考实现。架构三层解耦Web 调试 UI 的完整架构如下摘自 README 的架构图------------------- | Web browser | [debug UI] ------------------- | | http (port 9092) | socket.io v ------------------- | duk_debug.js | [debug client] ------------------- | /\ | || ----------||---- [example tcp transport] (port 9091) | || (application provides concrete transport) | || | ||---- [debug protocol stream] | || (between debug client and Duktape) | || - - | - - - - -|| - - : v || : : -------------||- : [target] : | application || | : : -------------||- : : ^ || : : | || : [debug API] : ----------||-------- debug transport callbacks : | || : (read, write, peek, read/write flush) : | || : implemented by application : | \/ : : ---------------- : : | Duktape | : : ---------------- : - - - - - - - - - - - 架构的关键在于两层独立调试传输transport与应用相关Duktape 命令行工具与本调试客户端使用的是示例TCP 传输。实际产品中传输机制完全由应用决定——Wi-Fi、串口、现有自定义协议均可。传输回调read、write、peek、read/write flush由应用实现通过duk_debugger_attach()提供给 Duktape。调试协议protocol与传输无关协议在传输流内部运行规范见 doc/debugger.rst本调试客户端则提供了协议如何被实际使用的具体范例。因此只要传输层对接成功任何实现了协议一端的客户端/目标都能互通。从源码看duk_debug.js内部维护了两个方向的数据流来自调试目标的二进制 dvalue 流由inputParser解析后根据 dvalue 类型req/rep/err/nfy等转换为对应的 JSON 消息写入 Web 或代理输出见 debugger/duk_debug.js 中writeJson附近的处理逻辑这正是 JSON 代理1:1 映射的实现基础。自定义传输的三种方案如果目标设备无法使用示例 TCP 传输需要为目标设备和调试客户端分别实现自定义传输。README 为调试客户端侧提供了三种可选方案方案一duk_debug.js 自定义 TCP 代理不改动duk_debug.js而是实现一个 TCP 代理它接受来自duk_debug.js的 TCP 连接再用自定义传输与目标通信-------------- TCP ------- custom -------- | duk_debug.js | ------ | proxy | --------- | target | -------------- ------- --------这是最直接的做法代理还可复用于其他调试客户端例如直接与目标对话的自定义脚本。也可以让代理通过 stdin/stdout 与duk_debug.js通信配合 netcat 使用。方案二duk_debug.js 自定义 Node.js 流把自定义传输实现为 Node.js streamReadable/Writable最好放在独立模块中以保持对duk_debug.js的最小 diff然后在duk_debug.js中搜索CUSTOMTRANSPORT标记将默认的 TCP stream 替换为自定义流。源码中该标记位于连接建立处this.targetStream.connect(optTargetPort, optTargetHost, ...)附近见 debugger/duk_debug.js替换目标是this.targetStream的赋值。方案三完全自定义调试客户端自行实现调试客户端与 UI此时需要同时实现 Duktape 调试协议的客户端部分。协议规范见 doc/debugger.rstduk_debug.js提供了可运行的协议参考实现。如果希望绕过二进制协议则可以直接基于 JSON 调试代理开发——JSON 协议提供与二进制协议大致 1:1 的映射但语法友好得多。目标设备侧目标设备侧需要实现duk_debugger_attach()所需的调试传输回调read、write、peek、flush 等细节见 doc/debugger.rst可运行的 TCP 传输参考实现在 examples/debug-trans-socketUnix 与 Windows 各一个.c文件。若希望在 Duktape 所在进程内就地终止调试连接仓库还提供了本地传输参考 examples/debug-trans-dvalue它内置了本地 dvalue 编解码器方便在 C 侧直接编写本地调试客户端。调试器能力速览根据 doc/debugger.rstDuktape 调试器提供的能力包括执行状态查看运行/暂停的文件行、调用栈、局部变量、执行控制暂停、恢复、单步进入/越过/跳出、基于文件/行对的断点与debugger语句、暂停时在调用栈任意帧上下文求值可支撑 watch 表达式、通过枚举内部元数据与原型链检视堆对象、任意调用栈层级的变量读写、应用自定义请求/通知AppRequest/AppNotify、日志转发以及全堆转储Web UI 可转换为 JSON。小结debugger/目录提供了一条从开箱即用的浏览器调试到自定义传输接入的完整路径make dukduk --debugger得到调试目标make启动 Web UImake runproxydukluv/make runproxynodejs启动 JSON 代理。理解传输可替换、协议固定的分层设计后你可以轻松把这套调试体验迁移到串口、Wi-Fi 等任何自定义传输之上或基于 JSON 协议快速实现自己的调试客户端。赞分享语言运行时嵌入式解释器【免费下载链接】duktapeDuktape - embeddable Javascript engine with a focus on portability and compact footprint项目地址https://gitcode.com/gh_mirrors/du/duktape点击查看免费下载相关推荐Duktape 调试器实战Web UI 调试客户端与 JSON 调试代理完全指南Duktape 调试器实战Web UI 调试客户端与 JSON 调试代理完全指南 导读 本文围绕 Duktape 2.7.0 发行版自带的调试工具集位于仓库开发工具QEMU GDB 调试指南使用 gdbstub 调试客户机代码的完整实践QEMU GDB 调试指南使用 gdbstub 调试客户机代码的完整实践 导读 本指南基于 QEMU 官方文档 docs/system/gdb.rst htt虚拟化硬件仿真SharpEmu 调试客户端实战通过 JSON-lines 协议驱动 PS5 模拟器实时调试服务器SharpEmu 调试客户端实战通过 JSON lines 协议驱动 PS5 模拟器实时调试服务器 本指南围绕 SharpEmu 仓库中的 SharpEmu.游戏开发图形学逆向工程上一篇Tesseract-OCR Tessdata_Fast 项目常见问题解决方案下一篇Wayland Clipboard Utilities - 使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考