lua没有提供专门的调试器,但却提供了一些接口函数,用以实现你自己的调试器。
下面实现了一个简单的基于命令行的lua调试器,提供一些最最基本的调试功能。
这里面只用到了3个基本的lua调试函数,debug.sethook,debug.getlocal,debug.getinfo.
函数的具体用法和功能可以参照lua手册。
下面是简单的实现代码:
Command.h
#ifndef _COMMAND_H #define _COMMAND_H /*命令定义说明 命令 参数 start filename :启动执行一个lua文件 list/l filename :显示文件内容 break/b filename:lineno :在filename的lineno设置断点 step/s :单步执行 continue/c :继续运行 exit/e :退出调试器 stop :停止调试 print/p variable :打印变量值 */ class CommandLine; //命令基类 class Command { public: Command(){} virtual bool Execute(bool callfromlua) = 0;//执行命令 }; class CommandStart : public Command { public: CommandStart(char *para):para(para){} bool Execute(bool callfromlua); private: char *para;//命令的参数 }; class CommandExit : public Command { public: CommandExit(){} bool Execute(bool callfromlua); }; class CommandBreak : public Command { public: CommandBreak(char *para):para(para){} bool Execute(bool callfromlua); private: char *para;//命令的参数 }; class CommandStep : public Command { public: CommandStep(){} bool Execute(bool callfromlua); }; class CommandContinue : public Command { public: CommandContinue(){} bool Execute(bool callfromlua); }; class CommandPrint : public Command { public: CommandPrint(char *valname):valname(valname){} bool Execute(bool callfromlua); private: char *valname; }; class CommandFactory { public: static Command *CreateCommand(char *commandline); }; #endif
command.cpp
#include "stdafx.h" #include "command.h" #include "commandline.h" extern lua_State *L; bool CommandStart::Execute(bool callfromlua) { if(CommandLine::isrunning) { printf("程序已经在运行/n"); return true; } CommandLine::isrunning = true; //执行lua文件 if (luaL_dofile(L, para)) { const char * error = lua_tostring(L, -1); lua_pop(L,1); } CommandLine::isrunning = false; printf("程序终止/n"); return true; } bool CommandBreak::Execute(bool callfromlua) { const char *filename = strtok(para,":"); const char *line = strtok(NULL,":"); if(!filename || !line) { printf("断点输入不正确/n"); return true; } int lineno = atol(line); CommandLine::InsertBreakPoint(filename,lineno); return true; } bool CommandStep::Execute(bool callfromlua) { if(!CommandLine::isrunning) { printf("请先运行程序/n"); return true; } CommandLine::setpState = true; return false; } bool CommandContinue::Execute(bool callfromlua) { CommandLine::setpState = false; if(callfromlua) return false; return true; } bool CommandExit::Execute(bool callfromlua) { printf("你确定要退出调试器吗?(y:是,n:否)"); int c = getchar(); if( c == 'y' || c == 'Y') exit(0); return false; } bool CommandPrint::Execute(bool callfromlua) { lua_getglobal(L,"printlocal"); lua_pushstring(L,valname); if(lua_pcall(L, 1, 0, 0) != 0) { const char *error = lua_tostring(L,-1); printf("%s/n",error); lua_pop(L,1); } return true; } Command *CommandFactory::CreateCommand(char *commandline) { char *cmd = strtok(commandline," "); if(!cmd || strcmp("",cmd) == 0) return NULL; if(strcmp("start",cmd) == 0) { char *para = strtok(NULL," "); return new CommandStart(para); } else if(strcmp("exit",cmd) == 0 || strcmp("e",cmd) == 0) { return new CommandExit(); } else if(strcmp("break",cmd) == 0 || strcmp("b",cmd) == 0) { char *para = strtok(NULL," "); return new CommandBreak(para); } else if(strcmp("step",cmd) == 0 || strcmp("s",cmd) == 0) { return new CommandStep(); } else if(strcmp("continue",cmd) == 0 || strcmp("c",cmd) == 0) { return new CommandContinue(); } else if(strcmp("print",cmd) == 0 || strcmp("p",cmd) == 0) { char *para = strtok(NULL," "); return new CommandPrint(para); } return NULL; }
Commandline.h
//命令行处理 #ifndef _COMMANDLINE_H #define _COMMANDLINE_H extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } #include <string> #include <map> #include <list> class CommandLine { public: static void Init(); //等待用户的命令输入 static void Wait4Command(bool fromlua = false); static char *GetInput(); //列出所有断点 static std::list<std::pair<std::string,std::pair<int,bool> > > ListBreakPoint(); //是否命中断点 static bool HitBreakPoint(std::string filename,int lineno) { std::map<std::string,std::map<int,bool> >::iterator it = breakpoints.find(filename); if( it != breakpoints.end()) { std::map<int,bool>::iterator it2 = it->second.find(lineno); if(it2 != it->second.end()) { return it2->second; } } return false; } //添加一个断点 static void InsertBreakPoint(std::string filename,int lineno) { std::map<std::string,std::map<int,bool> >::iterator it = breakpoints.find(filename); if( it != breakpoints.end()) { it->second.insert(std::make_pair(lineno,true)); } else { std::map<int,bool> tmp; tmp.insert(std::make_pair(lineno,true)); breakpoints.insert(std::make_pair(filename,tmp)); } } static bool setpState;//是否单步执行 static bool isrunning;//调试的程序正在运行 private: static std::map<std::string,std::map<int,bool> > breakpoints;//断点记录 }; #endif
Commandline.cpp
#include "stdafx.h" #include "commandline.h" #include <iostream> #include <string.h> #include "command.h" lua_State *L; bool CommandLine::setpState;//是否单步执行 bool CommandLine::isrunning;//调试的程序正在运行 std::map<std::string,std::map<int,bool> > CommandLine::breakpoints;//断点记录 static int readline(FILE * f, char *vptr, unsigned int maxlen) { unsigned int n, rc; char c, *ptr; ptr = (char*)vptr; for (n = 1; n < maxlen; n++) { if ( (rc = (unsigned int)fread(&c,1,1,f)) == 1) { if (c == '/n') { *ptr='/0'; break; } else *ptr++ = c; } else if (rc == 0) { *ptr = 0; return (n - 1); } else { printf("读取文件出错/n"); return (-1); } } *ptr = 0; return (n); } static int luaHook(lua_State *L) { const char *filename = lua_tostring(L,1); int lineno = lua_tonumber(L,2); if(CommandLine::HitBreakPoint(filename,lineno) || CommandLine::setpState) { //读取文件的lineno行,并打印 char buf[1024]; FILE *f = fopen(filename,"r"); if(f) { for(int i = 1; i <= lineno; ++i) readline(f,buf,1024); printf("%s/n",buf); } CommandLine::Wait4Command(true); } return 0; } void CommandLine::Init() { L = lua_open(); luaL_openlibs(L); lua_register(L, "CHook", luaHook); //注册钩子 if (luaL_dofile(L, "./hook.lua")) { const char * error = lua_tostring(L, -1); lua_pop(L,1); } setpState = false; isrunning = false; } char* CommandLine::GetInput() { static char cmd[1024]; int i = 0; for(; i < 1023; ++i) { cmd[i] = (char)getchar(); if(cmd[i] == '/n') break; } cmd[i] = '/0'; return cmd; } void CommandLine::Wait4Command(bool fromlua) { for( ; ;) { char *input = GetInput(); //分析并处理命令行 Command *cmd = CommandFactory::CreateCommand(input); if(!cmd) { continue; } if(!cmd->Execute(fromlua)) break; } }
luaDebuger.cpp
// luaDebuger.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "Commandline.h" int _tmain(int argc, _TCHAR* argv[]) { CommandLine::Init(); CommandLine::Wait4Command(false); return 0; }
hook.lua
function hookfunc(event,line) local s = debug.getinfo(2).short_src CHook(s,line) end function printlocal(valname) --lua中没法根据变量名获得变量的信息,所以下面只能取得栈中所有本地变量的信息, --然后一一比对,看看是否是要查看的变量 local i = 1 repeat local name,value = debug.getlocal(4,i) --第1层是当前函数,第2层是c++中的CHook,第3层是hookfunc,第4层才是我们要跟踪的函数栈 if name == nil then print("变量不存在") return end if name == valname then print(name .. ":" .. value) return end i = i + 1 until false end debug.sethook(hookfunc,"l")