A profiler for CounterStrikeSharp servers. Record where your plugins spend time and read it as a speedscope flame graph in the browser.
Install · Usage · Reading it in speedscope · Commands
The sampler (css_prof_*) snapshots every managed thread about 1000 times a second and records allocations while it runs. Overhead is low and constant. It answers the average question: which plugin costs the most, and what is it allocating.
The instrumented profiler (css_prof_calls_*) is the opposite trade-off. It wraps every plugin's event, listener, and command handler in a stopwatch and times each call on its own, so the single call that spiked shows up instead of getting averaged away. Use it when you need the exact cost of one call and its worst case.
Run one or the other, never both. The instrumented hooks would skew a sampled run.
Warning
This is a debugging tool, not something to leave running on a live server. A capture can eat gigabytes of disk (the sampler writes about 1.4 GB an hour), and while it records it adds enough overhead to stutter the game. Load it to measure something, stop the capture, then unload it.
- Download the latest
CSSProfilerzip from the Releases page. - Unzip it into
addons/counterstrikesharp/plugins/. The zip already contains the plugin DLL and itsMicrosoft.Diagnostics.*dependencies. - Load it at server boot (or
css_plugins load CSSProfiler). No load order matters.
The only requirement is that .NET diagnostics IPC is on, which it is by default. It's off only if someone set DOTNET_EnableDiagnostics=0 on the server process.
The flow is the same for both profilers: record, export, open in speedscope.
-
Record while the thing you want to measure is happening. Pass a duration in seconds so it stops itself:
css_prof_start 30 // sampler css_prof_calls_start 30 // instrumented -
Export once it stops. This writes a
.speedscope.jsonfile and logs its full path:css_prof_export css_prof_calls_export -
Open speedscope.app and drag the exported file onto the page (or click Browse and pick it). The file is under:
addons/counterstrikesharp/logs/CSS-Profiler/
You can also print a quick table to the console instead of exporting: css_prof_report or css_prof_calls_report.
Speedscope has three views, switched with the buttons at the top left.
Time Order lays every call out left to right in the order it happened. Time runs along the x-axis, so a wide box is a slow call. This is the view for hunting a specific lag spike: find the fat box, click it, read its exact duration. On an instrumented export, every box is one real invocation.
Left Heavy merges identical stacks and sorts them heaviest first, so the most expensive code is always on the left. Timing order is lost, but it's the fastest way to see what dominates overall.
Sandwich is a sortable table of every function by total and self time. Click a row and speedscope shows its callers above it and its callees below (the "sandwich"), which is how you trace who called an expensive function and what it called in turn.
Sampler:
| Command | What it does |
|---|---|
css_prof_start [seconds] |
Start recording. With seconds it stops itself; without, it runs until css_prof_stop. |
css_prof_stop |
Stop and analyze in the background. |
css_prof_status |
Show state and the last analysis summary. |
css_prof_report [top] [filter] |
Print the analysis tables (default top 25, optional substring filter). |
css_prof_export |
Write the capture as a speedscope profile. |
Instrumented:
| Command | What it does |
|---|---|
css_prof_calls_start [seconds] |
Hook every plugin handler and time each call. With seconds it stops itself. |
css_prof_calls_stop |
Stop and unhook everything. |
css_prof_calls_status |
Show whether recording is active and the call count so far. |
css_prof_calls_report [top] [filter] |
Print per-handler calls, average ms, worst-case ms, and total ms. |
css_prof_calls_export |
Write the recorded calls as an evented speedscope timeline. |
css_prof_calls_reset |
Discard the recorded calls. |
Prefer a seconds argument. An open-ended capture grows fast (about 1.4 GB of trace per hour for the sampler), and analyzing a long one needs several GB of RAM on the same machine as the game server. The instrumented profiler holds every call in memory while it runs. A window of seconds to a few minutes is almost always enough.
Everything lands in addons/counterstrikesharp/logs/CSS-Profiler/:
CSSProfiler_<timestamp>.nettrace— raw sampler trace (also opens in PerfView,dotnet-trace, or Visual Studio).CSSProfiler_<timestamp>.speedscope.json— sampler flame graph.CSSProfiler_calls_<timestamp>.speedscope.json— instrumented per-call timeline.
MIT © btnrv


