-
Notifications
You must be signed in to change notification settings - Fork 2
/
debugtools.lua
431 lines (371 loc) · 11.9 KB
/
debugtools.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
97
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
table.inspect = require("inspect") -- add table pretty printer that understands recursive tables
local getinfo = debug.getinfo
local max = math.max
local concat = table.concat
local function filtersource(src)
if not src then return "[?]" end
if src:sub(1, 1) == "@" then
src = src:sub(2)
end
return src
end
local function formatinfo(info)
if not info then return "**error**" end
local source = filtersource(info.source)
if info.currentline then
source = source..":"..info.currentline
end
return ("\t%s in (%s) %s (%s) <%d-%d>"):format(source, info.namewhat, info.name or "?", info.what, info.linedefined, info.lastlinedefined)
end
function printwrap(msg, ...)
if type(...) == "table" then
print(msg)
dumptable(..., 0, 0)
else
print(msg, ...)
end
return ...
end
function printsel(inst, ...)
if c_sel() == inst or (inst.inst and c_sel() == inst.inst) then
print(...)
end
end
function debugstack(start, top, bottom)
if not bottom then bottom = 10 end
if not top then top = 12 end
start = (start or 1) + 1
local count = max(2, start)
while getinfo(count) do
count = count + 1
end
count = count - start
if top + bottom >= count then
top = count
bottom = nil
end
local s = {"stack traceback:"}
for i = 1, top, 1 do
s[#s + 1] = formatinfo(getinfo(start + i - 1))
end
if bottom then
s[#s + 1] = "\t..."
for i = bottom , 1, -1 do
s[#s + 1] = formatinfo(getinfo(count - i + 1))
end
end
return concat(s, "\n")
end
function debugstack_oneline(linenum)
local num = linenum or 3
return formatinfo(getinfo(num))
end
-- Print callstack when any function is called. Useful for finding what's
-- calling C functions.
-- Usage:
-- TheNet = instrument_userdata(TheNet)
-- or return instrument_userdata(TheNet) from a getter.
function instrument_userdata(instance)
local methods = getmetatable(instance).__index
local Proxy = {}
for fn,def in pairs(methods) do
Proxy[fn] = function (junk, ...)
print("instrument_userdata", debugstack())
return instance[fn](instance, ...)
end
end
return Proxy
end
function debuglocals (level)
local t = {}
local index = 1
while true do
local name, value = debug.getlocal(level + 1, index)
if not name then break end
t[index] = string.format("%s = %s", name, tostring(value))
index = index + 1
end
return table.concat(t, "\n")
end
local function SortByTypeAndValue(a, b)
local typea, typeb = type(a), type(b)
return typea < typeb or (typea == typeb and (typea ~= "table" and a < b))
end
function dumptablequiet(obj, indent, recurse_levels, visit_table)
return dumptable(obj, indent, recurse_levels, visit_table, true)
end
function dumptable(obj, indent, recurse_levels, visit_table, is_terse)
local is_top_level = visit_table == nil
if visit_table == nil then
visit_table = {}
end
indent = indent or 1
local i_recurse_levels = recurse_levels or 5
if obj then
local dent = string.rep("\t", indent)
if type(obj)==type("") then
print(obj)
return
end
if type(obj) == "table" then
if visit_table[obj] ~= nil then
print(dent.."(Already visited",obj,"-- skipping.)")
return
else
visit_table[obj] = true
end
end
local keys = {}
for k,v in pairs(obj) do
table.insert(keys, k)
end
table.sort(keys, SortByTypeAndValue)
if not is_terse and is_top_level and #keys == 0 then
print(dent.."(empty)")
end
for i,k in ipairs(keys) do
local v = obj[k]
if type(v) == "table" and i_recurse_levels>0 then
if v.entity and v.entity:GetGUID() then
print(dent.."K: ",k," V: ", v, "(Entity -- skipping.)")
else
print(dent.."K: ",k," V: ", v)
dumptable(v, indent+1, i_recurse_levels-1, visit_table)
end
else
print(dent.."K: ",k," V: ",v)
end
end
elseif not is_terse then
print("nil")
end
end
function tabletodictstring(obj, fn)
if obj == nil then
return "{ }"
end
local s = "{ "
local first = true
for k,v in pairs(obj) do
if not first then
s = s..", "
else
first = false
end
if fn then k,v = fn(k,v) end
s = s..tostring(k).."="..tostring(v)
end
s = s.." }"
return s
end
function tabletoliststring(obj, fn)
if obj == nil then
return "[ ]"
end
local s = "[ "
local first = true
for i,v in ipairs(obj) do
if not first then
s = s..", "
else
first = false
end
if fn then v = fn(v) end
s = s..tostring(v)
end
s = s.." ]"
return s
end
--[[
Better control over IO
--]]
global("CWD")
local userName = ""
local dir = CWD or ""
dir = string.gsub(dir, "\\", "/")
-- Copied from scheduler.lua - puts tabs between items and LuaPrint adds a \n at the end
local function oldprint(...)
local str = ''
local arg = {n=select('#',...),...}
for i = 1, arg.n do
if str ~= '' then str = str .. '\t' end
str = str .. tostring( arg[i] )
end
--str = str .. '\n'
TheSim:LuaPrint( str )
end
-- Output directly to stdout after conversion to string representation - no extra cruft
local function DirectIO(...)
local arg = {n=select('#',...),...}
for i = 1, arg.n do
io.stdout:write( tostring( arg[i] ) )
end
end
--[[
Fast way to disable all the spew to the console window by defining a few globals (yes, setting these should probably be functions)
Will not output if:
CHEATS_ENABLE_DPRINT is false
If DPRINT_USERNAME is defined, then will only print if this equals the string returned by TheSim:GetUsersName()
if DPRINT_PRINT_SOURCELINE is true, acts like print and outputs calling file and line number
--]]
function dprint(...)
global("CHEATS_ENABLE_DPRINT")
global("DPRINT_PRINT_SOURCELINE")
global("DPRINT_USERNAME")
if not (CHEATS_ENABLED and CHEATS_ENABLE_DPRINT) then
return
end
if DPRINT_USERNAME then
if type(TheSim.GetUsersName) == "function" then
userName = TheSim:GetUsersName()
end
if userName ~= DPRINT_USERNAME then
return
end
end
if DPRINT_PRINT_SOURCELINE then
local info = debug.getinfo(2, "Sl")
local source = info.source
if info.source and string.sub(info.source,1,1)=="@" then
source = source:sub(2)
end
local defline = string.format("%s(%d,1)", tostring(source), info.currentline)
oldprint(defline, ...)
else
oldprint(...)
end
end
-- Raw characters to stdout without processing
function IOprint(...)
global("CHEATS_ENABLE_DPRINT")
global("DPRINT_USERNAME")
if not (CHEATS_ENABLED and CHEATS_ENABLE_DPRINT) then
return
end
if DPRINT_USERNAME then
if type(TheSim.GetUsersName) == "function" then
userName = TheSim:GetUsersName()
end
if userName ~= DPRINT_USERNAME then
return
end
end
DirectIO(...)
end
-- Only print if inst is debugentity
function eprint(inst,...)
if inst == GetDebugEntity() then
dprint(...)
end
end
-- Add debug hook to any object: Author DForsey
-- Usage:
-- EnableDebugOnEntity(thing) turns on all debug printing for this thing (same as calling with (thing,"all") )
-- EnableDebugOnEntity(thing,false) turns off all debug printing for this thing, resets all items and/or priority
-- EnableDebugOnEntity(thing,number) turns on debug printing for requests with priority<number
-- EnableDebugOnEntity(thing,"string") turns on debug printing only for requests tagged with "string"
-- EnableDebugOnEntity(thing,"off") turns off debug printing, but doesn't reset the tag list or priority
-- EnableDebugOnEntity(thing,"on") turns on debug printing without affecting priority or tag list
-- EnableDebugOnEntity(thing,"all") all Dbg calls will print
--
-- Dbg(thing,true,...) prints arg list if debug print is enabled for this thing
-- Dbg(thing,number,...) prints arg list with the given priority level
-- Dbg(thing,"string",...) prints arg list if "string" has been tagged for this thing
--
function Dbg(thing,level,...)
if not thing._DEBUG_List or not thing._DEBUG_List.on or not CHEATS_ENABLED then return end
thing._DEBUG_List.priority = thing._DEBUG_List.priority or 0
if type(level) == "string" and thing._DEBUG_List[level] then
oldprint(...)
elseif type(level) == "number" and thing._DEBUG_List.priority < level then
oldprint(...)
elseif thing._DEBUG_List["all"] then
oldprint(...)
end
end
function EnableDebugOnEntity(thing,items)
if type(thing) ~= "table" then return end
thing._DEBUG_List = thing._DEBUG_List or {on=true}
if items == false then
thing._DEBUG_List = {on=false}
return
elseif items == "on" then
thing._DEBUG_List = {on=true}
elseif items == "off" then
thing._DEBUG_List = {on=false}
end
if type(items) == string then
thing._DEBUG_List[items] = true
thing._DEBUG_List.on = true
elseif type(items) == "number" then
thing._DEBUG_List.priority = items
else
thing._DEBUG_List["all"] = true
thing._DEBUG_List.on = true
end
end
function ddump(obj, indent, recurse_levels, root)
indent = indent or 1
local i_recurse_levels = recurse_levels or 3
if obj then
local dent = ""
if indent then
for i=1,indent do dent = dent.." " end
end
if type(obj)==type("") then
dprint(obj)
return
end
for k,v in pairs(obj) do
if v and v == root then
dprint(dent.."K: <SELF>")
return
end
if type(v) == "table" and i_recurse_levels>0 then
dprint(dent.."K: ",k)
root = root or obj
ddump(v, indent+1, i_recurse_levels-1,root)
else
dprint(dent.."K: ",k," V: ",v)
end
end
end
end
function dtable( tab, depth )
if type(tab) ~= "table" then
dprint(tab)
elseif table.inspect then
depth = depth or 1
dprint(table.inspect(tab,depth))
end
end
function DrawLine(pos1,pos2)
-- debug draw of new map gen
local debugdrawmap = CreateEntity("DebugDrawMap")
local draw = debugdrawmap.entity:AddDebugRender()
draw:SetZ(0.1)
draw:Line(pos1.x, pos1.z, pos2.x,pos2.y, 255, 255, 255, 255)
draw:Flush()
--[[
for idx,node in ipairs(graph.nodes) do
local colour = graph.colours[node.c]
for i =1, #node.poly-1 do
draw:Line(node.poly[i][1], node.poly[i][2], node.poly[i+1][1], node.poly[i+1][2], colour.r, colour.g, colour.b, 255)
end
draw:Line(node.poly[1][1], node.poly[1][2], node.poly[#node.poly][1], node.poly[#node.poly][2], colour.r, colour.g, colour.b, 255)
draw:Poly(node.cent[1], node.cent[2], colour.r, colour.g, colour.b, colour.a, node.poly)
draw:String(graph.ids[idx].."("..node.cent[1]..","..node.cent[2]..")", node.cent[1], node.cent[2], node.ts)
end
draw:SetZ(0.15)
for idx,edge in ipairs(graph.edges) do
if edge.n1 ~= nil and edge.n2 ~= nil then
local colour = graph.colours[edge.c]
local n1 = graph.nodes[edge.n1]
local n2 = graph.nodes[edge.n2]
if n1 ~= nil and n2 ~= nil then
draw:Line(n1.cent[1], n1.cent[2], n2.cent[1], n2.cent[2], colour.r, colour.g, colour.b, colour.a)
end
end
end
--]]
end