Profile how long a function takes per call (MUTATES STATE via hookfunction)
trace-call-durationsProfile a Luau function by hooking it to record call count, total, average, min, and max execution time, then fetch live stats or stop to restore the original.
Instructions
WRITES LIVE GAME STATE — INSTALLS A PERSISTENT GLOBAL HOOK. Per-function profiler: hook a target so that every invocation is timed with os.clock(), accumulating call count plus total/min/max time, then read the aggregated stats, then restore the original. This is the fastest way to answer 'how expensive is this function and how often does it run?' — ideal for finding the hot path in an anticheat loop, a render step, or a remote handler. Unlike count-function-calls (count only) it also measures duration; unlike hook-and-log-function it stores only aggregates (no per-call args), so it is cheap enough for hot paths. WORKFLOW (stateful — survives across tool calls via getgenv().__mcp_callTimings, keyed by functionPath): 1. action='start' with functionPath — resolves the target, captures the original, installs a timing wrapper that transparently calls the original and records elapsed time. Returns { started, key }. 2. action='fetch' with the same functionPath — returns { count, totalMs, avgMs, minMs, maxMs } so far WITHOUT stopping. Poll to watch live. 3. action='stop' with the same functionPath — restores the original and clears the entry. Returns final stats. CAVEATS: the hook is GLOBAL and PERSISTS until you stop it (or the client restarts), adds (small) timing overhead on every call, and a live function hook CAN TRIP ANTICHEAT — always stop when done. The original is called through real-time (its return values are passed back unchanged); timing/aggregation is pcall-isolated. Requires hookfunction, newcclosure, and getgenv; restoration uses hookfunction(target, original) with a restorefunction fallback. Returns { error } if a capability is missing, the target cannot be resolved, or there is no active profile for fetch/stop. Signature: { action: "start" | "fetch" | "stop", functionPath: string?, threadContext: number? }. Phase: act; cost=medium; idempotency=contextual-write. Requires: active-client, resolved-target, explicit-mutation-approval. Produces: bounded-event-snapshot, operation-receipt. Verify with: assert-state. Safety: MUTATING; writes live game/client state. On failure: inspect tool-schema for exact fields, defaults, constraints, and an invocation example.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | 'start' installs the timing wrapper on functionPath; 'fetch' returns the aggregated timing stats so far (hook stays live); 'stop' restores the original function and clears the stats. Use the SAME functionPath for all three so they address the same registry entry. | |
| functionPath | No | Luau expression resolving to the function to profile, e.g. 'getsenv(game.Players.LocalPlayer.PlayerScripts.Main).update' or 'getrawmetatable(game).__namecall'. Evaluated as `return <functionPath>` and must resolve to a function. REQUIRED for 'start'. For 'fetch'/'stop' it is the registry key identifying which running profile to act on, so it must match the string used at start. | |
| threadContext | No | Optional Roblox thread identity for this call; omit it to use the server default. |