diff options
Diffstat (limited to 'libgo/go/cmd/go/bug.go')
-rw-r--r-- | libgo/go/cmd/go/bug.go | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/libgo/go/cmd/go/bug.go b/libgo/go/cmd/go/bug.go new file mode 100644 index 0000000000..658f6dabd9 --- /dev/null +++ b/libgo/go/cmd/go/bug.go @@ -0,0 +1,212 @@ +// Copyright 2016 The Go 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 main + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +var cmdBug = &Command{ + Run: runBug, + UsageLine: "bug", + Short: "start a bug report", + Long: ` +Bug opens the default browser and starts a new bug report. +The report includes useful system information. + `, +} + +func init() { + cmdBug.Flag.BoolVar(&buildV, "v", false, "") +} + +func runBug(cmd *Command, args []string) { + var buf bytes.Buffer + buf.WriteString(bugHeader) + inspectGoVersion(&buf) + fmt.Fprint(&buf, "#### System details\n\n") + fmt.Fprintln(&buf, "```") + fmt.Fprintf(&buf, "go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) + env := newEnv + env = append(env, extraEnvVars()...) + for _, e := range env { + // Hide the TERM environment variable from "go bug". + // See issue #18128 + if e.name != "TERM" { + fmt.Fprintf(&buf, "%s=\"%s\"\n", e.name, e.value) + } + } + printGoDetails(&buf) + printOSDetails(&buf) + printCDetails(&buf) + fmt.Fprintln(&buf, "```") + + body := buf.String() + url := "https://github.com/golang/go/issues/new?body=" + queryEscape(body) + if !openBrowser(url) { + fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n") + fmt.Print(body) + } +} + +const bugHeader = `Please answer these questions before submitting your issue. Thanks! + +#### What did you do? +If possible, provide a recipe for reproducing the error. +A complete runnable program is good. +A link on play.golang.org is best. + + +#### What did you expect to see? + + +#### What did you see instead? + + +` + +func printGoDetails(w io.Writer) { + printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version") + printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V") +} + +func printOSDetails(w io.Writer) { + switch runtime.GOOS { + case "darwin": + printCmdOut(w, "uname -v: ", "uname", "-v") + printCmdOut(w, "", "sw_vers") + case "linux": + printCmdOut(w, "uname -sr: ", "uname", "-sr") + printCmdOut(w, "", "lsb_release", "-a") + printGlibcVersion(w) + case "openbsd", "netbsd", "freebsd", "dragonfly": + printCmdOut(w, "uname -v: ", "uname", "-v") + case "solaris": + out, err := ioutil.ReadFile("/etc/release") + if err == nil { + fmt.Fprintf(w, "/etc/release: %s\n", out) + } else { + if buildV { + fmt.Printf("failed to read /etc/release: %v\n", err) + } + } + } +} + +func printCDetails(w io.Writer) { + printCmdOut(w, "lldb --version: ", "lldb", "--version") + cmd := exec.Command("gdb", "--version") + out, err := cmd.Output() + if err == nil { + // There's apparently no combination of command line flags + // to get gdb to spit out its version without the license and warranty. + // Print up to the first newline. + fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out)) + } else { + if buildV { + fmt.Printf("failed to run gdb --version: %v\n", err) + } + } +} + +func inspectGoVersion(w io.Writer) { + data, err := httpGET("https://golang.org/VERSION?m=text") + if err != nil { + if buildV { + fmt.Printf("failed to read from golang.org/VERSION: %v\n", err) + } + return + } + + // golang.org/VERSION currently returns a whitespace-free string, + // but just in case, protect against that changing. + // Similarly so for runtime.Version. + release := string(bytes.TrimSpace(data)) + vers := strings.TrimSpace(runtime.Version()) + + if vers == release { + // Up to date + return + } + + // Devel version or outdated release. Either way, this request is apropos. + fmt.Fprintf(w, "#### Does this issue reproduce with the latest release (%s)?\n\n\n", release) +} + +// printCmdOut prints the output of running the given command. +// It ignores failures; 'go bug' is best effort. +func printCmdOut(w io.Writer, prefix, path string, args ...string) { + cmd := exec.Command(path, args...) + out, err := cmd.Output() + if err != nil { + if buildV { + fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err) + } + return + } + fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out)) +} + +// firstLine returns the first line of a given byte slice. +func firstLine(buf []byte) []byte { + idx := bytes.IndexByte(buf, '\n') + if idx > 0 { + buf = buf[:idx] + } + return bytes.TrimSpace(buf) +} + +// printGlibcVersion prints information about the glibc version. +// It ignores failures. +func printGlibcVersion(w io.Writer) { + tempdir := os.TempDir() + if tempdir == "" { + return + } + src := []byte(`int main() {}`) + srcfile := filepath.Join(tempdir, "go-bug.c") + outfile := filepath.Join(tempdir, "go-bug") + err := ioutil.WriteFile(srcfile, src, 0644) + if err != nil { + return + } + defer os.Remove(srcfile) + cmd := exec.Command("gcc", "-o", outfile, srcfile) + if _, err = cmd.CombinedOutput(); err != nil { + return + } + defer os.Remove(outfile) + + cmd = exec.Command("ldd", outfile) + out, err := cmd.CombinedOutput() + if err != nil { + return + } + re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`) + m := re.FindStringSubmatch(string(out)) + if m == nil { + return + } + cmd = exec.Command(m[1]) + out, err = cmd.Output() + if err != nil { + return + } + fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out)) + + // print another line (the one containing version string) in case of musl libc + if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 { + fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:])) + } +} |