嵌入式Linux Lua使用ZeroBrane远程调试概述本文记录了在嵌入式 Linux 平台上移植 ZeroBrane 远程调试功能的过程。通过集成 MobDebug 和 LuaSocket 库实现了对 Lua 脚本的远程调试支持包括断点调试、单步执行、变量监视和调用堆栈等功能。背景目标平台是一个基于 Linux 的仪器控制系统使用嵌入式 Lua 作为脚本引擎。硬件访问通过 UIO (Userspace I/O) 和mmap()实现寄存器访问。为了方便开发和调试需要支持 ZeroBrane Studio 远程调试功能。技术架构ZeroBrane 调试原理ZeroBrane 使用 MobDebug 协议进行远程调试┌─────────────────┐ TCP/IP ┌─────────────────┐ │ ZeroBrane │◄──────────────────────►│ fan-instruments│ │ Studio │ port: 8172 │ (Lua 脚本) │ │ (调试器) │ │ (被调试端) │ └─────────────────┘ └─────────────────┘协议流程被调试端连接到 ZeroBrane 服务器服务器发送命令 (RUN, STEP, SETB 等)被调试端执行并返回状态 (202 Paused 等)依赖库库版本说明Lua5.4已有LuaSocket3.1.0新增提供 TCP 通信MobDebug0.805新增调试协议实现移植步骤1. 添加 LuaSocket 库从官方仓库获取源码gitclone--depth1https://github.com/lunarmodules/luasocket.git目录结构lib/luasocket/ ├── *.c, *.h # C 源码 ├── lua/ # Lua 封装层 │ ├── socket.lua │ ├── mime.lua │ └── ltn12.lua └── luasocket.cmake # 构建脚本构建脚本lib/luasocket/luasocket.cmakeadd_library(luasocket STATIC lib/luasocket/auxiliar.c lib/luasocket/buffer.c lib/luasocket/compat.c lib/luasocket/except.c lib/luasocket/inet.c lib/luasocket/io.c lib/luasocket/luasocket.c lib/luasocket/mime.c lib/luasocket/options.c lib/luasocket/select.c lib/luasocket/tcp.c lib/luasocket/timeout.c lib/luasocket/udp.c ) if(UNIX AND NOT APPLE) target_sources(luasocket PRIVATE lib/luasocket/usocket.c) elseif(WIN32) target_sources(luasocket PRIVATE lib/luasocket/wsocket.c) endif()2. 添加 MobDebug 库下载 MobDebugcurl-olib/mobdebug/mobdebug.lua\https://raw.githubusercontent.com/pkulchenko/MobDebug/master/src/mobdebug.lua由于嵌入式环境可能没有文件系统访问需要将 Lua 脚本编译为 C 字节数组嵌入可执行文件。创建转换脚本lib/mobdebug/lua_embed.py#!/usr/bin/env python3importsysdeflua_to_c(lua_file,output_c,var_name):withopen(lua_file,rb)asf:dataf.read()hex_values, .join(f0x{b:02x}forbindata)c_codefconst char{var_name}[] {{{hex_values}, 0 }};\nwithopen(output_c,w)asf:f.write(c_code)if__name____main__:lua_to_c(sys.argv[1],sys.argv[2],sys.argv[3])构建脚本lib/mobdebug/mobdebug.cmakefunction(lua_to_c LUA_FILE OUTPUT_C VAR_NAME) add_custom_command( OUTPUT ${OUTPUT_C} COMMAND ${Python3_EXECUTABLE} ${LUA_EMBED_SCRIPT} ${LUA_FILE} ${OUTPUT_C} ${VAR_NAME} DEPENDS ${LUA_FILE} ${LUA_EMBED_SCRIPT} ) endfunction() lua_to_c(${PROJECT_SOURCE_DIR}/lib/mobdebug/mobdebug.lua ${CMAKE_BINARY_DIR}/mobdebug_data.c mobdebug_lua) lua_to_c(${PROJECT_SOURCE_DIR}/lib/luasocket/lua/socket.lua ${CMAKE_BINARY_DIR}/socket_data.c socket_lua) # ... 其他 Lua 文件 add_library(mobdebug_embed STATIC ${CMAKE_BINARY_DIR}/mobdebug_data.c ${CMAKE_BINARY_DIR}/socket_data.c # ... )3. 创建调试模块lib/lua_debug.h#ifndefLUA_DEBUG_H#defineLUA_DEBUG_H#includelua.hppexternC{intluaopen_socket_core(lua_State*L);intluaopen_mime_core(lua_State*L);}intlua_debug_init(lua_State*L);intlua_debug_start(lua_State*L,constchar*host,intport);voidlua_debug_stop(lua_State*L);#endiflib/lua_debug.cpp关键实现// 注册 C 模块staticintregister_c_module(lua_State*L,constchar*name,lua_CFunction openfn){luaL_requiref(L,name,openfn,1);lua_setfield(L,-2,name);return0;}// 初始化调试模块intlua_debug_init(lua_State*L){// 注册 socket.core 和 mime.core (C 模块)register_c_module(L,socket.core,luaopen_socket_core);register_c_module(L,mime.core,luaopen_mime_core);// 加载 Lua 封装层 (嵌入的字节码)load_embedded_lua(L,socket,socket_lua);load_embedded_lua(L,mobdebug,mobdebug_lua);return0;}// 启动调试会话intlua_debug_start(lua_State*L,constchar*host,intport){lua_getglobal(L,mobdebug);lua_getfield(L,-1,start);lua_pushstring(L,host);lua_pushinteger(L,port);lua_pcall(L,2,1,0);// ...}4. 修改主程序添加命令行参数支持// main.cppintmain(intargc,char*argv[]){// 解析参数intdebug_mode0;std::string debug_hostlocalhost;intdebug_port8172;for(inti1;iargc;i){if(strcmp(argv[i],--debug)0){debug_mode1;}elseif(strcmp(argv[i],--debug-host)0i1argc){debug_hostargv[i];}elseif(strcmp(argv[i],--debug-port)0i1argc){debug_portatoi(argv[i]);}}// 初始化调试模块if(debug_mode){lua_debug_init(L);}// 运行脚本if(script_arg0){if(debug_mode){lua_debug_start(L,debug_host.c_str(),debug_port);}run_script(L,argv[script_arg]);if(debug_mode){lua_debug_stop(L);}}}5. 启用 Lua 标准库MobDebug 依赖io和os模块需要修改lib/lua/linit.cstaticconstluaL_Reg loadedlibs[]{{LUA_GNAME,luaopen_base},{LUA_LOADLIBNAME,luaopen_package},{LUA_COLIBNAME,luaopen_coroutine},{LUA_TABLIBNAME,luaopen_table},{LUA_IOLIBNAME,luaopen_io},// 启用{LUA_OSLIBNAME,luaopen_os},// 启用{LUA_STRLIBNAME,luaopen_string},{LUA_MATHLIBNAME,luaopen_math},{LUA_UTF8LIBNAME,luaopen_utf8},{LUA_DBLIBNAME,luaopen_debug},{NULL,NULL}};遇到的问题问题 1: module ‘io’ not found原因:linit.c中io和os库被注释禁用。解决: 启用LUA_IOLIBNAME和LUA_OSLIBNAME。问题 2: module ‘socket.core’ not found原因: LuaSocket 的 Lua 封装层 (socket.lua) 需要 C 核心模块 (socket.core)但只加载了 Lua 层。解决: 使用luaL_requiref()注册 C 模块到package.loadedluaL_requiref(L,socket.core,luaopen_socket_core,1);lua_setfield(L,-2,socket.core);问题 3: module ‘mime.core’ not found原因: 同上mime.lua依赖mime.core。解决: 同样注册mime.core。问题 4: require “mobdebug” 失败原因: mobdebug 只注册为全局变量未注册到package.loaded。解决: 同时注册到package.loaded和全局表lua_setfield(L,-2,mobdebug);// package.loaded.mobdebuglua_getfield(L,-1,mobdebug);lua_setglobal(L,mobdebug);// _G.mobdebug测试验证测试脚本test_debug.luaprint( ZeroBrane Debug Test )-- 检查模块localmodules{socket,socket.core,mime,mime.core,mobdebug}for_,modinipairs(modules)dolocalok,mpcall(require,mod)print(okand[OK]or[FAIL],mod)end-- 测试连接localsocketrequire(socket)localtcpsocket.tcp()tcp:settimeout(2)localok,errtcp:connect(localhost,8172)ifokthenprint([OK] Connected to debugger!)tcp:close()end-- 测试循环 (设置断点)fori1,5dolocalxi*2print(iteration ..i..: x..x)endMock 调试服务器test_debug_server.py#!/usr/bin/env python3importsocketdefdebug_server(port8172):serversocket.socket(socket.AF_INET,socket.SOCK_STREAM)server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)server.bind((0.0.0.0,port))server.listen(1)print(fWaiting for connection on port{port}...)conn,addrserver.accept()print(f*** CONNECTION SUCCESSFUL! ***)# 发送 RUN 命令让脚本继续执行conn.send(bRUN\n)conn.close()server.close()if__name____main__:debug_server()测试结果# 终端 1$ python3 test_debug_server.py Waitingforconnection on port8172... *** CONNECTION SUCCESSFUL!***# 终端 2$ ./build/fan-instruments--debugtest_debug.lua socket.core registered mime.core registered MobDebug module initialized Debugger connected to localhost:8172ZeroBrane Debug Test[OK]socket[OK]socket.core[OK]mime[OK]mime.core[OK]mobdebug[OK]Connected to debugger!iteration1:x2iteration2:x4...使用方法命令行参数参数说明默认值--debug启用调试模式---debug-host HOST调试服务器地址localhost--debug-port PORT调试服务器端口8172ZeroBrane Studio 配置启动 ZeroBrane Studio打开 Lua 脚本文件设置断点 (点击行号左侧)菜单: Project → Start Debugger Server在目标设备上运行./fan-instruments--debug--debug-hostPC_IPscript.lua调试功能断点调试: 在 ZeroBrane 中设置断点程序会暂停单步执行: Step Into (F11), Step Over (F10), Step Out (ShiftF11)变量监视: 在 Watch 窗口添加表达式调用堆栈: 查看 Stack 窗口文件清单lib/ ├── lua_debug.h # 调试模块头文件 ├── lua_debug.cpp # 调试模块实现 ├── lua/ │ └── linit.c # 修改: 启用 io/os 库 ├── luasocket/ # 新增: LuaSocket 库 │ ├── *.c, *.h │ ├── lua/*.lua │ └── luasocket.cmake └── mobdebug/ # 新增: MobDebug 库 ├── mobdebug.lua ├── mobdebug.cmake └── lua_embed.py main.cpp # 修改: 添加调试参数 CMakeLists.txt # 修改: 添加依赖 test_debug.lua # 测试脚本 test_debug_server.py # Mock 调试服务器上板测试总结本次移植主要工作集成 LuaSocket: 提供底层 TCP 通信能力集成 MobDebug: 实现调试协议嵌入 Lua 脚本: 将 Lua 文件编译为 C 数组避免文件系统依赖注册 C 模块: 正确注册socket.core和mime.core启用标准库: 开启io和os模块移植后的系统支持 ZeroBrane Studio 远程调试方便嵌入式 Lua 脚本的开发和调试。参考资料MobDebug GitHubLuaSocket GitHubZeroBrane StudioLua 5.4 Manual