Mercurial > prosody-hg
comparison util/debug.lua @ 4797:e239668aa6d2
Merge 0.9->trunk
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Sun, 29 Apr 2012 02:10:55 +0100 |
| parents | 127de6eec433 |
| children | bd0ff8ae98a8 |
comparison
equal
deleted
inserted
replaced
| 4796:04a34287dc12 | 4797:e239668aa6d2 |
|---|---|
| 5 password = true; | 5 password = true; |
| 6 passwd = true; | 6 passwd = true; |
| 7 pass = true; | 7 pass = true; |
| 8 pwd = true; | 8 pwd = true; |
| 9 }; | 9 }; |
| 10 local optimal_line_length = 65; | |
| 10 | 11 |
| 11 local function get_locals_table(level) | 12 local termcolours = require "util.termcolours"; |
| 13 local getstring = termcolours.getstring; | |
| 14 local styles; | |
| 15 do | |
| 16 _ = termcolours.getstyle; | |
| 17 styles = { | |
| 18 boundary_padding = _("bright"); | |
| 19 filename = _("bright", "blue"); | |
| 20 level_num = _("green"); | |
| 21 funcname = _("yellow"); | |
| 22 location = _("yellow"); | |
| 23 }; | |
| 24 end | |
| 25 module("debugx", package.seeall); | |
| 26 | |
| 27 function get_locals_table(level) | |
| 28 level = level + 1; -- Skip this function itself | |
| 12 local locals = {}; | 29 local locals = {}; |
| 13 for local_num = 1, math.huge do | 30 for local_num = 1, math.huge do |
| 14 local name, value = debug.getlocal(level, local_num); | 31 local name, value = debug.getlocal(level, local_num); |
| 15 if not name then break; end | 32 if not name then break; end |
| 16 table.insert(locals, { name = name, value = value }); | 33 table.insert(locals, { name = name, value = value }); |
| 17 end | 34 end |
| 18 return locals; | 35 return locals; |
| 19 end | 36 end |
| 20 | 37 |
| 21 local function get_upvalues_table(func) | 38 function get_upvalues_table(func) |
| 22 local upvalues = {}; | 39 local upvalues = {}; |
| 23 if func then | 40 if func then |
| 24 for upvalue_num = 1, math.huge do | 41 for upvalue_num = 1, math.huge do |
| 25 local name, value = debug.getupvalue(func, upvalue_num); | 42 local name, value = debug.getupvalue(func, upvalue_num); |
| 26 if not name then break; end | 43 if not name then break; end |
| 28 end | 45 end |
| 29 end | 46 end |
| 30 return upvalues; | 47 return upvalues; |
| 31 end | 48 end |
| 32 | 49 |
| 33 local function string_from_var_table(var_table, max_line_len, indent_str) | 50 function string_from_var_table(var_table, max_line_len, indent_str) |
| 34 local var_string = {}; | 51 local var_string = {}; |
| 35 local col_pos = 0; | 52 local col_pos = 0; |
| 36 max_line_len = max_line_len or math.huge; | 53 max_line_len = max_line_len or math.huge; |
| 37 indent_str = "\n"..(indent_str or ""); | 54 indent_str = "\n"..(indent_str or ""); |
| 38 for _, var in ipairs(var_table) do | 55 for _, var in ipairs(var_table) do |
| 69 function get_traceback_table(thread, start_level) | 86 function get_traceback_table(thread, start_level) |
| 70 local levels = {}; | 87 local levels = {}; |
| 71 for level = start_level, math.huge do | 88 for level = start_level, math.huge do |
| 72 local info; | 89 local info; |
| 73 if thread then | 90 if thread then |
| 74 info = debug.getinfo(thread, level); | 91 info = debug.getinfo(thread, level+1); |
| 75 else | 92 else |
| 76 info = debug.getinfo(level); | 93 info = debug.getinfo(level+1); |
| 77 end | 94 end |
| 78 if not info then break; end | 95 if not info then break; end |
| 79 | 96 |
| 80 levels[(level-start_level)+1] = { | 97 levels[(level-start_level)+1] = { |
| 81 level = level; | 98 level = level; |
| 82 info = info; | 99 info = info; |
| 83 locals = get_locals_table(level); | 100 locals = get_locals_table(level+1); |
| 84 upvalues = get_upvalues_table(info.func); | 101 upvalues = get_upvalues_table(info.func); |
| 85 }; | 102 }; |
| 86 end | 103 end |
| 87 return levels; | 104 return levels; |
| 88 end | 105 end |
| 89 | 106 |
| 90 function debug.traceback(thread, message, level) | 107 function traceback(...) |
| 91 if type(thread) ~= "thread" then | 108 local ok, ret = pcall(_traceback, ...); |
| 109 if not ok then | |
| 110 return "Error in error handling: "..ret; | |
| 111 end | |
| 112 return ret; | |
| 113 end | |
| 114 | |
| 115 local function build_source_boundary_marker(last_source_desc) | |
| 116 local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); | |
| 117 return getstring(styles.boundary_padding, "v"..padding).." "..getstring(styles.filename, last_source_desc).." "..getstring(styles.boundary_padding, padding..(#last_source_desc%2==0 and "-v" or "v ")); | |
| 118 end | |
| 119 | |
| 120 function _traceback(thread, message, level) | |
| 121 | |
| 122 -- Lua manual says: debug.traceback ([thread,] [message [, level]]) | |
| 123 -- I fathom this to mean one of: | |
| 124 -- () | |
| 125 -- (thread) | |
| 126 -- (message, level) | |
| 127 -- (thread, message, level) | |
| 128 | |
| 129 if thread == nil then -- Defaults | |
| 130 thread, message, level = coroutine.running(), message, level; | |
| 131 elseif type(thread) == "string" then | |
| 92 thread, message, level = coroutine.running(), thread, message; | 132 thread, message, level = coroutine.running(), thread, message; |
| 133 elseif type(thread) ~= "thread" then | |
| 134 return nil; -- debug.traceback() does this | |
| 93 end | 135 end |
| 94 if level and type(message) ~= "string" then | 136 |
| 95 return nil, "invalid message"; | 137 level = level or 1; |
| 96 elseif not level then | 138 |
| 97 level = message or 2; | |
| 98 end | |
| 99 | |
| 100 message = message and (message.."\n") or ""; | 139 message = message and (message.."\n") or ""; |
| 101 | 140 |
| 102 local levels = get_traceback_table(thread, level+2); | 141 -- +3 counts for this function, and the pcall() and wrapper above us |
| 142 local levels = get_traceback_table(thread, level+3); | |
| 143 | |
| 144 local last_source_desc; | |
| 103 | 145 |
| 104 local lines = {}; | 146 local lines = {}; |
| 105 for nlevel, level in ipairs(levels) do | 147 for nlevel, level in ipairs(levels) do |
| 106 local info = level.info; | 148 local info = level.info; |
| 107 local line = "..."; | 149 local line = "..."; |
| 108 local func_type = info.namewhat.." "; | 150 local func_type = info.namewhat.." "; |
| 151 local source_desc = (info.short_src == "[C]" and "C code") or info.short_src or "Unknown"; | |
| 109 if func_type == " " then func_type = ""; end; | 152 if func_type == " " then func_type = ""; end; |
| 110 if info.short_src == "[C]" then | 153 if info.short_src == "[C]" then |
| 111 line = "[ C ] "..func_type.."C function "..(info.name and ("%q"):format(info.name) or "(unknown name)") | 154 line = "[ C ] "..func_type.."C function "..getstring(styles.location, (info.name and ("%q"):format(info.name) or "(unknown name)")); |
| 112 elseif info.what == "main" then | 155 elseif info.what == "main" then |
| 113 line = "[Lua] "..info.short_src.." line "..info.currentline; | 156 line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline); |
| 114 else | 157 else |
| 115 local name = info.name or " "; | 158 local name = info.name or " "; |
| 116 if name ~= " " then | 159 if name ~= " " then |
| 117 name = ("%q"):format(name); | 160 name = ("%q"):format(name); |
| 118 end | 161 end |
| 119 if func_type == "global " or func_type == "local " then | 162 if func_type == "global " or func_type == "local " then |
| 120 func_type = func_type.."function "; | 163 func_type = func_type.."function "; |
| 121 end | 164 end |
| 122 line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..name.." defined on line "..info.linedefined; | 165 line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline).." in "..func_type..getstring(styles.funcname, name).." (defined on line "..info.linedefined..")"; |
| 166 end | |
| 167 if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous | |
| 168 last_source_desc = source_desc; | |
| 169 table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); | |
| 123 end | 170 end |
| 124 nlevel = nlevel-1; | 171 nlevel = nlevel-1; |
| 125 table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); | 172 table.insert(lines, "\t"..(nlevel==0 and ">" or " ")..getstring(styles.level_num, "("..nlevel..") ")..line); |
| 126 local npadding = (" "):rep(#tostring(nlevel)); | 173 local npadding = (" "):rep(#tostring(nlevel)); |
| 127 local locals_str = string_from_var_table(level.locals, 65, "\t "..npadding); | 174 local locals_str = string_from_var_table(level.locals, optimal_line_length, "\t "..npadding); |
| 128 if locals_str then | 175 if locals_str then |
| 129 table.insert(lines, "\t "..npadding.."Locals: "..locals_str); | 176 table.insert(lines, "\t "..npadding.."Locals: "..locals_str); |
| 130 end | 177 end |
| 131 local upvalues_str = string_from_var_table(level.upvalues, 65, "\t "..npadding); | 178 local upvalues_str = string_from_var_table(level.upvalues, optimal_line_length, "\t "..npadding); |
| 132 if upvalues_str then | 179 if upvalues_str then |
| 133 table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); | 180 table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); |
| 134 end | 181 end |
| 135 end | 182 end |
| 183 | |
| 184 -- table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); | |
| 185 | |
| 136 return message.."stack traceback:\n"..table.concat(lines, "\n"); | 186 return message.."stack traceback:\n"..table.concat(lines, "\n"); |
| 137 end | 187 end |
| 188 | |
| 189 function use() | |
| 190 debug.traceback = traceback; | |
| 191 end | |
| 192 | |
| 193 return _M; |
