1
2
3
4
5
6 package bug
7
8 import (
9 "bytes"
10 "context"
11 "fmt"
12 exec "internal/execabs"
13 "io"
14 urlpkg "net/url"
15 "os"
16 "path/filepath"
17 "regexp"
18 "runtime"
19 "strings"
20
21 "cmd/go/internal/base"
22 "cmd/go/internal/cfg"
23 "cmd/go/internal/envcmd"
24 "cmd/go/internal/web"
25 )
26
27 var CmdBug = &base.Command{
28 Run: runBug,
29 UsageLine: "go bug",
30 Short: "start a bug report",
31 Long: `
32 Bug opens the default browser and starts a new bug report.
33 The report includes useful system information.
34 `,
35 }
36
37 func init() {
38 CmdBug.Flag.BoolVar(&cfg.BuildV, "v", false, "")
39 }
40
41 func runBug(ctx context.Context, cmd *base.Command, args []string) {
42 if len(args) > 0 {
43 base.Fatalf("go: bug takes no arguments")
44 }
45 var buf bytes.Buffer
46 buf.WriteString(bugHeader)
47 printGoVersion(&buf)
48 buf.WriteString("### Does this issue reproduce with the latest release?\n\n\n")
49 printEnvDetails(&buf)
50 buf.WriteString(bugFooter)
51
52 body := buf.String()
53 url := "https://github.com/golang/go/issues/new?body=" + urlpkg.QueryEscape(body)
54 if !web.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 `
63 const bugFooter = `### What did you do?
64
65 <!--
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
72
73 ### What did you expect to see?
74
75
76
77 ### What did you see instead?
78
79 `
80
81 func printGoVersion(w io.Writer) {
82 fmt.Fprintf(w, "### What version of Go are you using (`go version`)?\n\n")
83 fmt.Fprintf(w, "<pre>\n")
84 fmt.Fprintf(w, "$ go version\n")
85 fmt.Fprintf(w, "go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
86 fmt.Fprintf(w, "</pre>\n")
87 fmt.Fprintf(w, "\n")
88 }
89
90 func printEnvDetails(w io.Writer) {
91 fmt.Fprintf(w, "### What operating system and processor architecture are you using (`go env`)?\n\n")
92 fmt.Fprintf(w, "<details><summary><code>go env</code> Output</summary><br><pre>\n")
93 fmt.Fprintf(w, "$ go env\n")
94 printGoEnv(w)
95 printGoDetails(w)
96 printOSDetails(w)
97 printCDetails(w)
98 fmt.Fprintf(w, "</pre></details>\n\n")
99 }
100
101 func printGoEnv(w io.Writer) {
102 env := envcmd.MkEnv()
103 env = append(env, envcmd.ExtraEnvVars()...)
104 env = append(env, envcmd.ExtraEnvVarsCostly()...)
105 envcmd.PrintEnv(w, env)
106 }
107
108 func printGoDetails(w io.Writer) {
109 gocmd := filepath.Join(runtime.GOROOT(), "bin/go")
110 printCmdOut(w, "GOROOT/bin/go version: ", gocmd, "version")
111 printCmdOut(w, "GOROOT/bin/go tool compile -V: ", gocmd, "tool", "compile", "-V")
112 }
113
114 func printOSDetails(w io.Writer) {
115 switch runtime.GOOS {
116 case "darwin", "ios":
117 printCmdOut(w, "uname -v: ", "uname", "-v")
118 printCmdOut(w, "", "sw_vers")
119 case "linux":
120 printCmdOut(w, "uname -sr: ", "uname", "-sr")
121 printCmdOut(w, "", "lsb_release", "-a")
122 printGlibcVersion(w)
123 case "openbsd", "netbsd", "freebsd", "dragonfly":
124 printCmdOut(w, "uname -v: ", "uname", "-v")
125 case "illumos", "solaris":
126
127 printCmdOut(w, "uname -srv: ", "/usr/bin/uname", "-srv")
128 out, err := os.ReadFile("/etc/release")
129 if err == nil {
130 fmt.Fprintf(w, "/etc/release: %s\n", out)
131 } else {
132 if cfg.BuildV {
133 fmt.Printf("failed to read /etc/release: %v\n", err)
134 }
135 }
136 }
137 }
138
139 func printCDetails(w io.Writer) {
140 printCmdOut(w, "lldb --version: ", "lldb", "--version")
141 cmd := exec.Command("gdb", "--version")
142 out, err := cmd.Output()
143 if err == nil {
144
145
146
147 fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out))
148 } else {
149 if cfg.BuildV {
150 fmt.Printf("failed to run gdb --version: %v\n", err)
151 }
152 }
153 }
154
155
156
157 func printCmdOut(w io.Writer, prefix, path string, args ...string) {
158 cmd := exec.Command(path, args...)
159 out, err := cmd.Output()
160 if err != nil {
161 if cfg.BuildV {
162 fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
163 }
164 return
165 }
166 fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
167 }
168
169
170 func firstLine(buf []byte) []byte {
171 idx := bytes.IndexByte(buf, '\n')
172 if idx > 0 {
173 buf = buf[:idx]
174 }
175 return bytes.TrimSpace(buf)
176 }
177
178
179
180 func printGlibcVersion(w io.Writer) {
181 tempdir := os.TempDir()
182 if tempdir == "" {
183 return
184 }
185 src := []byte(`int main() {}`)
186 srcfile := filepath.Join(tempdir, "go-bug.c")
187 outfile := filepath.Join(tempdir, "go-bug")
188 err := os.WriteFile(srcfile, src, 0644)
189 if err != nil {
190 return
191 }
192 defer os.Remove(srcfile)
193 cmd := exec.Command("gcc", "-o", outfile, srcfile)
194 if _, err = cmd.CombinedOutput(); err != nil {
195 return
196 }
197 defer os.Remove(outfile)
198
199 cmd = exec.Command("ldd", outfile)
200 out, err := cmd.CombinedOutput()
201 if err != nil {
202 return
203 }
204 re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`)
205 m := re.FindStringSubmatch(string(out))
206 if m == nil {
207 return
208 }
209 cmd = exec.Command(m[1])
210 out, err = cmd.Output()
211 if err != nil {
212 return
213 }
214 fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out))
215
216
217 if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 {
218 fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:]))
219 }
220 }
221
View as plain text