github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/src/cmd/go/bug.go (about)

     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 main
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"regexp"
    16  	"runtime"
    17  	"strings"
    18  )
    19  
    20  var cmdBug = &Command{
    21  	Run:       runBug,
    22  	UsageLine: "bug",
    23  	Short:     "print information for bug reports",
    24  	Long: `
    25  Bug prints information that helps file effective bug reports.
    26  
    27  Bugs may be reported at https://golang.org/issue/new.
    28  	`,
    29  }
    30  
    31  func init() {
    32  	cmdBug.Flag.BoolVar(&buildV, "v", false, "")
    33  }
    34  
    35  func runBug(cmd *Command, args []string) {
    36  	var buf bytes.Buffer
    37  	buf.WriteString(bugHeader)
    38  	inspectGoVersion(&buf)
    39  	fmt.Fprint(&buf, "#### System details\n\n")
    40  	fmt.Fprintln(&buf, "```")
    41  	fmt.Fprintf(&buf, "go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
    42  	env := mkEnv()
    43  	env = append(env, extraEnvVars()...)
    44  	for _, e := range env {
    45  		fmt.Fprintf(&buf, "%s=\"%s\"\n", e.name, e.value)
    46  	}
    47  	printGoDetails(&buf)
    48  	printOSDetails(&buf)
    49  	printCDetails(&buf)
    50  	fmt.Fprintln(&buf, "```")
    51  
    52  	body := buf.String()
    53  	url := "https://github.com/golang/go/issues/new?body=" + queryEscape(body)
    54  	if !openBrowser(url) {
    55  		fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n")
    56  		fmt.Print(body)
    57  	}
    58  }
    59  
    60  const bugHeader = `Please answer these questions before submitting your issue. Thanks!
    61  
    62  #### What did you do?
    63  If possible, provide a recipe for reproducing the error.
    64  A complete runnable program is good.
    65  A link on play.golang.org is best.
    66  
    67  
    68  #### What did you expect to see?
    69  
    70  
    71  #### What did you see instead?
    72  
    73  
    74  `
    75  
    76  func printGoDetails(w io.Writer) {
    77  	printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version")
    78  	printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V")
    79  }
    80  
    81  func printOSDetails(w io.Writer) {
    82  	switch runtime.GOOS {
    83  	case "darwin":
    84  		printCmdOut(w, "uname -v: ", "uname", "-v")
    85  		printCmdOut(w, "", "sw_vers")
    86  	case "linux":
    87  		printCmdOut(w, "uname -sr: ", "uname", "-sr")
    88  		printCmdOut(w, "", "lsb_release", "-a")
    89  		printGlibcVersion(w)
    90  	case "openbsd", "netbsd", "freebsd", "dragonfly":
    91  		printCmdOut(w, "uname -v: ", "uname", "-v")
    92  	case "solaris":
    93  		out, err := ioutil.ReadFile("/etc/release")
    94  		if err == nil {
    95  			fmt.Fprintf(w, "/etc/release: %s\n", out)
    96  		} else {
    97  			if buildV {
    98  				fmt.Printf("failed to read /etc/release: %v\n", err)
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func printCDetails(w io.Writer) {
   105  	printCmdOut(w, "lldb --version: ", "lldb", "--version")
   106  	cmd := exec.Command("gdb", "--version")
   107  	out, err := cmd.Output()
   108  	if err == nil {
   109  		// There's apparently no combination of command line flags
   110  		// to get gdb to spit out its version without the license and warranty.
   111  		// Print up to the first newline.
   112  		fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out))
   113  	} else {
   114  		if buildV {
   115  			fmt.Printf("failed to run gdb --version: %v\n", err)
   116  		}
   117  	}
   118  }
   119  
   120  func inspectGoVersion(w io.Writer) {
   121  	data, err := httpGET("https://golang.org/VERSION?m=text")
   122  	if err != nil {
   123  		if buildV {
   124  			fmt.Printf("failed to read from golang.org/VERSION: %v\n", err)
   125  		}
   126  		return
   127  	}
   128  
   129  	// golang.org/VERSION currently returns a whitespace-free string,
   130  	// but just in case, protect against that changing.
   131  	// Similarly so for runtime.Version.
   132  	release := string(bytes.TrimSpace(data))
   133  	vers := strings.TrimSpace(runtime.Version())
   134  
   135  	if vers == release {
   136  		// Up to date
   137  		return
   138  	}
   139  
   140  	// Devel version or outdated release. Either way, this request is apropos.
   141  	fmt.Fprintf(w, "#### Does this issue reproduce with the latest release (%s)?\n\n\n", release)
   142  }
   143  
   144  // printCmdOut prints the output of running the given command.
   145  // It ignores failures; 'go bug' is best effort.
   146  func printCmdOut(w io.Writer, prefix, path string, args ...string) {
   147  	cmd := exec.Command(path, args...)
   148  	out, err := cmd.Output()
   149  	if err != nil {
   150  		if buildV {
   151  			fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
   152  		}
   153  		return
   154  	}
   155  	fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
   156  }
   157  
   158  // firstLine returns the first line of a given byte slice.
   159  func firstLine(buf []byte) []byte {
   160  	idx := bytes.IndexByte(buf, '\n')
   161  	if idx > 0 {
   162  		buf = buf[:idx]
   163  	}
   164  	return bytes.TrimSpace(buf)
   165  }
   166  
   167  // printGlibcVersion prints information about the glibc version.
   168  // It ignores failures.
   169  func printGlibcVersion(w io.Writer) {
   170  	tempdir := os.TempDir()
   171  	if tempdir == "" {
   172  		return
   173  	}
   174  	src := []byte(`int main() {}`)
   175  	srcfile := filepath.Join(tempdir, "go-bug.c")
   176  	outfile := filepath.Join(tempdir, "go-bug")
   177  	err := ioutil.WriteFile(srcfile, src, 0644)
   178  	if err != nil {
   179  		return
   180  	}
   181  	defer os.Remove(srcfile)
   182  	cmd := exec.Command("gcc", "-o", outfile, srcfile)
   183  	if _, err = cmd.CombinedOutput(); err != nil {
   184  		return
   185  	}
   186  	defer os.Remove(outfile)
   187  
   188  	cmd = exec.Command("ldd", outfile)
   189  	out, err := cmd.CombinedOutput()
   190  	if err != nil {
   191  		return
   192  	}
   193  	re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`)
   194  	m := re.FindStringSubmatch(string(out))
   195  	if m == nil {
   196  		return
   197  	}
   198  	cmd = exec.Command(m[1])
   199  	out, err = cmd.Output()
   200  	if err != nil {
   201  		return
   202  	}
   203  	fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out))
   204  
   205  	// print another line (the one containing version string) in case of musl libc
   206  	if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 {
   207  		fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:]))
   208  	}
   209  }