随笔 - 505  文章 - 1034  trackbacks - 0
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567


子曾经曰过:编程无他,唯手熟尔!

常用链接

留言簿(94)

随笔分类(649)

随笔档案(505)

相册

BCB

Crytek

  • crymod
  • Crytek's Offical Modding Portal

Game Industry

OGRE

other

Programmers

Qt

WOW Stuff

搜索

  •  

积分与排名

  • 积分 - 903816
  • 排名 - 14

最新随笔

最新评论

阅读排行榜

评论排行榜

现在做网络游戏项目,很多东西策划经常变,比如悬浮提示之类的,写在c++代码里的,改了每次都得编译一下,很痛苦!
所以现在很多游戏逻辑都放在lua脚本里面(准确的说是嵌在xml里面的lua),但是没法单步调试了,只能靠打日志和注释掉某行的做法来定位问题,又很痛苦了!今天搜到了一个东东,那个惊喜啊!

Lua 简易调试器


云风大哥写的,果然牛人啊!现在我没空看,先放到这儿,有空了研究下!嘿嘿
 一个简单的调试系统:

---------------------------
--  bp.lua
---------------------------
 
local type=type
local tostring=tostring
local print=print
local setmetatable=setmetatable
local getfenv=getfenv
local ipairs=ipairs
local pairs=pairs
local xpcall=xpcall
local error=error
 
local table_insert=table.insert
local table_concat=table.concat
local debug=debug
 
module "bp"
 
local nil_value={}
 
local function traversal_r(tbl,num)
num = num or 64
local ret={}
local function insert(v)
table_insert(ret,v)
if #ret>num then
error()
end
end
local function traversal(e)
if e==nil_value or e==nil then
insert("nil,")
elseif type(e)=="table" then
insert("{")
local maxn=0
for i,v in ipairs(e) do
traversal(v)
maxn=i
end
for k,v in pairs(e) do
if not (type(k)=="number" and k>0 and k<=maxn) then
if type(k)=="number" then
insert("["..k.."]=")
else
insert(tostring(k).."=")
end
traversal(v)
end
end
insert("}")
elseif type(e)=="string" then
insert('"'..e..'",')
else
insert(tostring(e))
insert(",")
end
end
 
local err=xpcall(
function() traversal(tbl) end,
function() end
)
if not err then
table_insert(ret,"...")
end
 
return table_concat(ret)
end
 
function print_r(tbl,num)
print(traversal_r(tbl,num))
end
 
local function init_local(tbl,level)
local n=1
local index={}
while true do
local name,value=debug.getlocal(level,n)
if not name then
break
end
 
if name~="(*temporary)" then
if value==nil then
value=nil_value
end
tbl[name]=value
index["."..name]=n
end
 
n=n+1
end
setmetatable(tbl,{__index=index})
return tbl
end
 
local function init_upvalue(tbl,func)
local n=1
local index={}
while true do
local name,value=debug.getupvalue(func,n)
if not name then
break
end
 
if value==nil then
value=nil_value
end
tbl[name]=value
index["."..name]=n
 
n=n+1
end
setmetatable(tbl,{__index=index})
return tbl
end
 
 
function dbg(level)
level=level and level+2 or 2
 
local lv=init_local({},level+1)
local func=debug.getinfo(level,"f").func
local uv=init_upvalue({},func)
local _L={}
setmetatable(_L,{
__index=function(_,k)
local ret=lv[k]
return ret~=nil_value and ret or nil
end,
__newindex=function(_,k,v)
if lv[k] then
lv[k]= v~= nil and nil_value or v
debug.setlocal(level+3,lv["."..k],v)
else
print("error:invalid local name:",k)
end
end,
__tostring=function(_)
return traversal_r(lv)
end
})
print("_L=",traversal_r(lv))
local _U={}
setmetatable(_U,{
__index=function(_,k)
local ret=uv[k]
return ret~=nil_value and ret or nil
end,
__newindex=function(_,k,v)
if uv[k] then
uv[k]= v~= nil and nil_value or v
debug.setupvalue(func,uv["."..k],v)
else
print("error:invalid upvalue name",k)
end
end,
__tostring=function(_)
return traversal_r(uv)
end
})
print("_U=",traversal_r(uv))
 
local _G=getfenv(level)
_G._L,_G._U=_L,_U
debug.debug()
_G._L,_G._U=nil,nil
end
 
 
local _bp_list={}
local _bp_desc={}
 
