@@ -346,12 +346,53 @@ end
346346-- Stack Reverse Engineering
347347--
348348
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+
349390---
350391-- Turns a lua stacktrace into a Sentry stacktrace
351392-- @param stack Lua stacktrace in debug.getinfo style
352393-- @return A reversed stacktrace with different field names
353394local function sentrifyStack (stack )
354- -- Sentry likes stacks in the oposite order to lua
395+ -- Sentry likes stacks in the opposite order to lua
355396 stack = table .Reverse (stack );
356397
357398 -- The first entry from LuaError is sometimes useless
@@ -365,11 +406,16 @@ local function sentrifyStack(stack)
365406
366407 local ret = {}
367408 for i , frame in ipairs (stack ) do
409+ local vars = {};
410+ formatStackVariables (frame [" upvalues" ], vars );
411+ formatStackVariables (frame [" locals" ], vars );
412+
368413 ret [i ] = {
369414 filename = frame [" source" ]:sub (2 ),
370415 [" function" ] = frame [" name" ] or " <unknown>" ,
371416 module = modulify (frame [" source" ]),
372417 lineno = frame [" currentline" ],
418+ vars = vars ,
373419 }
374420 end
375421 return { frames = ret };
@@ -383,11 +429,37 @@ local function getStack()
383429
384430 local stack = {};
385431 while true do
386- local info = debug.getinfo (level , " Sln " );
432+ local info = debug.getinfo (level , " fSlnu " );
387433 if (not info ) then
388434 break ;
389435 end
390436
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+
391463 stack [level - 2 ] = info ;
392464
393465 level = level + 1 ;
@@ -868,6 +940,8 @@ function ExecuteTransaction(name, txn, func, ...)
868940
869941 local success = table.remove (res , 1 );
870942 if (not success ) then
943+ -- xpcallCB("Test") -- Uncomment this if you get "error in error handling" errors, useful for debugging
944+
871945 local err = res [1 ];
872946 SkipNext (err );
873947 -- Boom
0 commit comments