mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-19 19:38:40 +00:00
version: remove rsc.io/goversion dependency
rsc.io/goversion is really expensive. Running version.ReadExe on tailscaled on darwin allocates 47k objects, almost 11mb. All we want is the module info. For that, all we need to do is scan through the binary looking for the magic start/end strings and then grab the bytes in between them. We can do that easily and quickly with nothing but a 64k buffer. Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
parent
bdb93c5942
commit
a4e19f2233
@ -20,7 +20,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
|
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
|
||||||
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
|
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
|
||||||
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
|
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
|
||||||
rsc.io/goversion/version from tailscale.com/version
|
|
||||||
tailscale.com/atomicfile from tailscale.com/ipn
|
tailscale.com/atomicfile from tailscale.com/ipn
|
||||||
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
|
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
|
||||||
@ -101,9 +100,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
golang.org/x/time/rate from tailscale.com/cmd/tailscale/cli+
|
golang.org/x/time/rate from tailscale.com/cmd/tailscale/cli+
|
||||||
bufio from compress/flate+
|
bufio from compress/flate+
|
||||||
bytes from bufio+
|
bytes from bufio+
|
||||||
compress/flate from compress/gzip+
|
compress/flate from compress/gzip
|
||||||
compress/gzip from net/http
|
compress/gzip from net/http
|
||||||
compress/zlib from debug/elf+
|
|
||||||
container/list from crypto/tls+
|
container/list from crypto/tls+
|
||||||
context from crypto/tls+
|
context from crypto/tls+
|
||||||
crypto from crypto/ecdsa+
|
crypto from crypto/ecdsa+
|
||||||
@ -126,10 +124,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
crypto/tls from github.com/tcnksm/go-httpstat+
|
crypto/tls from github.com/tcnksm/go-httpstat+
|
||||||
crypto/x509 from crypto/tls+
|
crypto/x509 from crypto/tls+
|
||||||
crypto/x509/pkix from crypto/x509+
|
crypto/x509/pkix from crypto/x509+
|
||||||
debug/dwarf from debug/elf+
|
|
||||||
debug/elf from rsc.io/goversion/version
|
|
||||||
debug/macho from rsc.io/goversion/version
|
|
||||||
debug/pe from rsc.io/goversion/version
|
|
||||||
embed from tailscale.com/cmd/tailscale/cli
|
embed from tailscale.com/cmd/tailscale/cli
|
||||||
encoding from encoding/json+
|
encoding from encoding/json+
|
||||||
encoding/asn1 from crypto/x509+
|
encoding/asn1 from crypto/x509+
|
||||||
@ -143,8 +137,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
expvar from tailscale.com/derp+
|
expvar from tailscale.com/derp+
|
||||||
flag from github.com/peterbourgon/ff/v2+
|
flag from github.com/peterbourgon/ff/v2+
|
||||||
fmt from compress/flate+
|
fmt from compress/flate+
|
||||||
hash from compress/zlib+
|
hash from crypto+
|
||||||
hash/adler32 from compress/zlib
|
|
||||||
hash/crc32 from compress/gzip+
|
hash/crc32 from compress/gzip+
|
||||||
hash/maphash from go4.org/mem
|
hash/maphash from go4.org/mem
|
||||||
html from tailscale.com/ipn/ipnstate+
|
html from tailscale.com/ipn/ipnstate+
|
||||||
@ -171,10 +164,10 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
os/exec from github.com/toqueteos/webbrowser+
|
os/exec from github.com/toqueteos/webbrowser+
|
||||||
os/signal from tailscale.com/cmd/tailscale/cli
|
os/signal from tailscale.com/cmd/tailscale/cli
|
||||||
os/user from tailscale.com/util/groupmember
|
os/user from tailscale.com/util/groupmember
|
||||||
path from debug/dwarf+
|
path from html/template+
|
||||||
path/filepath from crypto/x509+
|
path/filepath from crypto/x509+
|
||||||
reflect from crypto/x509+
|
reflect from crypto/x509+
|
||||||
regexp from rsc.io/goversion/version+
|
regexp from github.com/tailscale/goupnp/httpu+
|
||||||
regexp/syntax from regexp
|
regexp/syntax from regexp
|
||||||
runtime/debug from golang.org/x/sync/singleflight
|
runtime/debug from golang.org/x/sync/singleflight
|
||||||
sort from compress/flate+
|
sort from compress/flate+
|
||||||
|
@ -87,7 +87,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
inet.af/netstack/waiter from inet.af/netstack/tcpip+
|
inet.af/netstack/waiter from inet.af/netstack/tcpip+
|
||||||
inet.af/peercred from tailscale.com/ipn/ipnserver
|
inet.af/peercred from tailscale.com/ipn/ipnserver
|
||||||
W 💣 inet.af/wf from tailscale.com/wf
|
W 💣 inet.af/wf from tailscale.com/wf
|
||||||
rsc.io/goversion/version from tailscale.com/version
|
|
||||||
tailscale.com/atomicfile from tailscale.com/ipn+
|
tailscale.com/atomicfile from tailscale.com/ipn+
|
||||||
tailscale.com/client/tailscale from tailscale.com/derp
|
tailscale.com/client/tailscale from tailscale.com/derp
|
||||||
tailscale.com/client/tailscale/apitype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/client/tailscale/apitype from tailscale.com/ipn/ipnlocal+
|
||||||
@ -216,9 +215,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
golang.org/x/time/rate from inet.af/netstack/tcpip/stack+
|
golang.org/x/time/rate from inet.af/netstack/tcpip/stack+
|
||||||
bufio from compress/flate+
|
bufio from compress/flate+
|
||||||
bytes from bufio+
|
bytes from bufio+
|
||||||
compress/flate from compress/gzip+
|
compress/flate from compress/gzip
|
||||||
compress/gzip from internal/profile+
|
compress/gzip from internal/profile+
|
||||||
compress/zlib from debug/elf+
|
|
||||||
container/heap from inet.af/netstack/tcpip/transport/tcp
|
container/heap from inet.af/netstack/tcpip/transport/tcp
|
||||||
container/list from crypto/tls+
|
container/list from crypto/tls+
|
||||||
context from crypto/tls+
|
context from crypto/tls+
|
||||||
@ -242,10 +240,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
crypto/tls from github.com/tcnksm/go-httpstat+
|
crypto/tls from github.com/tcnksm/go-httpstat+
|
||||||
crypto/x509 from crypto/tls+
|
crypto/x509 from crypto/tls+
|
||||||
crypto/x509/pkix from crypto/x509+
|
crypto/x509/pkix from crypto/x509+
|
||||||
debug/dwarf from debug/elf+
|
|
||||||
debug/elf from rsc.io/goversion/version
|
|
||||||
debug/macho from rsc.io/goversion/version
|
|
||||||
debug/pe from rsc.io/goversion/version
|
|
||||||
embed from tailscale.com/net/dns+
|
embed from tailscale.com/net/dns+
|
||||||
encoding from encoding/json+
|
encoding from encoding/json+
|
||||||
encoding/asn1 from crypto/x509+
|
encoding/asn1 from crypto/x509+
|
||||||
@ -259,8 +253,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
expvar from tailscale.com/derp+
|
expvar from tailscale.com/derp+
|
||||||
flag from tailscale.com/cmd/tailscaled+
|
flag from tailscale.com/cmd/tailscaled+
|
||||||
fmt from compress/flate+
|
fmt from compress/flate+
|
||||||
hash from compress/zlib+
|
hash from crypto+
|
||||||
hash/adler32 from compress/zlib
|
|
||||||
hash/crc32 from compress/gzip+
|
hash/crc32 from compress/gzip+
|
||||||
hash/fnv from tailscale.com/wgengine/magicsock+
|
hash/fnv from tailscale.com/wgengine/magicsock+
|
||||||
hash/maphash from go4.org/mem
|
hash/maphash from go4.org/mem
|
||||||
@ -288,7 +281,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
os/exec from github.com/coreos/go-iptables/iptables+
|
os/exec from github.com/coreos/go-iptables/iptables+
|
||||||
os/signal from tailscale.com/cmd/tailscaled+
|
os/signal from tailscale.com/cmd/tailscaled+
|
||||||
os/user from github.com/godbus/dbus/v5+
|
os/user from github.com/godbus/dbus/v5+
|
||||||
path from debug/dwarf+
|
path from github.com/godbus/dbus/v5+
|
||||||
path/filepath from crypto/x509+
|
path/filepath from crypto/x509+
|
||||||
reflect from crypto/x509+
|
reflect from crypto/x509+
|
||||||
regexp from github.com/coreos/go-iptables/iptables+
|
regexp from github.com/coreos/go-iptables/iptables+
|
||||||
|
1
go.mod
1
go.mod
@ -51,5 +51,4 @@ require (
|
|||||||
inet.af/netstack v0.0.0-20210622165351-29b14ebc044e
|
inet.af/netstack v0.0.0-20210622165351-29b14ebc044e
|
||||||
inet.af/peercred v0.0.0-20210318190834-4259e17bb763
|
inet.af/peercred v0.0.0-20210318190834-4259e17bb763
|
||||||
inet.af/wf v0.0.0-20210516214145-a5343001b756
|
inet.af/wf v0.0.0-20210516214145-a5343001b756
|
||||||
rsc.io/goversion v1.2.0
|
|
||||||
)
|
)
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"rsc.io/goversion/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdName returns either the base name of the current binary
|
// CmdName returns either the base name of the current binary
|
||||||
@ -30,13 +32,13 @@ func CmdName() string {
|
|||||||
fallbackName := filepath.Base(strings.TrimSuffix(strings.ToLower(e), ".exe"))
|
fallbackName := filepath.Base(strings.TrimSuffix(strings.ToLower(e), ".exe"))
|
||||||
|
|
||||||
var ret string
|
var ret string
|
||||||
v, err := version.ReadExe(e)
|
info, err := findModuleInfo(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fallbackName
|
return fallbackName
|
||||||
}
|
}
|
||||||
// v is like:
|
// v is like:
|
||||||
// "path\ttailscale.com/cmd/tailscale\nmod\ttailscale.com\t(devel)\t\ndep\tgithub.com/apenwarr/fixconsole\tv0.0.0-20191012055117-5a9f6489cc29\th1:muXWUcay7DDy1/hEQWrYlBy+g0EuwT70sBHg65SeUc4=\ndep\tgithub....
|
// "path\ttailscale.com/cmd/tailscale\nmod\ttailscale.com\t(devel)\t\ndep\tgithub.com/apenwarr/fixconsole\tv0.0.0-20191012055117-5a9f6489cc29\th1:muXWUcay7DDy1/hEQWrYlBy+g0EuwT70sBHg65SeUc4=\ndep\tgithub....
|
||||||
for _, line := range strings.Split(v.ModuleInfo, "\n") {
|
for _, line := range strings.Split(info, "\n") {
|
||||||
if strings.HasPrefix(line, "path\t") {
|
if strings.HasPrefix(line, "path\t") {
|
||||||
goPkg := strings.TrimPrefix(line, "path\t") // like "tailscale.com/cmd/tailscale"
|
goPkg := strings.TrimPrefix(line, "path\t") // like "tailscale.com/cmd/tailscale"
|
||||||
ret = path.Base(goPkg) // goPkg is always forward slashes; use path, not filepath
|
ret = path.Base(goPkg) // goPkg is always forward slashes; use path, not filepath
|
||||||
@ -48,3 +50,84 @@ func CmdName() string {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findModuleInfo returns the Go module info from the executable file.
|
||||||
|
func findModuleInfo(file string) (s string, err error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
// Scan through f until we find infoStart.
|
||||||
|
buf := make([]byte, 65536)
|
||||||
|
start, err := findOffset(f, buf, infoStart)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
start += int64(len(infoStart))
|
||||||
|
// Seek to the end of infoStart and scan for infoEnd.
|
||||||
|
_, err = f.Seek(start, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
end, err := findOffset(f, buf, infoEnd)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
length := end - start
|
||||||
|
// As of Aug 2021, tailscaled's mod info was about 2k.
|
||||||
|
if length > int64(len(buf)) {
|
||||||
|
return "", errors.New("mod info too large")
|
||||||
|
}
|
||||||
|
// We have located modinfo. Read it into buf.
|
||||||
|
buf = buf[:length]
|
||||||
|
_, err = f.Seek(start, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, err = io.ReadFull(f, buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findOffset finds the absolute offset of needle in f,
|
||||||
|
// starting at f's current read position,
|
||||||
|
// using temporary buffer buf.
|
||||||
|
func findOffset(f *os.File, buf, needle []byte) (int64, error) {
|
||||||
|
for {
|
||||||
|
// Fill buf and look within it.
|
||||||
|
n, err := f.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
i := bytes.Index(buf[:n], needle)
|
||||||
|
if i < 0 {
|
||||||
|
// Not found. Rewind a little bit in case we happened to end halfway through needle.
|
||||||
|
rewind, err := f.Seek(int64(-len(needle)), io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
// If we're at EOF and rewound exactly len(needle) bytes, return io.EOF.
|
||||||
|
_, err = f.ReadAt(buf[:1], rewind+int64(len(needle)))
|
||||||
|
if err == io.EOF {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Found! Figure out exactly where.
|
||||||
|
cur, err := f.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return cur - int64(n) + int64(i), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These constants are taken from rsc.io/goversion.
|
||||||
|
|
||||||
|
var (
|
||||||
|
infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
|
||||||
|
infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
|
||||||
|
)
|
||||||
|
29
version/modinfo_test.go
Normal file
29
version/modinfo_test.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2021 Tailscale Inc & 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 version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindModuleInfo(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
name := filepath.Join(dir, "tailscaled-version-test")
|
||||||
|
out, err := exec.Command("go", "build", "-o", name, "tailscale.com/cmd/tailscaled").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to build tailscaled: %v\n%s", err, out)
|
||||||
|
}
|
||||||
|
modinfo, err := findModuleInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
prefix := "path\ttailscale.com/cmd/tailscaled\nmod\ttailscale.com"
|
||||||
|
if !strings.HasPrefix(modinfo, prefix) {
|
||||||
|
t.Errorf("unexpected modinfo contents %q", modinfo)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user