-
Notifications
You must be signed in to change notification settings - Fork 0
Observed Section Attribute
A method-level attribute that registers and instruments a profiling section in a single declaration. It is the imperative complement to profiling.xml: where profiling.xml targets methods declaratively from XML, [ObservedSection] lives next to the method in source code. Both produce identical runtime behavior.
The attribute is discovered once at bootstrap by scanning every loaded mod assembly. No manual RegisterSection call is required.
using Cryptiklemur.RimObs.Api;
public class MyMod
{
// Name auto-derived from type + method name, then packageId-prefixed.
[ObservedSection]
public static void DoWork()
{
// body
}
// Explicit bare name; still packageId-prefixed at registration.
[ObservedSection("heavy_work")]
public static void NamedSection() { }
// Named with an optional subsystem tag for dashboard grouping.
[ObservedSection(Subsystem = "pawns.work")]
public static void TaggedSection() { }
// Explicit name and subsystem together.
[ObservedSection("patrol_tick", Subsystem = "pawns.work")]
public static void PatrolTick() { }
}| Usage | Resulting bare name |
|---|---|
[ObservedSection] |
TypeFullName.MethodName (dots in type name become underscores, e.g. MyMod_DoWork) |
[ObservedSection("name")] |
"name" as supplied |
[ObservedSection(Subsystem = "...")] |
TypeFullName.MethodName with subsystem tag applied |
In every case the bare name is automatically prefixed with the mod's packageId at registration (PRD §35.69). A method DoWork on MyMod in mod cryptiklemur.example becomes cryptiklemur.example.MyMod.DoWork on the wire and in the dashboard.
Name validation follows the same rules as Obs.Profile.RegisterSection: lowercase ASCII letters, digits, and underscores only; must start with a letter. Auto-derived names that fail validation are rejected with a warning.
During RimObsMod initialization, ObservedSectionScanner.Scan is called for every (packageId, assemblies) tuple from LoadedModManager.RunningModsListForReading. For each assembly it:
- Skips the
RimObsassembly itself. - Walks every type and method looking for
[ObservedSection]. - Skips methods that cannot be Harmony-patched (see Limitations) and logs a warning for each.
- Registers a section via
SectionCatalog-- equivalent to callingObs.Profile.RegisterSectionby hand. - Installs a Harmony IL transpiler at
Priority.Lowthat wraps the method body inProfiler.Start/Profiler.Stop.
Discovery runs once per session. The result is identical to a profiling.xml entry or a hand-written RegisterSection + Measure pair: the method is wrapped with a zero-allocation timing path.
Attribute scanning is controlled by library.attributes.enabled in Configuration (default: true). Setting it to false disables all [ObservedSection] discovery at startup. Individual sections cannot be toggled; the flag is all-or-nothing for the attribute scanner.
profiling.xml and the Profile API are unaffected by this flag.
| Style | Source location | Rebuild required | Targets code you own | Targets third-party code |
|---|---|---|---|---|
[ObservedSection] |
Next to the method | Yes | Yes | No |
| profiling.xml | Mod's About/ folder |
No | Yes | Yes |
| Profile API | Call site | Yes | Yes | No |
Use [ObservedSection] for methods you own and want to keep annotated in source. Use profiling.xml to instrument vanilla RimWorld or third-party mod methods without modifying their assemblies. Use the Profile API when you need fine-grained control: sub-range measurement, conditional instrumentation, or explicit Start/Stop pairs.
All three styles go through the same IL transpiler and share the same hot-path discipline rules: no allocation, exception-safe start/stop pairing, and near-zero cost when the profiler is disabled.
The following method kinds cannot be Harmony-patched. The scanner skips them and emits a [RimObs] warning in Player.log:
- Abstract methods
- Methods with generic type parameters (
void Foo<T>()) - Compiler-generated async state machines (
async Task Foo()) - Iterator state machines (
IEnumerable Foo()usingyield return) - Constructors and property accessors (use the Profile API for these)
If you see a warning for a method you want to time, use an explicit Obs.Profile.RegisterSection + Obs.Profile.Measure pair from the Profile API instead.
The attribute is read once at bootstrap. Changing [ObservedSection] annotations in source requires a rebuild and a game restart. Live patch management without a restart is a separate feature -- see the Instrumentation page in the dashboard tour and the PRD supersession note in the project rules.
-
Profile API - C# API for timed sections, including
Start/Stopand sub-range measurement - profiling.xml - declarative instrumentation for code you do not own
- Metrics API - counters, gauges, and histograms
- Hot-Path Discipline - allocation and performance rules that apply to all patched methods
-
Configuration -
library.attributes.enabledand other library settings
Players
Mod authors
- Quickstart
- Profile API
- Metrics API
- profiling.xml
- [ObservedSection] attribute
- Hot-path discipline
- Troubleshooting
Tool authors
Collector
Dashboard
Reference