Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MC Diagnostic Mode #10059

Merged
merged 6 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
51 changes: 51 additions & 0 deletions code/controllers/master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,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 @@ -489,6 +501,18 @@ 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
diagnostic_tick.total = 0

//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 @@ -550,6 +574,12 @@ 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
diagnostic_tick.total += tick_usage

if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
Expand Down Expand Up @@ -690,3 +720,24 @@ 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
var/total = 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]: [100 * fired_subsystems[ss]/total]%)"
else
output += "([ss.name]: [100 * fired_subsystems[ss]/total]%)"
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_NO_TICK_CHECK) ? " (No Tick Check)" : ""][(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_NO_TICK_CHECK) ? " (No Tick Check)" : ""][(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
Loading