@@ -346,12 +346,53 @@ end
346
346
-- Stack Reverse Engineering
347
347
--
348
348
349
+ local NIL_REPLACEMENT = " <nil>" ; -- We need some way of representing nil values without them disappearing from tables
350
+
351
+ ---
352
+ -- Recursively formats a table of variables into a dictionary of key -> values
353
+ -- @param vars The table of keys and values to process, recursively deals with table values
354
+ -- @param out The output dictionary
355
+ -- @param [opt] prefix What to prefix the variable names with, used for the recursion of tables
356
+ -- @param [opt] done A list of tables that have already been processed to prevent infinite recursion
357
+ local function formatStackVariables (vars , out , prefix , done )
358
+ prefix = prefix or " "
359
+ done = done or {}
360
+ done [vars ] = true
361
+
362
+ for k ,v in pairs (vars ) do
363
+ local vType = type (v )
364
+ if (vType == " table" and v .r and v .g and v .b and v .a ) then
365
+ out [prefix .. k ] = (" Color(%.0f, %.0f, %.0f, %.0f)" ):format (v .r , v .g , v .b , v .a )
366
+ elseif (vType == " table" and not done [v ]) then
367
+ formatStackVariables (v , out , prefix .. k .. " ." , done )
368
+ elseif (vType == " number" or vType == " bool" ) then
369
+ out [prefix .. k ] = tostring (v )
370
+ elseif (vType == " string" ) then -- nil values are included here aswell
371
+ out [prefix .. k ] = v
372
+ elseif (vType == " Vector" ) then
373
+ out [prefix .. k ] = (" Vector(%.3f, %.3f, %.3f)" ):format (v .x , v .y , v .z )
374
+ elseif (vType == " Angle" ) then
375
+ out [prefix .. k ] = (" Angle(%.3f, %.3f, %.3f)" ):format (v .p , v .y , v .r )
376
+ elseif (vType == " Player" and IsValid (v )) then
377
+ out [prefix .. k ] = (" Player[%q, %s]" ):format (v :Nick (), v :SteamID ())
378
+ elseif (vType == " NPC" and IsValid (v )) then
379
+ out [prefix .. k ] = (" NPC[%i, %s]" ):format (v :EntIndex (), v :GetClass ())
380
+ elseif (vType == " Weapon" and IsValid (v )) then
381
+ out [prefix .. k ] = (" Weapon[%i, %s]" ):format (v :EntIndex (), v :GetClass ())
382
+ elseif (vType == " Entity" and IsValid (v )) then
383
+ out [prefix .. k ] = (" Entity[%i, %s]" ):format (v :EntIndex (), v :GetClass ())
384
+ elseif (vType ~= " function" ) then -- Catch-all except functions cause they are boring
385
+ out [prefix .. k ] = tostring (v )
386
+ end
387
+ end
388
+ end
389
+
349
390
---
350
391
-- Turns a lua stacktrace into a Sentry stacktrace
351
392
-- @param stack Lua stacktrace in debug.getinfo style
352
393
-- @return A reversed stacktrace with different field names
353
394
local function sentrifyStack (stack )
354
- -- Sentry likes stacks in the oposite order to lua
395
+ -- Sentry likes stacks in the opposite order to lua
355
396
stack = table .Reverse (stack );
356
397
357
398
-- The first entry from LuaError is sometimes useless
@@ -365,11 +406,16 @@ local function sentrifyStack(stack)
365
406
366
407
local ret = {}
367
408
for i , frame in ipairs (stack ) do
409
+ local vars = {};
410
+ formatStackVariables (frame [" upvalues" ], vars );
411
+ formatStackVariables (frame [" locals" ], vars );
412
+
368
413
ret [i ] = {
369
414
filename = frame [" source" ]:sub (2 ),
370
415
[" function" ] = frame [" name" ] or " <unknown>" ,
371
416
module = modulify (frame [" source" ]),
372
417
lineno = frame [" currentline" ],
418
+ vars = vars ,
373
419
}
374
420
end
375
421
return { frames = ret };
@@ -383,11 +429,37 @@ local function getStack()
383
429
384
430
local stack = {};
385
431
while true do
386
- local info = debug.getinfo (level , " Sln " );
432
+ local info = debug.getinfo (level , " fSlnu " );
387
433
if (not info ) then
388
434
break ;
389
435
end
390
436
437
+ local locals = {}
438
+ local upvalues = {}
439
+
440
+ if (info .what == " Lua" ) then
441
+ -- Capture locals
442
+ local i = 1
443
+ while true do
444
+ local name , value = debug.getlocal (level , i );
445
+ if (not isstring (name )) then break ; end
446
+
447
+ if (# name > 0 and name [1 ] ~= " (" ) then -- Some locals are internal with names like "(*temporary)"
448
+ locals [name ] = value == nil and NIL_REPLACEMENT or value ;
449
+ end
450
+ i = i + 1
451
+ end
452
+
453
+ -- Capture upvalues
454
+ for j = 1 , info .nups do
455
+ local name , value = debug.getupvalue (info .func , j );
456
+ upvalues [name ] = value == nil and NIL_REPLACEMENT or value ;
457
+ end
458
+ end
459
+
460
+ info .locals = locals ;
461
+ info .upvalues = upvalues ;
462
+
391
463
stack [level - 2 ] = info ;
392
464
393
465
level = level + 1 ;
@@ -868,6 +940,8 @@ function ExecuteTransaction(name, txn, func, ...)
868
940
869
941
local success = table.remove (res , 1 );
870
942
if (not success ) then
943
+ -- xpcallCB("Test") -- Uncomment this if you get "error in error handling" errors, useful for debugging
944
+
871
945
local err = res [1 ];
872
946
SkipNext (err );
873
947
-- Boom
0 commit comments