mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-05 20:26:47 +02:00
CmdName was re-opening the running executable and scanning it in
64KiB chunks for the Go modinfo markers on every call. The same
modinfo is already parsed at startup and exposed via
runtime/debug.ReadBuildInfo, so prefer that on non-Windows. Windows
still takes the scanning path because its GUI-binary override keys
off the on-disk executable name.
benchstat of BenchmarkCmdName (Linux, before vs after):
goos: linux
goarch: amd64
pkg: tailscale.com/version
cpu: Intel(R) Xeon(R) 6975P-C
│ /tmp/old.txt │ /tmp/new.txt │
│ sec/op │ sec/op vs base │
CmdName-16 556045.5n ± 1% 825.6n ± 1% -99.85% (p=0.000 n=10)
│ /tmp/old.txt │ /tmp/new.txt │
│ B/op │ B/op vs base │
CmdName-16 64.587Ki ± 0% 1.156Ki ± 0% -98.21% (p=0.000 n=10)
│ /tmp/old.txt │ /tmp/new.txt │
│ allocs/op │ allocs/op vs base │
CmdName-16 8.000 ± 0% 7.000 ± 0% -12.50% (p=0.000 n=10)
Fixes #19486
Change-Id: I925c5e28b64815a602459beb6c8dab8779339a6c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
72 lines
1.5 KiB
Go
72 lines
1.5 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package version_test
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path"
|
|
"runtime/debug"
|
|
"testing"
|
|
|
|
ts "tailscale.com"
|
|
"tailscale.com/version"
|
|
)
|
|
|
|
func TestAlpineTag(t *testing.T) {
|
|
if tag := readAlpineTag(t, "../Dockerfile.base"); tag == "" {
|
|
t.Fatal(`"FROM alpine:" not found in Dockerfile.base`)
|
|
} else if tag != ts.AlpineDockerTag {
|
|
t.Errorf("alpine version mismatch: Dockerfile.base has %q; ALPINE.txt has %q", tag, ts.AlpineDockerTag)
|
|
}
|
|
if tag := readAlpineTag(t, "../Dockerfile"); tag == "" {
|
|
t.Fatal(`"FROM alpine:" not found in Dockerfile`)
|
|
} else if tag != ts.AlpineDockerTag {
|
|
t.Errorf("alpine version mismatch: Dockerfile has %q; ALPINE.txt has %q", tag, ts.AlpineDockerTag)
|
|
}
|
|
}
|
|
|
|
func readAlpineTag(t *testing.T, file string) string {
|
|
f, err := os.ReadFile(file)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for line := range bytes.SplitSeq(f, []byte{'\n'}) {
|
|
line = bytes.TrimSpace(line)
|
|
_, suf, ok := bytes.Cut(line, []byte("FROM alpine:"))
|
|
if !ok {
|
|
continue
|
|
}
|
|
return string(suf)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TestShortAllocs(t *testing.T) {
|
|
allocs := int(testing.AllocsPerRun(10000, func() {
|
|
_ = version.Short()
|
|
}))
|
|
if allocs > 0 {
|
|
t.Errorf("allocs = %v; want 0", allocs)
|
|
}
|
|
}
|
|
|
|
func BenchmarkCmdName(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
_ = version.CmdName()
|
|
}
|
|
}
|
|
|
|
func BenchmarkReadBuildInfo(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
info, ok := debug.ReadBuildInfo()
|
|
if !ok {
|
|
b.Fatal("ReadBuildInfo failed")
|
|
}
|
|
_ = path.Base(info.Path)
|
|
}
|
|
}
|