mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
os: depend on Readlink only when necessary
Currently Readlink gets linked into the binary even when Executable is
not needed.
This reduces a simple "os.Stdout.Write([]byte("hello"))" by ~10KiB.
Previously the executable path was read during init time, because
deleting the executable would make "Readlink" return "(deleted)" suffix.
There's probably a slight chance that the init time reading would return
it anyways.
Updates #6853
Change-Id: Ic76190c5b64d9320ceb489cd6a553108614653d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/311790
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Tobias Klauser <tobias.klauser@gmail.com>
This commit is contained in:
parent
ecfce58965
commit
cfe5d79c5c
2 changed files with 78 additions and 8 deletions
|
|
@ -12,10 +12,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// We query the executable path at init time to avoid the problem of
|
func executable() (string, error) {
|
||||||
// readlink returns a path appended with " (deleted)" when the original
|
|
||||||
// binary gets deleted.
|
|
||||||
var executablePath, executablePathErr = func() (string, error) {
|
|
||||||
var procfn string
|
var procfn string
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
default:
|
default:
|
||||||
|
|
@ -25,9 +22,17 @@ var executablePath, executablePathErr = func() (string, error) {
|
||||||
case "netbsd":
|
case "netbsd":
|
||||||
procfn = "/proc/curproc/exe"
|
procfn = "/proc/curproc/exe"
|
||||||
}
|
}
|
||||||
return Readlink(procfn)
|
path, err := Readlink(procfn)
|
||||||
}()
|
|
||||||
|
|
||||||
func executable() (string, error) {
|
// When the executable has been deleted then Readlink returns a
|
||||||
return executablePath, executablePathErr
|
// path appended with " (deleted)".
|
||||||
|
return stringsTrimSuffix(path, " (deleted)"), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringsTrimSuffix is the same as strings.TrimSuffix.
|
||||||
|
func stringsTrimSuffix(s, suffix string) string {
|
||||||
|
if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix {
|
||||||
|
return s[:len(s)-len(suffix)]
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,3 +86,68 @@ func init() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecutableDeleted(t *testing.T) {
|
||||||
|
testenv.MustHaveExec(t)
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
t.Skip("windows does not support deleting running binary")
|
||||||
|
case "openbsd", "freebsd":
|
||||||
|
t.Skipf("%v does not support reading deleted binary name", runtime.GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
src := filepath.Join(dir, "testdel.go")
|
||||||
|
exe := filepath.Join(dir, "testdel.exe")
|
||||||
|
|
||||||
|
err := os.WriteFile(src, []byte(testExecutableDeletion), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := osexec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
|
||||||
|
t.Logf("build output:\n%s", out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err = osexec.Command(exe).CombinedOutput()
|
||||||
|
t.Logf("exec output:\n%s", out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testExecutableDeletion = `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
before, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to read executable name before deletion: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(before)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to remove executable: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
after, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to read executable name after deletion: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if before != after {
|
||||||
|
fmt.Fprintf(os.Stderr, "before and after do not match: %v != %v\n", before, after)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue