diff --git a/code/__DEFINES/tracy.dm b/code/__DEFINES/tracy.dm new file mode 100644 index 00000000000..0a9ab8d68ee --- /dev/null +++ b/code/__DEFINES/tracy.dm @@ -0,0 +1,5 @@ +/// File path used for the "enable tracy next round" functionality +#define TRACY_ENABLE_PATH "data/enable_tracy" + +/// The DLL path for byond-tracy. +#define TRACY_DLL_PATH (world.system_type == MS_WINDOWS ? "prof.dll" : "./libprof.so") diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 3fe456e488e..9772ebff8d1 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -85,9 +85,13 @@ // If this is uncommented, will attempt to load and initialize prof.dll/libprof.so by default. // Even if it's not defined, you can pass "tracy" via -params in order to try to load it. -// We do not ship byond-tracy. Build it yourself here: https://github.com/mafemergency/byond-tracy/ +// We do not ship byond-tracy. Build it yourself here: https://github.com/mafemergency/byond-tracy, +// or the fork which writes profiling data to a file: https://github.com/ParadiseSS13/byond-tracy // #define USE_BYOND_TRACY +// If uncommented, will display info about byond-tracy's status in the MC tab. +// #define MC_TAB_TRACY_INFO + // If defined, we will compile with FULL timer debug info, rather then a limited scope // Be warned, this increases timer creation cost by 5x // #define TIMER_DEBUG diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index e1ed3284441..3648a1b564e 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -762,3 +762,11 @@ default = 100 min_val = 0 max_val = 100 + +/// If admins with +DEBUG can initialize byond-tracy midround. +/datum/config_entry/flag/allow_tracy_start + protection = CONFIG_ENTRY_LOCKED + +/// If admins with +DEBUG can queue byond-tracy to run the next round. +/datum/config_entry/flag/allow_tracy_queue + protection = CONFIG_ENTRY_LOCKED diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index cf158586ce4..9d33e977e2b 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -173,6 +173,24 @@ SUBSYSTEM_DEF(statpanels) list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)), list("","") ) +#if defined(MC_TAB_TRACY_INFO) || defined(SPACEMAN_DMM) + var/static/tracy_dll + var/static/tracy_present + if(isnull(tracy_dll)) + tracy_dll = TRACY_DLL_PATH + tracy_present = fexists(tracy_dll) + if(tracy_present) + if(GLOB.tracy_initialized) + mc_data.Insert(2, list(list("byond-tracy:", "Active (reason: [GLOB.tracy_init_reason || "N/A"])"))) + else if(GLOB.tracy_init_error) + mc_data.Insert(2, list(list("byond-tracy:", "Errored ([GLOB.tracy_init_error])"))) + else if(fexists(TRACY_ENABLE_PATH)) + mc_data.Insert(2, list(list("byond-tracy:", "Queued for next round"))) + else + mc_data.Insert(2, list(list("byond-tracy:", "Inactive"))) + else + mc_data.Insert(2, list(list("byond-tracy:", "[tracy_dll] not present"))) +#endif for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems) mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system)) mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet)) diff --git a/code/game/world.dm b/code/game/world.dm index fe55b3963b5..48ab5b1bd7c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -8,6 +8,13 @@ GLOBAL_VAR(restart_counter) GLOBAL_VAR(tracy_log) +GLOBAL_PROTECT(tracy_log) +GLOBAL_VAR(tracy_initialized) +GLOBAL_PROTECT(tracy_initialized) +GLOBAL_VAR(tracy_init_error) +GLOBAL_PROTECT(tracy_init_error) +GLOBAL_VAR(tracy_init_reason) +GLOBAL_PROTECT(tracy_init_reason) /** * WORLD INITIALIZATION @@ -66,15 +73,29 @@ GLOBAL_VAR(tracy_log) /world/proc/Genesis(tracy_initialized = FALSE) RETURN_TYPE(/datum/controller/master) + GLOB.tracy_initialized = FALSE +#ifndef OPENDREAM + if(!tracy_initialized) #ifdef USE_BYOND_TRACY #warn USE_BYOND_TRACY is enabled - if(!tracy_initialized) + var/should_init_tracy = TRUE + GLOB.tracy_init_reason = "USE_BYOND_TRACY defined" #else - if(!tracy_initialized && (USE_TRACY_PARAMETER in params)) + var/should_init_tracy = FALSE + if(USE_TRACY_PARAMETER in params) + should_init_tracy = TRUE + GLOB.tracy_init_reason = "world.params" + if(fexists(TRACY_ENABLE_PATH)) + GLOB.tracy_init_reason ||= "enabled for round" + SEND_TEXT(world.log, "[TRACY_ENABLE_PATH] exists, initializing byond-tracy!") + should_init_tracy = TRUE + fdel(TRACY_ENABLE_PATH) +#endif + if(should_init_tracy) + init_byond_tracy() + Genesis(tracy_initialized = TRUE) + return #endif - GLOB.tracy_log = init_byond_tracy() - Genesis(tracy_initialized = TRUE) - return Profile(PROFILE_RESTART) Profile(PROFILE_RESTART, type = "sendmaps") @@ -331,6 +352,7 @@ GLOBAL_VAR(tracy_log) if(do_hard_reboot) log_world("World hard rebooted at [time_stamp()]") shutdown_logging() // See comment below. + shutdown_byond_tracy() auxcleanup() TgsEndProcess() return ..() @@ -338,6 +360,7 @@ GLOBAL_VAR(tracy_log) log_world("World rebooted at [time_stamp()]") shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss. + shutdown_byond_tracy() auxcleanup() TgsReboot() // TGS can decide to kill us right here, so it's important to do it last @@ -351,6 +374,7 @@ GLOBAL_VAR(tracy_log) call_ext(debug_server, "auxtools_shutdown")() /world/Del() + shutdown_byond_tracy() auxcleanup() . = ..() @@ -483,21 +507,31 @@ GLOBAL_VAR(tracy_log) DREAMLUAU_SET_EXECUTION_LIMIT_MILLIS(tick_lag * 100) /world/proc/init_byond_tracy() - var/library + if(!fexists(TRACY_DLL_PATH)) + SEND_TEXT(world.log, "Error initializing byond-tracy: [TRACY_DLL_PATH] not found!") + CRASH("Error initializing byond-tracy: [TRACY_DLL_PATH] not found!") - switch (system_type) - if (MS_WINDOWS) - library = "prof.dll" - if (UNIX) - library = "libprof.so" - else - CRASH("Unsupported platform: [system_type]") - - var/init_result = call_ext(library, "init")("block") + var/init_result = call_ext(TRACY_DLL_PATH, "init")("block") if(length(init_result) != 0 && init_result[1] == ".") // if first character is ., then it returned the output filename - return init_result + SEND_TEXT(world.log, "byond-tracy initialized (logfile: [init_result])") + GLOB.tracy_initialized = TRUE + return GLOB.tracy_log = init_result + else if(init_result == "already initialized") // not gonna question it. + GLOB.tracy_initialized = TRUE + SEND_TEXT(world.log, "byond-tracy already initialized ([GLOB.tracy_log ? "logfile: [GLOB.tracy_log]" : "no logfile"])") else if(init_result != "0") + GLOB.tracy_init_error = init_result + SEND_TEXT(world.log, "Error initializing byond-tracy: [init_result]") CRASH("Error initializing byond-tracy: [init_result]") + else + GLOB.tracy_initialized = TRUE + SEND_TEXT(world.log, "byond-tracy initialized (no logfile)") + +/world/proc/shutdown_byond_tracy() + if(GLOB.tracy_initialized) + SEND_TEXT(world.log, "Shutting down byond-tracy") + GLOB.tracy_initialized = FALSE + call_ext(TRACY_DLL_PATH, "destroy")() /world/proc/init_debugger() var/dll = GetConfig("env", "AUXTOOLS_DEBUG_DLL") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index b6c5e10ca1d..13f1995c9ba 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -852,3 +852,42 @@ ADMIN_VERB(check_missing_sprites, R_DEBUG, "Debug Worn Item Sprites", "We're can actual_file_name = 'icons/mob/clothing/belt_mirror.dmi' if(!(sprite.icon_state in icon_states(actual_file_name))) to_chat(user, span_warning("ERROR sprites for [sprite.type]. Suit Storage slot."), confidential = TRUE) + +#ifndef OPENDREAM +ADMIN_VERB(start_tracy, R_DEBUG, "Run Tracy Now", "Start running the byond-tracy profiler immediately", ADMIN_CATEGORY_DEBUG) + if(GLOB.tracy_initialized) + to_chat(user, span_warning("byond-tracy is already running!"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + return + else if(GLOB.tracy_init_error) + to_chat(user, span_danger("byond-tracy failed to initialize during an earlier attempt: [GLOB.tracy_init_error]"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + return + message_admins(span_adminnotice("[key_name_admin(user)] is trying to start the byond-tracy profiler.")) + log_admin("[key_name(user)] is trying to start the byond-tracy profiler.") + GLOB.tracy_initialized = FALSE + GLOB.tracy_init_reason = "[user.ckey]" + world.init_byond_tracy() + if(GLOB.tracy_init_error) + to_chat(user, span_danger("byond-tracy failed to initialize: [GLOB.tracy_init_error]"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + message_admins(span_adminnotice("[key_name_admin(user)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])")) + log_admin("[key_name(user)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])") + return + to_chat(user, span_notice("byond-tracy successfully started!"), avoid_highlighting = TRUE, type = MESSAGE_TYPE_DEBUG, confidential = TRUE) + message_admins(span_adminnotice("[key_name_admin(user)] started the byond-tracy profiler.")) + log_admin("[key_name(user)] started the byond-tracy profiler.") + if(GLOB.tracy_log) + rustg_file_write("[GLOB.tracy_log]", "[GLOB.log_directory]/tracy.loc") + +ADMIN_VERB_CUSTOM_EXIST_CHECK(start_tracy) + return CONFIG_GET(flag/allow_tracy_start) && fexists(TRACY_DLL_PATH) + +ADMIN_VERB(queue_tracy, R_DEBUG, "Toggle Tracy Next Round", "Toggle running the byond-tracy profiler next round", ADMIN_CATEGORY_DEBUG) + if(fexists(TRACY_ENABLE_PATH)) + fdel(TRACY_ENABLE_PATH) + else + rustg_file_write("[user.ckey]", TRACY_ENABLE_PATH) + message_admins(span_adminnotice("[key_name_admin(user)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.")) + log_admin("[key_name(user)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.") + +ADMIN_VERB_CUSTOM_EXIST_CHECK(queue_tracy) + return CONFIG_GET(flag/allow_tracy_queue) && fexists(TRACY_DLL_PATH) +#endif diff --git a/config/config.txt b/config/config.txt index 1e5f5cb10e4..4d11dcd2ea2 100644 --- a/config/config.txt +++ b/config/config.txt @@ -609,3 +609,9 @@ UPLOAD_LIMIT 524288 ## Restricts admin client uploads to the server, defined in bytes, default is 5MB UPLOAD_LIMIT_ADMIN 5242880 + +## Uncomment to allow admins with +DEBUG to start the byond-tracy profiler during the round. +#ALLOW_TRACY_START + +## Uncomment to allow admins with +DEBUG to queue the next round to run the byond-tracy profiler. +#ALLOW_TRACY_QUEUE diff --git a/tgstation.dme b/tgstation.dme index d2d4aca3e0d..606ddb3453d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -244,6 +244,7 @@ #include "code\__DEFINES\time.dm" #include "code\__DEFINES\tools.dm" #include "code\__DEFINES\toys.dm" +#include "code\__DEFINES\tracy.dm" #include "code\__DEFINES\trader.dm" #include "code\__DEFINES\transport.dm" #include "code\__DEFINES\tts.dm"