Oxide Sync is live: your plugins install and update themselves →
Plugin Profiler Timed cover art
FreeOther

Plugin Profiler Timed

by Base Inspector · v1.0.0 · updated Nov 8, 2026

Profiles plugin hook time only during a timed window started via console. Idle by default (no overhead).

No dependencies: works on its own.

Installs itself with Oxide Sync. Add it to a linked server from My servers. Every future update included. How it works

  • Full C# source
  • Free updates
  • Signed releases
  • Oxide / uMod compatible

PluginProfilerTimed — Documentation

Plugin: PluginProfilerTimed
Author: Base Inspector
Version: 1.1.0
Purpose: Profile Oxide plugin hook execution time during a timed window, with optional engine-side diagnostics sampling (FPS estimate, GC, managed memory, entity counts + category breakdown).


What this plugin does

PluginProfilerTimed is a lightweight, “run it when you need it” profiler for Rust servers running Oxide/uMod.

When you start a session (e.g., pp.top 30), the plugin:

  1. Patches Oxide.Core.Plugins.Plugin.CallHook via Harmony.
  2. Measures how long each hook invocation takes (in milliseconds).
  3. Aggregates results by:
    • Plugin name
    • Hook name
    • Calls, Total time, Max time, and computed Avg time
  4. Optionally samples diagnostic snapshots every N seconds, including:
    • FPS estimate (Unity timing-based)
    • Managed memory (GC)
    • GC collection counts (Gen0/1/2 deltas)
    • Total server entities (fast count)
    • An incremental entity scan with category counts (building blocks, turrets, deployables, etc.)

Sessions auto-stop after the time window you specify (or you can stop manually).


Primary benefits

1) Quickly identify “top offenders”

You can get a ranked list of plugins consuming the most total hook time during the window, plus the heaviest hooks inside those plugins.

This is ideal when:

  • Server FPS tanks intermittently
  • GC spikes occur
  • You suspect a plugin is hammering hooks (e.g., OnEntityKill, OnTick, OnItemAddedToContainer, etc.)
  • You want evidence before refactoring/optimizing plugins

2) Correlate plugin time with engine symptoms

The diagnostics summary helps you correlate plugin activity with:

  • FPS dips
  • Increasing managed memory
  • GC collections rising during the sample window
  • Entity growth / abnormal categories (e.g., projectile floods, excessive deployables, turrets)

3) Minimal “always-on” overhead

The profiler is idle until started. It only patches and collects data during a session window you control.


How hook timing is measured

This plugin uses Harmony to patch Plugin.CallHook:

  • Prefix: records a timestamp when _running == true
  • Postfix: computes elapsed time and records it under:
    • plugin name (the plugin receiving the hook)
    • hook name
    • measured ms

Metrics tracked per hook:

  • Calls
  • TotalMs
  • MaxMs

Diagnostics sampling (engine-side indicators)

During a session, diagnostics are captured every diagInterval seconds:

Captured fields

Per sample:

  • t: seconds since session start
  • fps~: estimated FPS (1 / Time.unscaledDeltaTime) — trend indicator, not a perfect metric
  • mem: managed memory from GC.GetTotalMemory(false)
  • GC(0,1,2): collection deltas since session start
  • entities:
    • reportedTotal: fast count (BaseNetworkable.serverEntities?.Count)
    • counted: counted during the incremental scan
  • Category breakdown from incremental scan:
    • Building blocks
    • Deployables (heuristic category)
    • Storage
    • Turrets
    • Traps
    • Vehicles
    • NPCs
    • Animals
    • IO / Industrial
    • Dropped items
    • Projectiles

Why the scan is incremental

Enumerating all server entities can hitch on large servers. This plugin scans in batches (_scanBatchSize = 2500) and continues on subsequent ticks to reduce spike risk.


Commands

These are console commands ([ConsoleCommand]). That means they’re intended for:

  • Server console
  • RCON console
  • Admins with console access

Players cannot run these via chat unless you build a wrapper command.

pp.top <seconds> [diagIntervalSeconds]

Runs a timed profiling session and prints the results when complete (no dump file by default).

Defaults

  • seconds: 30
  • diagIntervalSeconds: 5

Examples

  • pp.top
    Runs 30s, diag every 5s
  • pp.top 45
    Runs 45s, diag every 5s
  • pp.top 30 10
    Runs 30s, diag every 10s

pp.dump <seconds> [diagIntervalSeconds]

Runs a timed profiling session and writes a dump file at the end.

Defaults

  • seconds: 60
  • diagIntervalSeconds: 5

Examples

  • pp.dump
    Runs 60s and writes dump
  • pp.dump 120 5
    Runs 120s and writes dump

pp.diag <seconds> [diagIntervalSeconds]

Runs a timed session and always writes a dump file, emphasizing diagnostics.

Defaults

  • seconds: 120
  • diagIntervalSeconds: 5

Examples

  • pp.diag
    Runs 120s and writes dump
  • pp.diag 180 10
    Runs 180s, diag every 10s, writes dump

pp.stop

Stops the current session early (no dump file unless you used pp.diag which forces a dump on completion).

Example

  • pp.stop

pp.hooks <PluginName>

After a session ends, prints the top hooks for a specific plugin from the last session.

