summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/bug
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/bug')
-rw-r--r--src/cmd/go/internal/bug/bug.go211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go
new file mode 100644
index 0000000..4aa08b4
--- /dev/null
+++ b/src/cmd/go/internal/bug/bug.go
@@ -0,0 +1,211 @@
+// 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 bug implements the ``go bug'' command.
+package bug
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ exec "internal/execabs"
+ "io"
+ urlpkg "net/url"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/web"
+)
+
+var CmdBug = &base.Command{
+ Run: runBug,
+ UsageLine: "go 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(&cfg.BuildV, "v", false, "")
+}
+
+func runBug(ctx context.Context, cmd *base.Command, args []string) {
+ if len(args) > 0 {
+ base.Fatalf("go bug: bug takes no arguments")
+ }
+ var buf bytes.Buffer
+ buf.WriteString(bugHeader)
+ printGoVersion(&buf)
+ buf.WriteString("### Does this issue reproduce with the latest release?\n\n\n")
+ printEnvDetails(&buf)
+ buf.WriteString(bugFooter)
+
+ body := buf.String()
+ url := "https://github.com/golang/go/issues/new?body=" + urlpkg.QueryEscape(body)
+ if !web.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! -->
+
+`
+const bugFooter = `### 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 printGoVersion(w io.Writer) {
+ fmt.Fprintf(w, "### What version of Go are you using (`go version`)?\n\n")
+ fmt.Fprintf(w, "<pre>\n")
+ fmt.Fprintf(w, "$ go version\n")
+ printCmdOut(w, "", "go", "version")
+ fmt.Fprintf(w, "</pre>\n")
+ fmt.Fprintf(w, "\n")
+}
+
+func printEnvDetails(w io.Writer) {
+ fmt.Fprintf(w, "### What operating system and processor architecture are you using (`go env`)?\n\n")
+ fmt.Fprintf(w, "<details><summary><code>go env</code> Output</summary><br><pre>\n")
+ fmt.Fprintf(w, "$ go env\n")
+ printCmdOut(w, "", "go", "env")
+ printGoDetails(w)
+ printOSDetails(w)
+ printCDetails(w)
+ fmt.Fprintf(w, "</pre></details>\n\n")
+}
+
+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", "ios":
+ 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 "illumos", "solaris":
+ // Be sure to use the OS-supplied uname, in "/usr/bin":
+ printCmdOut(w, "uname -srv: ", "/usr/bin/uname", "-srv")
+ out, err := os.ReadFile("/etc/release")
+ if err == nil {
+ fmt.Fprintf(w, "/etc/release: %s\n", out)
+ } else {
+ if cfg.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 cfg.BuildV {
+ fmt.Printf("failed to run gdb --version: %v\n", err)
+ }
+ }
+}
+
+// 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 cfg.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 := os.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:]))
+ }
+}