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