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