cmd/go: add file with list of all counters we collect

Maintain a list of counters we collect and test that it hasn't
changed. If it has, fail a test and have the user update the list. The
update process will print a reminder to update the list of collected
counters.

Also run go mod vendor to pull in
golang.org/x/telemetry/counter/countertest.

For #58894

Change-Id: I661a9c3d67cb33f42a5519f4639af7aa05c3821d
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/go/+/564555
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Michael Matloob 2024-02-15 16:07:40 -05:00
parent de487d5616
commit c8718f6a3d
44 changed files with 7320 additions and 6 deletions

View file

@ -0,0 +1,89 @@
// Copyright 2024 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 main_test
import (
"cmd/go/internal/base"
"flag"
"internal/diff"
"os"
"slices"
"strings"
"testing"
)
var update = flag.Bool("update", false, "if true update testdata/counternames.txt")
func TestCounterNamesUpToDate(t *testing.T) {
if !*update {
t.Parallel()
}
var counters []string
// -C is a special case because it's handled by handleChdirFlag rather than
// standard flag processing with FlagSets.
// cmd/go/subcommand:unknown is also a special case: it's used when the subcommand
// doesn't match any of the known commands.
counters = append(counters, "cmd/go/flag:C", "cmd/go/subcommand:unknown")
counters = append(counters, flagscounters("cmd/go/flag:", *flag.CommandLine)...)
for _, cmd := range base.Go.Commands {
counters = append(counters, cmdcounters(nil, cmd)...)
}
cstr := []byte(strings.Join(counters, "\n") + "\n")
const counterNamesFile = "testdata/counters.txt"
old, err := os.ReadFile(counterNamesFile)
if err != nil {
t.Fatalf("error reading %s: %v", counterNamesFile, err)
}
diff := diff.Diff(counterNamesFile, old, "generated counter names", cstr)
if diff == nil {
t.Logf("%s is up to date.", counterNamesFile)
return
}
if *update {
if err := os.WriteFile(counterNamesFile, cstr, 0666); err != nil {
t.Fatal(err)
}
t.Logf("wrote %d bytes to %s", len(cstr), counterNamesFile)
t.Logf("don't forget to file a proposal to update the list of collected counters")
} else {
t.Logf("\n%s", diff)
t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", counterNamesFile)
}
}
func flagscounters(prefix string, flagSet flag.FlagSet) []string {
var counters []string
flagSet.VisitAll(func(f *flag.Flag) {
counters = append(counters, prefix+f.Name)
})
return counters
}
func cmdcounters(previous []string, cmd *base.Command) []string {
const subcommandPrefix = "cmd/go/subcommand:"
const flagPrefix = "cmd/go/flag:"
var counters []string
previousComponent := strings.Join(previous, "-")
if len(previousComponent) > 0 {
previousComponent += "-"
}
if cmd.Runnable() {
counters = append(counters, subcommandPrefix+previousComponent+cmd.Name())
}
counters = append(counters, flagscounters(flagPrefix+previousComponent+cmd.Name()+"-", cmd.Flag)...)
if len(previous) != 0 {
counters = append(counters, subcommandPrefix+previousComponent+"help-"+cmd.Name())
}
counters = append(counters, subcommandPrefix+"help-"+previousComponent+cmd.Name())
for _, subcmd := range cmd.Commands {
counters = append(counters, cmdcounters(append(slices.Clone(previous), cmd.Name()), subcmd)...)
}
return counters
}

View file

@ -44,6 +44,8 @@ import (
"cmd/internal/sys"
cmdgo "cmd/go"
"golang.org/x/telemetry/counter/countertest"
)
func init() {
@ -153,6 +155,15 @@ func TestMain(m *testing.M) {
web.EnableTestHooks(interceptors)
}
cmdgo.TelemetryStart = func() {
// TODO(matloob): we'll ideally want to call telemetry.Start here
// but it calls counter.Open, which we don't want to do because
// we want to call countertest.Open.
if telemetryDir := os.Getenv("TESTGO_TELEMETRY_DIR"); telemetryDir != "" {
countertest.Open(telemetryDir)
}
}
cmdgo.Main()
os.Exit(0)
}

View file

