...

Source file src/pkg/cmd/go/internal/bug/bug.go

     1	// Copyright 2016 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// Package bug implements the ``go bug'' command.
     6	package bug
     7	
     8	import (
     9		"bytes"
    10		"fmt"
    11		"io"
    12		"io/ioutil"
    13		urlpkg "net/url"
    14		"os"
    15		"os/exec"
    16		"path/filepath"
    17		"regexp"
    18		"runtime"
    19		"strings"
    20	
    21		"cmd/go/internal/base"
    22		"cmd/go/internal/cfg"
    23		"cmd/go/internal/web"
    24	)
    25	
    26	var CmdBug = &base.Command{
    27		Run:       runBug,
    28		UsageLine: "go bug",
    29		Short:     "start a bug report",
    30		Long: `
    31	Bug opens the default browser and starts a new bug report.
    32	The report includes useful system information.
    33		`,
    34	}
    35	
    36	func init() {
    37		CmdBug.Flag.BoolVar(&cfg.BuildV, "v", false, "")
    38	}
    39	
    40	func runBug(cmd *base.Command, args []string) {
    41		if len(args) > 0 {
    42			base.Fatalf("go bug: bug takes no arguments")
    43		}
    44		var buf bytes.Buffer
    45		buf.WriteString(bugHeader)
    46		printGoVersion(&buf)
    47		buf.WriteString("### Does this issue reproduce with the latest release?\n\n\n")
    48		printEnvDetails(&buf)
    49		buf.WriteString(bugFooter)
    50	
    51		body := buf.String()
    52		url := "https://github.com/golang/go/issues/new?body=" + urlpkg.QueryEscape(body)
    53		if !web.OpenBrowser(url) {
    54			fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n")
    55			fmt.Print(body)
    56		}
    57	}
    58	
    59	const bugHeader = `<!-- Please answer these questions before submitting your issue. Thanks! -->
    60	
    61	`
    62	const bugFooter = `### What did you do?
    63	
    64	<!--
    65	If possible, provide a recipe for reproducing the error.
    66	A complete runnable program is good.
    67	A link on play.golang.org is best.
    68	-->
    69	
    70	
    71	
    72	### What did you expect to see?
    73	
    74	
    75	
    76	### What did you see instead?
    77	
    78	`
    79	
    80	func printGoVersion(w io.Writer) {
    81		fmt.Fprintf(w, "### What version of Go are you using (`go version`)?\n\n")
    82		fmt.Fprintf(w, "<pre>\n")
    83		fmt.Fprintf(w, "$ go version\n")
    84		printCmdOut(w, "", "go", "version")
    85		fmt.Fprintf(w, "</pre>\n")
    86		fmt.Fprintf(w, "\n")
    87	}
    88	
    89	func printEnvDetails(w io.Writer) {
    90		fmt.Fprintf(w, "### What operating system and processor architecture are you using (`go env`)?\n\n")
    91		fmt.Fprintf(w, "<details><summary><code>go env</code> Output</summary><br><pre>\n")
    92		fmt.Fprintf(w, "$ go env\n")
    93		printCmdOut(w, "", "go", "env")
    94		printGoDetails(w)
    95		printOSDetails(w)
    96		printCDetails(w)
    97		fmt.Fprintf(w, "</pre></details>\n\n")
    98	}
    99	
   100	func printGoDetails(w io.Writer) {
   101		printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version")
   102		printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V")
   103	}
   104	
   105	func printOSDetails(w io.Writer) {
   106		switch runtime.GOOS {
   107		case "darwin":
   108			printCmdOut(w, "uname -v: ", "uname", "-v")
   109			printCmdOut(w, "", "sw_vers")
   110		case "linux":
   111			printCmdOut(w, "uname -sr: ", "uname", "-sr")
   112			printCmdOut(w, "", "lsb_release", "-a")
   113			printGlibcVersion(w)
   114		case "openbsd", "netbsd", "freebsd", "dragonfly":
   115			printCmdOut(w, "uname -v: ", "uname", "-v")
   116		case "illumos", "solaris":
   117			// Be sure to use the OS-supplied uname, in "/usr/bin":
   118			printCmdOut(w, "uname -srv: ", "/usr/bin/uname", "-srv")
   119			out, err := ioutil.ReadFile("/etc/release")
   120			if err == nil {
   121				fmt.Fprintf(w, "/etc/release: %s\n", out)
   122			} else {
   123				if cfg.BuildV {
   124					fmt.Printf("failed to read /etc/release: %v\n", err)
   125				}
   126			}
   127		}
   128	}
   129	
   130	func printCDetails(w io.Writer) {
   131		printCmdOut(w, "lldb --version: ", "lldb", "--version")
   132		cmd := exec.Command("gdb", "--version")
   133		out, err := cmd.Output()
   134		if err == nil {
   135			// There's apparently no combination of command line flags
   136			// to get gdb to spit out its version without the license and warranty.
   137			// Print up to the first newline.
   138			fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out))
   139		} else {
   140			if cfg.BuildV {
   141				fmt.Printf("failed to run gdb --version: %v\n", err)
   142			}
   143		}
   144	}
   145	
   146	// printCmdOut prints the output of running the given command.
   147	// It ignores failures; 'go bug' is best effort.
   148	func printCmdOut(w io.Writer, prefix, path string, args ...string) {
   149		cmd := exec.Command(path, args...)
   150		out, err := cmd.Output()
   151		if err != nil {
   152			if cfg.BuildV {
   153				fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
   154			}
   155			return
   156		}
   157		fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
   158	}
   159	
   160	// firstLine returns the first line of a given byte slice.
   161	func firstLine(buf []byte) []byte {
   162		idx := bytes.IndexByte(buf, '\n')
   163		if idx > 0 {
   164			buf = buf[:idx]
   165		}
   166		return bytes.TrimSpace(buf)
   167	}
   168	
   169	// printGlibcVersion prints information about the glibc version.
   170	// It ignores failures.
   171	func printGlibcVersion(w io.Writer) {
   172		tempdir := os.TempDir()
   173		if tempdir == "" {
   174			return
   175		}
   176		src := []byte(`int main() {}`)
   177		srcfile := filepath.Join(tempdir, "go-bug.c")
   178		outfile := filepath.Join(tempdir, "go-bug")
   179		err := ioutil.WriteFile(srcfile, src, 0644)
   180		if err != nil {
   181			return
   182		}
   183		defer os.Remove(srcfile)
   184		cmd := exec.Command("gcc", "-o", outfile, srcfile)
   185		if _, err = cmd.CombinedOutput(); err != nil {
   186			return
   187		}
   188		defer os.Remove(outfile)
   189	
   190		cmd = exec.Command("ldd", outfile)
   191		out, err := cmd.CombinedOutput()
   192		if err != nil {
   193			return
   194		}
   195		re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`)
   196		m := re.FindStringSubmatch(string(out))
   197		if m == nil {
   198			return
   199		}
   200		cmd = exec.Command(m[1])
   201		out, err = cmd.Output()
   202		if err != nil {
   203			return
   204		}
   205		fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out))
   206	
   207		// print another line (the one containing version string) in case of musl libc
   208		if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 {
   209			fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:]))
   210		}
   211	}
   212	

View as plain text