Software /
code /
prosody
Comparison
util/debug.lua @ 4412:5d7d9a60bc7f
util.debug: Experimental new library for producing more extensive debug tracebacks
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Thu, 03 Nov 2011 12:41:21 +0000 |
child | 4418:70b5e533325d |
comparison
equal
deleted
inserted
replaced
4411:cf4e49b250c7 | 4412:5d7d9a60bc7f |
---|---|
1 -- Variables ending with these names will not | |
2 -- have their values printed ('password' includes | |
3 -- 'new_password', etc.) | |
4 local censored_names = { | |
5 password = true; | |
6 passwd = true; | |
7 pass = true; | |
8 pwd = true; | |
9 }; | |
10 | |
11 local function get_locals_table(level) | |
12 local locals = {}; | |
13 for local_num = 1, math.huge do | |
14 local name, value = debug.getlocal(level, local_num); | |
15 if not name then break; end | |
16 table.insert(locals, { name = name, value = value }); | |
17 end | |
18 return locals; | |
19 end | |
20 | |
21 local function get_upvalues_table(func) | |
22 local upvalues = {}; | |
23 for upvalue_num = 1, math.huge do | |
24 local name, value = debug.getupvalue(func, upvalue_num); | |
25 if not name then break; end | |
26 table.insert(upvalues, { name = name, value = value }); | |
27 end | |
28 return upvalues; | |
29 end | |
30 | |
31 local function string_from_var_table(var_table, max_line_len, indent_str) | |
32 local var_string = {}; | |
33 local col_pos = 0; | |
34 max_line_len = max_line_len or math.huge; | |
35 indent_str = "\n"..(indent_str or ""); | |
36 for _, var in ipairs(var_table) do | |
37 local name, value = var.name, var.value; | |
38 if name:sub(1,1) ~= "(" then | |
39 if type(value) == "string" then | |
40 if censored_names[name:match("%a+$")] then | |
41 value = "<hidden>"; | |
42 else | |
43 value = ("%q"):format(value); | |
44 end | |
45 else | |
46 value = tostring(value); | |
47 end | |
48 if #value > max_line_len then | |
49 value = value:sub(1, max_line_len-3).."…"; | |
50 end | |
51 local str = ("%s = %s"):format(name, tostring(value)); | |
52 col_pos = col_pos + #str; | |
53 if col_pos > max_line_len then | |
54 table.insert(var_string, indent_str); | |
55 col_pos = 0; | |
56 end | |
57 table.insert(var_string, str); | |
58 end | |
59 end | |
60 if #var_string == 0 then | |
61 return nil; | |
62 else | |
63 return "{ "..table.concat(var_string, ", "):gsub(indent_str..", ", indent_str).." }"; | |
64 end | |
65 end | |
66 | |
67 function get_traceback_table(thread, start_level) | |
68 local levels = {}; | |
69 for level = start_level, math.huge do | |
70 local info; | |
71 if thread then | |
72 info = debug.getinfo(thread, level); | |
73 else | |
74 info = debug.getinfo(level); | |
75 end | |
76 if not info then break; end | |
77 | |
78 levels[(level-start_level)+1] = { | |
79 level = level; | |
80 info = info; | |
81 locals = get_locals_table(level); | |
82 upvalues = get_upvalues_table(info.func); | |
83 }; | |
84 end | |
85 return levels; | |
86 end | |
87 | |
88 function debug.traceback(thread, message, level) | |
89 if type(thread) ~= "thread" then | |
90 thread, message, level = coroutine.running(), thread, message; | |
91 end | |
92 if level and type(message) ~= "string" then | |
93 return nil, "invalid message"; | |
94 elseif not level then | |
95 level = message or 2; | |
96 end | |
97 | |
98 message = message and (message.."\n") or ""; | |
99 | |
100 local levels = get_traceback_table(thread, level+2); | |
101 | |
102 local lines = {}; | |
103 for nlevel, level in ipairs(levels) do | |
104 local info = level.info; | |
105 local line = "..."; | |
106 local func_type = info.namewhat.." "; | |
107 if func_type == " " then func_type = ""; end; | |
108 if info.short_src == "[C]" then | |
109 line = "[ C ] "..func_type.."C function "..(info.name and ("%q"):format(info.name) or "(unknown name)") | |
110 elseif info.what == "main" then | |
111 line = "[Lua] "..info.short_src.." line "..info.currentline; | |
112 else | |
113 local name = info.name or " "; | |
114 if name ~= " " then | |
115 name = ("%q"):format(name); | |
116 end | |
117 if func_type == "global " or func_type == "local " then | |
118 func_type = func_type.."function "; | |
119 end | |
120 line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..name.." defined on line "..info.linedefined; | |
121 end | |
122 nlevel = nlevel-1; | |
123 table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); | |
124 local npadding = (" "):rep(#tostring(nlevel)); | |
125 local locals_str = string_from_var_table(level.locals, 65, "\t "..npadding); | |
126 if locals_str then | |
127 table.insert(lines, "\t "..npadding.."Locals: "..locals_str); | |
128 end | |
129 local upvalues_str = string_from_var_table(level.upvalues, 65, "\t "..npadding); | |
130 if upvalues_str then | |
131 table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); | |
132 end | |
133 end | |
134 return message.."stack traceback:\n"..table.concat(lines, "\n"); | |
135 end |