Source File
annotation.go
Belonging Package
runtime/trace
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package trace
import (
_
)
type traceContextKey struct{}
// NewTask creates a task instance with the type taskType and returns
// it along with a Context that carries the task.
// If the input context contains a task, the new task is its subtask.
//
// The taskType is used to classify task instances. Analysis tools
// like the Go execution tracer may assume there are only a bounded
// number of unique task types in the system.
//
// The returned Task's [Task.End] method is used to mark the task's end.
// The trace tool measures task latency as the time between task creation
// and when the End method is called, and provides the latency
// distribution per task type.
// If the End method is called multiple times, only the first
// call is used in the latency measurement.
//
// ctx, task := trace.NewTask(ctx, "awesomeTask")
// trace.WithRegion(ctx, "preparation", prepWork)
// // preparation of the task
// go func() { // continue processing the task in a separate goroutine.
// defer task.End()
// trace.WithRegion(ctx, "remainingWork", remainingWork)
// }()
func ( context.Context, string) ( context.Context, *Task) {
:= fromContext().id
:= newID()
userTaskCreate(, , )
:= &Task{id: }
return context.WithValue(, traceContextKey{}, ),
// We allocate a new task even when
// the tracing is disabled because the context and task
// can be used across trace enable/disable boundaries,
// which complicates the problem.
//
// For example, consider the following scenario:
// - trace is enabled.
// - trace.WithRegion is called, so a new context ctx
// with a new region is created.
// - trace is disabled.
// - trace is enabled again.
// - trace APIs with the ctx is called. Is the ID in the task
// a valid one to use?
//
// TODO(hyangah): reduce the overhead at least when
// tracing is disabled. Maybe the id can embed a tracing
// round number and ignore ids generated from previous
// tracing round.
}
func fromContext( context.Context) *Task {
if , := .Value(traceContextKey{}).(*Task); {
return
}
return &bgTask
}
// Task is a data type for tracing a user-defined, logical operation.
type Task struct {
id uint64
// TODO(hyangah): record parent id?
}
// End marks the end of the operation represented by the [Task].
func ( *Task) () {
userTaskEnd(.id)
}
var lastTaskID uint64 = 0 // task id issued last time
func newID() uint64 {
// TODO(hyangah): use per-P cache
return atomic.AddUint64(&lastTaskID, 1)
}
var bgTask = Task{id: uint64(0)}
// Log emits a one-off event with the given category and message.
// Category can be empty and the API assumes there are only a handful of
// unique categories in the system.
func ( context.Context, , string) {
:= fromContext().id
userLog(, , )
}
// Logf is like [Log], but the value is formatted using the specified format spec.
func ( context.Context, , string, ...any) {
if IsEnabled() {
// Ideally this should be just Log, but that will
// add one more frame in the stack trace.
:= fromContext().id
userLog(, , fmt.Sprintf(, ...))
}
}
const (
regionStartCode = uint64(0)
regionEndCode = uint64(1)
)
// WithRegion starts a region associated with its calling goroutine, runs fn,
// and then ends the region. If the context carries a task, the region is
// associated with the task. Otherwise, the region is attached to the background
// task.
//
// The regionType is used to classify regions, so there should be only a
// handful of unique region types.
func ( context.Context, string, func()) {
// NOTE:
// WithRegion helps avoiding misuse of the API but in practice,
// this is very restrictive:
// - Use of WithRegion makes the stack traces captured from
// region start and end are identical.
// - Refactoring the existing code to use WithRegion is sometimes
// hard and makes the code less readable.
// e.g. code block nested deep in the loop with various
// exit point with return values
// - Refactoring the code to use this API with closure can
// cause different GC behavior such as retaining some parameters
// longer.
// This causes more churns in code than I hoped, and sometimes
// makes the code less readable.
:= fromContext().id
userRegion(, regionStartCode, )
defer userRegion(, regionEndCode, )
()
}
// StartRegion starts a region and returns it.
// The returned Region's [Region.End] method must be called
// from the same goroutine where the region was started.
// Within each goroutine, regions must nest. That is, regions started
// after this region must be ended before this region can be ended.
// Recommended usage is
//
// defer trace.StartRegion(ctx, "myTracedRegion").End()
func ( context.Context, string) *Region {
if !IsEnabled() {
return noopRegion
}
:= fromContext().id
userRegion(, regionStartCode, )
return &Region{, }
}
// Region is a region of code whose execution time interval is traced.
type Region struct {
id uint64
regionType string
}
var noopRegion = &Region{}
// End marks the end of the traced code region.
func ( *Region) () {
if == noopRegion {
return
}
userRegion(.id, regionEndCode, .regionType)
}
// IsEnabled reports whether tracing is enabled.
// The information is advisory only. The tracing status
// may have changed by the time this function returns.
func () bool {
return tracing.enabled.Load()
}
//
// Function bodies are defined in runtime/trace.go
//
// emits UserTaskCreate event.
func userTaskCreate(, uint64, string)
// emits UserTaskEnd event.
func userTaskEnd( uint64)
// emits UserRegion event.
func userRegion(, uint64, string)
// emits UserLog event.
func userLog( uint64, , string)
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |