Skip to content

Commit

Permalink
MC Diagnostic Mode (#10059)
Browse files Browse the repository at this point in the history
* MC Diagnostic Mode

* Update master.dm

* Changes the percentage to a millisecond value instead

* Update master.dm

* Update mob_stat.dm
  • Loading branch information
PowerfulBacon authored Jan 13, 2024
1 parent 85b1445 commit e56b1d7
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 1 deletion.
9 changes: 8 additions & 1 deletion code/controllers/configuration/entries/general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@
min_val = 0 //oranges warned us
integer = FALSE

/datum/config_entry/flag/mc_diagnostics

/datum/config_entry/flag/mc_diagnostics/ValidateAndSet(str_val)
. = ..()
if (!.)
return FALSE
Master.diagnostic_mode = config_entry_value

/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system
protection = CONFIG_ENTRY_LOCKED

Expand Down Expand Up @@ -615,5 +623,4 @@

/datum/config_entry/flag/enable_mrat


/datum/config_entry/string/discord_ooc_tag
48 changes: 48 additions & 0 deletions code/controllers/master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,21 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
///used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING

var/diagnostic_mode = FALSE

var/list/queued_ticks = list()
/// A circular queue for diagnostic purposes
var/list/previous_ticks[10]
/// The current head of the circular queue
var/circular_queue_head = 1

/datum/controller/master/New()
if(!config)
config = new

for (var/i in 1 to length(previous_ticks))
previous_ticks[i] = new /datum/mc_tick

// Highlander-style: there can only be one! Kill off the old and replace it with the new.

if(!random_seed)
Expand Down Expand Up @@ -502,6 +514,17 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
var/tick_usage

var/datum/mc_tick/diagnostic_tick
if (diagnostic_mode)
diagnostic_tick = new()
diagnostic_tick.tick_number = world.time
queued_ticks += diagnostic_tick
previous_ticks[circular_queue_head = (circular_queue_head % length(previous_ticks)) + 1] = diagnostic_tick
else
diagnostic_tick = previous_ticks[circular_queue_head = (circular_queue_head % length(previous_ticks)) + 1]
diagnostic_tick.fired_subsystems.len = 0
diagnostic_tick.tick_number = world.time

//keep running while we have stuff to run and we haven't gone over a tick
// this is so subsystems paused eariler can use tick time that later subsystems never used
while (ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
Expand Down Expand Up @@ -546,6 +569,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/state = queue_node.ignite(queue_node_paused)
tick_usage = TICK_USAGE - tick_usage

if (diagnostic_tick.fired_subsystems[queue_node])
diagnostic_tick.fired_subsystems[queue_node] = diagnostic_tick.fired_subsystems[queue_node] + tick_usage
else
diagnostic_tick.fired_subsystems[queue_node] = tick_usage

if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
Expand Down Expand Up @@ -679,3 +707,23 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
for (var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
SS.OnConfigLoad()

/**
* Diagnostic queue information
*/

/datum/mc_tick
var/tick_number = 0
/// Assoc list containing a list of the subsystems that were fired
/// along with how much time thye used this tick
var/list/fired_subsystems = list()

/datum/mc_tick/proc/get_stat_text()
var/list/output = list()
var/list/tickers = list()
for (var/datum/controller/subsystem/ss as() in fired_subsystems)
if (ss.flags & SS_TICKER)
tickers += "([ss.name]: [TICK_DELTA_TO_MS(fired_subsystems[ss])]ms)"
else
output += "([ss.name]: [TICK_DELTA_TO_MS(fired_subsystems[ss])]ms)"
return "Systems: [jointext(output, " | ")] ######## Tickers: [jointext(tickers, " | ")]"
18 changes: 18 additions & 0 deletions code/controllers/subsystem/metrics.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ SUBSYSTEM_DEF(metrics)
out["harddel_count"] = length(GLOB.world_qdel_log)
out["round_id"] = text2num(GLOB.round_id) // This is so we can filter the metrics by a single round ID

if (Master.diagnostic_mode)
var/list/diagnostic_report = list()
for (var/datum/mc_tick/diag_tick in Master.queued_ticks)
var/list/current_tick_info = list()
for (var/datum/controller/subsystem/ss in diag_tick.fired_subsystems)
current_tick_info["[ss.ss_id]"] = diag_tick.fired_subsystems[ss]
diagnostic_report["[diag_tick.tick_number]"] = current_tick_info
out["master_controller"] = list(
"diagnostic_mode" = 1,
"diagnostic_report" = diagnostic_report
)
Master.queued_ticks.Cut()
else
out["master_controller"] = list(
"diagnostic_mode" = 0,
)

var/server_name = CONFIG_GET(string/serversqlname)
if(server_name)
out["server_name"] = server_name
Expand All @@ -56,6 +73,7 @@ SUBSYSTEM_DEF(metrics)
ss_data[SS.ss_id] = SS.get_metrics()

out["subsystems"] = ss_data

// And send it all
return json_encode(out)

Expand Down
14 changes: 14 additions & 0 deletions code/modules/mob/mob_stat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,20 @@
tab_data["divider_2"] = GENERATE_STAT_DIVIDER
for(var/datum/controller/subsystem/SS in Master.subsystems)
tab_data += SS.stat_entry()
tab_data["divider_3"] = GENERATE_STAT_DIVIDER
var/datum/controller/subsystem/queue_node = Master.last_type_processed
if (queue_node)
tab_data["Last Processed:"] = GENERATE_STAT_TEXT("[queue_node.name] \[FI: [queue_node.next_fire - world.time]ds\] [(queue_node.flags & SS_TICKER) ? " (Ticker)" : ""][(queue_node.flags & SS_BACKGROUND) ? " (Background)" : ""][(queue_node.flags & SS_KEEP_TIMING) ? " (Keep Timing)" : ""]")
queue_node = Master.queue_head
var/i = 0
while (queue_node)
tab_data["Queue [i++]:"] = GENERATE_STAT_TEXT("[queue_node.name] \[FI: [queue_node.next_fire - world.time]ds\] [(queue_node.flags & SS_TICKER) ? " (Ticker)" : ""][(queue_node.flags & SS_BACKGROUND) ? " (Background)" : ""][(queue_node.flags & SS_KEEP_TIMING) ? " (Keep Timing)" : ""]")
queue_node = queue_node.queue_next
tab_data["divider_4"] = GENERATE_STAT_DIVIDER
for (var/j in 1 to length(Master.previous_ticks))
var/datum/mc_tick/tick = Master.previous_ticks[(Master.circular_queue_head - j + length(Master.previous_ticks)) % length(Master.previous_ticks) + 1]
tab_data["Tick [tick.tick_number]"] = GENERATE_STAT_TEXT(tick.get_stat_text())
tab_data["divider_5"] = GENERATE_STAT_DIVIDER
tab_data += GLOB.cameranet.stat_entry()
return tab_data

Expand Down
4 changes: 4 additions & 0 deletions config/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ ALLOW_HOLIDAYS
##This is currently a testing optimized setting. A good value for production would be 98.
TICK_LIMIT_MC_INIT 500

## Uncomment to enable MC diagnostics, the MC will record all information about prior ticks and then report them
## to SSmetrics.
# MC_DIAGNOSTICS

##Defines the ticklag for the world. Ticklag is the amount of time between game ticks (aka byond ticks) (in 1/10ths of a second).
## This also controls the client network update rate, as well as the default client fps
TICKLAG 0.5
Expand Down

0 comments on commit e56b1d7

Please sign in to comment.