Documentation
¶
Overview ¶
Package wcprof implements cheap wall-clock profiling for the engine.
It records operation intervals, wait (blocked-on) intervals, and link events into an in-memory buffer, for offline analysis of where wall-clock time goes and which operation classes are bottlenecks. It is intentionally not OTel: events are fixed-size structs appended to sharded in-memory buffers, with all strings interned. The expensive work (graph reconstruction, counterfactual simulation) happens offline after dumping the buffer via the engine debug endpoint.
Recording can be enabled two ways:
- Engine-global: the engine's --wcprof flag, or POSTing "on" to the /debug/wcprof/enabled endpoint. All work on the engine is recorded until "off" is POSTed to the same endpoint.
- Per-session: a client connecting with ClientMetadata.Profile set (the hidden --profile CLI flag). Only work attributable to that session (including its nested module/SDK clients) is recorded: the session server marks the session's contexts via ContextWithProfiling, and the mark propagates to derived contexts.
When profiling has never been enabled, all recording calls are a single atomic load + nil check.
Index ¶
- Constants
- func ContextWithOpID(ctx context.Context, opID uint64) context.Context
- func ContextWithProfiling(ctx context.Context) context.Context
- func CurrentOpID(ctx context.Context) uint64
- func DisableGlobal()
- func EnableGlobal()
- func Enabled(ctx context.Context) bool
- func EnsureRecorder()
- func FlushStopDump(path string) error
- func GloballyEnabled() bool
- func Link(ctx context.Context, kind LinkKind, fromOpID, targetOpID uint64, ident string, ...)
- func LinkOTelParent(ctx context.Context, opID uint64, traceID, spanID string)
- func Lock(ctx context.Context, mu *sync.Mutex, ident string)
- func MapOTelSpan(ctx context.Context, opID uint64, traceID, spanID string)
- func NowNS() int64
- func ReadDump(rd io.Reader) (*DumpHeader, []DumpEvent, error)
- func ReadLock(ctx context.Context, mu *sync.RWMutex, ident string)
- func RecordOp(ctx context.Context, kind OpKind, class string, opts OpOpts, ...) uint64
- func RecordProducerComplete(ctx context.Context, producer string, spanCount, dropped int64)
- func RecordReported(ctx context.Context, op ReportedOp)
- func RecordSpan(ctx context.Context, opID uint64, traceID [16]byte, spanID [8]byte)
- func RecordUsage(ctx context.Context, opID uint64, scope string, u Usage)
- func RequestStopDump(path string)
- func SetActiveForTest(r *Recorder, on bool) func()
- func StopDumpPath(root string) string
- func WriteDumpFile(path string) (err error)
- func WriteLock(ctx context.Context, mu *sync.RWMutex, ident string)
- type DumpEvent
- type DumpHeader
- type DumpOpenOp
- type DumpProducer
- type Event
- type EventType
- type LinkKind
- type Op
- func (op *Op) End(outcome Outcome)
- func (op *Op) EndErr(err error)
- func (op *Op) EndWithResult(outcome Outcome, resultID uint64)
- func (op *Op) ID() uint64
- func (op *Op) OutcomeHint() Outcome
- func (op *Op) SetIdent(ident string)
- func (op *Op) SetOutcomeHint(outcome Outcome)
- func (op *Op) SetSpan(traceID [16]byte, spanID [8]byte)
- func (op *Op) StartTime() time.Time
- type OpKind
- type OpOpts
- type Outcome
- type Recorder
- type ReportedAcquisition
- type ReportedOp
- type ReportedResource
- type ReportedWait
- type Usage
- type Wait
- type WaitReason
- type WorkType
Constants ¶
const ( DumpTypeResource = "resource" DumpTypeAcquisition = "acquisition" )
Dump event types for reported resources and acquisitions.
const ( // UsageScopeExecCgroup is the exec's resource cgroup: the workload and the // mount helpers that run beside it, read after mount teardown. UsageScopeExecCgroup = "exec_cgroup" // UsageScopeMountHelpers is the mount helpers' own cgroup inside the // exec's resource cgroup, read with it, and only when a helper ran: the // workload's usage is the exec cgroup's less this. UsageScopeMountHelpers = "mount_helpers" )
Usage scopes of an exec.
const DumpSchemaVersion = 1
DumpSchemaVersion identifies the dump wire format.
const DumpTypeUsage = "usage"
DumpTypeUsage is the DumpEvent.Type of a resource reading (RecordUsage).
Variables ¶
This section is empty.
Functions ¶
func ContextWithOpID ¶
ContextWithOpID returns a context carrying the given op ID as the current op. Useful when handing work to a goroutine that should be attributed to an existing op.
func ContextWithProfiling ¶
ContextWithProfiling marks ctx (and any context derived from it) for recording even when engine-global recording is off. The session server applies this to contexts of sessions that opted into profiling.
func CurrentOpID ¶
CurrentOpID returns the profiling op ID carried by ctx, or 0.
func DisableGlobal ¶
func DisableGlobal()
DisableGlobal turns off engine-global recording. The recorder and its buffered events are retained for dumping, sessions that explicitly enabled profiling keep recording, and already-recording flows (contexts carrying a recorded op) run to completion.
func Enabled ¶
Enabled reports whether work running under ctx should be recorded. Call sites use it to skip computing op metadata (class/ident strings) when profiling is off. When profiling has never been enabled this is a single atomic load + nil check.
func EnsureRecorder ¶
func EnsureRecorder()
EnsureRecorder creates the recorder if it does not exist yet, without turning on engine-global recording. Used when a session opts into profiling.
func FlushStopDump ¶
FlushStopDump asks for the stop dump at path to be rewritten and waits for a write that began after the call, returning its error: the engine's last state before it exits is then on disk.
func GloballyEnabled ¶
func GloballyEnabled() bool
GloballyEnabled reports whether engine-global recording is on.
func Link ¶
func Link(ctx context.Context, kind LinkKind, fromOpID, targetOpID uint64, ident string, resultID uint64)
Link records a non-blocking correlation from fromOpID (0 means the current op in ctx) to an op, an interned ident, and/or a dagql result ID.
func LinkOTelParent ¶
LinkOTelParent records that the engine op opID was caused by the span (traceID, spanID), e.g. a request a reported task made: in the dump the link resolves to that span's reported op, if it was recorded. A flushing dump drops the links it couldn't resolve, as most callers aren't reported spans: take it after the clients have exported their spans.
func Lock ¶
Lock acquires mu. When mu is held by someone else and work under ctx is being recorded, the time spent blocked is recorded as a lock wait of the current op on the resource named ident; otherwise it is mu.Lock with a TryLock in front. Use it for engine-internal mutexes held across slow work, whose waits would otherwise show up as the waiter's own time.
func MapOTelSpan ¶
MapOTelSpan records that the engine op opID was begun under the span (traceID, spanID), e.g. a dagql call under its call span: a reported op whose causal parent is that span (a client inside the call's exec, or the module the call runs) resolves to opID in the dump. The first op mapped to a span keeps it, since calls under a telemetry-suppressed caller share the caller's span and the outermost of them is the one it stands for.
func NowNS ¶
func NowNS() int64
NowNS returns the current recorder-relative timestamp, or 0 when profiling is disabled.
func ReadDump ¶
func ReadDump(rd io.Reader) (*DumpHeader, []DumpEvent, error)
ReadDump parses a dump produced by WriteDump.
func RecordOp ¶
func RecordOp(ctx context.Context, kind OpKind, class string, opts OpOpts, startNS, endNS int64, outcome Outcome) uint64
RecordOp records an already-completed op interval with explicit timestamps (from NowNS). Useful when an op's boundaries are observed from callbacks where holding an *Op handle would race. Returns the op ID, or 0 when profiling is disabled.
func RecordProducerComplete ¶
RecordProducerComplete records a producer's declaration of how many spans it reported and how many it failed to export.
func RecordReported ¶
func RecordReported(ctx context.Context, op ReportedOp)
RecordReported records one reported op. A span already recorded is not recorded again (live and final exports of one span), and only ended spans are recorded. A no-op when profiling is disabled for ctx.
func RecordSpan ¶
RecordSpan is SetSpan for an op recorded by ID (RecordOp).
func RecordUsage ¶
RecordUsage attaches a resource reading for scope to the op opID. It is a no-op when profiling is disabled for ctx or opID is 0. Readings count against the recorder's event cap like other events.
func RequestStopDump ¶
func RequestStopDump(path string)
RequestStopDump asks for the engine's stop dump at path to be rewritten, while the engine records everything (GloballyEnabled), and returns without waiting for the write. Each write is an op of its own, wcprof.stopDump. The dump on disk lags the latest request by at most the write in progress and one more; FlushStopDump waits for it.
func SetActiveForTest ¶
SetActiveForTest installs r as the recorder, recording globally when on, and returns a function restoring the previous state. For tests of code that records through the package-level API.
func StopDumpPath ¶
StopDumpPath is where an engine with state directory root keeps the dump of its stop (see WriteDumpFile).
func WriteDumpFile ¶
WriteDumpFile writes the recorder's dump to path, gzipped, without flushing the recorder. It writes a temporary file beside path and renames it over path, so a reader, or an engine killed mid-write, never leaves a partial dump. It does nothing when profiling was never enabled.
The engine writes its stop dump this way (RequestStopDump) when it receives SIGTERM, and as each graceful-stop phase and final drain begins and ends: an engine killed before it exits leaves the dump of its last finished write, with the work still running then as open ops. It compresses at gzip's fastest level, since it runs during the stop it records.
Types ¶
type DumpEvent ¶
type DumpEvent struct {
Type string `json:"e"`
OpKind string `json:"k,omitempty"`
WorkType string `json:"w,omitempty"`
Outcome string `json:"o,omitempty"`
Reason string `json:"rs,omitempty"`
LinkKind string `json:"lk,omitempty"`
OpID uint64 `json:"id,omitempty"`
ParentID uint64 `json:"p,omitempty"`
TargetID uint64 `json:"t,omitempty"`
ResultID uint64 `json:"r,omitempty"`
ClassID uint32 `json:"c,omitempty"`
IdentID uint32 `json:"i,omitempty"`
ClientID uint32 `json:"cl,omitempty"`
MetaID uint32 `json:"m,omitempty"`
// TraceID and SpanID are a span event's OTel span, in lower-case hex.
TraceID string `json:"tr,omitempty"`
SpanID string `json:"sp,omitempty"`
StartNS int64 `json:"s"`
EndNS int64 `json:"d"`
// Reported ops (see reported.go) also carry their label and producer.
LabelID uint32 `json:"lb,omitempty"`
ProducerID uint32 `json:"pr,omitempty"`
// Resource events ("e":"resource") describe the resource op OpID; IdentID
// is its display name.
Capacity int64 `json:"cap,omitempty"`
PolicyID uint32 `json:"pol,omitempty"`
// Acquisition events ("e":"acquisition") describe the acquire op OpID:
// TargetID is its resource op, Priority its priority, MetaID its
// priority inputs (a JSON object), GrantedNS when it was granted (0 if
// never), Outcome released, cancelled or held.
Priority int64 `json:"pri,omitempty"`
GrantedNS int64 `json:"gr,omitempty"`
// Usage events ("e":"usage") carry a resource reading for the op OpID:
// ClassID names the scope (see the UsageScope constants), StartNS and
// EndNS are when it was read.
CPUUserUS int64 `json:"cu,omitempty"`
CPUSystemUS int64 `json:"cs,omitempty"`
MemPeakBytes int64 `json:"mp,omitempty"`
}
DumpEvent is the JSON form of an Event. Short keys keep dumps compact.
type DumpHeader ¶
type DumpHeader struct {
SchemaVersion int `json:"schema_version"`
EpochUnixNano int64 `json:"epoch_unix_nano"`
DumpedUnixNano int64 `json:"dumped_unix_nano"`
DroppedEvents uint64 `json:"dropped_events"`
EventCount int `json:"event_count"`
Strings []string `json:"strings"`
// OpenOps are ops begun but not ended at dump time (e.g. in-flight or
// hung work).
OpenOps []DumpOpenOp `json:"open_ops,omitempty"`
// Producers are the clients that reported their own work: per producer,
// the spans received and, once it declared them, the count it reported
// and how many it failed to export. A producer whose Received is below
// its Declared, or that never declared, makes the dump's reported work
// incomplete.
Producers []DumpProducer `json:"producers,omitempty"`
}
DumpHeader is the first line of a dump. The remaining lines are one DumpEvent JSON object per line.
type DumpOpenOp ¶
type DumpOpenOp struct {
OpID uint64 `json:"op_id"`
ParentID uint64 `json:"parent_id,omitempty"`
Kind string `json:"kind"`
WorkType string `json:"work_type,omitempty"`
ClassID uint32 `json:"class_id,omitempty"`
IdentID uint32 `json:"ident_id,omitempty"`
ClientID uint32 `json:"client_id,omitempty"`
MetaID uint32 `json:"meta_id,omitempty"`
StartNS int64 `json:"start_ns"`
}
DumpOpenOp describes an in-progress op at dump time.
type DumpProducer ¶
type DumpProducer struct {
Producer string `json:"producer"`
Received int64 `json:"received"`
Declared int64 `json:"declared,omitempty"`
Dropped int64 `json:"dropped,omitempty"`
Complete bool `json:"complete,omitempty"`
}
DumpProducer is one producer's completeness accounting.
type Event ¶
type Event struct {
Type EventType
OpKind OpKind
WorkType WorkType
Outcome Outcome
Reason WaitReason
LinkKind LinkKind
OpID uint64
ParentID uint64
TargetID uint64
ResultID uint64
ClassID uint32
IdentID uint32
ClientID uint32
// MetaID is an interned op-metadata string (currently the canonical
// scalar JSON-array encoding of a user exec's scrubbed argv), 0 when none.
MetaID uint32
StartNS int64
EndNS int64
}
Event is one fixed-size profiling record. Field meaning varies by Type:
- Op: OpID/ParentID identify the op and its structural parent. StartNS and EndNS bound the interval. ClassID/IdentID/ClientID are interned strings. ResultID is the dagql shared result ID when known.
- Wait: ParentID is the waiting op, TargetID the awaited op (or 0 when waiting on a named resource, in which case IdentID names it).
- Link: ParentID is the from-op, TargetID an op (optional), IdentID an interned identifier (optional), ResultID a dagql result (optional).
- Span: OpID is the op, TargetID and ResultID the high and low halves of its span's trace ID, ParentID the span ID, all big-endian.
type EventType ¶
type EventType uint8
EventType discriminates the union in Event.
const ( EventTypeInvalid EventType = iota // EventTypeOp is a completed operation interval. EventTypeOp // EventTypeWait is a completed wait interval: an op was blocked on // another op (or named resource) from Start to End. EventTypeWait // EventTypeLink is a non-blocking correlation between an op and another // op or an interned identifier. EventTypeLink // EventTypeSpan names the OTel span an op owns: the span the engine // emits for the same work. EventTypeSpan )
type LinkKind ¶
type LinkKind uint8
LinkKind classifies link events.
const ( LinkKindInvalid LinkKind = iota // LinkKindNestedClient: op (an exec) hosts a nested client whose ID is // the link's interned ident. Ops recorded for that client belong under // this exec. LinkKindNestedClient // LinkKindResult: op produced or returned the dagql result with // ResultID. LinkKindResult // LinkKindReusedResult: op was satisfied by reusing the dagql result // with ResultID (cache hit). LinkKindReusedResult // LinkKindReportedCause: the engine op (from) was caused by the reported // op (target), e.g. a request a reported task made. LinkKindReportedCause // LinkKindUnresolvedSpan: a reported op (from) refers to a span (the // ident, "<trace>/<span>") that wasn't recorded by the time of the dump. LinkKindUnresolvedSpan )
type Op ¶
type Op struct {
// contains filtered or unexported fields
}
Op is a handle for an in-progress operation. A nil *Op is valid and all methods are no-ops, so call sites do not need to check whether profiling is enabled.
func BeginOp ¶
BeginOp starts recording an operation. It returns a derived context that carries the new op as the current op (so nested ops and waits parent to it), and a handle to end it. When profiling is disabled it returns ctx unchanged and a nil handle.
func (*Op) EndWithResult ¶
EndWithResult records the op's completion, associating it with a dagql shared result ID when non-zero.
func (*Op) OutcomeHint ¶
OutcomeHint returns the hint set by SetOutcomeHint, or OutcomeNone.
func (*Op) SetIdent ¶
SetIdent updates the op's instance identity (useful when it only becomes known after the op began, e.g. a recipe digest derived mid-call). Must be called from the goroutine that owns the op, before End.
func (*Op) SetOutcomeHint ¶
outcomeHint carries an outcome decided mid-op (e.g. joined vs executed), read back by the code that ends the op.
type OpKind ¶
type OpKind uint8
OpKind classifies operations.
const ( OpKindInvalid OpKind = iota // OpKindCall is one dagql GetOrInitCall invocation (per caller, // including cache hits and singleflight joiners). OpKindCall // OpKindCallExec is the shared execution of a call's resolver function. // Singleflighted callers all wait on one of these. OpKindCallExec // OpKindLazy is one run of a lazy evaluation callback for a result. OpKindLazy // OpKindExecPhase is a setup/run phase of a container exec // (e.g. exec.setupNetwork, exec.runContainer). OpKindExecPhase // OpKindExec is the overall run of a container by the executor. OpKindExec // OpKindServiceStart is the start (incl. health check) of a service. OpKindServiceStart // OpKindSessionPhase is a per-query session serving phase // (e.g. session.workspaceLoad, session.query). OpKindSessionPhase // OpKindIO is a leaf I/O operation (git fetch, image pull, filesync...). OpKindIO // OpKindInternal is engine-internal background work (gc, persistence). OpKindInternal // OpKindTask is one unit of a client's own work, reported by the client // (e.g. one package of a Go build); see the reporting contract in // engine/wcprof/contract. OpKindTask // OpKindTaskGroup is a reported parent whose child tasks run concurrently, // ordered only by their dependency waits and resource acquisitions. OpKindTaskGroup // OpKindResource is a reported limited resource instance (capacity and // admission policy), for the life of the instance. OpKindResource // OpKindAcquire is a reported acquisition of one unit of a resource, from // the request to the release. OpKindAcquire )
func ParseOpKind ¶
ParseOpKind is the OpKind named s (as OpKind.String writes it), or OpKindInvalid.
type OpOpts ¶
type OpOpts struct {
// Ident is an instance identity for the op (e.g. recipe digest, exec ID).
Ident string
// ClientID is the dagger client this op serves, when known.
ClientID string
// ResultID may be set at Begin time when already known.
WorkType WorkType
// Argv is the user command for a container-exec op (already scrubbed and
// bounded by the caller). The recorder marshals it to the canonical scalar
// JSON-array string and interns it as the op's MetaID, so offline analysis
// can rank the exec by its real command. Empty for non-exec ops.
Argv []string
// Start, when set, is when the op began, for an op whose work began
// before BeginOp (e.g. at its OTel span's start); zero is now.
Start time.Time
}
OpOpts carries optional per-op metadata.
type Outcome ¶
type Outcome uint8
Outcome describes how an op completed.
const ( OutcomeNone Outcome = iota // OutcomeHit: dagql call satisfied from cache. OutcomeHit // OutcomeExecuted: dagql call missed cache and this caller spawned the // execution. OutcomeExecuted // OutcomeJoined: dagql call missed cache and joined an in-flight // execution started by another caller. OutcomeJoined // OutcomeDoNotCache: dagql call executed inline without caching. OutcomeDoNotCache // OutcomeOK: generic success for non-call ops. OutcomeOK // OutcomeError: op failed. OutcomeError // OutcomeCanceled: op canceled. OutcomeCanceled )
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder collects profiling events. Safe for concurrent use.
func Active ¶
func Active() *Recorder
Active returns the recorder, or nil if profiling was never enabled. The recorder outlives DisableGlobal so buffered events can still be dumped; use GloballyEnabled/Enabled to ask whether work is being recorded.
func NewRecorder ¶
NewRecorder returns a recorder with the given event cap (<=0 means default).
type ReportedAcquisition ¶
type ReportedAcquisition struct {
ResourceSpanID string
Priority int64
PriorityInputs map[string]string
// GrantedUnixNS is when the unit was granted, 0 if never.
GrantedUnixNS int64
Outcome string
}
ReportedAcquisition describes an "acquire" op.
type ReportedOp ¶
type ReportedOp struct {
TraceID string
SpanID string
// ParentSpanID is the op's causal parent span (same trace), "" for none.
ParentSpanID string
Kind OpKind
Work WorkType
// Name is the span's name, the op's class.
Name string
Label string
Producer string
StartUnixNS int64
EndUnixNS int64
Waits []ReportedWait
Resource *ReportedResource
Acquisition *ReportedAcquisition
}
ReportedOp is one reported span to record, already decoded.
type ReportedResource ¶
type ReportedResource struct {
// Name is the resource's display name, e.g. "gobuild.slots".
Name string
Capacity int64
Policy string
}
ReportedResource describes a "resource" op.
type ReportedWait ¶
type ReportedWait struct {
TargetTraceID string
TargetSpanID string
Reason WaitReason
Ident string
StartUnixNS int64
EndUnixNS int64
}
ReportedWait is a wait link of a reported op.
type Usage ¶
type Usage struct {
CPUUserUS int64
CPUSystemUS int64
// MemPeakBytes is the scope's peak memory, 0 when it couldn't be read.
MemPeakBytes int64
}
Usage is a resource reading attached to an op: the CPU time and peak memory of one scope (see the UsageScope constants), read once the scope's work is done.
type Wait ¶
type Wait struct {
// contains filtered or unexported fields
}
Wait is a handle for an in-progress wait interval. A nil *Wait is valid and End is a no-op.
func BeginWait ¶
func BeginWait(ctx context.Context, targetOpID uint64, reason WaitReason) *Wait
BeginWait starts recording that the current op (from ctx) is blocked on targetOpID for the given reason. Returns nil when profiling is disabled.
func BeginWaitIdent ¶
func BeginWaitIdent(ctx context.Context, ident string, reason WaitReason) *Wait
BeginWaitIdent is BeginWait for waits on a named resource (e.g. a lock) rather than another op.
type WaitReason ¶
type WaitReason uint8
WaitReason describes why an op was blocked.
const ( WaitReasonInvalid WaitReason = iota // WaitReasonCallExec: blocked waiting for a call's resolver execution // (the caller that spawned it). WaitReasonCallExec // WaitReasonSingleflight: blocked joining another caller's in-flight // execution of the same call. WaitReasonSingleflight // WaitReasonLazy: blocked waiting for a lazy evaluation to finish. WaitReasonLazy // WaitReasonService: blocked waiting for a service to start/be healthy. WaitReasonService // WaitReasonLock: blocked acquiring a lock (target is an interned // resource name, not an op). WaitReasonLock // WaitReasonExec: blocked waiting for a container exec to finish. WaitReasonExec // WaitReasonIO: blocked on external I/O. WaitReasonIO // WaitReasonDependency: a reported task waited for one of its // dependencies' results (zero-length when it was already done). WaitReasonDependency )
func ParseWaitReason ¶
func ParseWaitReason(s string) WaitReason
ParseWaitReason is the WaitReason named s, or WaitReasonInvalid.
func (WaitReason) String ¶
func (r WaitReason) String() string
type WorkType ¶
type WorkType uint8
WorkType is a coarse attribution of an op's self-time, used by analysis to separate actionable engine overhead from user workload time and external I/O time.
func ParseWorkType ¶
ParseWorkType is the WorkType named s, or WorkTypeEngine.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package contract decodes the wcprof reporting contract: how a client or a program inside an exec reports its own work (tasks and task groups), what each task waited for (dependency waits), its limited resources and the acquisitions of them, and its completeness, as OTel spans and links with the wcprof.* attributes of engine/telemetryattrs.
|
Package contract decodes the wcprof reporting contract: how a client or a program inside an exec reports its own work (tasks and task groups), what each task waited for (dependency waits), its limited resources and the acquisitions of them, and its completeness, as OTel spans and links with the wcprof.* attributes of engine/telemetryattrs. |