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: 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 printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version")
110 printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V")
111 }
112
113 func printOSDetails(w io.Writer) {
114 switch runtime.GOOS {
115 case "darwin", "ios":
116 printCmdOut(w, "uname -v: ", "uname", "-v")
117 printCmdOut(w, "", "sw_vers")
118 case "linux":
119 printCmdOut(w, "uname -sr: ", "uname", "-sr")
120 printCmdOut(w, "", "lsb_release", "-a")
121 printGlibcVersion(w)
122 case "openbsd", "netbsd", "freebsd", "dragonfly":
123 printCmdOut(w, "uname -v: ", "uname", "-v")
124 case "illumos", "solaris":
125
126 printCmdOut(w, "uname -srv: ", "/usr/bin/uname", "-srv")
127 out, err := os.ReadFile("/etc/release")
128 if err == nil {
129 fmt.Fprintf(w, "/etc/release: %s\n", out)
130 } else {
131 if cfg.BuildV {
132 fmt.Printf("failed to read /etc/release: %v\n", err)
133 }
134 }
135 }
136 }
137
138 func printCDetails(w io.Writer) {
139 printCmdOut(w, "lldb --version: ", "lldb", "--version")
140 cmd := exec.Command("gdb", "--version")
141 out, err := cmd.Output()
142 if err == nil {
143
144
145
146 fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out))
147 } else {
148 if cfg.BuildV {
149 fmt.Printf("failed to run gdb --version: %v\n", err)
150 }
151 }
152 }
153
154
155
156 func printCmdOut(w io.Writer, prefix, path string, args ...string) {
157 cmd := exec.Command(path, args...)
158 out, err := cmd.Output()
159 if err != nil {
160 if cfg.BuildV {
161 fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
162 }
163 return
164 }
165 fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
166 }
167
168
169 func firstLine(buf []byte) []byte {
170 idx := bytes.IndexByte(buf, '\n')
171 if idx > 0 {
172 buf = buf[:idx]
173 }
174 return bytes.TrimSpace(buf)
175 }
176
177
178
179 func printGlibcVersion(w io.Writer) {
180 tempdir := os.TempDir()
181 if tempdir == "" {
182 return
183 }
184 src := []byte(`int main() {}`)
185 srcfile := filepath.Join(tempdir, "go-bug.c")
186 outfile := filepath.Join(tempdir, "go-bug")
187 err := os.WriteFile(srcfile, src, 0644)
188 if err != nil {
189 return
190 }
191 defer os.Remove(srcfile)
192 cmd := exec.Command("gcc", "-o", outfile, srcfile)
193 if _, err = cmd.CombinedOutput(); err != nil {
194 return
195 }
196 defer os.Remove(outfile)
197
198 cmd = exec.Command("ldd", outfile)
199 out, err := cmd.CombinedOutput()
200 if err != nil {
201 return
202 }
203 re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`)
204 m := re.FindStringSubmatch(string(out))
205 if m == nil {
206 return
207 }
208 cmd = exec.Command(m[1])
209 out, err = cmd.Output()
210 if err != nil {
211 return
212 }
213 fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out))
214
215
216 if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 {
217 fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:]))
218 }
219 }
220
View as plain text