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 }