...

Source file src/runtime/metrics.go

Documentation: runtime

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime
     6  
     7  // Metrics implementation exported to runtime/metrics.
     8  
     9  import (
    10  	"runtime/internal/atomic"
    11  	"unsafe"
    12  )
    13  
    14  var (
    15  	// metrics is a map of runtime/metrics keys to data used by the runtime
    16  	// to sample each metric's value. metricsInit indicates it has been
    17  	// initialized.
    18  	//
    19  	// These fields are protected by metricsSema which should be
    20  	// locked/unlocked with metricsLock() / metricsUnlock().
    21  	metricsSema uint32 = 1
    22  	metricsInit bool
    23  	metrics     map[string]metricData
    24  
    25  	sizeClassBuckets []float64
    26  	timeHistBuckets  []float64
    27  )
    28  
    29  type metricData struct {
    30  	// deps is the set of runtime statistics that this metric
    31  	// depends on. Before compute is called, the statAggregate
    32  	// which will be passed must ensure() these dependencies.
    33  	deps statDepSet
    34  
    35  	// compute is a function that populates a metricValue
    36  	// given a populated statAggregate structure.
    37  	compute func(in *statAggregate, out *metricValue)
    38  }
    39  
    40  func metricsLock() {
    41  	// Acquire the metricsSema but with handoff. Operations are typically
    42  	// expensive enough that queueing up goroutines and handing off between
    43  	// them will be noticeably better-behaved.
    44  	semacquire1(&metricsSema, true, 0, 0)
    45  	if raceenabled {
    46  		raceacquire(unsafe.Pointer(&metricsSema))
    47  	}
    48  }
    49  
    50  func metricsUnlock() {
    51  	if raceenabled {
    52  		racerelease(unsafe.Pointer(&metricsSema))
    53  	}
    54  	semrelease(&metricsSema)
    55  }
    56  
    57  // initMetrics initializes the metrics map if it hasn't been yet.
    58  //
    59  // metricsSema must be held.
    60  func initMetrics() {
    61  	if metricsInit {
    62  		return
    63  	}
    64  
    65  	sizeClassBuckets = make([]float64, _NumSizeClasses, _NumSizeClasses+1)
    66  	// Skip size class 0 which is a stand-in for large objects, but large
    67  	// objects are tracked separately (and they actually get placed in
    68  	// the last bucket, not the first).
    69  	sizeClassBuckets[0] = 1 // The smallest allocation is 1 byte in size.
    70  	for i := 1; i < _NumSizeClasses; i++ {
    71  		// Size classes have an inclusive upper-bound
    72  		// and exclusive lower bound (e.g. 48-byte size class is
    73  		// (32, 48]) whereas we want and inclusive lower-bound
    74  		// and exclusive upper-bound (e.g. 48-byte size class is
    75  		// [33, 49). We can achieve this by shifting all bucket
    76  		// boundaries up by 1.
    77  		//
    78  		// Also, a float64 can precisely represent integers with
    79  		// value up to 2^53 and size classes are relatively small
    80  		// (nowhere near 2^48 even) so this will give us exact
    81  		// boundaries.
    82  		sizeClassBuckets[i] = float64(class_to_size[i] + 1)
    83  	}
    84  	sizeClassBuckets = append(sizeClassBuckets, float64Inf())
    85  
    86  	timeHistBuckets = timeHistogramMetricsBuckets()
    87  	metrics = map[string]metricData{
    88  		"/cgo/go-to-c-calls:calls": {
    89  			compute: func(_ *statAggregate, out *metricValue) {
    90  				out.kind = metricKindUint64
    91  				out.scalar = uint64(NumCgoCall())
    92  			},
    93  		},
    94  		"/gc/cycles/automatic:gc-cycles": {
    95  			deps: makeStatDepSet(sysStatsDep),
    96  			compute: func(in *statAggregate, out *metricValue) {
    97  				out.kind = metricKindUint64
    98  				out.scalar = in.sysStats.gcCyclesDone - in.sysStats.gcCyclesForced
    99  			},
   100  		},
   101  		"/gc/cycles/forced:gc-cycles": {
   102  			deps: makeStatDepSet(sysStatsDep),
   103  			compute: func(in *statAggregate, out *metricValue) {
   104  				out.kind = metricKindUint64
   105  				out.scalar = in.sysStats.gcCyclesForced
   106  			},
   107  		},
   108  		"/gc/cycles/total:gc-cycles": {
   109  			deps: makeStatDepSet(sysStatsDep),
   110  			compute: func(in *statAggregate, out *metricValue) {
   111  				out.kind = metricKindUint64
   112  				out.scalar = in.sysStats.gcCyclesDone
   113  			},
   114  		},
   115  		"/gc/heap/allocs-by-size:bytes": {
   116  			deps: makeStatDepSet(heapStatsDep),
   117  			compute: func(in *statAggregate, out *metricValue) {
   118  				hist := out.float64HistOrInit(sizeClassBuckets)
   119  				hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeAllocCount)
   120  				// Cut off the first index which is ostensibly for size class 0,
   121  				// but large objects are tracked separately so it's actually unused.
   122  				for i, count := range in.heapStats.smallAllocCount[1:] {
   123  					hist.counts[i] = uint64(count)
   124  				}
   125  			},
   126  		},
   127  		"/gc/heap/allocs:bytes": {
   128  			deps: makeStatDepSet(heapStatsDep),
   129  			compute: func(in *statAggregate, out *metricValue) {
   130  				out.kind = metricKindUint64
   131  				out.scalar = in.heapStats.totalAllocated
   132  			},
   133  		},
   134  		"/gc/heap/allocs:objects": {
   135  			deps: makeStatDepSet(heapStatsDep),
   136  			compute: func(in *statAggregate, out *metricValue) {
   137  				out.kind = metricKindUint64
   138  				out.scalar = in.heapStats.totalAllocs
   139  			},
   140  		},
   141  		"/gc/heap/frees-by-size:bytes": {
   142  			deps: makeStatDepSet(heapStatsDep),
   143  			compute: func(in *statAggregate, out *metricValue) {
   144  				hist := out.float64HistOrInit(sizeClassBuckets)
   145  				hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeFreeCount)
   146  				// Cut off the first index which is ostensibly for size class 0,
   147  				// but large objects are tracked separately so it's actually unused.
   148  				for i, count := range in.heapStats.smallFreeCount[1:] {
   149  					hist.counts[i] = uint64(count)
   150  				}
   151  			},
   152  		},
   153  		"/gc/heap/frees:bytes": {
   154  			deps: makeStatDepSet(heapStatsDep),
   155  			compute: func(in *statAggregate, out *metricValue) {
   156  				out.kind = metricKindUint64
   157  				out.scalar = in.heapStats.totalFreed
   158  			},
   159  		},
   160  		"/gc/heap/frees:objects": {
   161  			deps: makeStatDepSet(heapStatsDep),
   162  			compute: func(in *statAggregate, out *metricValue) {
   163  				out.kind = metricKindUint64
   164  				out.scalar = in.heapStats.totalFrees
   165  			},
   166  		},
   167  		"/gc/heap/goal:bytes": {
   168  			deps: makeStatDepSet(sysStatsDep),
   169  			compute: func(in *statAggregate, out *metricValue) {
   170  				out.kind = metricKindUint64
   171  				out.scalar = in.sysStats.heapGoal
   172  			},
   173  		},
   174  		"/gc/heap/objects:objects": {
   175  			deps: makeStatDepSet(heapStatsDep),
   176  			compute: func(in *statAggregate, out *metricValue) {
   177  				out.kind = metricKindUint64
   178  				out.scalar = in.heapStats.numObjects
   179  			},
   180  		},
   181  		"/gc/heap/tiny/allocs:objects": {
   182  			deps: makeStatDepSet(heapStatsDep),
   183  			compute: func(in *statAggregate, out *metricValue) {
   184  				out.kind = metricKindUint64
   185  				out.scalar = uint64(in.heapStats.tinyAllocCount)
   186  			},
   187  		},
   188  		"/gc/limiter/last-enabled:gc-cycle": {
   189  			compute: func(_ *statAggregate, out *metricValue) {
   190  				out.kind = metricKindUint64
   191  				out.scalar = uint64(gcCPULimiter.lastEnabledCycle.Load())
   192  			},
   193  		},
   194  		"/gc/pauses:seconds": {
   195  			compute: func(_ *statAggregate, out *metricValue) {
   196  				hist := out.float64HistOrInit(timeHistBuckets)
   197  				// The bottom-most bucket, containing negative values, is tracked
   198  				// as a separately as underflow, so fill that in manually and then
   199  				// iterate over the rest.
   200  				hist.counts[0] = atomic.Load64(&memstats.gcPauseDist.underflow)
   201  				for i := range memstats.gcPauseDist.counts {
   202  					hist.counts[i+1] = atomic.Load64(&memstats.gcPauseDist.counts[i])
   203  				}
   204  			},
   205  		},
   206  		"/gc/stack/starting-size:bytes": {
   207  			compute: func(in *statAggregate, out *metricValue) {
   208  				out.kind = metricKindUint64
   209  				out.scalar = uint64(startingStackSize)
   210  			},
   211  		},
   212  		"/memory/classes/heap/free:bytes": {
   213  			deps: makeStatDepSet(heapStatsDep),
   214  			compute: func(in *statAggregate, out *metricValue) {
   215  				out.kind = metricKindUint64
   216  				out.scalar = uint64(in.heapStats.committed - in.heapStats.inHeap -
   217  					in.heapStats.inStacks - in.heapStats.inWorkBufs -
   218  					in.heapStats.inPtrScalarBits)
   219  			},
   220  		},
   221  		"/memory/classes/heap/objects:bytes": {
   222  			deps: makeStatDepSet(heapStatsDep),
   223  			compute: func(in *statAggregate, out *metricValue) {
   224  				out.kind = metricKindUint64
   225  				out.scalar = in.heapStats.inObjects
   226  			},
   227  		},
   228  		"/memory/classes/heap/released:bytes": {
   229  			deps: makeStatDepSet(heapStatsDep),
   230  			compute: func(in *statAggregate, out *metricValue) {
   231  				out.kind = metricKindUint64
   232  				out.scalar = uint64(in.heapStats.released)
   233  			},
   234  		},
   235  		"/memory/classes/heap/stacks:bytes": {
   236  			deps: makeStatDepSet(heapStatsDep),
   237  			compute: func(in *statAggregate, out *metricValue) {
   238  				out.kind = metricKindUint64
   239  				out.scalar = uint64(in.heapStats.inStacks)
   240  			},
   241  		},
   242  		"/memory/classes/heap/unused:bytes": {
   243  			deps: makeStatDepSet(heapStatsDep),
   244  			compute: func(in *statAggregate, out *metricValue) {
   245  				out.kind = metricKindUint64
   246  				out.scalar = uint64(in.heapStats.inHeap) - in.heapStats.inObjects
   247  			},
   248  		},
   249  		"/memory/classes/metadata/mcache/free:bytes": {
   250  			deps: makeStatDepSet(sysStatsDep),
   251  			compute: func(in *statAggregate, out *metricValue) {
   252  				out.kind = metricKindUint64
   253  				out.scalar = in.sysStats.mCacheSys - in.sysStats.mCacheInUse
   254  			},
   255  		},
   256  		"/memory/classes/metadata/mcache/inuse:bytes": {
   257  			deps: makeStatDepSet(sysStatsDep),
   258  			compute: func(in *statAggregate, out *metricValue) {
   259  				out.kind = metricKindUint64
   260  				out.scalar = in.sysStats.mCacheInUse
   261  			},
   262  		},
   263  		"/memory/classes/metadata/mspan/free:bytes": {
   264  			deps: makeStatDepSet(sysStatsDep),
   265  			compute: func(in *statAggregate, out *metricValue) {
   266  				out.kind = metricKindUint64
   267  				out.scalar = in.sysStats.mSpanSys - in.sysStats.mSpanInUse
   268  			},
   269  		},
   270  		"/memory/classes/metadata/mspan/inuse:bytes": {
   271  			deps: makeStatDepSet(sysStatsDep),
   272  			compute: func(in *statAggregate, out *metricValue) {
   273  				out.kind = metricKindUint64
   274  				out.scalar = in.sysStats.mSpanInUse
   275  			},
   276  		},
   277  		"/memory/classes/metadata/other:bytes": {
   278  			deps: makeStatDepSet(heapStatsDep, sysStatsDep),
   279  			compute: func(in *statAggregate, out *metricValue) {
   280  				out.kind = metricKindUint64
   281  				out.scalar = uint64(in.heapStats.inWorkBufs+in.heapStats.inPtrScalarBits) + in.sysStats.gcMiscSys
   282  			},
   283  		},
   284  		"/memory/classes/os-stacks:bytes": {
   285  			deps: makeStatDepSet(sysStatsDep),
   286  			compute: func(in *statAggregate, out *metricValue) {
   287  				out.kind = metricKindUint64
   288  				out.scalar = in.sysStats.stacksSys
   289  			},
   290  		},
   291  		"/memory/classes/other:bytes": {
   292  			deps: makeStatDepSet(sysStatsDep),
   293  			compute: func(in *statAggregate, out *metricValue) {
   294  				out.kind = metricKindUint64
   295  				out.scalar = in.sysStats.otherSys
   296  			},
   297  		},
   298  		"/memory/classes/profiling/buckets:bytes": {
   299  			deps: makeStatDepSet(sysStatsDep),
   300  			compute: func(in *statAggregate, out *metricValue) {
   301  				out.kind = metricKindUint64
   302  				out.scalar = in.sysStats.buckHashSys
   303  			},
   304  		},
   305  		"/memory/classes/total:bytes": {
   306  			deps: makeStatDepSet(heapStatsDep, sysStatsDep),
   307  			compute: func(in *statAggregate, out *metricValue) {
   308  				out.kind = metricKindUint64
   309  				out.scalar = uint64(in.heapStats.committed+in.heapStats.released) +
   310  					in.sysStats.stacksSys + in.sysStats.mSpanSys +
   311  					in.sysStats.mCacheSys + in.sysStats.buckHashSys +
   312  					in.sysStats.gcMiscSys + in.sysStats.otherSys
   313  			},
   314  		},
   315  		"/sched/gomaxprocs:threads": {
   316  			compute: func(_ *statAggregate, out *metricValue) {
   317  				out.kind = metricKindUint64
   318  				out.scalar = uint64(gomaxprocs)
   319  			},
   320  		},
   321  		"/sched/goroutines:goroutines": {
   322  			compute: func(_ *statAggregate, out *metricValue) {
   323  				out.kind = metricKindUint64
   324  				out.scalar = uint64(gcount())
   325  			},
   326  		},
   327  		"/sched/latencies:seconds": {
   328  			compute: func(_ *statAggregate, out *metricValue) {
   329  				hist := out.float64HistOrInit(timeHistBuckets)
   330  				hist.counts[0] = atomic.Load64(&sched.timeToRun.underflow)
   331  				for i := range sched.timeToRun.counts {
   332  					hist.counts[i+1] = atomic.Load64(&sched.timeToRun.counts[i])
   333  				}
   334  			},
   335  		},
   336  	}
   337  	metricsInit = true
   338  }
   339  
   340  // statDep is a dependency on a group of statistics
   341  // that a metric might have.
   342  type statDep uint
   343  
   344  const (
   345  	heapStatsDep statDep = iota // corresponds to heapStatsAggregate
   346  	sysStatsDep                 // corresponds to sysStatsAggregate
   347  	numStatsDeps
   348  )
   349  
   350  // statDepSet represents a set of statDeps.
   351  //
   352  // Under the hood, it's a bitmap.
   353  type statDepSet [1]uint64
   354  
   355  // makeStatDepSet creates a new statDepSet from a list of statDeps.
   356  func makeStatDepSet(deps ...statDep) statDepSet {
   357  	var s statDepSet
   358  	for _, d := range deps {
   359  		s[d/64] |= 1 << (d % 64)
   360  	}
   361  	return s
   362  }
   363  
   364  // differennce returns set difference of s from b as a new set.
   365  func (s statDepSet) difference(b statDepSet) statDepSet {
   366  	var c statDepSet
   367  	for i := range s {
   368  		c[i] = s[i] &^ b[i]
   369  	}
   370  	return c
   371  }
   372  
   373  // union returns the union of the two sets as a new set.
   374  func (s statDepSet) union(b statDepSet) statDepSet {
   375  	var c statDepSet
   376  	for i := range s {
   377  		c[i] = s[i] | b[i]
   378  	}
   379  	return c
   380  }
   381  
   382  // empty returns true if there are no dependencies in the set.
   383  func (s *statDepSet) empty() bool {
   384  	for _, c := range s {
   385  		if c != 0 {
   386  			return false
   387  		}
   388  	}
   389  	return true
   390  }
   391  
   392  // has returns true if the set contains a given statDep.
   393  func (s *statDepSet) has(d statDep) bool {
   394  	return s[d/64]&(1<<(d%64)) != 0
   395  }
   396  
   397  // heapStatsAggregate represents memory stats obtained from the
   398  // runtime. This set of stats is grouped together because they
   399  // depend on each other in some way to make sense of the runtime's
   400  // current heap memory use. They're also sharded across Ps, so it
   401  // makes sense to grab them all at once.
   402  type heapStatsAggregate struct {
   403  	heapStatsDelta
   404  
   405  	// Derived from values in heapStatsDelta.
   406  
   407  	// inObjects is the bytes of memory occupied by objects,
   408  	inObjects uint64
   409  
   410  	// numObjects is the number of live objects in the heap.
   411  	numObjects uint64
   412  
   413  	// totalAllocated is the total bytes of heap objects allocated
   414  	// over the lifetime of the program.
   415  	totalAllocated uint64
   416  
   417  	// totalFreed is the total bytes of heap objects freed
   418  	// over the lifetime of the program.
   419  	totalFreed uint64
   420  
   421  	// totalAllocs is the number of heap objects allocated over
   422  	// the lifetime of the program.
   423  	totalAllocs uint64
   424  
   425  	// totalFrees is the number of heap objects freed over
   426  	// the lifetime of the program.
   427  	totalFrees uint64
   428  }
   429  
   430  // compute populates the heapStatsAggregate with values from the runtime.
   431  func (a *heapStatsAggregate) compute() {
   432  	memstats.heapStats.read(&a.heapStatsDelta)
   433  
   434  	// Calculate derived stats.
   435  	a.totalAllocs = a.largeAllocCount
   436  	a.totalFrees = a.largeFreeCount
   437  	a.totalAllocated = a.largeAlloc
   438  	a.totalFreed = a.largeFree
   439  	for i := range a.smallAllocCount {
   440  		na := a.smallAllocCount[i]
   441  		nf := a.smallFreeCount[i]
   442  		a.totalAllocs += na
   443  		a.totalFrees += nf
   444  		a.totalAllocated += na * uint64(class_to_size[i])
   445  		a.totalFreed += nf * uint64(class_to_size[i])
   446  	}
   447  	a.inObjects = a.totalAllocated - a.totalFreed
   448  	a.numObjects = a.totalAllocs - a.totalFrees
   449  }
   450  
   451  // sysStatsAggregate represents system memory stats obtained
   452  // from the runtime. This set of stats is grouped together because
   453  // they're all relatively cheap to acquire and generally independent
   454  // of one another and other runtime memory stats. The fact that they
   455  // may be acquired at different times, especially with respect to
   456  // heapStatsAggregate, means there could be some skew, but because of
   457  // these stats are independent, there's no real consistency issue here.
   458  type sysStatsAggregate struct {
   459  	stacksSys      uint64
   460  	mSpanSys       uint64
   461  	mSpanInUse     uint64
   462  	mCacheSys      uint64
   463  	mCacheInUse    uint64
   464  	buckHashSys    uint64
   465  	gcMiscSys      uint64
   466  	otherSys       uint64
   467  	heapGoal       uint64
   468  	gcCyclesDone   uint64
   469  	gcCyclesForced uint64
   470  }
   471  
   472  // compute populates the sysStatsAggregate with values from the runtime.
   473  func (a *sysStatsAggregate) compute() {
   474  	a.stacksSys = memstats.stacks_sys.load()
   475  	a.buckHashSys = memstats.buckhash_sys.load()
   476  	a.gcMiscSys = memstats.gcMiscSys.load()
   477  	a.otherSys = memstats.other_sys.load()
   478  	a.heapGoal = gcController.heapGoal()
   479  	a.gcCyclesDone = uint64(memstats.numgc)
   480  	a.gcCyclesForced = uint64(memstats.numforcedgc)
   481  
   482  	systemstack(func() {
   483  		lock(&mheap_.lock)
   484  		a.mSpanSys = memstats.mspan_sys.load()
   485  		a.mSpanInUse = uint64(mheap_.spanalloc.inuse)
   486  		a.mCacheSys = memstats.mcache_sys.load()
   487  		a.mCacheInUse = uint64(mheap_.cachealloc.inuse)
   488  		unlock(&mheap_.lock)
   489  	})
   490  }
   491  
   492  // statAggregate is the main driver of the metrics implementation.
   493  //
   494  // It contains multiple aggregates of runtime statistics, as well
   495  // as a set of these aggregates that it has populated. The aggergates
   496  // are populated lazily by its ensure method.
   497  type statAggregate struct {
   498  	ensured   statDepSet
   499  	heapStats heapStatsAggregate
   500  	sysStats  sysStatsAggregate
   501  }
   502  
   503  // ensure populates statistics aggregates determined by deps if they
   504  // haven't yet been populated.
   505  func (a *statAggregate) ensure(deps *statDepSet) {
   506  	missing := deps.difference(a.ensured)
   507  	if missing.empty() {
   508  		return
   509  	}
   510  	for i := statDep(0); i < numStatsDeps; i++ {
   511  		if !missing.has(i) {
   512  			continue
   513  		}
   514  		switch i {
   515  		case heapStatsDep:
   516  			a.heapStats.compute()
   517  		case sysStatsDep:
   518  			a.sysStats.compute()
   519  		}
   520  	}
   521  	a.ensured = a.ensured.union(missing)
   522  }
   523  
   524  // metricValidKind is a runtime copy of runtime/metrics.ValueKind and
   525  // must be kept structurally identical to that type.
   526  type metricKind int
   527  
   528  const (
   529  	// These values must be kept identical to their corresponding Kind* values
   530  	// in the runtime/metrics package.
   531  	metricKindBad metricKind = iota
   532  	metricKindUint64
   533  	metricKindFloat64
   534  	metricKindFloat64Histogram
   535  )
   536  
   537  // metricSample is a runtime copy of runtime/metrics.Sample and
   538  // must be kept structurally identical to that type.
   539  type metricSample struct {
   540  	name  string
   541  	value metricValue
   542  }
   543  
   544  // metricValue is a runtime copy of runtime/metrics.Sample and
   545  // must be kept structurally identical to that type.
   546  type metricValue struct {
   547  	kind    metricKind
   548  	scalar  uint64         // contains scalar values for scalar Kinds.
   549  	pointer unsafe.Pointer // contains non-scalar values.
   550  }
   551  
   552  // float64HistOrInit tries to pull out an existing float64Histogram
   553  // from the value, but if none exists, then it allocates one with
   554  // the given buckets.
   555  func (v *metricValue) float64HistOrInit(buckets []float64) *metricFloat64Histogram {
   556  	var hist *metricFloat64Histogram
   557  	if v.kind == metricKindFloat64Histogram && v.pointer != nil {
   558  		hist = (*metricFloat64Histogram)(v.pointer)
   559  	} else {
   560  		v.kind = metricKindFloat64Histogram
   561  		hist = new(metricFloat64Histogram)
   562  		v.pointer = unsafe.Pointer(hist)
   563  	}
   564  	hist.buckets = buckets
   565  	if len(hist.counts) != len(hist.buckets)-1 {
   566  		hist.counts = make([]uint64, len(buckets)-1)
   567  	}
   568  	return hist
   569  }
   570  
   571  // metricFloat64Histogram is a runtime copy of runtime/metrics.Float64Histogram
   572  // and must be kept structurally identical to that type.
   573  type metricFloat64Histogram struct {
   574  	counts  []uint64
   575  	buckets []float64
   576  }
   577  
   578  // agg is used by readMetrics, and is protected by metricsSema.
   579  //
   580  // Managed as a global variable because its pointer will be
   581  // an argument to a dynamically-defined function, and we'd
   582  // like to avoid it escaping to the heap.
   583  var agg statAggregate
   584  
   585  // readMetrics is the implementation of runtime/metrics.Read.
   586  //
   587  //go:linkname readMetrics runtime/metrics.runtime_readMetrics
   588  func readMetrics(samplesp unsafe.Pointer, len int, cap int) {
   589  	// Construct a slice from the args.
   590  	sl := slice{samplesp, len, cap}
   591  	samples := *(*[]metricSample)(unsafe.Pointer(&sl))
   592  
   593  	metricsLock()
   594  
   595  	// Ensure the map is initialized.
   596  	initMetrics()
   597  
   598  	// Clear agg defensively.
   599  	agg = statAggregate{}
   600  
   601  	// Sample.
   602  	for i := range samples {
   603  		sample := &samples[i]
   604  		data, ok := metrics[sample.name]
   605  		if !ok {
   606  			sample.value.kind = metricKindBad
   607  			continue
   608  		}
   609  		// Ensure we have all the stats we need.
   610  		// agg is populated lazily.
   611  		agg.ensure(&data.deps)
   612  
   613  		// Compute the value based on the stats we have.
   614  		data.compute(&agg, &sample.value)
   615  	}
   616  
   617  	metricsUnlock()
   618  }
   619  

View as plain text