github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/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 "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 "github.com/go-asm/go/cmd/go/base" 22 "github.com/go-asm/go/cmd/go/cfg" 23 "github.com/go-asm/go/cmd/go/envcmd" 24 "github.com/go-asm/go/cmd/go/web" 25 "github.com/go-asm/go/cmd/go/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) 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 // Be sure to use the OS-supplied uname, in "/usr/bin": 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 // There's apparently no combination of command line flags 149 // to get gdb to spit out its version without the license and warranty. 150 // Print up to the first newline. 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 // printCmdOut prints the output of running the given command. 160 // It ignores failures; 'go bug' is best effort. 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 // firstLine returns the first line of a given byte slice. 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 // printGlibcVersion prints information about the glibc version. 183 // It ignores failures. 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 // print another line (the one containing version string) in case of musl libc 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 }