1if not modules then modules = { } end modules ['util-deb'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13local type, next, tostring, tonumber, xpcall, print = type, next, tostring, tonumber, xpcall, print
14local format, find, sub, gsub = string.format, string.find, string.sub, string.gsub
15local insert, remove, sort = table.insert, table.remove, table.sort
16local setmetatableindex = table.setmetatableindex
17
18utilities = utilities or { }
19local debugger = utilities.debugger or { }
20utilities.debugger = debugger
21
22local report = logs.reporter("debugger")
23
24local ticks = os.gettimeofday or os.clock
25local seconds = function(n) return n or 0 end
26local overhead = 0
27local dummycalls = 10*1000
28local nesting = 0
29local names = { }
30
31local function initialize()
32 ticks = lua.getpreciseticks
33 seconds = lua.getpreciseseconds
34 initialize = false
35end
36
37setmetatableindex(names,function(t,name)
38 local v = setmetatableindex(function(t,source)
39 local v = setmetatableindex(function(t,line)
40
41 local v = { 0, 0, 0, 0 }
42 t[line] = v
43 return v
44 end)
45 t[source] = v
46 return v
47 end)
48 t[name] = v
49 return v
50end)
51
52local getinfo = nil
53local sethook = nil
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97local getdebuginfo = lua.getdebuginfo
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141local function hook(where)
142 local name, source, line = getdebuginfo()
143 if name then
144 local data = names[name][source][line]
145 if where == "call" then
146 local nesting = data[3]
147 if nesting == 0 then
148 data[2] = data[2] + 1
149 data[3] = 1
150 data[4] = ticks()
151 else
152 data[3] = nesting + 1
153 end
154
155 else
156 local nesting = data[3]
157 if nesting == 1 then
158 local t = data[4]
159 if t then
160 data[1] = data[1] + ticks() - t
161 end
162 data[3] = 0
163 elseif nesting > 0 then
164 data[3] = nesting - 1
165 end
166
167
168
169
170
171
172
173
174
175
176
177 end
178 end
179end
180
181function debugger.showstats(printer,threshold)
182 local printer = printer or report
183 local calls = 0
184 local functions = 0
185 local dataset = { }
186 local length = 0
187 local realtime = 0
188 local totaltime = 0
189 local threshold = threshold or 0
190 for name, sources in next, names do
191 for source, lines in next, sources do
192 for line, data in next, lines do
193
194 local count = data[2]
195 if count > threshold then
196 if #name > length then
197 length = #name
198 end
199
200 local total = data[1]
201 local real = total
202 if real > 0 then
203 real = total - (count * overhead / dummycalls)
204 if real < 0 then
205 real = 0
206 end
207 realtime = realtime + real
208 end
209 totaltime = totaltime + total
210 if line < 0 then
211 line = 0
212 end
213
214
215
216 dataset[#dataset+1] = { real, total, count, name, source, line }
217 end
218 end
219 end
220 end
221 sort(dataset,function(a,b)
222 if a[1] == b[1] then
223 if a[2] == b[2] then
224 if a[3] == b[3] then
225 if a[4] == b[4] then
226 if a[5] == b[5] then
227 return a[6] < b[6]
228 else
229 return a[5] < b[5]
230 end
231 else
232 return a[4] < b[4]
233 end
234 else
235 return b[3] < a[3]
236 end
237 else
238 return b[2] < a[2]
239 end
240 else
241 return b[1] < a[1]
242 end
243 end)
244 if length > 50 then
245 length = 50
246 end
247 local fmt = string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-" .. length .. "s %4i %s"]
248 for i=1,#dataset do
249 local data = dataset[i]
250 local real = data[1]
251 local total = data[2]
252 local count = data[3]
253 local name = data[4]
254 local source = data[5]
255 local line = data[6]
256 calls = calls + count
257 functions = functions + 1
258 name = gsub(name,"%s+"," ")
259 if #name > length then
260 name = sub(name,1,length)
261 end
262 printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source))
263 end
264 printer("")
265 printer(format("functions : %i", functions))
266 printer(format("calls : %i", calls))
267 printer(format("overhead : %f", seconds(overhead/1000)))
268
269
270end
271
272local function getdebug()
273 if sethook and getinfo then
274 return
275 end
276 if not debug then
277 local okay
278 okay, debug = pcall(require,"debug")
279 end
280 if type(debug) ~= "table" then
281 return
282 end
283 getinfo = debug.getinfo
284 sethook = debug.sethook
285 if type(getinfo) ~= "function" then
286 getinfo = nil
287 end
288 if type(sethook) ~= "function" then
289 sethook = nil
290 end
291end
292
293function debugger.savestats(filename,threshold)
294 local f = io.open(filename,'w')
295 if f then
296 debugger.showstats(function(str) f:write(str,"\n") end,threshold)
297 f:close()
298 end
299end
300
301function debugger.enable()
302 getdebug()
303 if sethook and getinfo and nesting == 0 then
304 running = true
305 if initialize then
306 initialize()
307 end
308 sethook(hook,"cr")
309
310
311 local function dummy() end
312 local t = ticks()
313 for i=1,dummycalls do
314 dummy()
315 end
316 overhead = ticks() - t
317 end
318 if nesting > 0 then
319 nesting = nesting + 1
320 end
321end
322
323function debugger.disable()
324 if nesting > 0 then
325 nesting = nesting - 1
326 end
327 if sethook and getinfo and nesting == 0 then
328 sethook()
329 end
330end
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349local function showtraceback(rep)
350 getdebug()
351 if getinfo then
352 local level = 2
353 local reporter = rep or report
354 while true do
355 local info = getinfo(level, "Sl")
356 if not info then
357 break
358 elseif info.what == "C" then
359 reporter("%2i : %s",level-1,"C function")
360 else
361 reporter("%2i : %s : %s",level-1,info.short_src,info.currentline)
362 end
363 level = level + 1
364 end
365 end
366end
367
368debugger.showtraceback = showtraceback
369
370
371
372
373if luac then
374
375 local show, dump = luac.print, string.dump
376
377 function luac.inspect(v)
378 if type(v) == "function" then
379 local ok, str = xpcall(dump,function() end,v)
380 if ok then
381 v = str
382 end
383 end
384 if type(v) == "string" then
385 show(v,true)
386 else
387 print(v)
388 end
389 end
390
391end
392 |