local function bp_list_name(name)
local t=type(_bp_desc[name])
print("["..name.."]",_bp_list[name] and "on" or "off")
if t=="table" then
for _,v in ipairs(_bp_desc[name]) do
print("----",v)
end
elseif t=="string" then
print("---",_bp_desc[name])
end
end
 
local function bp_list(name)
if name then
bp_list_name(name)
else
for k,v in pairs(_bp_list) do
bp_list_name(k)
end
end
end
 
local _bp_unnamed={}
local _bp_unnamed_desc={}
local _bp_unnamed_index={}
local _bp_index=1
do
local weak={__mode="kv"}
setmetatable(_bp_unnamed,weak)
setmetatable(_bp_unnamed_desc,{__mode="k"})
setmetatable(_bp_unnamed_index,weak)
end
 
local function bp_add_index()
local info=debug.getinfo(3,"Slf")
local func=info.func
if _bp_unnamed[info.func]==nil then
local desc=info.source.."("..info.currentline..")"
_bp_unnamed[func]=true
_bp_unnamed_desc[func]=desc
_bp_unnamed_index[func]=_bp_index
_bp_unnamed_index[_bp_index]=func
_bp_index=_bp_index+1
end
return _bp_unnamed[func],_bp_unnamed_index[func]
end
 
local _bp_named={}
local _bp_named_desc={}
 
local function bp_add_name(name)
local info=debug.getinfo(3,"Sl")
local desc=info.source.."("..info.currentline..")"
if _bp_named[name]==nil then
_bp_named[name]=false
end
 
local tbl=_bp_named_desc[name]
if tbl then
tbl[desc] = true
else
_bp_named_desc[name]={ [desc] = true }
end
 
return _bp_named[name],name
end
 
local function bp_show(n)
if type(n)=="number" then
local func=_bp_unnamed_index[n]
if func==nil then
error "invalid break point id"
end
print("break point:",n,
_bp_unnamed[func] and "on" or "off",
_bp_unnamed_desc[func]
)
else
print("break point:",n,_bp_named[n] and "on" or "off")
if _bp_named_desc[n] then
for k,v in pairs(_bp_named_desc[n]) do
print("",k)
end
else
print("tundefined")
end
end
end
 
local function bp_show_all()
for k,_ in pairs(_bp_unnamed) do
bp_show(_bp_unnamed_index[k])
end
for k,_ in pairs(_bp_named) do
bp_show(k)
end
end
 
function bp(n)
local trigger,name
if n==nil then
trigger,name=bp_add_index()
else
trigger,name=bp_add_name(n)
end
 
if trigger then
bp_show(name)
dbg(1)
else
_bp_list[name]=false
end
end
 
function trigger(n,on)
on = on~=false
if type(n)=="number" then
local func=_bp_unnamed_index[n]
if func==nil then
error "invalid break point id"
end
_bp_unnamed[func]=on
else
_bp_named[n]=on
end
end
 
function list(v)
if v then
bp_show(v)
else
bp_show_all()
end
end
 
function error_handle(msg)
print(msg)
dbg(1)
end


可以这样测试一下:

require "bp"
 
function foo(arg)
bp.bp()    -- 设置一个匿名断点
return arg+1
end
 
=foo(0)    -- 输出 foo(0) 
 


运行后会进入调试控制台:
可以发现断点被编了号(1 号)并被触发,局部变量放在 _L 这张表里可以直接操作,upvalue 放在了 _U 表中 。

break point:    1       on      =stdin(2)
_L=     {arg=0,}
_U=     {}


我们可以改一下 arg 的值,如 _L.arg=1 然后 cont 退出控制台。可以发现 foo 函数返回了 2 。
这种匿名断点添加方便,而且断点跟随逻辑而不是和代码行做映射。也就是说,同一个 function 的不同实例(不同的 closure )可以有不同 id 的断点,单独控制。

我们也可以使用具名断点,方法是 bp.bp "bpname"
具名断点缺省是 disable 状态的。如果需要激活可以用 bp.trigger "bpname" 。
给断点起名字的好处是,可以给一组断点起同一个名字,这样可以批量开关。

bp.trigger(name,true/false) 用于开关断点,name 可以是字符串也可以是 id 。

列出所有断点可以用 bp.list()
也可以用 bp.list(name) 来列出一个断点的状态,name 可以是字符串也可以是 id 。

还有一种方式,就是把调试控制台用在错误处理函数中,这样函数一出错就自动切入控制台。
方法是 xpcall(some_function,bp.error_handle)

下一步打算做单步执行 :D
posted on 2007-05-03 07:14 七星重剑 阅读(2642) 评论(0)  编辑 收藏 引用 所属分类: PL--c/c++

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理