Bläddra i källkod

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 <[email protected]>
Josh Bleecher Snyder 4 år sedan
förälder
incheckning
a4e19f2233
5 ändrade filer med 123 tillägg och 26 borttagningar
  1. 4 11
      cmd/tailscale/depaware.txt
  2. 3 10
      cmd/tailscaled/depaware.txt
  3. 0 1
      go.mod
  4. 87 4
      version/cmdname.go
  5. 29 0
      version/modinfo_test.go

+ 4 - 11
cmd/tailscale/depaware.txt

@@ -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
    W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg           from tailscale.com/net/interfaces+
         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/client/tailscale                               from tailscale.com/cmd/tailscale/cli+
         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+
         bufio                                                        from compress/flate+
         bytes                                                        from bufio+
-        compress/flate                                               from compress/gzip+
+        compress/flate                                               from compress/gzip
         compress/gzip                                                from net/http
-        compress/zlib                                                from debug/elf+
         container/list                                               from crypto/tls+
         context                                                      from crypto/tls+
         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/x509                                                  from crypto/tls+
         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
         encoding                                                     from encoding/json+
         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+
         flag                                                         from github.com/peterbourgon/ff/v2+
         fmt                                                          from compress/flate+
-        hash                                                         from compress/zlib+
-        hash/adler32                                                 from compress/zlib
+        hash                                                         from crypto+
         hash/crc32                                                   from compress/gzip+
         hash/maphash                                                 from go4.org/mem
         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/signal                                                    from tailscale.com/cmd/tailscale/cli
         os/user                                                      from tailscale.com/util/groupmember
-        path                                                         from debug/dwarf+
+        path                                                         from html/template+
         path/filepath                                                from crypto/x509+
         reflect                                                      from crypto/x509+
-        regexp                                                       from rsc.io/goversion/version+
+        regexp                                                       from github.com/tailscale/goupnp/httpu+
         regexp/syntax                                                from regexp
         runtime/debug                                                from golang.org/x/sync/singleflight
         sort                                                         from compress/flate+

+ 3 - 10
cmd/tailscaled/depaware.txt

@@ -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/peercred                                             from tailscale.com/ipn/ipnserver
    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/client/tailscale                               from tailscale.com/derp
         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+
         bufio                                                        from compress/flate+
         bytes                                                        from bufio+
-        compress/flate                                               from compress/gzip+
+        compress/flate                                               from compress/gzip
         compress/gzip                                                from internal/profile+
-        compress/zlib                                                from debug/elf+
         container/heap                                               from inet.af/netstack/tcpip/transport/tcp
         container/list                                               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/x509                                                  from crypto/tls+
         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+
         encoding                                                     from encoding/json+
         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+
         flag                                                         from tailscale.com/cmd/tailscaled+
         fmt                                                          from compress/flate+
-        hash                                                         from compress/zlib+
-        hash/adler32                                                 from compress/zlib
+        hash                                                         from crypto+
         hash/crc32                                                   from compress/gzip+
         hash/fnv                                                     from tailscale.com/wgengine/magicsock+
         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/signal                                                    from tailscale.com/cmd/tailscaled+
         os/user                                                      from github.com/godbus/dbus/v5+
-        path                                                         from debug/dwarf+
+        path                                                         from github.com/godbus/dbus/v5+
         path/filepath                                                from crypto/x509+
         reflect                                                      from crypto/x509+
         regexp                                                       from github.com/coreos/go-iptables/iptables+

+ 0 - 1
go.mod

@@ -51,5 +51,4 @@ require (
 	inet.af/netstack v0.0.0-20210622165351-29b14ebc044e
 	inet.af/peercred v0.0.0-20210318190834-4259e17bb763
 	inet.af/wf v0.0.0-20210516214145-a5343001b756
-	rsc.io/goversion v1.2.0
 )

+ 87 - 4
version/cmdname.go

@@ -8,12 +8,14 @@
 package version
 
 import (
+	"bytes"
+	"encoding/hex"
+	"errors"
+	"io"
 	"os"
 	"path"
 	"path/filepath"
 	"strings"
-
-	"rsc.io/goversion/version"
 )
 
 // 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"))
 
 	var ret string
-	v, err := version.ReadExe(e)
+	info, err := findModuleInfo(e)
 	if err != nil {
 		return fallbackName
 	}
 	// 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....
-	for _, line := range strings.Split(v.ModuleInfo, "\n") {
+	for _, line := range strings.Split(info, "\n") {
 		if strings.HasPrefix(line, "path\t") {
 			goPkg := strings.TrimPrefix(line, "path\t") // like "tailscale.com/cmd/tailscale"
 			ret = path.Base(goPkg)                      // goPkg is always forward slashes; use path, not filepath
@@ -48,3 +50,84 @@ func CmdName() string {
 	}
 	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 - 0
version/modinfo_test.go

@@ -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)
+	}
+}