Files
vgstation13/code/modules/error_handler/error_handler.dm
DamianX ada12cfc5c Rewrote runtime condenser (#16495)
* Rewrote runtime condenser

* Addressed 2/3 reviews

* fuck

* REMOVE .clone()

* Reduce unecessary allocations and hash map lookups.

Previously, when parsing the details of a runtime, every single details line needed the runtime to be looked up again in the hash map, and every runtime parsed needed at least one allocation to allow for the former. Both of this is has been optimized out by re-ordering the code completely.

* All the features

* Some untested work

* More work on supporting crap stack traces, still WiP

* Infinite loop and stack overflow traces work mostly.

* Add to travis

* Fix compiler warning

* Run cargo fmt

* Removed silly

* Removed more silly

* Verbose option

* rustfmt

* Sorted verbose output

* Stdout by default, stderr for errors

* rustfmt

* Don't panic on invalid UTF-8, static dispatch

* PARALLELISM

* Removed condenser from travis
2017-11-26 19:38:43 +01:00

113 lines
3.9 KiB
Plaintext

/var/list/error_last_seen = list()
// error_cooldown items will either be positive (cooldown time) or negative (silenced error)
// If negative, starts at -1, and goes down by 1 each time that error gets skipped
/var/list/error_cooldown = list()
/var/total_runtimes = 0
/var/total_runtimes_skipped = 0
// The ifdef needs to be down here, since the error viewer references total_runtimes
#ifdef DEBUG
/world/Error(var/exception/e)
if (!istype(e)) // Something threw an unusual exception
world.log << "\[[time_stamp()]] Uncaught exception: [e]"
return ..()
if (!global.error_last_seen) // A runtime is occurring too early in start-up initialization
return ..()
global.total_runtimes++
var/erroruid = "[e.file][e.line]:[e]"
var/last_seen = global.error_last_seen[erroruid]
var/cooldown = global.error_cooldown[erroruid] || 0
if (last_seen == null) // A new error!
global.error_last_seen[erroruid] = world.time
last_seen = world.time
if (cooldown < 0)
global.error_cooldown[erroruid]-- // Used to keep track of skip count for this error
global.total_runtimes_skipped++
return // Error is currently silenced, skip handling it
// Handle cooldowns and silencing spammy errors
var/silencing = 0
// We can runtime before config is initialized because BYOND initialize objs/map before a bunch of other stuff happens.
// This is a bunch of workaround code for that. Hooray!
var/configured_error_cooldown
var/configured_error_limit
var/configured_error_silence_time
if(config)
configured_error_cooldown = config.error_cooldown
configured_error_limit = config.error_limit
configured_error_silence_time = config.error_silence_time
else
configured_error_cooldown = initial(config.error_cooldown)
configured_error_limit = initial(config.error_limit)
configured_error_silence_time = initial(config.error_silence_time)
// Each occurrence of a unique error adds to its "cooldown" time...
cooldown = max(0, cooldown - (world.time - last_seen)) + configured_error_cooldown
// ... which is used to silence an error if it occurs too often, too fast
if (cooldown > configured_error_cooldown * configured_error_limit)
cooldown = -1
silencing = 1
spawn (0)
usr = null
sleep(configured_error_silence_time)
var/skipcount = abs(global.error_cooldown[erroruid]) - 1
global.error_cooldown[erroruid] = 0
if (skipcount > 0)
world.log << "\[[time_stamp()]] Skipped [skipcount] runtimes in [e.file],[e.line]: [e]"
error_cache.log_error(e, skip_count = skipcount)
global.error_last_seen[erroruid] = world.time
global.error_cooldown[erroruid] = cooldown
// The detailed error info needs some tweaking to make it look nice
var/list/usrinfo = null
var/locinfo
if (istype(usr))
usrinfo = list(" usr: [datum_info_line(usr)]")
locinfo = atom_loc_line(usr)
if (locinfo)
usrinfo += " usr.loc: [locinfo]"
// The proceeding mess will almost definitely break if error messages are ever changed
// I apologize in advance
var/list/splitlines = splittext(e.desc, "\n")
var/list/desclines = list()
if (splitlines.len > ERROR_USEFUL_LEN) // If there aren't at least three lines, there's no info
for (var/line in splitlines)
if (length(line) < 3 || findtext(line, "source file:") || findtext(line, "usr.loc:"))
continue
if (findtext(line, "usr:"))
if (usrinfo)
desclines.Add(usrinfo)
usrinfo = null
continue // Our usr info is better, replace it
if (copytext(line, 1, 3) != " ")
desclines += (" " + line) // Pad any unpadded lines, so they look pretty
else
desclines += line
if (usrinfo) // If this isn't null, it hasn't been added yet
desclines.Add(usrinfo)
if (silencing)
desclines += " (This error will now be silenced for [configured_error_silence_time / 600] minutes)"
// Now to actually output the error info...
world.log << "\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]"
for (var/line in desclines)
world.log << line
if (global.error_cache)
global.error_cache.log_error(e, desclines)
#endif