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