Example

  • pp.hooks Loadouts
  • pp.hooks BetterChat

Session limits & clamping behavior

To keep profiling safe and predictable, the plugin clamps values:

Duration (seconds)

  • If <= 0:
    defaults to 30 (or 60 for dump, forced for diag)
  • Max: 600 seconds (10 minutes)

Diagnostics interval (diagIntervalSeconds)

  • If <= 0: default 5
  • Min: 2
  • Max: 60

Output overview

When a session ends, you get:

1) Session header

Includes:

  • reason ended (timer elapsed / manual stop / unload)
  • elapsed seconds
  • number of plugins tracked
  • number of diag samples

2) “Top plugins by total hook time”

Shows up to 12 plugins, ordered by total hook time over the window.

For each plugin:

  • total time
  • calls
  • average time per hook call
  • top 5 hooks by total time for that plugin (hook-level totals + max)

3) Diagnostics summary

Includes:

  • fps~ min/avg/max (estimate)
  • managed memory start/end/delta
  • GC collection deltas
  • entity counts + category breakdown
  • “worst sample” line for lowest fps snapshot

4) Dump file (when using pp.dump or pp.diag)

A text file is written to:

  • oxide/logs/PluginProfilerTimed_YYYYMMDD_HHMMSS_<seconds>s.txt

The dump contains:

  • full plugin totals + all hooks (not just top 5)
  • all diagnostic samples (one per line)
  • diagnostic summary

Permissions & access control

This plugin does not register or check Oxide permissions.

Access is effectively controlled by:

  • who can run console/RCON commands

If you want explicit Oxide permission control (recommended if you add chat commands later), you’d add something like:

  • permission.RegisterPermission("pluginprofilertimed.use", this);
  • validate in command handlers

But as-is, no permissions are defined.


Practical usage patterns

“Find the culprit plugin”

  1. Trigger the lag/fps issue (or wait for it).
  2. Run:
    • pp.top 30 5
  3. When it stops, look at:
    • top plugin by total ms
    • top hook(s) inside that plugin
  4. Then run:
    • pp.hooks <ThatPluginName>
  5. Repeat with a longer window if needed:
    • pp.dump 120 5

“Capture evidence for later analysis”

Use:

  • pp.dump 180 5

Then archive the dump file and compare runs after you change:

  • plugin versions
  • plugin configs
  • entity cleanup rules
  • map wipes

“Diagnose entity/GC problems”

Use:

  • pp.diag 300 10

You’ll get:

  • a dump file automatically
  • a better story about memory/GC and entity category distribution over time

Performance & safety notes

  • Hook timing adds overhead because every CallHook is measured while running.
    • Keep windows short (30–120s) unless necessary.
  • Diagnostics entity scanning is incremental to reduce hitching.
    • If you still see hitches, consider lowering _scanBatchSize in the plugin code.
  • FPS is an estimate and can be noisy; use it as a trend signal, not a strict benchmark.
  • This profiler is best used in combination with:
    • Carbon profiler dumps (if you run Carbon)
    • server FPS graphs
    • entity counts / kill metrics
    • plugin-specific debug logs

Installation

  1. Save the plugin as:
    • oxide/plugins/PluginProfilerTimed.cs
  2. Ensure your server supports Harmony patching (this plugin references HarmonyLib).
  3. Reload:
    • oxide.reload PluginProfilerTimed
  4. Verify console shows:
    • “Loaded. Idle until you run: pp.top …”

Troubleshooting

“Profiler could not patch CallHook”

This means the plugin couldn’t find Oxide.Core.Plugins.Plugin.CallHook or Harmony patching failed.

Check:

  • Oxide/uMod version compatibility
  • Harmony availability in your server environment
  • server logs for the printed error:
    • “Failed to find Oxide.Core.Plugins.Plugin.CallHook. Cannot profile.”

“No plugin data collected”

If the server was idle, or hooks were not firing during the window, totals may be empty.

Try:

  • run during a known busy time (raids, large PvP, wipe events)
  • increase duration:
    • pp.top 90 5

Command quick reference

CommandWhat it doesWrites dump?
pp.top <sec> [diag]Profile and print summaryNo
pp.dump <sec> [diag]Profile, print summary, save dumpYes
pp.diag <sec> [diag]Diagnostics-focused run, always saves dumpYes (forced)
pp.stopStop earlyNo
pp.hooks <PluginName>Show top hooks for plugin from last sessionN/A

Example output snippet (what to look for)

You’ll typically see lines like:

  • SomePlugin | total=1234.56ms | calls=98765 | avg=0.012500ms
  • - OnTick | total=900.00ms | calls=60000 | max=4.20ms

Red flags

  • Extremely high call counts on frequent hooks (OnTick, OnServerTick, OnPlayerTick)
  • High max times (spiky hitches) on hooks like OnEntityTakeDamage
  • Large GC deltas + rising managed memory during the same window
  • Abnormal entity categories (projectile floods, dropped items explosion, etc.)

Notes for future enhancements (optional)

If you decide to extend this plugin later, common upgrades include:

  • Add chat commands for admins
  • Add Oxide permission checks
  • Allow toggling diagnostics on/off per session
  • Export CSV/JSON for automated analysis
  • Add filters (only track specific hooks/plugins)

You may also like