gvisorgvisor.dev/gvisor/pkg/timing Index | Files

package timing

import "gvisor.dev/gvisor/pkg/timing"

Package timing provides a way to record the timing of a series of operations across one or more goroutines.

Index

Types

type Lease

type Lease struct {
	// contains filtered or unexported fields
}

A Lease is a reference to a Timeline that is valid until the Lease is canceled. After calling Lease on a Timeline, the caller should no longer use the Timeline directly, and should instead use the Lease exclusively.

Leases should typically not cross function boundaries.

Leases are useful in complex functions where ownership of a Timeline needs to be *conditionally transferred* to a different goroutine at some late point in the function. Consider this example:

```

func SomeLongFunction(timeline *timing.Timeline) {
  defer timeline.End() // Convenient to defer `End` to hit all the `return` branches.
  timeline.Reached("some_point")

  if err := something(timeline); err != nil {
    return
  }
  timeline.Reached("some_other_point")
  if err := somethingElse(); err != nil {
    timeline.Reached("some_error")
    return
  }
  timeline.Reached("another_point")
  go doSomethingElse(timeline)

  // Don't want to call `End` anymore here!
}

```

With a Lease:

```

func SomeLongFunction(timeline *timing.Timeline) {
  lease := timeline.Lease()
  defer lease.End()
  lease.Reached("some_point")

  if err := something(); err != nil {
    lease.Reached("some_error")
    return
  }
  if err := somethingElse(); err != nil {
    lease.Reached("some_other_error")
    return
  }
  lease.Reached("another_point")
  go doSomethingElse(lease.Transfer())

  // `End` is not called anymore here.
}

```

func (*Lease) End

func (l *Lease) End()

End ends the Timeline if the Lease is valid. The lease is invalidated after this call. See `Timeline.End` for more details.

func (*Lease) Fork

func (l *Lease) Fork(name string) *Timeline

Fork forks the Timeline if the Lease is valid. See `Timeline.Fork` for more details.

func (*Lease) MultiFork

func (l *Lease) MultiFork(names []string) []*Timeline

MultiFork forks the Timeline if the Lease is valid. See `Timeline.MultiFork` for more details.

func (*Lease) Reached

func (l *Lease) Reached(name string)

Reached records a new midpoint on the Timeline if the Lease is valid. See `Timeline.Reached` for more details.

func (*Lease) Transfer

func (l *Lease) Transfer() *Timeline

Transfer invalidates the current Lease and returns the underlying Timeline. Typically useful when transferring ownership of a Timeline to a different goroutine while giving up ownership in the current one. See `Lease` documentation for example usage.

type MidPoint

type MidPoint struct {
	// contains filtered or unexported fields
}

MidPoint is a named point in time on a Timeline. The starting and ending points of a Timeline are not MidPoints.

type Timeline

type Timeline struct {
	// contains filtered or unexported fields
}

Timeline is a series of points in time.

A Timeline always has a defined start time, and will eventually have an end time. For this reason, `End` must always be called on a Timeline.

A Timeline may have zero or more mid-points contained between the start and end times.

A Timeline may fork to represent other timelines running concurrently. Such children Timelines may or may not end later than the parent does.

A single Timeline struct should be owned by a single goroutine at a given time until it ends (i.e. its endpoint becomes defined), at which point ownership transfers to the goroutine that owns the Timer that created it.

A Timeline may be nil, in which case all methods are no-ops. This means all code that takes in a Timeline parameter does not need to check for nilness.

func OrphanTimeline

func OrphanTimeline(name string, startTime time.Time) *Timeline

OrphanTimeline creates a new Timeline that is not owned by any Timer. This is useful for operations that are part of a broader sequence of operations represented by a Timer, but where timing measurements are desired before this broader parent Timer is known. The returned Timeline must be parented with `timer.Adopt` in order to be useful.

func (*Timeline) End

func (s *Timeline) End()

End marks the Timeline as having ended. It must be eventually called on all Timelines. After End is called, the ownership of the Timeline struct moves to the goroutine that owns the Timer that created it.

func (*Timeline) Fork

func (s *Timeline) Fork(name string) *Timeline

Fork creates a new Timeline that is a child of this one. A midpoint is implicitly added to the current Timeline.

