github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/metrics.go (about) 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 "internal/godebugs" 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, waitReasonSemacquire) 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 "/cpu/classes/gc/mark/assist:cpu-seconds": { 95 deps: makeStatDepSet(cpuStatsDep), 96 compute: func(in *statAggregate, out *metricValue) { 97 out.kind = metricKindFloat64 98 out.scalar = float64bits(nsToSec(in.cpuStats.gcAssistTime)) 99 }, 100 }, 101 "/cpu/classes/gc/mark/dedicated:cpu-seconds": { 102 deps: makeStatDepSet(cpuStatsDep), 103 compute: func(in *statAggregate, out *metricValue) { 104 out.kind = metricKindFloat64 105 out.scalar = float64bits(nsToSec(in.cpuStats.gcDedicatedTime)) 106 }, 107 }, 108 "/cpu/classes/gc/mark/idle:cpu-seconds": { 109 deps: makeStatDepSet(cpuStatsDep), 110 compute: func(in *statAggregate, out *metricValue) { 111 out.kind = metricKindFloat64 112 out.scalar = float64bits(nsToSec(in.cpuStats.gcIdleTime)) 113 }, 114 }, 115 "/cpu/classes/gc/pause:cpu-seconds": { 116 deps: makeStatDepSet(cpuStatsDep), 117 compute: func(in *statAggregate, out *metricValue) { 118 out.kind = metricKindFloat64 119 out.scalar = float64bits(nsToSec(in.cpuStats.gcPauseTime)) 120 }, 121 }, 122 "/cpu/classes/gc/total:cpu-seconds": { 123 deps: makeStatDepSet(cpuStatsDep), 124 compute: func(in *statAggregate, out *metricValue) { 125 out.kind = metricKindFloat64 126 out.scalar = float64bits(nsToSec(in.cpuStats.gcTotalTime)) 127 }, 128 }, 129 "/cpu/classes/idle:cpu-seconds": { 130 deps: makeStatDepSet(cpuStatsDep), 131 compute: func(in *statAggregate, out *metricValue) { 132 out.kind = metricKindFloat64 133 out.scalar = float64bits(nsToSec(in.cpuStats.idleTime)) 134 }, 135 }, 136 "/cpu/classes/scavenge/assist:cpu-seconds": { 137 deps: makeStatDepSet(cpuStatsDep), 138 compute: func(in *statAggregate, out *metricValue) { 139 out.kind = metricKindFloat64 140 out.scalar = float64bits(nsToSec(in.cpuStats.scavengeAssistTime)) 141 }, 142 }, 143 "/cpu/classes/scavenge/background:cpu-seconds": { 144 deps: makeStatDepSet(cpuStatsDep), 145 compute: func(in *statAggregate, out *metricValue) { 146 out.kind = metricKindFloat64 147 out.scalar = float64bits(nsToSec(in.cpuStats.scavengeBgTime)) 148 }, 149 }, 150 "/cpu/classes/scavenge/total:cpu-seconds": { 151 deps: makeStatDepSet(cpuStatsDep), 152 compute: func(in *statAggregate, out *metricValue) { 153 out.kind = metricKindFloat64 154 out.scalar = float64bits(nsToSec(in.cpuStats.scavengeTotalTime)) 155 }, 156 }, 157 "/cpu/classes/total:cpu-seconds": { 158 deps: makeStatDepSet(cpuStatsDep), 159 compute: func(in *statAggregate, out *metricValue) { 160 out.kind = metricKindFloat64 161 out.scalar = float64bits(nsToSec(in.cpuStats.totalTime)) 162 }, 163 }, 164 "/cpu/classes/user:cpu-seconds": { 165 deps: makeStatDepSet(cpuStatsDep), 166 compute: func(in *statAggregate, out *metricValue) { 167 out.kind = metricKindFloat64 168 out.scalar = float64bits(nsToSec(in.cpuStats.userTime)) 169 }, 170 }, 171 "/gc/cycles/automatic:gc-cycles": { 172 deps: makeStatDepSet(sysStatsDep), 173 compute: func(in *statAggregate, out *metricValue) { 174 out.kind = metricKindUint64 175 out.scalar = in.sysStats.gcCyclesDone - in.sysStats.gcCyclesForced 176 }, 177 }, 178 "/gc/cycles/forced:gc-cycles": { 179 deps: makeStatDepSet(sysStatsDep), 180 compute: func(in *statAggregate, out *metricValue) { 181 out.kind = metricKindUint64 182 out.scalar = in.sysStats.gcCyclesForced 183 }, 184 }, 185 "/gc/cycles/total:gc-cycles": { 186 deps: makeStatDepSet(sysStatsDep), 187 compute: func(in *statAggregate, out *metricValue) { 188 out.kind = metricKindUint64 189 out.scalar = in.sysStats.gcCyclesDone 190 }, 191 }, 192 "/gc/scan/globals:bytes": { 193 deps: makeStatDepSet(gcStatsDep), 194 compute: func(in *statAggregate, out *metricValue) { 195 out.kind = metricKindUint64 196 out.scalar = in.gcStats.globalsScan 197 }, 198 }, 199 "/gc/scan/heap:bytes": { 200 deps: makeStatDepSet(gcStatsDep), 201 compute: func(in *statAggregate, out *metricValue) { 202 out.kind = metricKindUint64 203 out.scalar = in.gcStats.heapScan 204 }, 205 }, 206 "/gc/scan/stack:bytes": { 207 deps: makeStatDepSet(gcStatsDep), 208 compute: func(in *statAggregate, out *metricValue) { 209 out.kind = metricKindUint64 210 out.scalar = in.gcStats.stackScan 211 }, 212 }, 213 "/gc/scan/total:bytes": { 214 deps: makeStatDepSet(gcStatsDep), 215 compute: func(in *statAggregate, out *metricValue) { 216 out.kind = metricKindUint64 217 out.scalar = in.gcStats.totalScan 218 }, 219 }, 220 "/gc/heap/allocs-by-size:bytes": { 221 deps: makeStatDepSet(heapStatsDep), 222 compute: func(in *statAggregate, out *metricValue) { 223 hist := out.float64HistOrInit(sizeClassBuckets) 224 hist.counts[len(hist.counts)-1] = in.heapStats.largeAllocCount 225 // Cut off the first index which is ostensibly for size class 0, 226 // but large objects are tracked separately so it's actually unused. 227 for i, count := range in.heapStats.smallAllocCount[1:] { 228 hist.counts[i] = count 229 } 230 }, 231 }, 232 "/gc/heap/allocs:bytes": { 233 deps: makeStatDepSet(heapStatsDep), 234 compute: func(in *statAggregate, out *metricValue) { 235 out.kind = metricKindUint64 236 out.scalar = in.heapStats.totalAllocated 237 }, 238 }, 239 "/gc/heap/allocs:objects": { 240 deps: makeStatDepSet(heapStatsDep), 241 compute: func(in *statAggregate, out *metricValue) { 242 out.kind = metricKindUint64 243 out.scalar = in.heapStats.totalAllocs 244 }, 245 }, 246 "/gc/heap/frees-by-size:bytes": { 247 deps: makeStatDepSet(heapStatsDep), 248 compute: func(in *statAggregate, out *metricValue) { 249 hist := out.float64HistOrInit(sizeClassBuckets) 250 hist.counts[len(hist.counts)-1] = in.heapStats.largeFreeCount 251 // Cut off the first index which is ostensibly for size class 0, 252 // but large objects are tracked separately so it's actually unused. 253 for i, count := range in.heapStats.smallFreeCount[1:] { 254 hist.counts[i] = count 255 } 256 }, 257 }, 258 "/gc/heap/frees:bytes": { 259 deps: makeStatDepSet(heapStatsDep), 260 compute: func(in *statAggregate, out *metricValue) { 261 out.kind = metricKindUint64 262 out.scalar = in.heapStats.totalFreed 263 }, 264 }, 265 "/gc/heap/frees:objects": { 266 deps: makeStatDepSet(heapStatsDep), 267 compute: func(in *statAggregate, out *metricValue) { 268 out.kind = metricKindUint64 269 out.scalar = in.heapStats.totalFrees 270 }, 271 }, 272 "/gc/heap/goal:bytes": { 273 deps: makeStatDepSet(sysStatsDep), 274 compute: func(in *statAggregate, out *metricValue) { 275 out.kind = metricKindUint64 276 out.scalar = in.sysStats.heapGoal 277 }, 278 }, 279 "/gc/gomemlimit:bytes": { 280 compute: func(in *statAggregate, out *metricValue) { 281 out.kind = metricKindUint64 282 out.scalar = uint64(gcController.memoryLimit.Load()) 283 }, 284 }, 285 "/gc/gogc:percent": { 286 compute: func(in *statAggregate, out *metricValue) { 287 out.kind = metricKindUint64 288 out.scalar = uint64(gcController.gcPercent.Load()) 289 }, 290 }, 291 "/gc/heap/live:bytes": { 292 deps: makeStatDepSet(heapStatsDep), 293 compute: func(in *statAggregate, out *metricValue) { 294 out.kind = metricKindUint64 295 out.scalar = gcController.heapMarked 296 }, 297 }, 298 "/gc/heap/objects:objects": { 299 deps: makeStatDepSet(heapStatsDep), 300 compute: func(in *statAggregate, out *metricValue) { 301 out.kind = metricKindUint64 302 out.scalar = in.heapStats.numObjects 303 }, 304 }, 305 "/gc/heap/tiny/allocs:objects": { 306 deps: makeStatDepSet(heapStatsDep), 307 compute: func(in *statAggregate, out *metricValue) { 308 out.kind = metricKindUint64 309 out.scalar = in.heapStats.tinyAllocCount 310 }, 311 }, 312 "/gc/limiter/last-enabled:gc-cycle": { 313 compute: func(_ *statAggregate, out *metricValue) { 314 out.kind = metricKindUint64 315 out.scalar = uint64(gcCPULimiter.lastEnabledCycle.Load()) 316 }, 317 }, 318 "/gc/pauses:seconds": { 319 compute: func(_ *statAggregate, out *metricValue) { 320 // N.B. this is identical to /sched/pauses/total/gc:seconds. 321 sched.stwTotalTimeGC.write(out) 322 }, 323 }, 324 "/gc/stack/starting-size:bytes": { 325 compute: func(in *statAggregate, out *metricValue) { 326 out.kind = metricKindUint64 327 out.scalar = uint64(startingStackSize) 328 }, 329 }, 330 "/memory/classes/heap/free:bytes": { 331 deps: makeStatDepSet(heapStatsDep), 332 compute: func(in *statAggregate, out *metricValue) { 333 out.kind = metricKindUint64 334 out.scalar = uint64(in.heapStats.committed - in.heapStats.inHeap - 335 in.heapStats.inStacks - in.heapStats.inWorkBufs - 336 in.heapStats.inPtrScalarBits) 337 }, 338 }, 339 "/memory/classes/heap/objects:bytes": { 340 deps: makeStatDepSet(heapStatsDep), 341 compute: func(in *statAggregate, out *metricValue) { 342 out.kind = metricKindUint64 343 out.scalar = in.heapStats.inObjects 344 }, 345 }, 346 "/memory/classes/heap/released:bytes": { 347 deps: makeStatDepSet(heapStatsDep), 348 compute: func(in *statAggregate, out *metricValue) { 349 out.kind = metricKindUint64 350 out.scalar = uint64(in.heapStats.released) 351 }, 352 }, 353 "/memory/classes/heap/stacks:bytes": { 354 deps: makeStatDepSet(heapStatsDep), 355 compute: func(in *statAggregate, out *metricValue) { 356 out.kind = metricKindUint64 357 out.scalar = uint64(in.heapStats.inStacks) 358 }, 359 }, 360 "/memory/classes/heap/unused:bytes": { 361 deps: makeStatDepSet(heapStatsDep), 362 compute: func(in *statAggregate, out *metricValue) { 363 out.kind = metricKindUint64 364 out.scalar = uint64(in.heapStats.inHeap) - in.heapStats.inObjects 365 }, 366 }, 367 "/memory/classes/metadata/mcache/free:bytes": { 368 deps: makeStatDepSet(sysStatsDep), 369 compute: func(in *statAggregate, out *metricValue) { 370 out.kind = metricKindUint64 371 out.scalar = in.sysStats.mCacheSys - in.sysStats.mCacheInUse 372 }, 373 }, 374 "/memory/classes/metadata/mcache/inuse:bytes": { 375 deps: makeStatDepSet(sysStatsDep), 376 compute: func(in *statAggregate, out *metricValue) { 377 out.kind = metricKindUint64 378 out.scalar = in.sysStats.mCacheInUse 379 }, 380 }, 381 "/memory/classes/metadata/mspan/free:bytes": { 382 deps: makeStatDepSet(sysStatsDep), 383 compute: func(in *statAggregate, out *metricValue) { 384 out.kind = metricKindUint64 385 out.scalar = in.sysStats.mSpanSys - in.sysStats.mSpanInUse 386 }, 387 }, 388 "/memory/classes/metadata/mspan/inuse:bytes": { 389 deps: makeStatDepSet(sysStatsDep), 390 compute: func(in *statAggregate, out *metricValue) { 391 out.kind = metricKindUint64 392 out.scalar = in.sysStats.mSpanInUse 393 }, 394 }, 395 "/memory/classes/metadata/other:bytes": { 396 deps: makeStatDepSet(heapStatsDep, sysStatsDep), 397 compute: func(in *statAggregate, out *metricValue) { 398 out.kind = metricKindUint64 399 out.scalar = uint64(in.heapStats.inWorkBufs+in.heapStats.inPtrScalarBits) + in.sysStats.gcMiscSys 400 }, 401 }, 402 "/memory/classes/os-stacks:bytes": { 403 deps: makeStatDepSet(sysStatsDep), 404 compute: func(in *statAggregate, out *metricValue) { 405 out.kind = metricKindUint64 406 out.scalar = in.sysStats.stacksSys 407 }, 408 }, 409 "/memory/classes/other:bytes": { 410 deps: makeStatDepSet(sysStatsDep), 411 compute: func(in *statAggregate, out *metricValue) { 412 out.kind = metricKindUint64 413 out.scalar = in.sysStats.otherSys 414 }, 415 }, 416 "/memory/classes/profiling/buckets:bytes": { 417 deps: makeStatDepSet(sysStatsDep), 418 compute: func(in *statAggregate, out *metricValue) { 419 out.kind = metricKindUint64 420 out.scalar = in.sysStats.buckHashSys 421 }, 422 }, 423 "/memory/classes/total:bytes": { 424 deps: makeStatDepSet(heapStatsDep, sysStatsDep), 425 compute: func(in *statAggregate, out *metricValue) { 426 out.kind = metricKindUint64 427 out.scalar = uint64(in.heapStats.committed+in.heapStats.released) + 428 in.sysStats.stacksSys + in.sysStats.mSpanSys + 429 in.sysStats.mCacheSys + in.sysStats.buckHashSys + 430 in.sysStats.gcMiscSys + in.sysStats.otherSys 431 }, 432 }, 433 "/sched/gomaxprocs:threads": { 434 compute: func(_ *statAggregate, out *metricValue) { 435 out.kind = metricKindUint64 436 out.scalar = uint64(gomaxprocs) 437 }, 438 }, 439 "/sched/goroutines:goroutines": { 440 compute: func(_ *statAggregate, out *metricValue) { 441 out.kind = metricKindUint64 442 out.scalar = uint64(gcount()) 443 }, 444 }, 445 "/sched/latencies:seconds": { 446 compute: func(_ *statAggregate, out *metricValue) { 447 sched.timeToRun.write(out) 448 }, 449 }, 450 "/sched/pauses/stopping/gc:seconds": { 451 compute: func(_ *statAggregate, out *metricValue) { 452 sched.stwStoppingTimeGC.write(out) 453 }, 454 }, 455 "/sched/pauses/stopping/other:seconds": { 456 compute: func(_ *statAggregate, out *metricValue) { 457 sched.stwStoppingTimeOther.write(out) 458 }, 459 }, 460 "/sched/pauses/total/gc:seconds": { 461 compute: func(_ *statAggregate, out *metricValue) { 462 sched.stwTotalTimeGC.write(out) 463 }, 464 }, 465 "/sched/pauses/total/other:seconds": { 466 compute: func(_ *statAggregate, out *metricValue) { 467 sched.stwTotalTimeOther.write(out) 468 }, 469 }, 470 "/sync/mutex/wait/total:seconds": { 471 compute: func(_ *statAggregate, out *metricValue) { 472 out.kind = metricKindFloat64 473 out.scalar = float64bits(nsToSec(totalMutexWaitTimeNanos())) 474 }, 475 }, 476 } 477 478 for _, info := range godebugs.All { 479 if !info.Opaque { 480 metrics["/godebug/non-default-behavior/"+info.Name+":events"] = metricData{compute: compute0} 481 } 482 } 483 484 metricsInit = true 485 } 486 487 func compute0(_ *statAggregate, out *metricValue) { 488 out.kind = metricKindUint64 489 out.scalar = 0 490 } 491 492 type metricReader func() uint64 493 494 func (f metricReader) compute(_ *statAggregate, out *metricValue) { 495 out.kind = metricKindUint64 496 out.scalar = f() 497 } 498 499 //go:linkname godebug_registerMetric internal/godebug.registerMetric 500 func godebug_registerMetric(name string, read func() uint64) { 501 metricsLock() 502 initMetrics() 503 d, ok := metrics[name] 504 if !ok { 505 throw("runtime: unexpected metric registration for " + name) 506 } 507 d.compute = metricReader(read).compute 508 metrics[name] = d 509 metricsUnlock() 510 } 511 512 // statDep is a dependency on a group of statistics 513 // that a metric might have. 514 type statDep uint 515 516 const ( 517 heapStatsDep statDep = iota // corresponds to heapStatsAggregate 518 sysStatsDep // corresponds to sysStatsAggregate 519 cpuStatsDep // corresponds to cpuStatsAggregate 520 gcStatsDep // corresponds to gcStatsAggregate 521 numStatsDeps 522 ) 523 524 // statDepSet represents a set of statDeps. 525 // 526 // Under the hood, it's a bitmap. 527 type statDepSet [1]uint64 528 529 // makeStatDepSet creates a new statDepSet from a list of statDeps. 530 func makeStatDepSet(deps ...statDep) statDepSet { 531 var s statDepSet 532 for _, d := range deps { 533 s[d/64] |= 1 << (d % 64) 534 } 535 return s 536 } 537 538 // difference returns set difference of s from b as a new set. 539 func (s statDepSet) difference(b statDepSet) statDepSet { 540 var c statDepSet 541 for i := range s { 542 c[i] = s[i] &^ b[i] 543 } 544 return c 545 } 546 547 // union returns the union of the two sets as a new set. 548 func (s statDepSet) union(b statDepSet) statDepSet { 549 var c statDepSet 550 for i := range s { 551 c[i] = s[i] | b[i] 552 } 553 return c 554 } 555 556 // empty returns true if there are no dependencies in the set. 557 func (s *statDepSet) empty() bool { 558 for _, c := range s { 559 if c != 0 { 560 return false 561 } 562 } 563 return true 564 } 565 566 // has returns true if the set contains a given statDep. 567 func (s *statDepSet) has(d statDep) bool { 568 return s[d/64]&(1<<(d%64)) != 0 569 } 570 571 // heapStatsAggregate represents memory stats obtained from the 572 // runtime. This set of stats is grouped together because they 573 // depend on each other in some way to make sense of the runtime's 574 // current heap memory use. They're also sharded across Ps, so it 575 // makes sense to grab them all at once. 576 type heapStatsAggregate struct { 577 heapStatsDelta 578 579 // Derived from values in heapStatsDelta. 580 581 // inObjects is the bytes of memory occupied by objects, 582 inObjects uint64 583 584 // numObjects is the number of live objects in the heap. 585 numObjects uint64 586 587 // totalAllocated is the total bytes of heap objects allocated 588 // over the lifetime of the program. 589 totalAllocated uint64 590 591 // totalFreed is the total bytes of heap objects freed 592 // over the lifetime of the program. 593 totalFreed uint64 594 595 // totalAllocs is the number of heap objects allocated over 596 // the lifetime of the program. 597 totalAllocs uint64 598 599 // totalFrees is the number of heap objects freed over 600 // the lifetime of the program. 601 totalFrees uint64 602 } 603 604 // compute populates the heapStatsAggregate with values from the runtime. 605 func (a *heapStatsAggregate) compute() { 606 memstats.heapStats.read(&a.heapStatsDelta) 607 608 // Calculate derived stats. 609 a.totalAllocs = a.largeAllocCount 610 a.totalFrees = a.largeFreeCount 611 a.totalAllocated = a.largeAlloc 612 a.totalFreed = a.largeFree 613 for i := range a.smallAllocCount { 614 na := a.smallAllocCount[i] 615 nf := a.smallFreeCount[i] 616 a.totalAllocs += na 617 a.totalFrees += nf 618 a.totalAllocated += na * uint64(class_to_size[i]) 619 a.totalFreed += nf * uint64(class_to_size[i]) 620 } 621 a.inObjects = a.totalAllocated - a.totalFreed 622 a.numObjects = a.totalAllocs - a.totalFrees 623 } 624 625 // sysStatsAggregate represents system memory stats obtained 626 // from the runtime. This set of stats is grouped together because 627 // they're all relatively cheap to acquire and generally independent 628 // of one another and other runtime memory stats. The fact that they 629 // may be acquired at different times, especially with respect to 630 // heapStatsAggregate, means there could be some skew, but because of 631 // these stats are independent, there's no real consistency issue here. 632 type sysStatsAggregate struct { 633 stacksSys uint64 634 mSpanSys uint64 635 mSpanInUse uint64 636 mCacheSys uint64 637 mCacheInUse uint64 638 buckHashSys uint64 639 gcMiscSys uint64 640 otherSys uint64 641 heapGoal uint64 642 gcCyclesDone uint64 643 gcCyclesForced uint64 644 } 645 646 // compute populates the sysStatsAggregate with values from the runtime. 647 func (a *sysStatsAggregate) compute() { 648 a.stacksSys = memstats.stacks_sys.load() 649 a.buckHashSys = memstats.buckhash_sys.load() 650 a.gcMiscSys = memstats.gcMiscSys.load() 651 a.otherSys = memstats.other_sys.load() 652 a.heapGoal = gcController.heapGoal() 653 a.gcCyclesDone = uint64(memstats.numgc) 654 a.gcCyclesForced = uint64(memstats.numforcedgc) 655 656 systemstack(func() { 657 lock(&mheap_.lock) 658 a.mSpanSys = memstats.mspan_sys.load() 659 a.mSpanInUse = uint64(mheap_.spanalloc.inuse) 660 a.mCacheSys = memstats.mcache_sys.load() 661 a.mCacheInUse = uint64(mheap_.cachealloc.inuse) 662 unlock(&mheap_.lock) 663 }) 664 } 665 666 // cpuStatsAggregate represents CPU stats obtained from the runtime 667 // acquired together to avoid skew and inconsistencies. 668 type cpuStatsAggregate struct { 669 cpuStats 670 } 671 672 // compute populates the cpuStatsAggregate with values from the runtime. 673 func (a *cpuStatsAggregate) compute() { 674 a.cpuStats = work.cpuStats 675 // TODO(mknyszek): Update the CPU stats again so that we're not 676 // just relying on the STW snapshot. The issue here is that currently 677 // this will cause non-monotonicity in the "user" CPU time metric. 678 // 679 // a.cpuStats.accumulate(nanotime(), gcphase == _GCmark) 680 } 681 682 // gcStatsAggregate represents various GC stats obtained from the runtime 683 // acquired together to avoid skew and inconsistencies. 684 type gcStatsAggregate struct { 685 heapScan uint64 686 stackScan uint64 687 globalsScan uint64 688 totalScan uint64 689 } 690 691 // compute populates the gcStatsAggregate with values from the runtime. 692 func (a *gcStatsAggregate) compute() { 693 a.heapScan = gcController.heapScan.Load() 694 a.stackScan = gcController.lastStackScan.Load() 695 a.globalsScan = gcController.globalsScan.Load() 696 a.totalScan = a.heapScan + a.stackScan + a.globalsScan 697 } 698 699 // nsToSec takes a duration in nanoseconds and converts it to seconds as 700 // a float64. 701 func nsToSec(ns int64) float64 { 702 return float64(ns) / 1e9 703 } 704 705 // statAggregate is the main driver of the metrics implementation. 706 // 707 // It contains multiple aggregates of runtime statistics, as well 708 // as a set of these aggregates that it has populated. The aggregates 709 // are populated lazily by its ensure method. 710 type statAggregate struct { 711 ensured statDepSet 712 heapStats heapStatsAggregate 713 sysStats sysStatsAggregate 714 cpuStats cpuStatsAggregate 715 gcStats gcStatsAggregate 716 } 717 718 // ensure populates statistics aggregates determined by deps if they 719 // haven't yet been populated. 720 func (a *statAggregate) ensure(deps *statDepSet) { 721 missing := deps.difference(a.ensured) 722 if missing.empty() { 723 return 724 } 725 for i := statDep(0); i < numStatsDeps; i++ { 726 if !missing.has(i) { 727 continue 728 } 729 switch i { 730 case heapStatsDep: 731 a.heapStats.compute() 732 case sysStatsDep: 733 a.sysStats.compute() 734 case cpuStatsDep: 735 a.cpuStats.compute() 736 case gcStatsDep: 737 a.gcStats.compute() 738 } 739 } 740 a.ensured = a.ensured.union(missing) 741 } 742 743 // metricKind is a runtime copy of runtime/metrics.ValueKind and 744 // must be kept structurally identical to that type. 745 type metricKind int 746 747 const ( 748 // These values must be kept identical to their corresponding Kind* values 749 // in the runtime/metrics package. 750 metricKindBad metricKind = iota 751 metricKindUint64 752 metricKindFloat64 753 metricKindFloat64Histogram 754 ) 755 756 // metricSample is a runtime copy of runtime/metrics.Sample and 757 // must be kept structurally identical to that type. 758 type metricSample struct { 759 name string 760 value metricValue 761 } 762 763 // metricValue is a runtime copy of runtime/metrics.Sample and 764 // must be kept structurally identical to that type. 765 type metricValue struct { 766 kind metricKind 767 scalar uint64 // contains scalar values for scalar Kinds. 768 pointer unsafe.Pointer // contains non-scalar values. 769 } 770 771 // float64HistOrInit tries to pull out an existing float64Histogram 772 // from the value, but if none exists, then it allocates one with 773 // the given buckets. 774 func (v *metricValue) float64HistOrInit(buckets []float64) *metricFloat64Histogram { 775 var hist *metricFloat64Histogram 776 if v.kind == metricKindFloat64Histogram && v.pointer != nil { 777 hist = (*metricFloat64Histogram)(v.pointer) 778 } else { 779 v.kind = metricKindFloat64Histogram 780 hist = new(metricFloat64Histogram) 781 v.pointer = unsafe.Pointer(hist) 782 } 783 hist.buckets = buckets 784 if len(hist.counts) != len(hist.buckets)-1 { 785 hist.counts = make([]uint64, len(buckets)-1) 786 } 787 return hist 788 } 789 790 // metricFloat64Histogram is a runtime copy of runtime/metrics.Float64Histogram 791 // and must be kept structurally identical to that type. 792 type metricFloat64Histogram struct { 793 counts []uint64 794 buckets []float64 795 } 796 797 // agg is used by readMetrics, and is protected by metricsSema. 798 // 799 // Managed as a global variable because its pointer will be 800 // an argument to a dynamically-defined function, and we'd 801 // like to avoid it escaping to the heap. 802 var agg statAggregate 803 804 type metricName struct { 805 name string 806 kind metricKind 807 } 808 809 // readMetricNames is the implementation of runtime/metrics.readMetricNames, 810 // used by the runtime/metrics test and otherwise unreferenced. 811 // 812 //go:linkname readMetricNames runtime/metrics_test.runtime_readMetricNames 813 func readMetricNames() []string { 814 metricsLock() 815 initMetrics() 816 n := len(metrics) 817 metricsUnlock() 818 819 list := make([]string, 0, n) 820 821 metricsLock() 822 for name := range metrics { 823 list = append(list, name) 824 } 825 metricsUnlock() 826 827 return list 828 } 829 830 // readMetrics is the implementation of runtime/metrics.Read. 831 // 832 //go:linkname readMetrics runtime/metrics.runtime_readMetrics 833 func readMetrics(samplesp unsafe.Pointer, len int, cap int) { 834 metricsLock() 835 836 // Ensure the map is initialized. 837 initMetrics() 838 839 // Read the metrics. 840 readMetricsLocked(samplesp, len, cap) 841 metricsUnlock() 842 } 843 844 // readMetricsLocked is the internal, locked portion of readMetrics. 845 // 846 // Broken out for more robust testing. metricsLock must be held and 847 // initMetrics must have been called already. 848 func readMetricsLocked(samplesp unsafe.Pointer, len int, cap int) { 849 // Construct a slice from the args. 850 sl := slice{samplesp, len, cap} 851 samples := *(*[]metricSample)(unsafe.Pointer(&sl)) 852 853 // Clear agg defensively. 854 agg = statAggregate{} 855 856 // Sample. 857 for i := range samples { 858 sample := &samples[i] 859 data, ok := metrics[sample.name] 860 if !ok { 861 sample.value.kind = metricKindBad 862 continue 863 } 864 // Ensure we have all the stats we need. 865 // agg is populated lazily. 866 agg.ensure(&data.deps) 867 868 // Compute the value based on the stats we have. 869 data.compute(&agg, &sample.value) 870 } 871 }