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 |