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