@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:generate go test cmd/go -v -run=^TestDocsUpToDate$ -fixdocs
//go:generate go test cmd/go -v -run=^TestCounterNamesUpToDate$ -update
package main
@ -91,7 +92,7 @@ var _ = go11tag
func main() {
log.SetFlags(0)
counter.Open() // Open the telemetry counter file so counters can be written to it.
TelemetryStart() // Open the telemetry counter file so counters can be written to it.
handleChdirFlag()
toolchain.Select()
@ -153,7 +154,6 @@ func main() {
cmd, used := lookupCmd(args)
cfg.CmdName = strings.Join(args[:used], " ")
counter.Inc("cmd/go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-"))
if len(cmd.Commands) > 0 {
if used >= len(args) {
help.PrintUsage(os.Stderr, cmd)
@ -162,6 +162,7 @@ func main() {
}
if args[used] == "help" {
// Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'.
counter.Inc("cmd/go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "-" + strings.Join(args[used:], "-"))
help.Help(os.Stdout, append(slices.Clip(args[:used]), args[used+1:]...))
base.Exit()
}
@ -173,10 +174,12 @@ func main() {
if cmdName == "" {
cmdName = args[0]
}
counter.Inc("cmd/go/subcommand:unknown")
fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cmdName, helpArg)
base.SetExitStatus(2)
base.Exit()
}
counter.Inc("cmd/go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-"))
invoke(cmd, args[used-1:])
base.Exit()
}
@ -241,7 +244,7 @@ func invoke(cmd *base.Command, args []string) {
} else {
base.SetFromGOFLAGS(&cmd.Flag)
cmd.Flag.Parse(args[1:])
counter.CountFlags("cmd/go/flag:"+cmd.Name()+"-", cmd.Flag)
counter.CountFlags("cmd/go/flag:"+strings.ReplaceAll(cfg.CmdName, " ", "-")+"-", cmd.Flag)
args = cmd.Flag.Args()
}
@ -326,7 +329,7 @@ func handleChdirFlag() {
_, dir, _ = strings.Cut(a, "=")
os.Args = slices.Delete(os.Args, used, used+1)
}
counter.Inc("cmd/go:flag-C")
counter.Inc("cmd/go/flag:C")
if err := os.Chdir(dir); err != nil {
base.Fatalf("go: %v", err)

View file

@ -13,6 +13,7 @@ import (
"bufio"
"bytes"
"context"
_ "embed"
"flag"
"internal/testenv"
"internal/txtar"
@ -21,6 +22,7 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
"time"
@ -29,6 +31,8 @@ import (
"cmd/go/internal/script"
"cmd/go/internal/script/scripttest"
"cmd/go/internal/vcweb/vcstest"
"golang.org/x/telemetry/counter/countertest"
)
var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`)
@ -124,7 +128,7 @@ func TestScript(t *testing.T) {
if err != nil {
t.Fatal(err)
}
initScriptDirs(t, s)
telemetryDir := initScriptDirs(t, s)
if err := s.ExtractFiles(a); err != nil {
t.Fatal(err)
}
@ -154,6 +158,7 @@ func TestScript(t *testing.T) {
// will work better seeing the full path relative to cmd/go
// (where the "go test" command is usually run).
scripttest.Run(t, engine, s, file, bytes.NewReader(a.Comment))
checkCounters(t, telemetryDir)
})
}
}
@ -177,7 +182,7 @@ func tbFromContext(ctx context.Context) (testing.TB, bool) {
// initScriptState creates the initial directory structure in s for unpacking a
// cmd/go script.
func initScriptDirs(t testing.TB, s *script.State) {
func initScriptDirs(t testing.TB, s *script.State) (telemetryDir string) {
must := func(err error) {
if err != nil {
t.Helper()
@ -188,6 +193,10 @@ func initScriptDirs(t testing.TB, s *script.State) {
work := s.Getwd()
must(s.Setenv("WORK", work))
telemetryDir = filepath.Join(work, "telemetry")
must(os.MkdirAll(telemetryDir, 0777))
must(s.Setenv("TESTGO_TELEMETRY_DIR", filepath.Join(work, "telemetry")))
must(os.MkdirAll(filepath.Join(work, "tmp"), 0777))
must(s.Setenv(tempEnvName(), filepath.Join(work, "tmp")))
@ -196,6 +205,7 @@ func initScriptDirs(t testing.TB, s *script.State) {
gopathSrc := filepath.Join(gopath, "src")
must(os.MkdirAll(gopathSrc, 0777))
must(s.Chdir(gopathSrc))
return telemetryDir
}
func scriptEnv(srv *vcstest.Server, srvCertFile string) ([]string, error) {
@ -357,3 +367,53 @@ func updateSum(t testing.TB, e *script.Engine, s *script.State, archive *txtar.A
}
return rewrite
}
func readCounters(t *testing.T, telemetryDir string) map[string]uint64 {
localDir := filepath.Join(telemetryDir, "local")
dirents, err := os.ReadDir(localDir)
if err != nil {
if os.IsNotExist(err) {
return nil // The Go command didn't ever run so the local dir wasn't created
}
t.Fatalf("reading telemetry local dir: %v", err)
}
totals := map[string]uint64{}
for _, dirent := range dirents {
if dirent.IsDir() || !strings.HasSuffix(dirent.Name(), ".count") {
// not a counter file
continue
}
counters, _, err := countertest.ReadFile(filepath.Join(localDir, dirent.Name()))
if err != nil {
t.Fatalf("reading counter file: %v", err)
}
for k, v := range counters {
totals[k] += v
}
}
return totals
}
//go:embed testdata/counters.txt
var countersTxt string
var (
allowedCountersOnce sync.Once
allowedCounters = map[string]bool{} // Set of allowed counters.
)
func checkCounters(t *testing.T, telemetryDir string) {
allowedCountersOnce.Do(func() {
for _, counter := range strings.Fields(countersTxt) {
allowedCounters[counter] = true
}
})
counters := readCounters(t, telemetryDir)
for name := range counters {
if !allowedCounters[name] {
t.Fatalf("incremented counter %q is not in testdata/counters.txt. "+
"Please update counters_test.go to produce an entry for it.", name)
}
}
}

13
src/cmd/go/telemetry.go Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2024 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.
//go:build !cmd_go_bootstrap
package main
import "golang.org/x/telemetry"
var TelemetryStart = func() {
telemetry.Start(telemetry.Config{Upload: true})
}

View file

@ -0,0 +1,9 @@
// Copyright 2024 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.
//go:build cmd_go_bootstrap
package main
var TelemetryStart = func() {}

661
src/cmd/go/testdata/counters.txt vendored Normal file
View file

@ -0,0 +1,661 @@
cmd/go/flag:C
cmd/go/subcommand:unknown
cmd/go/flag:fixdocs
cmd/go/flag:fixreadme
cmd/go/flag:flaky
cmd/go/flag:proxy
cmd/go/flag:test.bench
cmd/go/flag:test.benchmem
cmd/go/flag:test.benchtime
cmd/go/flag:test.blockprofile
cmd/go/flag:test.blockprofilerate
cmd/go/flag:test.count
cmd/go/flag:test.coverprofile
cmd/go/flag:test.cpu
cmd/go/flag:test.cpuprofile
cmd/go/flag:test.failfast
cmd/go/flag:test.fullpath
cmd/go/flag:test.fuzz
cmd/go/flag:test.fuzzcachedir
cmd/go/flag:test.fuzzminimizetime
cmd/go/flag:test.fuzztime
cmd/go/flag:test.fuzzworker
cmd/go/flag:test.gocoverdir
cmd/go/flag:test.list
cmd/go/flag:test.memprofile
cmd/go/flag:test.memprofilerate
cmd/go/flag:test.mutexprofile
cmd/go/flag:test.mutexprofilefraction
cmd/go/flag:test.outputdir
cmd/go/flag:test.paniconexit0
cmd/go/flag:test.parallel
cmd/go/flag:test.run
cmd/go/flag:test.short
cmd/go/flag:test.shuffle
cmd/go/flag:test.skip
cmd/go/flag:test.testlogfile
cmd/go/flag:test.timeout
cmd/go/flag:test.trace
cmd/go/flag:test.v
cmd/go/flag:testsum
cmd/go/flag:testwork
cmd/go/flag:update
cmd/go/subcommand:bug
cmd/go/flag:bug-C
cmd/go/flag:bug-v
cmd/go/subcommand:help-bug
cmd/go/subcommand:build
cmd/go/flag:build-C
cmd/go/flag:build-a
cmd/go/flag:build-asan
cmd/go/flag:build-asmflags
cmd/go/flag:build-buildmode
cmd/go/flag:build-buildvcs
cmd/go/flag:build-compiler
cmd/go/flag:build-cover
cmd/go/flag:build-covermode
cmd/go/flag:build-coverpkg
cmd/go/flag:build-debug-actiongraph
cmd/go/flag:build-debug-runtime-trace
cmd/go/flag:build-debug-trace
cmd/go/flag:build-gccgoflags
cmd/go/flag:build-gcflags
cmd/go/flag:build-installsuffix
cmd/go/flag:build-ldflags
cmd/go/flag:build-linkshared
cmd/go/flag:build-mod
cmd/go/flag:build-modcacherw
cmd/go/flag:build-modfile
cmd/go/flag:build-msan
cmd/go/flag:build-n
cmd/go/flag:build-o
cmd/go/flag:build-overlay
cmd/go/flag:build-p
cmd/go/flag:build-pgo
cmd/go/flag:build-pkgdir
cmd/go/flag:build-race
cmd/go/flag:build-tags
cmd/go/flag:build-toolexec
cmd/go/flag:build-trimpath
cmd/go/flag:build-v
cmd/go/flag:build-work
cmd/go/flag:build-x
cmd/go/subcommand:help-build
cmd/go/subcommand:clean
cmd/go/flag:clean-C
cmd/go/flag:clean-a
cmd/go/flag:clean-asan
cmd/go/flag:clean-asmflags
cmd/go/flag:clean-buildmode
cmd/go/flag:clean-buildvcs
cmd/go/flag:clean-cache
cmd/go/flag:clean-compiler
cmd/go/flag:clean-debug-actiongraph
cmd/go/flag:clean-debug-runtime-trace
cmd/go/flag:clean-debug-trace
cmd/go/flag:clean-fuzzcache
cmd/go/flag:clean-gccgoflags
cmd/go/flag:clean-gcflags
cmd/go/flag:clean-i
cmd/go/flag:clean-installsuffix
cmd/go/flag:clean-ldflags
cmd/go/flag:clean-linkshared
cmd/go/flag:clean-mod
cmd/go/flag:clean-modcache
cmd/go/flag:clean-modcacherw
cmd/go/flag:clean-modfile
cmd/go/flag:clean-msan
cmd/go/flag:clean-n
cmd/go/flag:clean-overlay
cmd/go/flag:clean-p
cmd/go/flag:clean-pgo
cmd/go/flag:clean-pkgdir
cmd/go/flag:clean-r
cmd/go/flag:clean-race
cmd/go/flag:clean-tags
cmd/go/flag:clean-testcache
cmd/go/flag:clean-toolexec
cmd/go/flag:clean-trimpath
cmd/go/flag:clean-v
cmd/go/flag:clean-work
cmd/go/flag:clean-x
cmd/go/subcommand:help-clean
cmd/go/subcommand:doc
cmd/go/subcommand:help-doc
cmd/go/subcommand:env
cmd/go/flag:env-C
cmd/go/flag:env-json
cmd/go/flag:env-n
cmd/go/flag:env-u
cmd/go/flag:env-w
cmd/go/flag:env-x
cmd/go/subcommand:help-env
cmd/go/subcommand:fix
cmd/go/flag:fix-C
cmd/go/flag:fix-a
cmd/go/flag:fix-asan
cmd/go/flag:fix-asmflags
cmd/go/flag:fix-buildmode
cmd/go/flag:fix-buildvcs
cmd/go/flag:fix-compiler
cmd/go/flag:fix-debug-actiongraph
cmd/go/flag:fix-debug-runtime-trace
cmd/go/flag:fix-debug-trace
cmd/go/flag:fix-fix
cmd/go/flag:fix-gccgoflags
cmd/go/flag:fix-gcflags
cmd/go/flag:fix-installsuffix
cmd/go/flag:fix-ldflags
cmd/go/flag:fix-linkshared
cmd/go/flag:fix-mod
cmd/go/flag:fix-modcacherw
cmd/go/flag:fix-modfile
cmd/go/flag:fix-msan
cmd/go/flag:fix-n
cmd/go/flag:fix-overlay
cmd/go/flag:fix-p
cmd/go/flag:fix-pgo
cmd/go/flag:fix-pkgdir
cmd/go/flag:fix-race
cmd/go/flag:fix-tags
cmd/go/flag:fix-toolexec
cmd/go/flag:fix-trimpath
cmd/go/flag:fix-v
cmd/go/flag:fix-work
cmd/go/flag:fix-x
cmd/go/subcommand:help-fix
cmd/go/subcommand:fmt
cmd/go/flag:fmt-C
cmd/go/flag:fmt-mod
cmd/go/flag:fmt-modcacherw
cmd/go/flag:fmt-modfile
cmd/go/flag:fmt-n
cmd/go/flag:fmt-overlay
cmd/go/flag:fmt-x
cmd/go/subcommand:help-fmt
cmd/go/subcommand:generate
cmd/go/flag:generate-C
cmd/go/flag:generate-a
cmd/go/flag:generate-asan
cmd/go/flag:generate-asmflags
cmd/go/flag:generate-buildmode
cmd/go/flag:generate-buildvcs
cmd/go/flag:generate-compiler
cmd/go/flag:generate-debug-actiongraph
cmd/go/flag:generate-debug-runtime-trace
cmd/go/flag:generate-debug-trace
cmd/go/flag:generate-gccgoflags
cmd/go/flag:generate-gcflags
cmd/go/flag:generate-installsuffix
cmd/go/flag:generate-ldflags
cmd/go/flag:generate-linkshared
cmd/go/flag:generate-mod
cmd/go/flag:generate-modcacherw
cmd/go/flag:generate-modfile
cmd/go/flag:generate-msan
cmd/go/flag:generate-n
cmd/go/flag:generate-overlay
cmd/go/flag:generate-p
cmd/go/flag:generate-pgo
cmd/go/flag:generate-pkgdir
cmd/go/flag:generate-race
cmd/go/flag:generate-run
cmd/go/flag:generate-skip
cmd/go/flag:generate-tags
cmd/go/flag:generate-toolexec
cmd/go/flag:generate-trimpath
cmd/go/flag:generate-v
cmd/go/flag:generate-work
cmd/go/flag:generate-x
cmd/go/subcommand:help-generate
cmd/go/subcommand:get
cmd/go/flag:get-C
cmd/go/flag:get-a
cmd/go/flag:get-asan
cmd/go/flag:get-asmflags
cmd/go/flag:get-buildmode
cmd/go/flag:get-buildvcs
cmd/go/flag:get-compiler
cmd/go/flag:get-d
cmd/go/flag:get-debug-actiongraph
cmd/go/flag:get-debug-runtime-trace
cmd/go/flag:get-debug-trace
cmd/go/flag:get-f
cmd/go/flag:get-fix
cmd/go/flag:get-gccgoflags
cmd/go/flag:get-gcflags
cmd/go/flag:get-insecure
cmd/go/flag:get-installsuffix
cmd/go/flag:get-ldflags
cmd/go/flag:get-linkshared
cmd/go/flag:get-m
cmd/go/flag:get-modcacherw
cmd/go/flag:get-modfile
cmd/go/flag:get-msan
cmd/go/flag:get-n
cmd/go/flag:get-overlay
cmd/go/flag:get-p
cmd/go/flag:get-pgo
cmd/go/flag:get-pkgdir
cmd/go/flag:get-race
cmd/go/flag:get-t
cmd/go/flag:get-tags
cmd/go/flag:get-toolexec
cmd/go/flag:get-trimpath
cmd/go/flag:get-u
cmd/go/flag:get-v
cmd/go/flag:get-work
cmd/go/flag:get-x
cmd/go/subcommand:help-get
cmd/go/subcommand:install
cmd/go/flag:install-C
cmd/go/flag:install-a
cmd/go/flag:install-asan
cmd/go/flag:install-asmflags
cmd/go/flag:install-buildmode
cmd/go/flag:install-buildvcs
cmd/go/flag:install-compiler
cmd/go/flag:install-cover
cmd/go/flag:install-covermode
cmd/go/flag:install-coverpkg
cmd/go/flag:install-debug-actiongraph
cmd/go/flag:install-debug-runtime-trace
cmd/go/flag:install-debug-trace
cmd/go/flag:install-gccgoflags
cmd/go/flag:install-gcflags
cmd/go/flag:install-installsuffix
cmd/go/flag:install-ldflags
cmd/go/flag:install-linkshared
cmd/go/flag:install-mod
cmd/go/flag:install-modcacherw
cmd/go/flag:install-modfile
cmd/go/flag:install-msan
cmd/go/flag:install-n
cmd/go/flag:install-overlay
cmd/go/flag:install-p
cmd/go/flag:install-pgo
cmd/go/flag:install-pkgdir
cmd/go/flag:install-race
cmd/go/flag:install-tags
cmd/go/flag:install-toolexec
cmd/go/flag:install-trimpath
cmd/go/flag:install-v
cmd/go/flag:install-work
cmd/go/flag:install-x
cmd/go/subcommand:help-install
cmd/go/subcommand:list
cmd/go/flag:list-C
cmd/go/flag:list-a
cmd/go/flag:list-asan
cmd/go/flag:list-asmflags
cmd/go/flag:list-buildmode
cmd/go/flag:list-buildvcs
cmd/go/flag:list-compiled
cmd/go/flag:list-compiler
cmd/go/flag:list-cover
cmd/go/flag:list-covermode
cmd/go/flag:list-coverpkg
cmd/go/flag:list-debug-actiongraph
cmd/go/flag:list-debug-runtime-trace
cmd/go/flag:list-debug-trace
cmd/go/flag:list-deps
cmd/go/flag:list-e
cmd/go/flag:list-export
cmd/go/flag:list-f
cmd/go/flag:list-find
cmd/go/flag:list-gccgoflags
cmd/go/flag:list-gcflags
cmd/go/flag:list-installsuffix
cmd/go/flag:list-json
cmd/go/flag:list-ldflags
cmd/go/flag:list-linkshared
cmd/go/flag:list-m
cmd/go/flag:list-mod
cmd/go/flag:list-modcacherw
cmd/go/flag:list-modfile
cmd/go/flag:list-msan
cmd/go/flag:list-n
cmd/go/flag:list-overlay
cmd/go/flag:list-p
cmd/go/flag:list-pgo
cmd/go/flag:list-pkgdir
cmd/go/flag:list-race
cmd/go/flag:list-retracted
cmd/go/flag:list-reuse
cmd/go/flag:list-tags
cmd/go/flag:list-test
cmd/go/flag:list-toolexec
cmd/go/flag:list-trimpath
cmd/go/flag:list-u
cmd/go/flag:list-v
cmd/go/flag:list-versions
cmd/go/flag:list-work
cmd/go/flag:list-x
cmd/go/subcommand:help-list
cmd/go/subcommand:help-mod
cmd/go/subcommand:mod-download
cmd/go/flag:mod-download-C
cmd/go/flag:mod-download-json
cmd/go/flag:mod-download-modcacherw
cmd/go/flag:mod-download-modfile
cmd/go/flag:mod-download-overlay
cmd/go/flag:mod-download-reuse
cmd/go/flag:mod-download-x
cmd/go/subcommand:mod-help-download
cmd/go/subcommand:help-mod-download
cmd/go/subcommand:mod-edit
cmd/go/flag:mod-edit-C
cmd/go/flag:mod-edit-dropexclude
cmd/go/flag:mod-edit-dropreplace
cmd/go/flag:mod-edit-droprequire
cmd/go/flag:mod-edit-dropretract
cmd/go/flag:mod-edit-exclude
cmd/go/flag:mod-edit-fmt
cmd/go/flag:mod-edit-go
cmd/go/flag:mod-edit-json
cmd/go/flag:mod-edit-modcacherw
cmd/go/flag:mod-edit-modfile
cmd/go/flag:mod-edit-module
cmd/go/flag:mod-edit-n
cmd/go/flag:mod-edit-overlay
cmd/go/flag:mod-edit-print
cmd/go/flag:mod-edit-replace
cmd/go/flag:mod-edit-require
cmd/go/flag:mod-edit-retract
cmd/go/flag:mod-edit-toolchain
cmd/go/flag:mod-edit-x
cmd/go/subcommand:mod-help-edit
cmd/go/subcommand:help-mod-edit
cmd/go/subcommand:mod-graph
cmd/go/flag:mod-graph-C
cmd/go/flag:mod-graph-go
cmd/go/flag:mod-graph-modcacherw
cmd/go/flag:mod-graph-modfile
cmd/go/flag:mod-graph-overlay
cmd/go/flag:mod-graph-x
cmd/go/subcommand:mod-help-graph
cmd/go/subcommand:help-mod-graph
cmd/go/subcommand:mod-init
cmd/go/flag:mod-init-C
cmd/go/flag:mod-init-modcacherw
cmd/go/flag:mod-init-modfile
cmd/go/flag:mod-init-overlay
cmd/go/subcommand:mod-help-init
cmd/go/subcommand:help-mod-init
cmd/go/subcommand:mod-tidy
cmd/go/flag:mod-tidy-C
cmd/go/flag:mod-tidy-compat
cmd/go/flag:mod-tidy-e
cmd/go/flag:mod-tidy-go
cmd/go/flag:mod-tidy-modcacherw
cmd/go/flag:mod-tidy-modfile
cmd/go/flag:mod-tidy-overlay
cmd/go/flag:mod-tidy-v
cmd/go/flag:mod-tidy-x
cmd/go/subcommand:mod-help-tidy
cmd/go/subcommand:help-mod-tidy
cmd/go/subcommand:mod-vendor
cmd/go/flag:mod-vendor-C
cmd/go/flag:mod-vendor-e
cmd/go/flag:mod-vendor-modcacherw
cmd/go/flag:mod-vendor-modfile
cmd/go/flag:mod-vendor-o
cmd/go/flag:mod-vendor-overlay
cmd/go/flag:mod-vendor-v
cmd/go/subcommand:mod-help-vendor
cmd/go/subcommand:help-mod-vendor
cmd/go/subcommand:mod-verify
cmd/go/flag:mod-verify-C
cmd/go/flag:mod-verify-modcacherw
cmd/go/flag:mod-verify-modfile
cmd/go/flag:mod-verify-overlay
cmd/go/subcommand:mod-help-verify
cmd/go/subcommand:help-mod-verify
cmd/go/subcommand:mod-why
cmd/go/flag:mod-why-C
cmd/go/flag:mod-why-m
cmd/go/flag:mod-why-modcacherw
cmd/go/flag:mod-why-modfile
cmd/go/flag:mod-why-overlay
cmd/go/flag:mod-why-vendor
cmd/go/subcommand:mod-help-why
cmd/go/subcommand:help-mod-why
cmd/go/subcommand:help-work
cmd/go/subcommand:work-edit
cmd/go/flag:work-edit-C
cmd/go/flag:work-edit-dropreplace
cmd/go/flag:work-edit-dropuse
cmd/go/flag:work-edit-fmt
cmd/go/flag:work-edit-go
cmd/go/flag:work-edit-json
cmd/go/flag:work-edit-print
cmd/go/flag:work-edit-replace
cmd/go/flag:work-edit-toolchain
cmd/go/flag:work-edit-use
cmd/go/subcommand:work-help-edit
cmd/go/subcommand:help-work-edit
cmd/go/subcommand:work-init
cmd/go/flag:work-init-C
cmd/go/flag:work-init-modcacherw
cmd/go/flag:work-init-modfile
cmd/go/flag:work-init-overlay
cmd/go/subcommand:work-help-init
cmd/go/subcommand:help-work-init
cmd/go/subcommand:work-sync
cmd/go/flag:work-sync-C
cmd/go/flag:work-sync-modcacherw
cmd/go/flag:work-sync-modfile
cmd/go/flag:work-sync-overlay
cmd/go/subcommand:work-help-sync
cmd/go/subcommand:help-work-sync
cmd/go/subcommand:work-use
cmd/go/flag:work-use-C
cmd/go/flag:work-use-modcacherw
cmd/go/flag:work-use-modfile
cmd/go/flag:work-use-overlay
cmd/go/flag:work-use-r
cmd/go/subcommand:work-help-use
cmd/go/subcommand:help-work-use
cmd/go/subcommand:work-vendor
cmd/go/flag:work-vendor-C
cmd/go/flag:work-vendor-e
cmd/go/flag:work-vendor-modcacherw
cmd/go/flag:work-vendor-modfile
cmd/go/flag:work-vendor-o
cmd/go/flag:work-vendor-overlay
cmd/go/flag:work-vendor-v
cmd/go/subcommand:work-help-vendor
cmd/go/subcommand:help-work-vendor
cmd/go/subcommand:run
cmd/go/flag:run-C
cmd/go/flag:run-a
cmd/go/flag:run-asan
cmd/go/flag:run-asmflags
cmd/go/flag:run-buildmode
cmd/go/flag:run-buildvcs
cmd/go/flag:run-compiler
cmd/go/flag:run-cover
cmd/go/flag:run-covermode
cmd/go/flag:run-coverpkg
cmd/go/flag:run-debug-actiongraph
cmd/go/flag:run-debug-runtime-trace
cmd/go/flag:run-debug-trace
cmd/go/flag:run-exec
cmd/go/flag:run-gccgoflags
cmd/go/flag:run-gcflags
cmd/go/flag:run-installsuffix
cmd/go/flag:run-ldflags
cmd/go/flag:run-linkshared
cmd/go/flag:run-mod
cmd/go/flag:run-modcacherw
cmd/go/flag:run-modfile
cmd/go/flag:run-msan
cmd/go/flag:run-n
cmd/go/flag:run-overlay
cmd/go/flag:run-p
cmd/go/flag:run-pgo
cmd/go/flag:run-pkgdir
cmd/go/flag:run-race
cmd/go/flag:run-tags
cmd/go/flag:run-toolexec
cmd/go/flag:run-trimpath
cmd/go/flag:run-v
cmd/go/flag:run-work
cmd/go/flag:run-x
cmd/go/subcommand:help-run
cmd/go/subcommand:test
cmd/go/flag:test-C
cmd/go/flag:test-a
cmd/go/flag:test-asan
cmd/go/flag:test-asmflags
cmd/go/flag:test-bench
cmd/go/flag:test-benchmem
cmd/go/flag:test-benchtime
cmd/go/flag:test-blockprofile
cmd/go/flag:test-blockprofilerate
cmd/go/flag:test-buildmode
cmd/go/flag:test-buildvcs
cmd/go/flag:test-c
cmd/go/flag:test-compiler
cmd/go/flag:test-count
cmd/go/flag:test-cover
cmd/go/flag:test-covermode
cmd/go/flag:test-coverpkg
cmd/go/flag:test-coverprofile
cmd/go/flag:test-cpu
cmd/go/flag:test-cpuprofile
cmd/go/flag:test-debug-actiongraph
cmd/go/flag:test-debug-runtime-trace
cmd/go/flag:test-debug-trace
cmd/go/flag:test-exec
cmd/go/flag:test-failfast
cmd/go/flag:test-fullpath
cmd/go/flag:test-fuzz
cmd/go/flag:test-fuzzminimizetime
cmd/go/flag:test-fuzztime
cmd/go/flag:test-gccgoflags
cmd/go/flag:test-gcflags
cmd/go/flag:test-installsuffix
cmd/go/flag:test-json
cmd/go/flag:test-ldflags
cmd/go/flag:test-linkshared
cmd/go/flag:test-list
cmd/go/flag:test-memprofile
cmd/go/flag:test-memprofilerate
cmd/go/flag:test-mod
cmd/go/flag:test-modcacherw
cmd/go/flag:test-modfile
cmd/go/flag:test-msan
cmd/go/flag:test-mutexprofile
cmd/go/flag:test-mutexprofilefraction
cmd/go/flag:test-n
cmd/go/flag:test-o
cmd/go/flag:test-outputdir
cmd/go/flag:test-overlay
cmd/go/flag:test-p
cmd/go/flag:test-parallel
cmd/go/flag:test-pgo
cmd/go/flag:test-pkgdir
cmd/go/flag:test-race
cmd/go/flag:test-run
cmd/go/flag:test-short
cmd/go/flag:test-shuffle
cmd/go/flag:test-skip
cmd/go/flag:test-tags
cmd/go/flag:test-test.bench
cmd/go/flag:test-test.benchmem
cmd/go/flag:test-test.benchtime
cmd/go/flag:test-test.blockprofile
cmd/go/flag:test-test.blockprofilerate
cmd/go/flag:test-test.count
cmd/go/flag:test-test.coverprofile
cmd/go/flag:test-test.cpu
cmd/go/flag:test-test.cpuprofile
cmd/go/flag:test-test.failfast
cmd/go/flag:test-test.fullpath
cmd/go/flag:test-test.fuzz
cmd/go/flag:test-test.fuzzminimizetime
cmd/go/flag:test-test.fuzztime
cmd/go/flag:test-test.list
cmd/go/flag:test-test.memprofile
cmd/go/flag:test-test.memprofilerate
cmd/go/flag:test-test.mutexprofile
cmd/go/flag:test-test.mutexprofilefraction
cmd/go/flag:test-test.outputdir
cmd/go/flag:test-test.parallel
cmd/go/flag:test-test.run
cmd/go/flag:test-test.short
cmd/go/flag:test-test.shuffle
cmd/go/flag:test-test.skip
cmd/go/flag:test-test.timeout
cmd/go/flag:test-test.trace
cmd/go/flag:test-test.v
cmd/go/flag:test-timeout
cmd/go/flag:test-toolexec
cmd/go/flag:test-trace
cmd/go/flag:test-trimpath
cmd/go/flag:test-v
cmd/go/flag:test-vet
cmd/go/flag:test-work
cmd/go/flag:test-x
cmd/go/subcommand:help-test
cmd/go/subcommand:tool
cmd/go/flag:tool-C
cmd/go/flag:tool-n
cmd/go/subcommand:help-tool
cmd/go/subcommand:version
cmd/go/flag:version-C
cmd/go/flag:version-m
cmd/go/flag:version-v
cmd/go/subcommand:help-version
cmd/go/subcommand:vet
cmd/go/flag:vet-C
cmd/go/flag:vet-a
cmd/go/flag:vet-asan
cmd/go/flag:vet-asmflags
cmd/go/flag:vet-buildmode
cmd/go/flag:vet-buildvcs
cmd/go/flag:vet-compiler
cmd/go/flag:vet-debug-actiongraph
cmd/go/flag:vet-debug-runtime-trace
cmd/go/flag:vet-debug-trace
cmd/go/flag:vet-gccgoflags
cmd/go/flag:vet-gcflags
cmd/go/flag:vet-installsuffix
cmd/go/flag:vet-ldflags
cmd/go/flag:vet-linkshared
cmd/go/flag:vet-mod
cmd/go/flag:vet-modcacherw
cmd/go/flag:vet-modfile
cmd/go/flag:vet-msan
cmd/go/flag:vet-n
cmd/go/flag:vet-overlay
cmd/go/flag:vet-p
cmd/go/flag:vet-pgo
cmd/go/flag:vet-pkgdir
cmd/go/flag:vet-race
cmd/go/flag:vet-tags
cmd/go/flag:vet-toolexec
cmd/go/flag:vet-trimpath
cmd/go/flag:vet-v
cmd/go/flag:vet-vettool
cmd/go/flag:vet-work
cmd/go/flag:vet-x
cmd/go/subcommand:help-vet
cmd/go/subcommand:help-buildconstraint
cmd/go/subcommand:help-buildmode
cmd/go/subcommand:help-c
cmd/go/subcommand:help-cache
cmd/go/subcommand:help-environment
cmd/go/subcommand:help-filetype
cmd/go/subcommand:help-go.mod
cmd/go/subcommand:help-gopath
cmd/go/subcommand:help-goproxy
cmd/go/subcommand:help-importpath
cmd/go/subcommand:help-modules
cmd/go/subcommand:help-module-auth
cmd/go/subcommand:help-packages
cmd/go/subcommand:help-private
cmd/go/subcommand:help-testflag
cmd/go/subcommand:help-testfunc
cmd/go/subcommand:help-vcs

135
src/cmd/vendor/golang.org/x/sync/errgroup/errgroup.go generated vendored Normal file
View file

@ -0,0 +1,135 @@
// Copyright 2016 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 errgroup provides synchronization, error propagation, and Context
// cancelation for groups of goroutines working on subtasks of a common task.
//
// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks
// returning errors.
package errgroup
import (
"context"
"fmt"
"sync"
)
type token struct{}
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
//
// A zero Group is valid, has no limit on the number of active goroutines,
// and does not cancel on error.
type Group struct {
cancel func(error)
wg sync.WaitGroup
sem chan token
errOnce sync.Once
err error
}
func (g *Group) done() {
if g.sem != nil {
<-g.sem
}
g.wg.Done()
}
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := withCancelCause(ctx)
return &Group{cancel: cancel}, ctx
}
// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel(g.err)
}
return g.err
}
// Go calls the given function in a new goroutine.
// It blocks until the new goroutine can be added without the number of
// active goroutines in the group exceeding the configured limit.
//
// The first call to return a non-nil error cancels the group's context, if the
// group was created by calling WithContext. The error will be returned by Wait.
func (g *Group) Go(f func() error) {
if g.sem != nil {
g.sem <- token{}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
}
// TryGo calls the given function in a new goroutine only if the number of
// active goroutines in the group is currently below the configured limit.
//
// The return value reports whether the goroutine was started.
func (g *Group) TryGo(f func() error) bool {
if g.sem != nil {
select {
case g.sem <- token{}:
// Note: this allows barging iff channels in general allow barging.
default:
return false
}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
return true
}
// SetLimit limits the number of active goroutines in this group to at most n.
// A negative value indicates no limit.
//
// Any subsequent call to the Go method will block until it can add an active
// goroutine without exceeding the configured limit.
//
// The limit must not be modified while any goroutines in the group are active.
func (g *Group) SetLimit(n int) {
if n < 0 {
g.sem = nil
return
}
if len(g.sem) != 0 {
panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
}
g.sem = make(chan token, n)
}

13
src/cmd/vendor/golang.org/x/sync/errgroup/go120.go generated vendored Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2023 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.
//go:build go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
return context.WithCancelCause(parent)
}

14
src/cmd/vendor/golang.org/x/sync/errgroup/pre_go120.go generated vendored Normal file
View file

@ -0,0 +1,14 @@
// Copyright 2023 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.
//go:build !go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
ctx, cancel := context.WithCancel(parent)
return ctx, func(error) { cancel() }
}

17
src/cmd/vendor/golang.org/x/telemetry/.dockerignore generated vendored Normal file
View file

@ -0,0 +1,17 @@
.git
.localstorage
node_modules
devtools
.eslint*
.gitignore
.prettier*
.stylelint*
CONTRIBUTING.md
LICENSE
npm
npx
package-lock.json
package.json
PATENTS
README.md
tsconfig.json

11
src/cmd/vendor/golang.org/x/telemetry/.eslintrc.json generated vendored Normal file
View file

@ -0,0 +1,11 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"root": true,
"ignorePatterns": ["*.min.js"]
}

14
src/cmd/vendor/golang.org/x/telemetry/.gitattributes generated vendored Normal file
View file

@ -0,0 +1,14 @@
# Treat all files in the repo as binary, with no git magic updating
# line endings. This produces predictable results in different environments.
#
# Windows users contributing to Go will need to use a modern version
# of git and editors capable of LF line endings.
#
# Windows .bat files are known to have multiple bugs when run with LF
# endings. So if they are checked in with CRLF endings, there should
# be a test like the one in test/winbatch.go in the go repository.
# (See golang.org/issue/37791.)
#
# See golang.org/issue/9281.
* -text

2
src/cmd/vendor/golang.org/x/telemetry/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.localstorage

View file

@ -0,0 +1 @@
{"proseWrap": "always"}

View file

@ -0,0 +1,11 @@
{
"extends": ["stylelint-config-standard"],
"rules": {
"declaration-property-value-allowed-list": {
"/color/": ["/^var\\(--/", "transparent"]
},
"unit-disallowed-list": ["px"],
"selector-class-pattern": "^[a-zA-Z\\-]+$"
},
"ignoreFiles": ["**/*.min.css"]
}

30
src/cmd/vendor/golang.org/x/telemetry/CONTRIBUTING.md generated vendored Normal file
View file

@ -0,0 +1,30 @@
# Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these
five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the
[golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead
of the issue tracker. The gophers there will answer or ask you to file an issue
if you've tripped over a bug.
## Contributing code
Please read the
[Contribution Guidelines](https://golang.org/doc/contribute.html) before sending
patches.
Unless otherwise noted, the Go source files are distributed under the BSD-style
license found in the LICENSE file.

60
src/cmd/vendor/golang.org/x/telemetry/README.md generated vendored Normal file
View file

@ -0,0 +1,60 @@
# Go Telemetry
This repository holds the Go Telemetry server code and libraries, used for
hosting [telemetry.go.dev](https://telemetry.go.dev) and instrumenting Go
toolchain programs with opt-in telemetry.
**Warning**: this repository is intended for use only in tools maintained by
the Go team, including tools in the Go distribution and auxiliary tools like
[gopls](https://pkg.go.dev/golang.org/x/tools/gopls) or
[govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck). There are
no compatibility guarantees for any of the packages here: public APIs will
change in breaking ways as the telemetry integration is refined.
## Notable Packages
- The [x/telemetry/counter](https://pkg.go.dev/golang.org/x/telemetry/counter)
package provides a library for instrumenting programs with counters and stack
reports.
- The [x/telemetry/upload](https://pkg.go.dev/golang.org/x/telemetry/upload)
package provides a hook for Go toolchain programs to upload telemetry data,
if the user has opted in to telemetry uploading.
- The [x/telemetry/cmd/gotelemetry](https://pkg.go.dev/pkg/golang.org/x/telemetry/cmd/gotelemetry)
command is used for managing telemetry data and configuration.
- The [x/telemetry/config](https://pkg.go.dev/pkg/golang.org/x/telemetry/config)
package defines the subset of telemetry data that has been approved for
uploading by the telemetry proposal process.
- The [x/telemetry/godev](https://pkg.go.dev/pkg/golang.org/x/telemetry/godev) directory defines
the services running at [telemetry.go.dev](https://telemetry.go.dev).
## Contributing
This repository uses Gerrit for code changes. To learn how to submit changes to
this repository, see https://golang.org/doc/contribute.html.
The main issue tracker for the time repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/telemetry:" in
the subject line, so it is easy to find.
### Linting & Formatting
This repository uses [eslint](https://eslint.org/) to format TS files,
[stylelint](https://stylelint.io/) to format CSS files, and
[prettier](https://prettier.io/) to format TS, CSS, Markdown, and YAML files.
See the style guides:
- [TypeScript](https://google.github.io/styleguide/tsguide.html)
- [CSS](https://go.dev/wiki/CSSStyleGuide)
It is encouraged that all TS and CSS code be run through formatters before
submitting a change. However, it is not a strict requirement enforced by CI.
### Installing npm Dependencies:
1. Install [docker](https://docs.docker.com/get-docker/)
2. Run `./npm install`
### Run ESLint, Stylelint, & Prettier
./npm run all

View file

@ -0,0 +1,62 @@
// Copyright 2023 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.
//go:build go1.19
// countertest provides testing utilities for counters.
// This package cannot be used except for testing.
package countertest
import (
"path/filepath"
"sync"
"golang.org/x/telemetry/counter"
ic "golang.org/x/telemetry/internal/counter"
"golang.org/x/telemetry/internal/telemetry"
)
var (
openedMu sync.Mutex
opened bool
)
func isOpen() bool {
openedMu.Lock()
defer openedMu.Unlock()
return opened
}
// Open enables telemetry data writing to disk.
// This is supposed to be called once during the program execution
// (i.e. typically in TestMain), and must not be used with
// golang.org/x/telemetry/counter.Open.
func Open(telemetryDir string) {
openedMu.Lock()
defer openedMu.Unlock()
if opened {
panic("Open was called more than once")
}
telemetry.ModeFile = telemetry.ModeFilePath(filepath.Join(telemetryDir, "mode"))
telemetry.LocalDir = filepath.Join(telemetryDir, "local")
telemetry.UploadDir = filepath.Join(telemetryDir, "upload")
counter.Open()
opened = true
}
// ReadCounter reads the given counter.
func ReadCounter(c *counter.Counter) (count uint64, _ error) {
return ic.Read(c)
}
// ReadStackCounter reads the given StackCounter.
func ReadStackCounter(c *counter.StackCounter) (stackCounts map[string]uint64, _ error) {
return ic.ReadStack(c)
}
// ReadFile reads the counters and stack counters from the given file.
func ReadFile(name string) (counters, stackCounters map[string]uint64, _ error) {
return ic.ReadFile(name)
}

View file

@ -0,0 +1,21 @@
// Copyright 2024 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.
//go:build !go1.19
package countertest
import "golang.org/x/telemetry/counter"
func Open(telemetryDir string) {}
func ReadCounter(c *counter.Counter) (count uint64, _ error) {
return 0, nil
}
func ReadStackCounter(c *counter.StackCounter) (stackCounts map[string]uint64, _ error) {
return nil, nil
}
func ReadFile(name string) (map[string]uint64, map[string]uint64, error) { return nil, nil, nil }

View file

@ -0,0 +1,16 @@
// Copyright 2023 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.
//go:build go1.21
package countertest
import "testing"
func init() {
// Extra safety check for go1.21+.
if !testing.Testing() {
panic("use of this package is disallowed in non-testing code")
}
}

1
src/cmd/vendor/golang.org/x/telemetry/doc.go generated vendored Normal file
View file

@ -0,0 +1 @@
package telemetry

View file

@ -0,0 +1,140 @@
// Copyright 2023 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 config provides methods for loading and querying a
// telemetry upload config file.
package config
import (
"encoding/json"
"os"
"strings"
"golang.org/x/telemetry/internal/telemetry"
)
// Config is a wrapper around telemetry.UploadConfig that provides some
// convenience methods for checking the contents of a report.
type Config struct {
*telemetry.UploadConfig
program map[string]bool
goos map[string]bool
goarch map[string]bool
goversion map[string]bool
pgversion map[pgkey]bool
pgcounter map[pgkey]bool
pgcounterprefix map[pgkey]bool
pgstack map[pgkey]bool
rate map[pgkey]float64
}
type pgkey struct {
program, key string
}
func ReadConfig(file string) (*Config, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var cfg telemetry.UploadConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return NewConfig(&cfg), nil
}
func NewConfig(cfg *telemetry.UploadConfig) *Config {
ucfg := Config{UploadConfig: cfg}
ucfg.goos = set(ucfg.GOOS)
ucfg.goarch = set(ucfg.GOARCH)
ucfg.goversion = set(ucfg.GoVersion)
ucfg.program = make(map[string]bool, len(ucfg.Programs))
ucfg.pgversion = make(map[pgkey]bool, len(ucfg.Programs))
ucfg.pgcounter = make(map[pgkey]bool, len(ucfg.Programs))
ucfg.pgcounterprefix = make(map[pgkey]bool, len(ucfg.Programs))
ucfg.pgstack = make(map[pgkey]bool, len(ucfg.Programs))
ucfg.rate = make(map[pgkey]float64)
for _, p := range ucfg.Programs {
ucfg.program[p.Name] = true
for _, v := range p.Versions {
ucfg.pgversion[pgkey{p.Name, v}] = true
}
for _, c := range p.Counters {
for _, e := range Expand(c.Name) {
ucfg.pgcounter[pgkey{p.Name, e}] = true
ucfg.rate[pgkey{p.Name, e}] = c.Rate
}
prefix, _, found := strings.Cut(c.Name, ":")
if found {
ucfg.pgcounterprefix[pgkey{p.Name, prefix}] = true
}
}
for _, s := range p.Stacks {
ucfg.pgstack[pgkey{p.Name, s.Name}] = true
ucfg.rate[pgkey{p.Name, s.Name}] = s.Rate
}
}
return &ucfg
}
func (r *Config) HasProgram(s string) bool {
return r.program[s]
}
func (r *Config) HasGOOS(s string) bool {
return r.goos[s]
}
func (r *Config) HasGOARCH(s string) bool {
return r.goarch[s]
}
func (r *Config) HasGoVersion(s string) bool {
return r.goversion[s]
}
func (r *Config) HasVersion(program, version string) bool {
return r.pgversion[pgkey{program, version}]
}
func (r *Config) HasCounter(program, counter string) bool {
return r.pgcounter[pgkey{program, counter}]
}
func (r *Config) HasCounterPrefix(program, prefix string) bool {
return r.pgcounterprefix[pgkey{program, prefix}]
}
func (r *Config) HasStack(program, stack string) bool {
return r.pgstack[pgkey{program, stack}]
}
func (r *Config) Rate(program, name string) float64 {
return r.rate[pgkey{program, name}]
}
func set(slice []string) map[string]bool {
s := make(map[string]bool, len(slice))
for _, v := range slice {
s[v] = true
}
return s
}
// Expand takes a counter defined with buckets and expands it into distinct
// strings for each bucket
func Expand(counter string) []string {
prefix, rest, hasBuckets := strings.Cut(counter, "{")
var counters []string
if hasBuckets {
buckets := strings.Split(strings.TrimSuffix(rest, "}"), ",")
for _, b := range buckets {
counters = append(counters, prefix+b)
}
} else {
counters = append(counters, prefix)
}
return counters
}

View file

@ -0,0 +1,78 @@
// Copyright 2023 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 configstore abstracts interaction with the telemetry config server.
// Telemetry config (golang.org/x/telemetry/config) is distributed as a go
// module containing go.mod and config.json. Programs that upload collected
// counters download the latest config using `go mod download`. This provides
// verification of downloaded configuration and cacheability.
package configstore
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"golang.org/x/telemetry/internal/telemetry"
)
const (
configModulePath = "golang.org/x/telemetry/config"
configFileName = "config.json"
)
// DownloadOption is an option for Download.
type DownloadOption struct {
// Env holds the environment variables used when downloading the configuration.
// If nil, the process's environment variables are used.
Env []string
}
// Download fetches the requested telemetry UploadConfig using "go mod download".
//
// The second result is the canonical version of the requested configuration.
func Download(version string, opts *DownloadOption) (telemetry.UploadConfig, string, error) {
if version == "" {
version = "latest"
}
if opts == nil {
opts = &DownloadOption{}
}
modVer := configModulePath + "@" + version
var stdout, stderr bytes.Buffer
cmd := exec.Command("go", "mod", "download", "-json", modVer)
cmd.Env = opts.Env
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
var info struct {
Error string
}
if err := json.Unmarshal(stdout.Bytes(), &info); err == nil && info.Error != "" {
return telemetry.UploadConfig{}, "", fmt.Errorf("failed to download config module: %v", info.Error)
}
return telemetry.UploadConfig{}, "", fmt.Errorf("failed to download config module: %w\n%s", err, &stderr)
}
var info struct {
Dir string
Version string
Error string
}
if err := json.Unmarshal(stdout.Bytes(), &info); err != nil || info.Dir == "" {
return telemetry.UploadConfig{}, "", fmt.Errorf("failed to download config module (invalid JSON): %w", err)
}
data, err := os.ReadFile(filepath.Join(info.Dir, configFileName))
if err != nil {
return telemetry.UploadConfig{}, "", fmt.Errorf("invalid config module: %w", err)
}
var cfg telemetry.UploadConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return telemetry.UploadConfig{}, "", fmt.Errorf("invalid config: %w", err)
}
return cfg, info.Version, nil
}

View file

@ -0,0 +1,14 @@
// Copyright 2024 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.
//go:build go1.23
// +build go1.23
package crashmonitor
import "runtime/debug"
func init() {
setCrashOutput = debug.SetCrashOutput
}

View file

@ -0,0 +1,256 @@
// Copyright 2024 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 crashmonitor
// This file defines a monitor that reports arbitrary Go runtime
// crashes to telemetry.
import (
"bytes"
"fmt"
"io"
"log"
"os"
"reflect"
"runtime/debug"
"strconv"
"strings"
"golang.org/x/telemetry/internal/counter"
)
// Supported reports whether the runtime supports [runtime.SetCrashOutput].
//
// TODO(adonovan): eliminate once go1.23+ is assured.
func Supported() bool { return setCrashOutput != nil }
var setCrashOutput func(*os.File) error // = runtime.SetCrashOutput on go1.23+
// Parent sets up the parent side of the crashmonitor. It requires
// exclusive use of a writable pipe connected to the child process's stdin.
func Parent(pipe *os.File) {
writeSentinel(pipe)
// Ensure that we get pc=0x%x values in the traceback.
debug.SetTraceback("system")
setCrashOutput(pipe)
}
// Child runs the part of the crashmonitor that runs in the child process.
// It expects its stdin to be connected via a pipe to the parent which has
// run Parent.
func Child() {
// Wait for parent process's dying gasp.
// If the parent dies for any reason this read will return.
data, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatalf("failed to read from input pipe: %v", err)
}
// If the only line is the sentinel, it wasn't a crash.
if bytes.Count(data, []byte("\n")) < 2 {
childExitHook()
os.Exit(0) // parent exited without crash report
}
log.Printf("parent reported crash:\n%s", data)
// Parse the stack out of the crash report
// and record a telemetry count for it.
name, err := telemetryCounterName(data)
if err != nil {
// Keep count of how often this happens
// so that we can investigate if necessary.
incrementCounter("crash/malformed")
// Something went wrong.
// Save the crash securely in the file system.
f, err := os.CreateTemp(os.TempDir(), "*.crash")
if err != nil {
log.Fatal(err)
}
if _, err := f.Write(data); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
log.Printf("failed to report crash to telemetry: %v", err)
log.Fatalf("crash report saved at %s", f.Name())
}
incrementCounter(name)
childExitHook()
log.Fatalf("telemetry crash recorded")
}
// (stubbed by test)
var (
incrementCounter = func(name string) { counter.New(name).Inc() }
childExitHook = func() {}
)
// The sentinel function returns its address. The difference between
// this value as observed by calls in two different processes of the
// same executable tells us the relative offset of their text segments.
//
// It would be nice if SetCrashOutput took care of this as it's fiddly
// and likely to confuse every user at first.
func sentinel() uint64 {
return uint64(reflect.ValueOf(sentinel).Pointer())
}
func writeSentinel(out io.Writer) {
fmt.Fprintf(out, "sentinel %x\n", sentinel())
}
// telemetryCounterName parses a crash report produced by the Go
// runtime, extracts the stack of the first runnable goroutine,
// converts each line into telemetry form ("symbol:relative-line"),
// and returns this as the name of a counter.
func telemetryCounterName(crash []byte) (string, error) {
pcs, err := parseStackPCs(string(crash))
if err != nil {
return "", err
}
// Limit the number of frames we request.
pcs = pcs[:min(len(pcs), 16)]
if len(pcs) == 0 {
// This can occur if all goroutines are idle, as when
// caught in a deadlock, or killed by an async signal
// while blocked.
//
// TODO(adonovan): consider how to report such
// situations. Reporting a goroutine in [sleep] or
// [select] state could be quite confusing without
// further information about the nature of the crash,
// as the problem is not local to the code location.
//
// For now, we keep count of this situation so that we
// can access whether it needs a more involved solution.
return "crash/no-running-goroutine", nil
}
// This string appears at the start of all
// crashmonitor-generated counter names.
//
// It is tempting to expose this as a parameter of Start, but
// it is not without risk. What value should most programs
// provide? There's no point giving the name of the executable
// as this is already recorded by telemetry. What if the
// application runs in multiple modes? Then it might be useful
// to record the mode. The problem is that an application with
// multiple modes probably doesn't know its mode by line 1 of
// main.main: it might require flag or argument parsing, or
// even validation of an environment variable, and we really
// want to steer users aware from any logic before Start. The
// flags and arguments will be wrong in the child process, and
// every extra conditional branch creates a risk that the
// recursively executed child program will behave not like the
// monitor but like the application. If the child process
// exits before calling Start, then the parent application
// will not have a monitor, and its crash reports will be
// discarded (written in to a pipe that is never read).
//
// So for now, we use this constant string.
const prefix = "crash/crash"
return counter.EncodeStack(pcs, prefix), nil
}
// parseStackPCs parses the parent process's program counters for the
// first running goroutine out of a GOTRACEBACK=system traceback,
// adjusting them so that they are valid for the child process's text
// segment.
//
// This function returns only program counter values, ensuring that
// there is no possibility of strings from the crash report (which may
// contain PII) leaking into the telemetry system.
func parseStackPCs(crash string) ([]uintptr, error) {
// getPC parses the PC out of a line of the form:
// \tFILE:LINE +0xRELPC sp=... fp=... pc=...
getPC := func(line string) (uint64, error) {
_, pcstr, ok := strings.Cut(line, " pc=") // e.g. pc=0x%x
if !ok {
return 0, fmt.Errorf("no pc= for stack frame: %s", line)
}
return strconv.ParseUint(pcstr, 0, 64) // 0 => allow 0x prefix
}
var (
pcs []uintptr
parentSentinel uint64
childSentinel = sentinel()
on = false // are we in the first running goroutine?
lines = strings.Split(crash, "\n")
)
for i := 0; i < len(lines); i++ {
line := lines[i]
// Read sentinel value.
if parentSentinel == 0 && strings.HasPrefix(line, "sentinel ") {
_, err := fmt.Sscanf(line, "sentinel %x", &parentSentinel)
if err != nil {
return nil, fmt.Errorf("can't read sentinel line")
}
continue
}
// Search for "goroutine GID [STATUS]"
if !on {
if strings.HasPrefix(line, "goroutine ") &&
strings.Contains(line, " [running]:") {
on = true
if parentSentinel == 0 {
return nil, fmt.Errorf("no sentinel value in crash report")
}
}
continue
}
// A blank line marks end of a goroutine stack.
if line == "" {
break
}
// Skip the final "created by SYMBOL in goroutine GID" part.
if strings.HasPrefix(line, "created by ") {
break
}
// Expect a pair of lines:
// SYMBOL(ARGS)
// \tFILE:LINE +0xRELPC sp=0x%x fp=0x%x pc=0x%x
// Note: SYMBOL may contain parens "pkg.(*T).method"
// The RELPC is sometimes missing.
// Skip the symbol(args) line.
i++
if i == len(lines) {
break
}
line = lines[i]
// Parse the PC, and correct for the parent and child's
// different mappings of the text section.
pc, err := getPC(line)
if err != nil {
// Inlined frame, perhaps; skip it.
continue
}
pcs = append(pcs, uintptr(pc-parentSentinel+childSentinel))
}
return pcs, nil
}
func min(x, y int) int {
if x < y {
return x
} else {
return y
}
}

View file

@ -0,0 +1,45 @@
The upload process converts count files into reports, and
uploads reports. There will be only one report, named YYYY-MM-DD.json,
for a given day.
First phase. Look at the localdir (os.UserConfigdir()/go/telemetry/local)
and find all .count and .json files. Find the count files that are no
longer active by looking at their metadata.
Second phase. Group the inactive count files by their expiry date, and
for each date generate the local report and the upload report. (The upload
report only contains the counters in the upload configuration.) The upload
report is saved in the local directory with a name like YYYY-MM-DD.json, if
there is no file already existing with that name.
If the local report is different, it is saved in the local directory
with a name like local.YYYY-MM-DD.json. The new upload report is
added to the list of .json files from the first phase. At this point
the count files are no longer needed and can be deleted.
Third phase. Look at the .json files in the list from the first phase.
If the name starts with local, skip it. If there is a file with the
identical name in the upload directory, remove the one in the local directory.
Otherwise try to upload the one in the local directory,
If the upload succeeds, move the file to the uploaded directory.
There are various error conditions.
1. Several processes could look at localdir and see work to do.
1A. They could see different sets of expired count files for some day.
This could happen if another process is removing count files. In this
case there is already a YYYY-MM-DD.json file either in localdir
or updatedir, so the process seeing fewer count files will not generate
a report.
1B. They could see the same count files, and no report in either directory.
They will both generate (in memory) reports and check to see if there
is a YYYY-MM-DD.json file in either directory. They could both then
write two files with the same name, but different X values, but
otherwise the same contents. The X values are very close to the front
of the file. Assuming reasonable file system semantics one version of
the file will be written. To minimize this, just before writing reports
the code checks again to see if they exist.
1C. Once there is an existing well-formed file YYYY-MM-DD.json in localdir
eventually the upload will succeed, and the file will be moved to updatedir.
It is possible that other processes will not see the file in updatedir and
upload it again and also move it to uploaddir. This is harmless as all
the uploaded files are identical.

View file

@ -0,0 +1,91 @@
// Copyright 2023 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 upload
import (
"fmt"
"os"
"sync"
"time"
"golang.org/x/telemetry/internal/counter"
)
// time and date handling
var distantPast = 21 * 24 * time.Hour
// reports that are too old (21 days) are not uploaded
func tooOld(date string, uploadStartTime time.Time) bool {
t, err := time.Parse("2006-01-02", date)
if err != nil {
logger.Printf("tooOld: %v", err)
return false
}
age := uploadStartTime.Sub(t)
return age > distantPast
}
// a time in the far future for the expiry time with errors
var farFuture = time.UnixMilli(1 << 62)
// counterDateSpan parses the counter file named fname and returns the (begin, end) span
// recorded in its metadata.
// On any error, it returns (0, farFuture), so that invalid files don't look
// like they can be used.
//
// TODO(rfindley): just return an error to make this explicit.
func (u *Uploader) counterDateSpan(fname string) (begin, end time.Time) {
parsed, err := u.parse(fname)
if err != nil {
logger.Printf("expiry Parse: %v for %s", err, fname)
return time.Time{}, farFuture
}
begin, err = time.Parse(time.RFC3339, parsed.Meta["TimeBegin"])
if err != nil {
logger.Printf("time.Parse(%s[TimeBegin]) failed: %v", fname, err)
return time.Time{}, farFuture
}
end, err = time.Parse(time.RFC3339, parsed.Meta["TimeEnd"])
if err != nil {
logger.Printf("time.Parse(%s[TimeEnd]) failed: %v", fname, err)
return time.Time{}, farFuture
}
return begin, end
}
// stillOpen returns true if the counter file might still be active
func (u *Uploader) stillOpen(fname string) bool {
_, expiry := u.counterDateSpan(fname)
return expiry.After(u.StartTime)
}
// avoid parsing count files multiple times
type parsedCache struct {
mu sync.Mutex
m map[string]*counter.File
}
func (u *Uploader) parse(fname string) (*counter.File, error) {
u.cache.mu.Lock()
defer u.cache.mu.Unlock()
if u.cache.m == nil {
u.cache.m = make(map[string]*counter.File)
}
if f, ok := u.cache.m[fname]; ok {
return f, nil
}
buf, err := os.ReadFile(fname)
if err != nil {
return nil, fmt.Errorf("parse ReadFile: %v for %s", err, fname)
}
f, err := counter.Parse(fname, buf)
if err != nil {
return nil, fmt.Errorf("parse Parse: %v for %s", err, fname)
}
u.cache.m[fname] = f
return f, nil
}

View file

@ -0,0 +1,96 @@
// Copyright 2023 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 upload
import (
"os"
"path/filepath"
"strings"
)
// files to handle
type work struct {
// absolute file names
countfiles []string // count files to process
readyfiles []string // old reports to upload
// relative names
uploaded map[string]bool // reports that have been uploaded
}
// find all the files that look like counter files or reports
// that need to be uploaded. (There may be unexpected leftover files
// and uploading is supposed to be idempotent.)
func (u *Uploader) findWork() work {
localdir, uploaddir := u.LocalDir, u.UploadDir
var ans work
fis, err := os.ReadDir(localdir)
if err != nil {
logger.Printf("could not read %s, progress impossible (%v)", localdir, err)
return ans
}
mode, asof := u.ModeFilePath.Mode()
logger.Printf("mode %s, asof %s", mode, asof)
// count files end in .v1.count
// reports end in .json. If they are not to be uploaded they
// start with local.
for _, fi := range fis {
if strings.HasSuffix(fi.Name(), ".v1.count") {
fname := filepath.Join(localdir, fi.Name())
if u.stillOpen(fname) {
logger.Printf("still active: %s", fname)
continue
}
ans.countfiles = append(ans.countfiles, fname)
} else if strings.HasPrefix(fi.Name(), "local.") {
// skip
} else if strings.HasSuffix(fi.Name(), ".json") && mode == "on" {
// Collect reports that are ready for upload.
reportDate := uploadReportDate(fi.Name())
if !asof.IsZero() && !reportDate.IsZero() {
// If both the mode asof date and the report date are present, do the
// right thing...
//
// (see https://github.com/golang/go/issues/63142#issuecomment-1734025130)
if asof.Before(reportDate) {
// Note: since this report was created after telemetry was enabled,
// we can only assume that the process that created it checked that
// the counter data contained therein was all from after the asof
// date.
//
// TODO(rfindley): store the begin date in reports, so that we can
// verify this assumption.
logger.Printf("uploadable %s", fi.Name())
ans.readyfiles = append(ans.readyfiles, filepath.Join(localdir, fi.Name()))
}
} else {
// ...otherwise fall back on the old behavior of uploading all
// unuploaded files.
//
// TODO(rfindley): invert this logic following more testing. We
// should only upload if we know both the asof date and the report
// date, and they are acceptable.
logger.Printf("uploadable anyway %s", fi.Name())
ans.readyfiles = append(ans.readyfiles, filepath.Join(localdir, fi.Name()))
}
}
}
fis, err = os.ReadDir(uploaddir)
if err != nil {
os.MkdirAll(uploaddir, 0777)
return ans
}
// There should be only one of these per day; maybe sometime
// we'll want to clean the directory.
ans.uploaded = make(map[string]bool)
for _, fi := range fis {
if strings.HasSuffix(fi.Name(), ".json") {
ans.uploaded[fi.Name()] = true
}
}
return ans
}

View file

@ -0,0 +1,311 @@
// Copyright 2023 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 upload
import (
"crypto/rand"
"encoding/binary"
"encoding/json"
"fmt"
"math"
"os"
"path/filepath"
"strings"
"time"
"golang.org/x/telemetry/internal/config"
"golang.org/x/telemetry/internal/configstore"
"golang.org/x/telemetry/internal/counter"
"golang.org/x/telemetry/internal/telemetry"
)
// reports generates reports from inactive count files
func (u *Uploader) reports(todo *work) ([]string, error) {
if mode, _ := u.ModeFilePath.Mode(); mode == "off" {
return nil, nil // no reports
}
thisInstant := u.StartTime
today := thisInstant.Format("2006-01-02")
lastWeek := latestReport(todo.uploaded)
if lastWeek >= today { //should never happen
lastWeek = ""
}
logger.Printf("lastWeek %q, today %s", lastWeek, today)
countFiles := make(map[string][]string) // expiry date string->filenames
earliest := make(map[string]time.Time) // earliest begin time for any counter
for _, f := range todo.countfiles {
begin, end := u.counterDateSpan(f)
if end.Before(thisInstant) {
expiry := end.Format(dateFormat)
countFiles[expiry] = append(countFiles[expiry], f)
if earliest[expiry].IsZero() || earliest[expiry].After(begin) {
earliest[expiry] = begin
}
}
}
for expiry, files := range countFiles {
if notNeeded(expiry, *todo) {
logger.Printf("files for %s not needed, deleting %v", expiry, files)
// The report already exists.
// There's another check in createReport.
deleteFiles(files)
continue
}
fname, err := u.createReport(earliest[expiry], expiry, files, lastWeek)
if err != nil {
return nil, err
}
if fname != "" {
todo.readyfiles = append(todo.readyfiles, fname)
}
}
return todo.readyfiles, nil
}
// latestReport returns the YYYY-MM-DD of the last report uploaded
// or the empty string if there are no reports.
func latestReport(uploaded map[string]bool) string {
var latest string
for name := range uploaded {
if strings.HasSuffix(name, ".json") {
if name > latest {
latest = name
}
}
}
if latest == "" {
return ""
}
// strip off the .json
return latest[:len(latest)-len(".json")]
}
// notNeeded returns true if the report for date has already been created
func notNeeded(date string, todo work) bool {
if todo.uploaded != nil && todo.uploaded[date+".json"] {
return true
}
// maybe the report is already in todo.readyfiles
for _, f := range todo.readyfiles {
if strings.Contains(f, date) {
return true
}
}
return false
}
func deleteFiles(files []string) {
for _, f := range files {
if err := os.Remove(f); err != nil {
// this could be a race condition.
// conversely, on Windows, err may be nil and
// the file not deleted if anyone has it open.
logger.Printf("%v failed to remove %s", err, f)
}
}
}
// createReport for all the count files for the same date.
// returns the absolute path name of the file containing the report
func (u *Uploader) createReport(start time.Time, expiryDate string, files []string, lastWeek string) (string, error) {
if u.Config == nil {
a, v, err := configstore.Download("latest", nil)
if err != nil {
logger.Print(err) // or something (e.g., panic(err))
}
u.Config = &a
u.ConfigVersion = v
}
uploadOK := true
mode, asof := u.ModeFilePath.Mode()
if u.Config == nil || mode != "on" {
logger.Printf("no upload config or mode %q is not 'on'", mode)
uploadOK = false // no config, nothing to upload
}
if tooOld(expiryDate, u.StartTime) {
logger.Printf("expiryDate %s is too old", expiryDate)
uploadOK = false
}
// If the mode is recorded with an asof date, don't upload if the report
// includes any data on or before the asof date.
if !asof.IsZero() && !asof.Before(start) {
logger.Printf("asof %s is not before start %s", asof, start)
uploadOK = false
}
// should we check that all the x.Meta are consistent for GOOS, GOARCH, etc?
report := &telemetry.Report{
Config: u.ConfigVersion,
X: computeRandom(), // json encodes all the bits
Week: expiryDate,
LastWeek: lastWeek,
}
if report.X > u.Config.SampleRate && u.Config.SampleRate > 0 {
logger.Printf("X:%f > SampleRate:%f, not uploadable", report.X, u.Config.SampleRate)
uploadOK = false
}
var succeeded bool
for _, f := range files {
x, err := u.parse(string(f))
if err != nil {
logger.Printf("unparseable (%v) %s", err, f)
continue
}
prog := findProgReport(x.Meta, report)
for k, v := range x.Count {
if counter.IsStackCounter(k) {
// stack
prog.Stacks[k] += int64(v)
} else {
// counter
prog.Counters[k] += int64(v)
}
succeeded = true
}
}
if !succeeded {
return "", fmt.Errorf("all %d count files were unparseable", len(files))
}
// 1. generate the local report
localContents, err := json.MarshalIndent(report, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal report (%v)", err)
}
// check that the report can be read back
// TODO(pjw): remove for production?
var x telemetry.Report
if err := json.Unmarshal(localContents, &x); err != nil {
return "", fmt.Errorf("failed to unmarshal local report (%v)", err)
}
var uploadContents []byte
if uploadOK {
// 2. create the uploadable version
cfg := config.NewConfig(u.Config)
upload := &telemetry.Report{
Week: report.Week,
LastWeek: report.LastWeek,
X: report.X,
Config: report.Config,
}
for _, p := range report.Programs {
// does the uploadConfig want this program?
// if so, copy over the Stacks and Counters
// that the uploadConfig mentions.
if !cfg.HasGoVersion(p.GoVersion) || !cfg.HasProgram(p.Program) || !cfg.HasVersion(p.Program, p.Version) {
continue
}
x := &telemetry.ProgramReport{
Program: p.Program,
Version: p.Version,
GOOS: p.GOOS,
GOARCH: p.GOARCH,
GoVersion: p.GoVersion,
Counters: make(map[string]int64),
Stacks: make(map[string]int64),
}
upload.Programs = append(upload.Programs, x)
for k, v := range p.Counters {
if cfg.HasCounter(p.Program, k) && report.X <= cfg.Rate(p.Program, k) {
x.Counters[k] = v
}
}
// and the same for Stacks
// this can be made more efficient, when it matters
for k, v := range p.Stacks {
before, _, _ := strings.Cut(k, "\n")
if cfg.HasStack(p.Program, before) && report.X <= cfg.Rate(p.Program, before) {
x.Stacks[k] = v
}
}
}
uploadContents, err = json.MarshalIndent(upload, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal upload report (%v)", err)
}
}
localFileName := filepath.Join(u.LocalDir, "local."+expiryDate+".json")
uploadFileName := filepath.Join(u.LocalDir, expiryDate+".json")
/* Prepare to write files */
// if either file exists, someone has been here ahead of us
// (there is still a race, but this check shortens the open window)
if _, err := os.Stat(localFileName); err == nil {
deleteFiles(files)
return "", fmt.Errorf("local report %s already exists", localFileName)
}
if _, err := os.Stat(uploadFileName); err == nil {
deleteFiles(files)
return "", fmt.Errorf("report %s already exists", uploadFileName)
}
// write the uploadable file
var errUpload, errLocal error
if uploadOK {
errUpload = os.WriteFile(uploadFileName, uploadContents, 0644)
}
// write the local file
errLocal = os.WriteFile(localFileName, localContents, 0644)
/* Wrote the files */
// even though these errors won't occur, what should happen
// if errUpload == nil and it is ok to upload, and errLocal != nil?
if errLocal != nil {
return "", fmt.Errorf("failed to write local file %s (%v)", localFileName, errLocal)
}
if errUpload != nil {
return "", fmt.Errorf("failed to write upload file %s (%v)", uploadFileName, errUpload)
}
logger.Printf("created %q, deleting %v", uploadFileName, files)
deleteFiles(files)
if uploadOK {
return uploadFileName, nil
}
return "", nil
}
// return an existing ProgremReport, or create anew
func findProgReport(meta map[string]string, report *telemetry.Report) *telemetry.ProgramReport {
for _, prog := range report.Programs {
if prog.Program == meta["Program"] && prog.Version == meta["Version"] &&
prog.GoVersion == meta["GoVersion"] && prog.GOOS == meta["GOOS"] &&
prog.GOARCH == meta["GOARCH"] {
return prog
}
}
prog := telemetry.ProgramReport{
Program: meta["Program"],
Version: meta["Version"],
GoVersion: meta["GoVersion"],
GOOS: meta["GOOS"],
GOARCH: meta["GOARCH"],
Counters: make(map[string]int64),
Stacks: make(map[string]int64),
}
report.Programs = append(report.Programs, &prog)
return &prog
}
// turn 8 random bytes into a float64 in [0,1]
func computeRandom() float64 {
for {
b := make([]byte, 8)
_, err := rand.Read(b)
if err != nil {
logger.Fatalf("rand.Read: %v", err)
}
// and turn it into a float64
x := math.Float64frombits(binary.LittleEndian.Uint64(b))
if math.IsNaN(x) || math.IsInf(x, 0) {
continue
}
x = math.Abs(x)
if x < 0x1p-1000 { // avoid underflow patterns
continue
}
frac, _ := math.Frexp(x) // 52 bits of randomness
return frac*2 - 1
}
}

View file

@ -0,0 +1,135 @@
// Copyright 2023 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 upload
import (
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"runtime/debug"
"strings"
"time"
"golang.org/x/telemetry/internal/telemetry"
)
var logger *log.Logger
func init() {
logger = log.New(io.Discard, "", 0)
}
// keep track of what SetLogOutput has seen
var seenlogwriters []io.Writer
// SetLogOutput sets the default logger's output destination.
func SetLogOutput(logging io.Writer) {
if logging == nil {
return
}
logger.SetOutput(logging) // the common case
seenlogwriters = append(seenlogwriters, logging)
if len(seenlogwriters) > 1 {
// The client asked for logging, and there is also a debug dir
logger.SetOutput(io.MultiWriter(seenlogwriters...))
}
}
// LogIfDebug arranges to write a log file in the directory
// dirname, if it exists. If dirname is the empty string,
// the function tries the directory it.Localdir/debug.
func LogIfDebug(dirname string) error {
dname := filepath.Join(telemetry.LocalDir, "debug")
if dirname != "" {
dname = dirname
}
fd, err := os.Stat(dname)
if err != nil {
return err
}
if fd == nil || !fd.IsDir() {
// debug doesn't exist or isn't a directory
return nil
}
info, ok := debug.ReadBuildInfo()
if !ok {
return fmt.Errorf("no build info")
}
year, month, day := time.Now().UTC().Date()
goVers := info.GoVersion
// E.g., goVers:"go1.22-20240109-RC01 cl/597041403 +dcbe772469 X:loopvar"
words := strings.Fields(goVers)
goVers = words[0]
progPkgPath := info.Path
if progPkgPath == "" {
progPkgPath = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
}
prog := path.Base(progPkgPath)
progVers := info.Main.Version
fname := filepath.Join(dname, fmt.Sprintf("%s-%s-%s-%4d%02d%02d-%d.log",
prog, progVers, goVers, year, month, day, os.Getpid()))
fname = strings.ReplaceAll(fname, " ", "")
if _, err := os.Stat(fname); err == nil {
// This process previously called upload.Run
return nil
}
logfd, err := os.Create(fname)
if err != nil {
return err
}
SetLogOutput(logfd)
return nil
}
// Uploader carries parameters needed for upload.
type Uploader struct {
// Config is used to select counters to upload.
Config *telemetry.UploadConfig
// ConfigVersion is the version of the config.
ConfigVersion string
// LocalDir is where the local counter files are.
LocalDir string
// UploadDir is where uploader leaves the copy of uploaded data.
UploadDir string
// ModeFilePath is the file.
ModeFilePath telemetry.ModeFilePath
UploadServerURL string
StartTime time.Time
cache parsedCache
}
// NewUploader creates a default uploader.
func NewUploader(config *telemetry.UploadConfig) *Uploader {
return &Uploader{
Config: config,
ConfigVersion: "custom",
LocalDir: telemetry.LocalDir,
UploadDir: telemetry.UploadDir,
ModeFilePath: telemetry.ModeFile,
UploadServerURL: "https://telemetry.go.dev/upload",
StartTime: time.Now().UTC(),
}
}
// Run generates and uploads reports
func (u *Uploader) Run() {
if telemetry.DisabledOnPlatform {
return
}
todo := u.findWork()
ready, err := u.reports(&todo)
if err != nil {
logger.Printf("reports: %v", err)
}
for _, f := range ready {
u.uploadReport(f)
}
}

View file

@ -0,0 +1,85 @@
// Copyright 2023 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 upload
import (
"bytes"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
var (
dateRE = regexp.MustCompile(`(\d\d\d\d-\d\d-\d\d)[.]json$`)
dateFormat = "2006-01-02"
// TODO(rfindley): use dateFormat throughout.
)
// uploadReportDate returns the date component of the upload file name, or "" if the
// date was unmatched.
func uploadReportDate(fname string) time.Time {
match := dateRE.FindStringSubmatch(fname)
if match == nil || len(match) < 2 {
logger.Printf("malformed report name: missing date: %q", filepath.Base(fname))
return time.Time{}
}
d, err := time.Parse(dateFormat, match[1])
if err != nil {
logger.Printf("malformed report name: bad date: %q", filepath.Base(fname))
return time.Time{}
}
return d
}
func (u *Uploader) uploadReport(fname string) {
thisInstant := u.StartTime
// TODO(rfindley): use uploadReportDate here, once we've done a gopls release.
// first make sure it is not in the future
today := thisInstant.Format("2006-01-02")
match := dateRE.FindStringSubmatch(fname)
if match == nil || len(match) < 2 {
logger.Printf("report name seemed to have no date %q", filepath.Base(fname))
} else if match[1] > today {
logger.Printf("report %q is later than today %s", filepath.Base(fname), today)
return // report is in the future, which shouldn't happen
}
buf, err := os.ReadFile(fname)
if err != nil {
logger.Printf("%v reading %s", err, fname)
return
}
if u.uploadReportContents(fname, buf) {
// anything left to do?
}
}
// try to upload the report, 'true' if successful
func (u *Uploader) uploadReportContents(fname string, buf []byte) bool {
b := bytes.NewReader(buf)
fdate := strings.TrimSuffix(filepath.Base(fname), ".json")
fdate = fdate[len(fdate)-len("2006-01-02"):]
server := u.UploadServerURL + "/" + fdate
resp, err := http.Post(server, "application/json", b)
if err != nil {
logger.Printf("error on Post: %v %q for %q", err, server, fname)
return false
}
if resp.StatusCode != 200 {
logger.Printf("resp error on upload %q: %v for %q %q [%+v]", server, resp.Status, fname, fdate, resp)
return false
}
// put a copy in the uploaded directory
newname := filepath.Join(u.UploadDir, fdate+".json")
if err := os.WriteFile(newname, buf, 0644); err == nil {
os.Remove(fname) // if it exists
}
logger.Printf("uploaded %s to %q", fdate+".json", server)
return true
}

40
src/cmd/vendor/golang.org/x/telemetry/mode.go generated vendored Normal file
View file

@ -0,0 +1,40 @@
// Copyright 2023 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 telemetry
import (
"golang.org/x/telemetry/internal/telemetry"
)
// Mode returns the current telemetry mode.
//
// The telemetry mode is a global value that controls both the local collection
// and uploading of telemetry data. Possible mode values are:
// - "on": both collection and uploading is enabled
// - "local": collection is enabled, but uploading is disabled
// - "off": both collection and uploading are disabled
//
// When mode is "on", or "local", telemetry data is written to the local file
// system and may be inspected with the [gotelemetry] command.
//
// If an error occurs while reading the telemetry mode from the file system,
// Mode returns the default value "local".
//
// [gotelemetry]: https://pkg.go.dev/golang.org/x/telemetry/cmd/gotelemetry
func Mode() string {
mode, _ := telemetry.Mode()
return mode
}
// SetMode sets the global telemetry mode to the given value.
//
// See the documentation of [Mode] for a description of the supported mode
// values.
//
// An error is returned if the provided mode value is invalid, or if an error
// occurs while persisting the mode value to the file system.
func SetMode(mode string) error {
return telemetry.SetMode(mode)
}

13
src/cmd/vendor/golang.org/x/telemetry/npm generated vendored Normal file
View file

@ -0,0 +1,13 @@
#!/bin/bash
# Copyright 2022 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.
docker run \
--rm \
--volume $(pwd):/workspace \
--workdir /workspace \
--env NODE_OPTIONS="--dns-result-order=ipv4first" \
--entrypoint npm \
node:18.16.0-slim \
$@

13
src/cmd/vendor/golang.org/x/telemetry/npx generated vendored Normal file
View file

@ -0,0 +1,13 @@
#!/bin/bash
# Copyright 2022 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.
docker run \
--rm \
--volume $(pwd):/workspace \
--workdir /workspace \
--env NODE_OPTIONS="--dns-result-order=ipv4first" \
--entrypoint npx \
node:18.16.0-slim \
$@

4363
src/cmd/vendor/golang.org/x/telemetry/package-lock.json generated vendored Normal file

File diff suppressed because it is too large Load diff

23
src/cmd/vendor/golang.org/x/telemetry/package.json generated vendored Normal file
View file

@ -0,0 +1,23 @@
{
"scripts": {
"eslint": "eslint . --fix",
"stylelint": "stylelint '**/*.css' --fix",
"prettier": "prettier --write **/*.{css,ts,md,yaml} !**/*.min.css",
"all": "run-s --continue-on-error eslint stylelint prettier"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "5.59.6",
"@typescript-eslint/parser": "5.59.6",
"eslint": "8.40.0",
"eslint-config-prettier": "8.8.0",
"npm-run-all": "4.1.5",
"prettier": "2.8.8",
"stylelint": "15.6.2",
"stylelint-config-standard": "33.0.0",
"typescript": "5.0.4"
},
"dependencies": {
"@observablehq/plot": "0.6.9",
"d3": "7.8.5"
}
}

213
src/cmd/vendor/golang.org/x/telemetry/start.go generated vendored Normal file
View file

@ -0,0 +1,213 @@
// Copyright 2024 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 telemetry
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/telemetry/counter"
"golang.org/x/telemetry/internal/crashmonitor"
"golang.org/x/telemetry/internal/telemetry"
"golang.org/x/telemetry/upload"
)
// Config controls the behavior of [Start].
type Config struct {
// ReportCrashes, if set, will enable crash reporting.
// ReportCrashes uses the [debug.SetCrashOutput] mechanism, which is a
// process-wide resource.
// Do not make other calls to that function within your application.
// ReportCrashes is a non-functional unless the program is built with go1.23+.
ReportCrashes bool
// Upload causes this program to periodically upload approved counters
// from the local telemetry database to telemetry.go.dev.
//
// This option has no effect unless the user has given consent
// to enable data collection, for example by running
// cmd/gotelemetry or affirming the gopls dialog.
//
// (This feature is expected to be used only by gopls.
// Longer term, the go command may become the sole program
// responsible for uploading.)
Upload bool
}
// Start initializes telemetry using the specified configuration.
//
// Start opens the local telemetry database so that counter increment
// operations are durably recorded in the local file system.
//
// If [Config.Upload] is set, and the user has opted in to telemetry
// uploading, this process may attempt to upload approved counters
// to telemetry.go.dev.
//
// If [Config.ReportCrashes] is set, any fatal crash will be
// recorded by incrementing a counter named for the stack of the
// first running goroutine in the traceback.
//
// If either of these flags is set, Start re-executes the current
// executable as a child process, in a special mode in which it
// acts as a telemetry sidecar for the parent process (the application).
// In that mode, the call to Start will never return, so Start must
// be called immediately within main, even before such things as
// inspecting the command line. The application should avoid expensive
// steps or external side effects in init functions, as they will
// be executed twice (parent and child).
func Start(config Config) {
counter.Open()
// Crash monitoring and uploading both require a sidecar process.
if (config.ReportCrashes && crashmonitor.Supported()) || config.Upload {
if os.Getenv(telemetryChildVar) != "" {
child(config)
os.Exit(0)
}
parent(config)
}
}
var daemonize = func(cmd *exec.Cmd) {}
const telemetryChildVar = "X_TELEMETRY_CHILD"
func parent(config Config) {
// This process is the application (parent).
// Fork+exec the telemetry child.
exe, err := os.Executable()
if err != nil {
log.Fatal(err)
}
cmd := exec.Command(exe, "** telemetry **") // this unused arg is just for ps(1)
daemonize(cmd)
cmd.Env = append(os.Environ(), telemetryChildVar+"=1")
// The child process must write to a log file, not
// the stderr file it inherited from the parent, as
// the child may outlive the parent but should not prolong
// the life of any pipes created (by the grandparent)
// to gather the output of the parent.
//
// By default, we discard the child process's stderr,
// but in line with the uploader, log to a file in local/debug
// only if that directory was created by the user.
localDebug := filepath.Join(telemetry.LocalDir, "debug")
fd, err := os.Stat(localDebug)
if err != nil {
if !os.IsNotExist(err) {
log.Fatalf("failed to stat debug directory: %v", err)
}
} else if fd.IsDir() {
// local/debug exists and is a directory. Set stderr to a log file path
// in local/debug.
childLogPath := filepath.Join(localDebug, "sidecar.log")
childLog, err := os.OpenFile(childLogPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
log.Fatalf("opening sidecar log file for child: %v", err)
}
defer childLog.Close()
cmd.Stderr = childLog
}
if config.ReportCrashes {
pipe, err := cmd.StdinPipe()
if err != nil {
log.Fatalf("StdinPipe: %v", err)
}
crashmonitor.Parent(pipe.(*os.File)) // (this conversion is safe)
}
if err := cmd.Start(); err != nil {
log.Fatalf("can't start telemetry child process: %v", err)
}
go cmd.Wait() // Release resources if cmd happens not to outlive this process.
}
func child(config Config) {
log.SetPrefix(fmt.Sprintf("telemetry-sidecar (pid %v): ", os.Getpid()))
// Start crashmonitoring and uploading depending on what's requested
// and wait for the longer running child to complete before exiting:
// if we collected a crash before the upload finished, wait for the
// upload to finish before exiting
var g errgroup.Group
if config.Upload {
g.Go(func() error {
uploaderChild()
return nil
})
}
if config.ReportCrashes {
g.Go(func() error {
crashmonitor.Child()
return nil
})
}
g.Wait()
}
func uploaderChild() {
tokenfilepath := filepath.Join(telemetry.LocalDir, "upload.token")
ok, err := acquireUploadToken(tokenfilepath)
if err != nil {
log.Printf("error acquiring upload token: %v", err)
return
} else if !ok {
// It hasn't been a day since the last upload.Run attempt or there's
// a concurrently running uploader.
return
}
upload.Run(&upload.Control{Logger: os.Stderr})
}
// acquireUploadToken acquires a token permitting the caller to upload.
// To limit the frequency of uploads, only one token is issue per
// machine per time period.
// The boolean indicates whether the token was acquired.
func acquireUploadToken(tokenfile string) (bool, error) {
const period = 24 * time.Hour
// A process acquires a token by successfully creating a
// well-known file. If the file already exists and has an
// mtime age less then than the period, the process does
// not acquire the token. If the file is older than the
// period, the process is allowed to remove the file and
// try to re-create it.
fi, err := os.Stat(tokenfile)
if err == nil {
if time.Since(fi.ModTime()) < period {
return false, nil
}
// There's a possible race here where two processes check the
// token file and see that it's older than the period, then the
// first one removes it and creates another, and then a second one
// removes the newly created file and creates yet another
// file. Then both processes would act as though they had the token.
// This is very rare, but it's also okay because we're only grabbing
// the token to do rate limiting, not for correctness.
_ = os.Remove(tokenfile)
} else if !os.IsNotExist(err) {
return false, fmt.Errorf("statting token file: %v", err)
}
f, err := os.OpenFile(tokenfile, os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
if os.IsExist(err) {
return false, nil
}
return false, fmt.Errorf("creating token file: %v", err)
}
_ = f.Close()
return true, nil
}

22
src/cmd/vendor/golang.org/x/telemetry/start_posix.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
// Copyright 2024 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package telemetry
import (
"os/exec"
"syscall"
)
func init() {
daemonize = daemonizePosix
}
func daemonizePosix(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
}
}

29
src/cmd/vendor/golang.org/x/telemetry/start_windows.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
// Copyright 2024 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.
//go:build windows
package telemetry
import (
"os/exec"
"syscall"
"golang.org/x/sys/windows"
)
func init() {
daemonize = daemonizeWindows
}
func daemonizeWindows(cmd *exec.Cmd) {
// Set DETACHED_PROCESS creation flag so that closing
// the console window the parent process was run in
// does not kill the child.
// See documentation of creation flags in the Microsoft documentation:
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: windows.DETACHED_PROCESS,
}
}

26
src/cmd/vendor/golang.org/x/telemetry/tsconfig.json generated vendored Normal file
View file

@ -0,0 +1,26 @@
{
/* Visit https://aka.ms/tsconfig.json to read more about this file */
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "node",
"strict": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"checkJs": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
}

21
src/cmd/vendor/golang.org/x/telemetry/types_alias.go generated vendored Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2024 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 telemetry
import "golang.org/x/telemetry/internal/telemetry"
// Common types and directories used by multiple packages.
// An UploadConfig controls what data is uploaded.
type UploadConfig = telemetry.UploadConfig
type ProgramConfig = telemetry.ProgramConfig
type CounterConfig = telemetry.CounterConfig
// A Report is what's uploaded (or saved locally)
type Report = telemetry.Report
type ProgramReport = telemetry.ProgramReport

38
src/cmd/vendor/golang.org/x/telemetry/upload/upload.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
// Copyright 2023 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 upload
import (
"io"
"log"
"golang.org/x/telemetry/internal/upload"
)
// Run generates and uploads reports, as allowed by the mode file.
// A nil Control is legal.
func Run(c *Control) {
if c != nil && c.Logger != nil {
upload.SetLogOutput(c.Logger)
}
// ignore error: failed logging should not block uploads
upload.LogIfDebug("")
defer func() {
if err := recover(); err != nil {
log.Printf("upload recover: %v", err)
}
}()
upload.NewUploader(nil).Run()
}
// A Control allows the user to override various default
// reporting and uploading choices.
// Future versions may also allow the user to set the upload URL.
type Control struct {
// Logger provides a io.Writer for error messages during uploading
// nil is legal and no log messages get generated
Logger io.Writer
}

View file

@ -38,6 +38,7 @@ golang.org/x/mod/sumdb/tlog
golang.org/x/mod/zip
# golang.org/x/sync v0.6.0
## explicit; go 1.18
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
# golang.org/x/sys v0.17.0
## explicit; go 1.18
@ -46,10 +47,17 @@ golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/telemetry v0.0.0-20240229223025-3d5706d2d0fb
## explicit; go 1.20
golang.org/x/telemetry
golang.org/x/telemetry/counter
golang.org/x/telemetry/counter/countertest
golang.org/x/telemetry/internal/config
golang.org/x/telemetry/internal/configstore
golang.org/x/telemetry/internal/counter
golang.org/x/telemetry/internal/crashmonitor
golang.org/x/telemetry/internal/mmap
golang.org/x/telemetry/internal/telemetry
golang.org/x/telemetry/internal/upload
golang.org/x/telemetry/upload
# golang.org/x/term v0.17.0
## explicit; go 1.18
golang.org/x/term