diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 2e5950c6cfb..4beb0c2c628 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -243,7 +243,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/profiler_start,
/datum/admins/proc/force_initialize_weather,
/datum/admins/proc/force_weather_state,
- /datum/admins/proc/force_kill_weather
+ /datum/admins/proc/force_kill_weather,
+ /client/proc/check_timer_sources
))
GLOBAL_LIST_INIT(admin_verbs_paranoid_debug, list(
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index dc5682411f3..84493bc85f3 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -515,3 +515,63 @@
to_chat(src, SPAN_INFO("You can now right click to use inspect on browsers."))
winset(src, null, list("browser-options" = "+devtools"))
winset(src, null, list("browser-options" = "+find"))
+
+/// A debug verb to check the sources of currently running timers
+/client/proc/check_timer_sources()
+ set category = "Debug"
+ set name = "Check Timer Sources"
+ set desc = "Checks the sources of the running timers"
+ if (!check_rights(R_DEBUG))
+ return
+
+ var/bucket_list_output = generate_timer_source_output(SStimer.bucket_list)
+ var/second_queue = generate_timer_source_output(SStimer.second_queue)
+ var/html_body = {"
+
bucket_list
+ [bucket_list_output]
+
+ second_queue
+ [second_queue]
+ "}
+ usr << browse(HTML_SKELETON(html_body), "window=check_timer_sources;size=700x700")
+
+/proc/generate_timer_source_output(list/datum/timedevent/events)
+ var/list/per_source = list()
+
+ // Collate all events and figure out what sources are creating the most
+ for (var/_event in events)
+ if (!_event)
+ continue
+ var/datum/timedevent/event = _event
+
+ do
+ if (event.source)
+ if (per_source[event.source] == null)
+ per_source[event.source] = 1
+ else
+ per_source[event.source] += 1
+ event = event.next
+ while (event && event != _event)
+
+ // Now, sort them in order
+ var/list/sorted = list()
+ for (var/source in per_source)
+ sorted += list(list("source" = source, "count" = per_source[source]))
+ sorted = sortTim(sorted, .proc/cmp_timer_data)
+
+ // Now that everything is sorted, compile them into an HTML output
+ var/output = ""
+
+ for (var/_timer_data in sorted)
+ var/list/timer_data = _timer_data
+ output += {"
+ | [timer_data["source"]] |
+ [timer_data["count"]] |
+
"}
+
+ output += "
"
+
+ return output
+
+/proc/cmp_timer_data(list/a, list/b)
+ return b["count"] - a["count"]
diff --git a/html/changelogs/johnwildkins-timer.yml b/html/changelogs/johnwildkins-timer.yml
new file mode 100644
index 00000000000..841b813b82b
--- /dev/null
+++ b/html/changelogs/johnwildkins-timer.yml
@@ -0,0 +1,13 @@
+# Your name.
+author: JohnWildkins
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
+# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Added a debug command for devs/admins to list timer sources."