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 dataset[#dataset+1] = { real, total, count, name, source, line < 0 and 0 or line }
211 end
212 end
213 end
214 end
215 sort(dataset,function(a,b)
216 if a[1] == b[1] then
217 if a[2] == b[2] then
218 if a[3] == b[3] then
219 if a[4] == b[4] then
220 if a[5] == b[5] then
221 return a[6] < b[6]
222 else
223 return a[5] < b[5]
224 end
225 else
226 return a[4] < b[4]
227 end
228 else
229 return b[3] < a[3]
230 end
231 else
232 return b[2] < a[2]
233 end
234 else
235 return b[1] < a[1]
236 end
237 end)
238 if length > 50 then
239 length = 50
240 end
241 local fmt = string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-" .. length .. "s %4i %s"]
242 for i=1,#dataset do
243 local data = dataset[i]
244 local real = data[1]
245 local total = data[2]
246 local count = data[3]
247 local name = data[4]
248 local source = data[5]
249 local line = data[6]
250 calls = calls + count
251 functions = functions + 1
252 name = gsub(name,"%s+"," ")
253 if #name > length then
254 name = sub(name,1,length)
255 end
256 printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source))
257 end
258 printer("")
259 printer(format("functions : %i", functions))
260 printer(format("calls : %i", calls))
261 printer(format("overhead : %f", seconds(overhead/1000)))
262
263
264end
265
266local function getdebug()
267 if sethook and getinfo then
268 return
269 end
270 if not debug then
271 local okay
272 okay, debug = pcall(require,"debug")
273 end
274 if type(debug) ~= "table" then
275 return
276 end
277 getinfo = debug.getinfo
278 sethook = debug.sethook
279 if type(getinfo) ~= "function" then
280 getinfo = nil
281 end
282 if type(sethook) ~= "function" then
283 sethook = nil
284 end
285end
286
287function debugger.savestats(filename,threshold)
288 local f = io.open(filename,'w')
289 if f then
290 debugger.showstats(function(str) f:write(str,"\n") end,threshold)
291 f:close()
292 end
293end
294
295function debugger.enable()
296 getdebug()
297 if sethook and getinfo and nesting == 0 then
298 running = true
299 if initialize then
300 initialize()
301 end
302 sethook(hook,"cr")
303
304
305 local function dummy() end
306 local t = ticks()
307 for i=1,dummycalls do
308 dummy()
309 end
310 overhead = ticks() - t
311 end
312 if nesting > 0 then
313 nesting = nesting + 1
314 end
315end
316
317function debugger.disable()
318 if nesting > 0 then
319 nesting = nesting - 1
320 end
321 if sethook and getinfo and nesting == 0 then
322 sethook()
323 end
324end
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343local function showtraceback(rep)
344 getdebug()
345 if getinfo then
346 local level = 2
347 local reporter = rep or report
348 while true do
349 local info = getinfo(level, "Sl")
350 if not info then
351 break
352 elseif info.what == "C" then
353 reporter("%2i : %s",level-1,"C function")
354 else
355 reporter("%2i : %s : %s",level-1,info.short_src,info.currentline)
356 end
357 level = level + 1
358 end
359 end
360end
361
362debugger.showtraceback = showtraceback
363
364
365
366
367if luac then
368
369 local show, dump = luac.print, string.dump
370
371 function luac.inspect(v)
372 if type(v) == "function" then
373 local ok, str = xpcall(dump,function() end,v)
374 if ok then
375 v = str
376 end
377 end
378 if type(v) == "string" then
379 show(v,true)
380 else
381 print(v)
382 end
383 end
384
385end
386 |