Mercurial > prosody-hg
comparison tools/modtrace.lua @ 11200:bf8f2da84007
Merge 0.11->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 05 Nov 2020 22:31:25 +0100 |
| parents | 50f182931bdd |
| children | 39ae08180c81 |
comparison
equal
deleted
inserted
replaced
| 11199:6c7c50a4de32 | 11200:bf8f2da84007 |
|---|---|
| 1 -- Trace module calls and method calls on created objects | |
| 2 -- | |
| 3 -- Very rough and for debugging purposes only. It makes many | |
| 4 -- assumptions and there are many ways it could fail. | |
| 5 -- | |
| 6 -- Example use: | |
| 7 -- | |
| 8 -- local dbuffer = require "tools.modtrace".trace("util.dbuffer"); | |
| 9 -- | |
| 10 | |
| 11 local t_pack = require "util.table".pack; | |
| 12 local serialize = require "util.serialization".serialize; | |
| 13 local unpack = table.unpack or unpack; --luacheck: ignore 113 | |
| 14 local set = require "util.set"; | |
| 15 | |
| 16 local serialize_cfg = { | |
| 17 preset = "oneline"; | |
| 18 freeze = true; | |
| 19 fatal = false; | |
| 20 fallback = function (v) return "<"..tostring(v)..">" end; | |
| 21 }; | |
| 22 | |
| 23 local function stringify_value(v) | |
| 24 if type(v) == "string" and #v > 20 then | |
| 25 return ("<string(%d)>"):format(#v); | |
| 26 elseif type(v) == "function" then | |
| 27 return tostring(v); | |
| 28 end | |
| 29 return serialize(v, serialize_cfg); | |
| 30 end | |
| 31 | |
| 32 local function stringify_params(...) | |
| 33 local n = select("#", ...); | |
| 34 local r = {}; | |
| 35 for i = 1, n do | |
| 36 table.insert(r, stringify_value((select(i, ...)))); | |
| 37 end | |
| 38 return table.concat(r, ", "); | |
| 39 end | |
| 40 | |
| 41 local function stringify_result(ret) | |
| 42 local r = {}; | |
| 43 for i = 1, ret.n do | |
| 44 table.insert(r, stringify_value(ret[i])); | |
| 45 end | |
| 46 return table.concat(r, ", "); | |
| 47 end | |
| 48 | |
| 49 local function stringify_call(method_name, ...) | |
| 50 return ("%s(%s)"):format(method_name, stringify_params(...)); | |
| 51 end | |
| 52 | |
| 53 local function wrap_method(original_obj, original_method, method_name) | |
| 54 method_name = ("<%s>:%s"):format(getmetatable(original_obj).__name or "object", method_name); | |
| 55 return function (new_obj_self, ...) | |
| 56 local opts = new_obj_self._modtrace_opts; | |
| 57 local f = opts.output or io.stderr; | |
| 58 f:write(stringify_call(method_name, ...)); | |
| 59 local ret = t_pack(original_method(original_obj, ...)); | |
| 60 if ret.n > 0 then | |
| 61 f:write(" = ", stringify_result(ret), "\n"); | |
| 62 else | |
| 63 f:write("\n"); | |
| 64 end | |
| 65 return unpack(ret, 1, ret.n); | |
| 66 end; | |
| 67 end | |
| 68 | |
| 69 local function wrap_function(original_function, function_name, opts) | |
| 70 local f = opts.output or io.stderr; | |
| 71 return function (...) | |
| 72 f:write(stringify_call(function_name, ...)); | |
| 73 local ret = t_pack(original_function(...)); | |
| 74 if ret.n > 0 then | |
| 75 f:write(" = ", stringify_result(ret), "\n"); | |
| 76 else | |
| 77 f:write("\n"); | |
| 78 end | |
| 79 return unpack(ret, 1, ret.n); | |
| 80 end; | |
| 81 end | |
| 82 | |
| 83 local function wrap_metamethod(name, method) | |
| 84 if name == "__index" then | |
| 85 return function (new_obj, k) | |
| 86 local original_method; | |
| 87 if type(method) == "table" then | |
| 88 original_method = new_obj._modtrace_original_obj[k]; | |
| 89 else | |
| 90 original_method = method(new_obj._modtrace_original_obj, k); | |
| 91 end | |
| 92 if original_method == nil then | |
| 93 return nil; | |
| 94 end | |
| 95 return wrap_method(new_obj._modtrace_original_obj, original_method, k); | |
| 96 end; | |
| 97 end | |
| 98 return function (new_obj, ...) | |
| 99 return method(new_obj._modtrace_original_obj, ...); | |
| 100 end; | |
| 101 end | |
| 102 | |
| 103 local function wrap_mt(original_mt) | |
| 104 local new_mt = {}; | |
| 105 for k, v in pairs(original_mt) do | |
| 106 new_mt[k] = wrap_metamethod(k, v); | |
| 107 end | |
| 108 return new_mt; | |
| 109 end | |
| 110 | |
| 111 local function wrap_obj(original_obj, opts) | |
| 112 local new_mt = wrap_mt(getmetatable(original_obj)); | |
| 113 return setmetatable({_modtrace_original_obj = original_obj, _modtrace_opts = opts}, new_mt); | |
| 114 end | |
| 115 | |
| 116 local function wrap_new(original_new, function_name, opts) | |
| 117 local f = opts.output or io.stderr; | |
| 118 return function (...) | |
| 119 f:write(stringify_call(function_name, ...)); | |
| 120 local ret = t_pack(original_new(...)); | |
| 121 local obj = ret[1]; | |
| 122 | |
| 123 if ret.n == 1 and type(ret[1]) == "table" then | |
| 124 f:write(" = <", getmetatable(ret[1]).__name or "object", ">", "\n"); | |
| 125 elseif ret.n > 0 then | |
| 126 f:write(" = ", stringify_result(ret), "\n"); | |
| 127 else | |
| 128 f:write("\n"); | |
| 129 end | |
| 130 | |
| 131 if obj then | |
| 132 ret[1] = wrap_obj(obj, opts); | |
| 133 end | |
| 134 return unpack(ret, 1, ret.n); | |
| 135 end; | |
| 136 end | |
| 137 | |
| 138 local function trace(module, opts) | |
| 139 if type(module) == "string" then | |
| 140 module = require(module); | |
| 141 end | |
| 142 opts = opts or {}; | |
| 143 local new_methods = set.new(opts.new_methods or {"new"}); | |
| 144 local fake_module = setmetatable({}, { | |
| 145 __index = function (_, k) | |
| 146 if new_methods:contains(k) then | |
| 147 return wrap_new(module[k], k, opts); | |
| 148 else | |
| 149 return wrap_function(module[k], k, opts); | |
| 150 end | |
| 151 end; | |
| 152 }); | |
| 153 return fake_module; | |
| 154 end | |
| 155 | |
| 156 return { | |
| 157 wrap = trace; | |
| 158 trace = trace; | |
| 159 } |
