golang.org/x/mod v0.15.1-0.20240207185259-766dc5df63e3
golang.org/x/sync v0.6.0
golang.org/x/sys v0.17.0
- golang.org/x/telemetry v0.0.0-20240131160148-1cb064e7d4f2
+ golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804
golang.org/x/term v0.16.0
golang.org/x/tools v0.17.1-0.20240119231502-e1555a36d006
)
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/telemetry v0.0.0-20240131160148-1cb064e7d4f2 h1:FXbfUwJ0hJkKMC/Cj47x49pH41jylMW5eMiIrJgmv2E=
-golang.org/x/telemetry v0.0.0-20240131160148-1cb064e7d4f2/go.mod h1:ZthVHHkOi8rlMEsfFr3Ie42Ym1NonbFNNRKW3ci0UrU=
+golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804 h1:mLYQpgq+cJOnmn3pR2U9o5rzEuOVgnmw59GHPgypGeo=
+golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.19
+//go:build go1.19 && !openbsd && !js && !wasip1 && !solaris && !android && !plan9 && !386
package counter
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !go1.19
+//go:build !go1.19 || openbsd || js || wasip1 || solaris || android || plan9 || 386
package counter
import (
"flag"
- "fmt"
)
func Add(string, int64) {}
func (c *Counter) Inc() {}
func (c *Counter) Name() string { return c.name }
-type File struct {
- Meta map[string]string
- Count map[string]uint64
-}
-
-func Parse(filename string, data []byte) (*File, error) { return nil, fmt.Errorf("unimplemented") }
-
type StackCounter struct{ name string }
func NewStack(name string, _ int) *StackCounter { return &StackCounter{name} }
"bytes"
"errors"
"fmt"
- "log"
"math/rand"
"os"
"path"
}
// ErrDisabled is the error returned when telemetry is disabled.
-var ErrDisabled = errors.New("counter: disabled by GOTELEMETRY=off")
+var ErrDisabled = errors.New("counter: disabled as Go telemetry is off")
var (
errNoBuildInfo = errors.New("counter: missing build info")
return
}
// now it is safe to clean up the old mapping
- if err := previous.f.Close(); err != nil {
- log.Print(err)
- }
- if err := munmap(previous.mapping); err != nil {
- log.Print(err)
- }
+ // Quim Montel pointed out the previous coeanup was incomplete
+ previous.close()
}
name, expire, err := f.filename(counterTime())
// telemetry might have been off
return
}
- mmap.Munmap(mf.mapping)
- mf.f.Close() // best effort
+ mf.close()
}
}
f: f,
meta: meta,
}
+ // without this files cannot be cleanedup on Windows (affects tests)
runtime.SetFinalizer(m, (*mappedFile).close)
defer func() {
if err != nil {
if _, ok := f.Count[string(ename)]; ok {
return corrupt()
}
- ctrName := expandName(ename)
+ ctrName := DecodeStack(string(ename))
f.Count[ctrName] = v.Load()
off = next
}
}
return f, nil
}
-
-func expandName(ename []byte) string {
- if !bytes.Contains(ename, []byte{'\n'}) {
- // not a stack counter
- return string(ename)
- }
- lines := bytes.Split(ename, []byte{'\n'})
- var lastPath []byte // empty or ends with .
- for i, line := range lines {
- path, rest := splitLine(line)
- if len(path) == 0 {
- continue // unchanged
- }
- if len(path) == 1 && path[0] == '"' {
- path = append([]byte{}, lastPath...) //need a deep copy
- lines[i] = append(path, rest...)
- } else {
- lastPath = append(path, '.')
- // line unchanged
- }
- }
- return string(bytes.Join(lines, []byte{'\n'})) // trailing \n?
-}
-
-// input is <import path>.<function name>
-// output is (import path, function name)
-func splitLine(x []byte) ([]byte, []byte) {
- i := bytes.LastIndex(x, []byte{'.'})
- if i < 0 {
- return []byte{}, x
- }
- return x[:i], x[i+1:]
-}
pcs := make([]uintptr, c.depth)
n := runtime.Callers(2, pcs) // caller of Inc
pcs = pcs[:n]
+
c.mu.Lock()
defer c.mu.Unlock()
+
+ // Existing counter?
+ var ctr *Counter
for _, s := range c.stacks {
if eq(s.pcs, pcs) {
if s.counter != nil {
- s.counter.Inc()
+ ctr = s.counter
+ break
}
- return
}
}
- // have to create the new counter's name, and the new counter itself
- locs := make([]string, 0, c.depth)
+
+ if ctr == nil {
+ // Create new counter.
+ ctr = &Counter{
+ name: EncodeStack(pcs, c.name),
+ file: c.file,
+ }
+ c.stacks = append(c.stacks, stack{pcs: pcs, counter: ctr})
+ }
+
+ ctr.Inc()
+}
+
+// EncodeStack returns the name of the counter to
+// use for the given stack of program counters.
+// The name encodes the stack.
+func EncodeStack(pcs []uintptr, prefix string) string {
+ var locs []string
lastImport := ""
frs := runtime.CallersFrames(pcs)
- for i := 0; ; i++ {
+ for {
fr, more := frs.Next()
- pcline := fr.Line
- entryptr := fr.Entry
- var locline string
- path, fname := splitPath(fr.Function)
+ // TODO(adonovan): this CutLast(".") operation isn't
+ // appropriate for generic function symbols.
+ path, fname := cutLastDot(fr.Function)
if path == lastImport {
- path = "\""
+ path = `"` // (a ditto mark)
} else {
lastImport = path
}
+ var loc string
if fr.Func != nil {
- _, entryline := fr.Func.FileLine(entryptr)
- if pcline >= entryline {
- locline = fmt.Sprintf("%s.%s:%d", path, fname, pcline-entryline)
- } else {
- // unexpected
- locline = fmt.Sprintf("%s.%s:??%d", path, fname, pcline)
- lastImport = ""
- }
+ // Use function-relative line numbering.
+ // f:+2 means two lines into function f.
+ // f:-1 should never happen, but be conservative.
+ _, entryLine := fr.Func.FileLine(fr.Entry)
+ loc = fmt.Sprintf("%s.%s:%+d", path, fname, fr.Line-entryLine)
} else {
- // might happen if the function is non-Go code or is fully inlined.
- locline = fmt.Sprintf("%s.%s:?%d", path, fname, pcline)
- lastImport = ""
+ // The function is non-Go code or is fully inlined:
+ // use absolute line number within enclosing file.
+ loc = fmt.Sprintf("%s.%s:=%d", path, fname, fr.Line)
}
- locs = append(locs, locline)
+ locs = append(locs, loc)
if !more {
break
}
}
- name := c.name + "\n" + strings.Join(locs, "\n")
+ name := prefix + "\n" + strings.Join(locs, "\n")
if len(name) > maxNameLen {
const bad = "\ntruncated\n"
name = name[:maxNameLen-len(bad)] + bad
}
- ctr := &Counter{name: name, file: c.file}
- c.stacks = append(c.stacks, stack{pcs: pcs, counter: ctr})
- ctr.Inc()
+ return name
+}
+
+// DecodeStack expands the (compressed) stack encoded in the counter name.
+func DecodeStack(ename string) string {
+ if !strings.Contains(ename, "\n") {
+ return ename // not a stack counter
+ }
+ lines := strings.Split(ename, "\n")
+ var lastPath string // empty or ends with .
+ for i, line := range lines {
+ path, rest := cutLastDot(line)
+ if len(path) == 0 {
+ continue // unchanged
+ }
+ if len(path) == 1 && path[0] == '"' {
+ lines[i] = lastPath + rest
+ } else {
+ lastPath = path + "."
+ // line unchanged
+ }
+ }
+ return strings.Join(lines, "\n") // trailing \n?
}
// input is <import path>.<function name>
// output is (import path, function name)
-func splitPath(x string) (string, string) {
+func cutLastDot(x string) (before, after string) {
i := strings.LastIndex(x, ".")
if i < 0 {
return "", x
"fmt"
"os"
"path/filepath"
+ "runtime"
"strings"
"time"
)
return mode, time.Time{}
}
+
+// DisabledOnPlatform indicates whether telemetry is disabled
+// due to bugs in the current platform.
+const DisabledOnPlatform = false ||
+ // The following platforms could potentially be supported in the future:
+ runtime.GOOS == "openbsd" || // #60614
+ runtime.GOOS == "solaris" || // #60968 #60970
+ runtime.GOOS == "android" || // #60967
+ // These platforms fundamentally can't be supported:
+ runtime.GOOS == "js" || // #60971
+ runtime.GOOS == "wasip1" || // #60971
+ runtime.GOOS == "plan9" || // https://github.com/golang/go/issues/57540#issuecomment-1470766639
+ // Work is in progress to support 386:
+ runtime.GOARCH == "386" // #60615 #60692 #60965 #60967
golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/telemetry v0.0.0-20240131160148-1cb064e7d4f2
+# golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804
## explicit; go 1.20
golang.org/x/telemetry/counter
golang.org/x/telemetry/internal/counter