├── main.lua ├── README └── trace.lua /main.lua: -------------------------------------------------------------------------------- 1 | trace = require "trace" 2 | 3 | local function factorial(n) 4 | if n <= 1 then 5 | return 1 6 | end 7 | return factorial(n-1) * n 8 | end 9 | 10 | function foo(n) 11 | trace.trace("n s t.k1.k2",n) 12 | local s = factorial(100) 13 | local t = {k1={k2="hello"}} 14 | t.k1.k2 = "world" 15 | return s 16 | end 17 | 18 | function hello() 19 | print "hello" 20 | end 21 | 22 | foo(3) 23 | hello() 24 | foo() 25 | 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | It's trace tool for lua. 2 | 3 | You can use trace.trace("varname") to trace the code execute. For example, if you run "lua main.lua" , you may got 4 | 5 | main.lua : 12 6 | main.lua : 4 7 | n local 100 8 | main.lua : 7 9 | main.lua : 4 10 | n local 99 11 | main.lua : 7 12 | main.lua : 4 13 | n local 98 14 | main.lua : 7 15 | main.lua : 13 16 | n global nil 17 | s local 9.3326215443944e+157 18 | 19 | http://blog.codingnow.com/2012/02/lua_trace.html (In Chinese) 20 | 21 | -------------------------------------------------------------------------------- /trace.lua: -------------------------------------------------------------------------------- 1 | local debug = debug 2 | local print = print 3 | local ipairs = ipairs 4 | local pairs = pairs 5 | local string = string 6 | local rawget = rawget 7 | 8 | local info = { file = {} } 9 | 10 | local trace = {} 11 | trace.print = print 12 | 13 | local function setname(level) 14 | info.filename = debug.getinfo(level + 1,"S").short_src 15 | if info.file[info.filename] == nil then 16 | info.file[info.filename] = {} 17 | end 18 | info.var = info.file[info.filename] 19 | end 20 | 21 | local function split(name) 22 | local keys = {} 23 | for key in name:gmatch("[^.]+") do 24 | keys[#keys+1] = key 25 | end 26 | return keys 27 | end 28 | 29 | local function get_table_field(tbl,keys) 30 | for i,key in ipairs(keys) do 31 | if tbl[key] then 32 | tbl = tbl[key] 33 | else 34 | return nil 35 | end 36 | end 37 | return tbl 38 | end 39 | 40 | local function is_table_field(var_name,name,v) 41 | if type(v) == "table" then 42 | local len = #name 43 | if var_name:sub(1,len) == name and var_name:sub(len+1,len+1) == "." then 44 | return true 45 | end 46 | end 47 | return false 48 | end 49 | 50 | local function make_local(index,name,is_field) 51 | return function() 52 | local _ , value = debug.getlocal(4 , index) 53 | if not is_field then 54 | return name,"local",value 55 | else 56 | local keys = split(name) 57 | table.remove(keys,1) 58 | return name,"local",get_table_field(value,keys) 59 | end 60 | end 61 | end 62 | 63 | local function make_upvalue(func, index,name,is_field) 64 | return function() 65 | local _, value = debug.getupvalue(func, index) 66 | if not is_field then 67 | return name,"upvalue",value 68 | else 69 | local keys = split(name) 70 | table.remove(keys,1) 71 | return name,"upvalue",get_table_field(value,keys) 72 | end 73 | end 74 | end 75 | 76 | local function make_global(env, name) 77 | return function() 78 | if rawget(env,name) then 79 | return name,"global",rawget(env,name) 80 | else 81 | local keys = split(name) 82 | return name,"global",get_table_field(env,keys) 83 | end 84 | end 85 | end 86 | 87 | local function gen_var(var_name, level) 88 | local i = 1 89 | while true do 90 | local name,v = debug.getlocal(5,i) 91 | if name == var_name then 92 | return make_local(i,var_name) 93 | end 94 | if is_table_field(var_name,name,v) then 95 | return make_local(i,var_name,true) 96 | end 97 | if name == nil then 98 | break 99 | end 100 | i=i+1 101 | end 102 | i = 1 103 | local f = debug.getinfo(5, "f").func 104 | while true do 105 | local name, v = debug.getupvalue(f,i) 106 | if name == var_name then 107 | return make_upvalue(f,i,var_name) 108 | end 109 | if is_table_field(var_name,name,v) then 110 | return make_upvalue(f,i,var_name,true) 111 | end 112 | if name == nil then 113 | break 114 | end 115 | i=i+1 116 | end 117 | local name,env = debug.getupvalue(f,1) 118 | if name == '_ENV' then 119 | return make_global(env, var_name) 120 | end 121 | end 122 | 123 | local function gen_vars(var, call) 124 | local ret = {} 125 | for _,k in ipairs(var) do 126 | local f = gen_var(k, call) 127 | if f then 128 | table.insert(ret, f) 129 | end 130 | end 131 | return ret 132 | end 133 | 134 | local function hookline(var , call, line) 135 | trace.print(info.filename, ":" , line) 136 | if info.var[line] == nil then 137 | info.var[line] = gen_vars(var, call) 138 | end 139 | 140 | for _,v in ipairs(info.var[line]) do 141 | local name , type , value = v() 142 | if info.last[name] ~= value then 143 | trace.print(name , type, value) 144 | info.last[name] = value 145 | end 146 | end 147 | end 148 | 149 | local function hook(var , level) 150 | local call = 0 151 | local index = {} 152 | for w in string.gmatch(var, "%S+") do 153 | table.insert(index,w) 154 | end 155 | local function f (mode, line) 156 | if mode == 'return' then 157 | if call <= 0 then 158 | debug.sethook() 159 | trace.on = nil 160 | return 161 | end 162 | setname(3) 163 | call = call - 1 164 | if call == level then 165 | debug.sethook(f,'crl') 166 | end 167 | elseif mode == 'call' then 168 | setname(2) 169 | call = call + 1 170 | if call > level then 171 | debug.sethook(f,'cr') 172 | end 173 | elseif mode == 'tail call' then 174 | setname(2) 175 | elseif mode == 'line' then 176 | hookline(index , call, line) 177 | end 178 | end 179 | 180 | return f 181 | end 182 | 183 | local function up(level, f) 184 | local call = 0 185 | return function(mode) 186 | if mode == 'return' then 187 | call = call + 1 188 | if call == level then 189 | setname(3) 190 | debug.sethook(f,'crl') 191 | end 192 | elseif mode == 'call' then 193 | call = call - 1 194 | end 195 | end 196 | end 197 | 198 | function trace.trace(var , level) 199 | if trace.on then 200 | return 201 | end 202 | 203 | trace.on = true 204 | info.last = {} 205 | debug.sethook(up(2 , hook(var or "" , level or 0)) , 'cr') 206 | end 207 | 208 | return trace 209 | --------------------------------------------------------------------------------