From 5b0982e48810c4d06d876f00b75b904cc5d075c2 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Mon, 14 Mar 2022 12:24:16 -0400 Subject: [PATCH] grunning: add library for precise on-CPU time measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Package grunning is a library that's able to retrieve on-CPU running time for individual goroutines. It relies on using a patched Go and provides a primitive for fine-grained CPU attribution and control through a single API: package grunning // Time returns the time spent by the current goroutine in the // running state. func Time() time.Duration The motivating RFC is over at #82356. Informs #82625. We build CRDB using use the patched Go runtime for all officially supported platforms when built using Bazel (#84867). Engineers commonly building CRDB also use happen to use two platforms we don't use a patched Go for: - FreeBSD (we don't have cross-compilers setup), and - M1/M2 Macs (we don't have a code-signing pipeline, yet). We use '(darwin && arm64) || freebsd || !bazel' as the build tag to exclude such platforms. See #84867 for more details. This package tests various properties we should expect over the running time value. It does not make assertions given the CI environments we run these under (CPU-starved, lot of OS thread pre-emption, dissimilar to healthy CRDB deployments). This is also why they're skipped under stress. Still, these tests are useful to understand the properties we expect running time to have: === RUN TestEquivalentGoroutines thread=03 expected≈10.00% got= 9.98% of on-cpu time thread=06 expected≈10.00% got=10.00% of on-cpu time thread=02 expected≈10.00% got=10.01% of on-cpu time thread=10 expected≈10.00% got=10.01% of on-cpu time thread=07 expected≈10.00% got= 9.99% of on-cpu time thread=04 expected≈10.00% got= 9.99% of on-cpu time thread=09 expected≈10.00% got=10.00% of on-cpu time thread=01 expected≈10.00% got= 9.99% of on-cpu time thread=08 expected≈10.00% got=10.02% of on-cpu time thread=05 expected≈10.00% got=10.02% of on-cpu time --- PASS: TestEquivalentGoroutines (0.56s) === RUN TestProportionalGoroutines thread=01 got 1.82% of on-cpu time: expected≈ 1.00x got=1.00x thread=02 got 3.64% of on-cpu time: expected≈ 2.00x got=2.00x thread=03 got 5.47% of on-cpu time: expected≈ 3.00x got=3.00x thread=04 got 7.28% of on-cpu time: expected≈ 4.00x got=4.00x thread=05 got 9.09% of on-cpu time: expected≈ 5.00x got=4.99x thread=06 got 10.91% of on-cpu time: expected≈ 6.00x got=5.99x thread=07 got 12.73% of on-cpu time: expected≈ 7.00x got=6.99x thread=08 got 14.54% of on-cpu time: expected≈ 8.00x got=7.99x thread=09 got 16.36% of on-cpu time: expected≈ 9.00x got=8.99x thread=10 got 18.16% of on-cpu time: expected≈10.00x got=9.97x --- PASS: TestProportionalGoroutines (1.72s) === RUN TestPingPongHog pinger/ponger expected≈1.00x got=0.96x --- PASS: TestPingPongHog (0.91s) Release note: None --- .github/CODEOWNERS | 2 + pkg/BUILD.bazel | 4 + pkg/util/grunning/BUILD.bazel | 284 +++++++++++++++++++++++++++++ pkg/util/grunning/disabled.go | 20 ++ pkg/util/grunning/disabled_test.go | 29 +++ pkg/util/grunning/enabled.go | 28 +++ pkg/util/grunning/enabled_test.go | 202 ++++++++++++++++++++ pkg/util/grunning/grunning.go | 43 +++++ 8 files changed, 612 insertions(+) create mode 100644 pkg/util/grunning/BUILD.bazel create mode 100644 pkg/util/grunning/disabled.go create mode 100644 pkg/util/grunning/disabled_test.go create mode 100644 pkg/util/grunning/enabled.go create mode 100644 pkg/util/grunning/enabled_test.go create mode 100644 pkg/util/grunning/grunning.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b78ce5af6e7a..39cadd872b58 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -352,6 +352,8 @@ /pkg/util/addr/ @cockroachdb/cli-prs @cockroachdb/obs-inf-prs /pkg/util/metric/ @cockroachdb/obs-inf-prs /pkg/util/stop/ @cockroachdb/kv-prs +/pkg/util/grunning/ @cockroachdb/kv-prs +/pkg/util/admission/ @cockroachdb/kv-prs /pkg/util/tracing @cockroachdb/obs-inf-prs /pkg/workload/ @cockroachdb/sql-experience-noreview /pkg/obsservice/ @cockroachdb/obs-inf-prs diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel index 178177aeff77..a7f90e2223e6 100644 --- a/pkg/BUILD.bazel +++ b/pkg/BUILD.bazel @@ -532,6 +532,7 @@ ALL_TESTS = [ "//pkg/util/fuzzystrmatch:fuzzystrmatch_test", "//pkg/util/goschedstats:goschedstats_test", "//pkg/util/grpcutil:grpcutil_test", + "//pkg/util/grunning:grunning_test", "//pkg/util/hlc:hlc_test", "//pkg/util/httputil:httputil_test", "//pkg/util/humanizeutil:humanizeutil_test", @@ -1866,6 +1867,8 @@ GO_TARGETS = [ "//pkg/util/growstack:growstack", "//pkg/util/grpcutil:grpcutil", "//pkg/util/grpcutil:grpcutil_test", + "//pkg/util/grunning:grunning", + "//pkg/util/grunning:grunning_test", "//pkg/util/hlc:hlc", "//pkg/util/hlc:hlc_test", "//pkg/util/httputil:httputil", @@ -2821,6 +2824,7 @@ GET_X_DATA_TARGETS = [ "//pkg/util/goschedstats:get_x_data", "//pkg/util/growstack:get_x_data", "//pkg/util/grpcutil:get_x_data", + "//pkg/util/grunning:get_x_data", "//pkg/util/hlc:get_x_data", "//pkg/util/httputil:get_x_data", "//pkg/util/humanizeutil:get_x_data", diff --git a/pkg/util/grunning/BUILD.bazel b/pkg/util/grunning/BUILD.bazel new file mode 100644 index 000000000000..9fe68e765990 --- /dev/null +++ b/pkg/util/grunning/BUILD.bazel @@ -0,0 +1,284 @@ +load("//build/bazelutil/unused_checker:unused.bzl", "get_x_data") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "grunning", + srcs = [ + "disabled.go", + "enabled.go", + "grunning.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/util/grunning", + visibility = ["//visibility:public"], +) + +go_test( + name = "grunning_test", + srcs = [ + "disabled_test.go", + "enabled_test.go", + ], + deps = select({ + "@io_bazel_rules_go//go/platform:aix_ppc64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:android_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:android_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:android_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:android_arm64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:darwin_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:darwin_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:darwin_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:darwin_arm64": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:dragonfly_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:freebsd_386": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:freebsd_amd64": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:freebsd_arm": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:freebsd_arm64": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:illumos_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:ios_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:ios_arm64": [ + ":grunning", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:js_wasm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_arm64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_mips": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_mips64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_mips64le": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_mipsle": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_ppc64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_ppc64le": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_riscv64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:linux_s390x": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:netbsd_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:netbsd_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:netbsd_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:netbsd_arm64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:openbsd_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:openbsd_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:openbsd_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:openbsd_arm64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:plan9_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:plan9_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:plan9_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:solaris_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:windows_386": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:windows_amd64": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "@io_bazel_rules_go//go/platform:windows_arm": [ + ":grunning", + "//pkg/testutils/skip", + "//pkg/util/syncutil", + "@com_github_stretchr_testify//require", + ], + "//conditions:default": [], + }), +) + +get_x_data(name = "get_x_data") diff --git a/pkg/util/grunning/disabled.go b/pkg/util/grunning/disabled.go new file mode 100644 index 000000000000..3cf943e508e5 --- /dev/null +++ b/pkg/util/grunning/disabled.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// See grunning.Supported() for an explanation behind this build tag. +// +//go:build (darwin && arm64) || freebsd || !bazel +// +build darwin,arm64 freebsd !bazel + +package grunning + +func grunningnanos() int64 { return 0 } + +func supported() bool { return false } diff --git a/pkg/util/grunning/disabled_test.go b/pkg/util/grunning/disabled_test.go new file mode 100644 index 000000000000..effc8d6f4948 --- /dev/null +++ b/pkg/util/grunning/disabled_test.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// See grunning.Supported() for an explanation behind this build tag. +// +//go:build (darwin && arm64) || freebsd || !bazel +// +build darwin,arm64 freebsd !bazel + +package grunning_test + +import ( + "testing" + + "github.com/cockroachdb/cockroach/pkg/util/grunning" + "github.com/stretchr/testify/require" +) + +func TestDisabled(t *testing.T) { + require.False(t, grunning.Supported()) + require.Zero(t, grunning.Time()) + require.Zero(t, grunning.Subtract(grunning.Time(), grunning.Time())) +} diff --git a/pkg/util/grunning/enabled.go b/pkg/util/grunning/enabled.go new file mode 100644 index 000000000000..48a4aad35eb2 --- /dev/null +++ b/pkg/util/grunning/enabled.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// See grunning.Supported() for an explanation behind this build tag. +// +//go:build !((darwin && arm64) || freebsd || !bazel) +// +build !darwin !arm64 +// +build !freebsd +// +build bazel + +package grunning + +import _ "unsafe" // for go:linkname + +// grunningnanos returns the running time observed by the current goroutine by +// linking to a private symbol in the (patched) runtime package. +// +//go:linkname grunningnanos runtime.grunningnanos +func grunningnanos() int64 + +func supported() bool { return true } diff --git a/pkg/util/grunning/enabled_test.go b/pkg/util/grunning/enabled_test.go new file mode 100644 index 000000000000..068a86c71e95 --- /dev/null +++ b/pkg/util/grunning/enabled_test.go @@ -0,0 +1,202 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// See grunning.Supported() for an explanation behind this build tag. +// +//go:build !((darwin && arm64) || freebsd || !bazel) +// +build !darwin !arm64 +// +build !freebsd +// +build bazel + +package grunning_test + +import ( + "runtime" + "sync" + "sync/atomic" + "testing" + + "github.com/cockroachdb/cockroach/pkg/testutils/skip" + "github.com/cockroachdb/cockroach/pkg/util/grunning" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "github.com/stretchr/testify/require" +) + +// This file tests various properties we should expect over the running time +// value. It does not make assertions given the CI environments we run these +// under (CPU-starved, lot of OS thread pre-emption, dissimilar to healthy CRDB +// deployments). This is also why they're skipped under stress. Still, these +// tests are useful to understand the properties we expect running time to have. + +func TestEnabled(t *testing.T) { + require.True(t, grunning.Supported()) +} + +// TestEquivalentGoroutines is a variant of the "parallel test" in +// https://github.com/golang/go/issues/36821. It tests whether goroutines that +// (should) spend the same amount of time on-CPU have similar measured on-CPU +// time. +func TestEquivalentGoroutines(t *testing.T) { + skip.UnderStress(t, "not applicable") + + mu := struct { + syncutil.Mutex + nanos map[int]int64 + }{} + mu.nanos = make(map[int]int64) + + f := func(wg *sync.WaitGroup, id int) { + defer wg.Done() + + var sum int + for i := 0; i < 500000000; i++ { + sum -= i / 2 + sum *= i + sum /= i/3 + 1 + sum -= i / 4 + } + + nanos := grunning.Time().Nanoseconds() + mu.Lock() + mu.nanos[id] = nanos + mu.Unlock() + } + + const threads = 10 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + i := i // copy loop variable + wg.Add(1) + go f(&wg, i) + } + wg.Wait() + + mu.Lock() + defer mu.Unlock() + + total := int64(0) + for _, nanos := range mu.nanos { + total += nanos + } + + exp := 1.0 / threads + for i, nanos := range mu.nanos { + got := float64(nanos) / float64(total) + + t.Logf("thread=%02d expected≈%5.2f%% got=%5.2f%% of on-cpu time", + i+1, exp*100, got*100) + } +} + +// TestProportionalGoroutines is a variant of the "serial test" in +// https://github.com/golang/go/issues/36821. It tests whether goroutines that +// (should) spend a proportion of time on-CPU actually do so as measured by this +// package. +func TestProportionalGoroutines(t *testing.T) { + skip.UnderStress(t, "not applicable") + + f := func(wg *sync.WaitGroup, v uint64, trip uint64, result *int64) { + defer wg.Done() + + ret := v + for i := trip; i > 0; i-- { + ret += i + ret = ret ^ (i + 0xcafebabe) + } + + nanos := grunning.Time().Nanoseconds() + atomic.AddInt64(result, nanos) + } + + results := make([]int64, 10) + var wg sync.WaitGroup + + for iters := 0; iters < 10000; iters++ { + for i := uint64(0); i < 10; i++ { + i := i // copy loop variable + wg.Add(1) + go f(&wg, i+1, (i+1)*100000, &results[i]) + } + } + + wg.Wait() + + total := int64(0) + for _, result := range results { + total += result + } + + initial := float64(results[0]) / float64(total) + + for i, result := range results { + got := float64(result) / float64(total) + mult := got / initial + t.Logf("thread=%02d got %5.2f%% of on-cpu time: expected≈%5.2fx got=%4.2fx ", + i+1, got*100, float64(i+1), mult) + } +} + +// TestPingPongHog is adapted from a benchmark in the Go runtime, forcing the +// scheduler to continually schedule goroutines. +func TestPingPongHog(t *testing.T) { + skip.UnderStress(t, "not applicable") + + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + // Create a CPU hog. + stop, done := make(chan bool), make(chan bool) + go func() { + for { + select { + case <-stop: + done <- true + return + default: //lint:ignore SA5004 empty default case is intentional, we want it to spin + } + } + }() + + // Ping-pong 1000000 times. + const large = 1000000 + ping, pong := make(chan bool), make(chan bool) + var pingern, pongern int64 + go func() { + for j := 0; j < large; j++ { + pong <- <-ping + } + pingern = grunning.Time().Nanoseconds() + close(stop) + done <- true + }() + go func() { + for i := 0; i < large; i++ { + ping <- <-pong + } + pongern = grunning.Time().Nanoseconds() + done <- true + }() + ping <- true // start ping-pong + <-stop + <-ping // let last ponger exit + <-done // make sure goroutines exit + <-done + <-done + + mult := float64(pingern) / float64(pongern) + t.Logf("pinger/ponger expected≈1.00x got=%0.2fx", mult) +} + +// BenchmarkGRunningTime measures how costly it is to read the current +// goroutine's running time. +func BenchmarkGRunningTime(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = grunning.Time() + } +} diff --git a/pkg/util/grunning/grunning.go b/pkg/util/grunning/grunning.go new file mode 100644 index 000000000000..c89306e0bb85 --- /dev/null +++ b/pkg/util/grunning/grunning.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// Package grunning is a library that's able to retrieve on-CPU running time for +// individual goroutines. It relies on using a patched Go and provides a +// primitive for fine-grained CPU attribution and control. See #82356 for more +// details. +package grunning + +import "time" + +// Time returns the time spent by the current goroutine in the running state. +func Time() time.Duration { + return time.Duration(grunningnanos()) +} + +// Subtract is a helper function to subtract a duration from another. It's +// commonly used to measure how much running time is spent doing some piece of +// work. +// +//gcassert:inline +func Subtract(end, start time.Duration) time.Duration { + return time.Duration(end.Nanoseconds() - start.Nanoseconds()) +} + +// Supported returns true iff per-goroutine running time is available in this +// build. We use a patched Go runtime for all platforms officially supported for +// CRDB when built using Bazel. Engineers commonly building CRDB also use happen +// to use two platforms we don't use a patched Go for: +// - FreeBSD (we don't have cross-compilers setup), and +// - M1/M2 Macs (we don't have a code-signing pipeline, yet). +// We use '(darwin && arm64) || freebsd || !bazel' as the build tag to exclude +// unsupported platforms. +func Supported() bool { + return supported() +}