Files
Bubberstation/code/modules/admin/view_variables/reference_tracking.dm
LemonInTheDark 1aa42a3188 Ref Tracking: Revengance (#57728)
* Ref Tracking: Revengance

Fixes reference tracking ignoring self references due to a poorly thought out tick checking system.
Fixes reference tracking ignoring the contents of assoc lists
Makes the reference tracking printouts actually describe what list the ref is in, rather then just saying "list"

Adds REFERENCE_TRACKING_DEBUG, a define which toggles tracking info for the ref tracking procs, which allows for
oversight on how the proc is working

Allows for direct calls of qdel_and_find_ref_if_fail(), makes it use ref rather then REF(), fixing it breaking
for mobs. (Ditto for the qdel hint which does the same thing)

Moves REAGENTS_TESTING out of the reftracking define block
Makes unit tests define REFERENCE_TRACKING, REFERENCE_TRACKING_DEBUG, and FIND_REF_NO_CHECK_TICK

Adds a unit test that sanity checks the reference finder proc
2021-03-25 22:01:23 -07:00

148 lines
4.6 KiB
Plaintext

#ifdef REFERENCE_TRACKING
/datum/verb/find_refs()
set category = "Debug"
set name = "Find References"
set src in world
find_references(FALSE)
/datum/proc/find_references(skip_alert)
running_find_references = type
if(usr?.client)
if(usr.client.running_find_references)
testing("CANCELLED search for references to a [usr.client.running_find_references].")
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
return
if(!skip_alert && alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
running_find_references = null
return
//this keeps the garbage collector from failing to collect objects being searched for in here
SSgarbage.can_fire = FALSE
if(usr?.client)
usr.client.running_find_references = type
testing("Beginning search for references to a [type].")
var/starting_time = world.time
DoSearchVar(GLOB, "GLOB") //globals
for(var/datum/thing in world) //atoms (don't beleive its lies)
DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time)
for(var/datum/thing) //datums
DoSearchVar(thing, "Datums -> [thing.type]", search_time = starting_time)
for(var/client/thing) //clients
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
testing("Completed search for references to a [type].")
if(usr?.client)
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
set src in world
qdel(src, TRUE) //force a qdel
if(!running_find_references)
find_references(TRUE)
/datum/verb/qdel_then_if_fail_find_references()
set category = "Debug"
set name = "qdel() then Find References if GC failure"
set src in world
qdel_and_find_ref_if_fail(src, TRUE)
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time)
#ifdef REFERENCE_TRACKING_DEBUG
if(!found_refs)
found_refs = list()
#endif
if(usr?.client && !usr.client.running_find_references)
return
if(!recursive_limit)
testing("Recursion limit reached. [container_name]")
return
if(istype(potential_container, /datum))
var/datum/datum_container = potential_container
if(datum_container.last_find_references == search_time)
return
datum_container.last_find_references = search_time
var/list/vars_list = datum_container.vars
for(var/varname in vars_list)
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
continue
var/variable = vars_list[varname]
if(variable == src)
#ifdef REFERENCE_TRACKING_DEBUG
found_refs[varname] = TRUE
#endif
testing("Found [type] \ref[src] in [datum_container.type]'s \ref[datum_container] [varname] var. [container_name]")
else if(islist(variable))
DoSearchVar(variable, "[container_name] \ref[datum_container] -> [varname] (list)", recursive_limit - 1, search_time)
else if(islist(potential_container))
var/normal = IS_NORMAL_LIST(potential_container)
for(var/element_in_list in potential_container)
//Check normal entrys
if(element_in_list == src)
#ifdef REFERENCE_TRACKING_DEBUG
found_refs[potential_container] = TRUE
#endif
testing("Found [type] \ref[src] in list [container_name].")
//Check assoc entrys
else if(element_in_list && !isnum(element_in_list) && normal && potential_container[element_in_list] == src)
#ifdef REFERENCE_TRACKING_DEBUG
found_refs[potential_container] = TRUE
#endif
testing("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
//Check normal sublists
else if(islist(element_in_list))
DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
//Check assoc sublists
else if(element_in_list && !isnum(element_in_list) && normal && islist(potential_container[element_in_list]))
DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [potential_container[element_in_list]] (list)", recursive_limit - 1, search_time)
#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
thing_to_del.qdel_and_find_ref_if_fail(force)
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
SSgarbage.reference_find_on_fail["\ref[src]"] = TRUE
qdel(src, force)
#endif