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