The returned Timeline is initially owned by the caller, but may be passed to another goroutine if desired.

A child timeline may but does not need to end before the parent does.

Forked timelines are useful to represent parallel operations like separate goroutines, and are actually required in such cases so that the goroutine can own its own Timeline, but non-concurrent code may also use Fork to represent its own linear operations as a tree if it so desires.

func (*Timeline) Lease

func (s *Timeline) Lease() *Lease

Lease returns a Lease for the Timeline. The Lease is valid until it is canceled by calling `End` or `Transfer`. See `Lease` for example usage.

func (*Timeline) MultiFork

func (s *Timeline) MultiFork(names []string) []*Timeline

MultiFork creates new Timelines that are children of this one. It returns as many Timelines as there are names in `names`. All of them share the same start time. A midpoint is implicitly added to the current Timeline. The same semantics as `Timeline.Fork` apply.

func (*Timeline) Reached

func (s *Timeline) Reached(name string)

Reached records a new midpoint on the Timeline.

func (*Timeline) ReachedAt

func (s *Timeline) ReachedAt(name string, when time.Time)

ReachedAt records a new midpoint on the root Timeline of the Timer with the given timestamp.

type Timer

type Timer struct {
	// contains filtered or unexported fields
}

Timer is a root Timeline. It keeps track of one or more running Timelines, and can be pretty-printed to show timing information once all Timelines have ended.

A Timer struct may move between goroutines, but only one goroutine may own it at a time.

A Timer struct may be nil, in which case all methods are no-ops. This means all code that takes in a Timer parameter does not need to check for nilness.

func New

func New(name string, startTime time.Time) *Timer

New creates a new Timer. The given name is used to identify the Timer in pretty-printed output. The given startTime is used as the start time of the Timer's root Timeline.

func (*Timer) Adopt

func (t *Timer) Adopt(child *Timeline)

Adopt adopts a Timeline into this Timer. May only be called with Timelines created by `OrphanTimeline`, and may only be called once per such Timeline. The calling goroutine must own both the Timeline and the Timer.

func (*Timer) End

func (t *Timer) End()

End waits for all Timelines owned by this Timer to end, then pretty-prints timing information. If not all Timelines have ended by the time End is called, End will spin in place until they do, and eventually print a warning log if it spins for too long (but will not give up). End is called implicitly by Log, so it is not necessary to call End explicitly unless there is a need to end the root timeline at a different time than when logging its data is desired.

func (*Timer) Fork

func (t *Timer) Fork(name string) *Timeline

Fork creates a new Timeline that is a child of the root Timeline of this Timer. The returned Timeline is initially owned by the caller, but may be passed to another goroutine if desired. This child Timeline may but does not need to end before the root timeline does. Forked timelines are useful to represent parallel operations like separate goroutines, and are actually required in such cases so that the goroutine can own its own Timeline, but non-concurrent code may also use Fork to represent its own linear operations as a tree if it so desires.

func (*Timer) Log

func (t *Timer) Log()

Log pretty-prints timing information for the root Timeline of the Timer. If `t.End` has not yet been called, it will be called implicitly. This also means that this function will wait for all child Timelines to end before pretty-printing, and will spin in place until this is the case. If debug logging is enabled, this function will also log a flat list of events that can be easily machine-parsed to the debug log.

func (*Timer) MultiFork

func (t *Timer) MultiFork(names []string) []*Timeline

MultiFork creates new Timelines that are children of the root Timeline of this Timer. See Timeline.MultiFork for more details.

func (*Timer) Reached

func (t *Timer) Reached(name string)

Reached records a new midpoint on the root Timeline of the Timer.

func (*Timer) ReachedAt

func (t *Timer) ReachedAt(name string, when time.Time)

ReachedAt records a new midpoint on the root Timeline of the Timer with the given timestamp.

func (*Timer) StartTime

func (t *Timer) StartTime() time.Time

StartTime returns the start time of the Timer.

Source Files

timing.go

Version
v0.0.0-20250514213907-8db5d40e4cb5 (latest)
Published
May 14, 2025
Platform
linux/amd64
Imports
7 packages
Last checked
3 hours ago

Tools for package owners.