1 // Copyright 2019 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.
5 // Package debug exports debug information for gopls.
17 "golang.org/x/tools/internal/lsp/source"
23 PlainText = PrintMode(iota)
28 // Version is a manually-updated mechanism for tracking versions.
29 const Version = "master"
31 // ServerVersion is the format used by gopls to report its version to the
32 // client. This format is structured so that the client can parse it easily.
33 type ServerVersion struct {
35 Deps []*Module `json:"deps,omitempty"`
40 Replace *ModuleVersion `json:"replace,omitempty"`
43 type ModuleVersion struct {
44 Path string `json:"path,omitempty"`
45 Version string `json:"version,omitempty"`
46 Sum string `json:"sum,omitempty"`
49 // VersionInfo returns the build info for the gopls process. If it was not
50 // built in module mode, we return a GOPATH-specific message with the
52 func VersionInfo() *ServerVersion {
53 if info, ok := debug.ReadBuildInfo(); ok {
54 return getVersion(info)
56 path := "gopls, built in GOPATH mode"
57 return &ServerVersion{
59 ModuleVersion: ModuleVersion{
67 func getVersion(info *debug.BuildInfo) *ServerVersion {
68 serverVersion := ServerVersion{
70 ModuleVersion: ModuleVersion{
72 Version: info.Main.Version,
77 for _, d := range info.Deps {
79 ModuleVersion: ModuleVersion{
86 m.Replace = &ModuleVersion{
88 Version: d.Replace.Version,
91 serverVersion.Deps = append(serverVersion.Deps, m)
96 // PrintServerInfo writes HTML debug info to w for the Instance.
97 func (i *Instance) PrintServerInfo(ctx context.Context, w io.Writer) {
98 section(w, HTML, "Server Instance", func() {
99 fmt.Fprintf(w, "Start time: %v\n", i.StartTime)
100 fmt.Fprintf(w, "LogFile: %s\n", i.Logfile)
101 fmt.Fprintf(w, "Working directory: %s\n", i.Workdir)
102 fmt.Fprintf(w, "Address: %s\n", i.ServerAddress)
103 fmt.Fprintf(w, "Debug address: %s\n", i.DebugAddress)
105 PrintVersionInfo(ctx, w, true, HTML)
106 section(w, HTML, "Command Line", func() {
107 fmt.Fprintf(w, "<a href=/debug/pprof/cmdline>cmdline</a>")
111 // PrintVersionInfo writes version information to w, using the output format
112 // specified by mode. verbose controls whether additional information is
113 // written, including section headers.
114 func PrintVersionInfo(ctx context.Context, w io.Writer, verbose bool, mode PrintMode) {
115 info := VersionInfo()
117 printBuildInfo(w, info, false, mode)
120 section(w, mode, "Build info", func() {
121 printBuildInfo(w, info, true, mode)
125 func section(w io.Writer, mode PrintMode, title string, body func()) {
128 fmt.Fprintln(w, title)
129 fmt.Fprintln(w, strings.Repeat("-", len(title)))
132 fmt.Fprintf(w, "#### %s\n\n```\n", title)
134 fmt.Fprintf(w, "```\n")
136 fmt.Fprintf(w, "<h3>%s</h3>\n<pre>\n", title)
138 fmt.Fprint(w, "</pre>\n")
142 func printBuildInfo(w io.Writer, info *ServerVersion, verbose bool, mode PrintMode) {
143 fmt.Fprintf(w, "%v %v\n", info.Path, Version)
144 printModuleInfo(w, &info.Module, mode)
148 for _, dep := range info.Deps {
149 printModuleInfo(w, dep, mode)
153 func printModuleInfo(w io.Writer, m *Module, mode PrintMode) {
154 fmt.Fprintf(w, " %s@%s", m.Path, m.Version)
156 fmt.Fprintf(w, " %s", m.Sum)
158 if m.Replace != nil {
159 fmt.Fprintf(w, " => %v", m.Replace.Path)
170 // find all the options. The presumption is that the Options are nested structs
171 // and that pointers don't need to be dereferenced
172 func swalk(t reflect.Type, ix []int, indent string) {
175 for i := 0; i < t.NumField(); i++ {
177 ixx := append(append([]int{}, ix...), i)
178 swalk(fld.Type, ixx, indent+". ")
181 // everything is either a struct or a field (that's an assumption about Options)
182 fields = append(fields, field{ix})
186 func showOptions(o *source.Options) []string {
187 // non-breaking spaces for indenting current and defaults when they are on a separate line
188 const indent = "\u00a0\u00a0\u00a0\u00a0\u00a0"
189 var ans strings.Builder
190 t := reflect.TypeOf(*o)
191 swalk(t, []int{}, "")
192 v := reflect.ValueOf(*o)
193 do := reflect.ValueOf(*source.DefaultOptions())
194 for _, f := range fields {
195 val := v.FieldByIndex(f.index)
196 def := do.FieldByIndex(f.index)
197 tx := t.FieldByIndex(f.index)
198 prefix := fmt.Sprintf("%s (type is %s): ", tx.Name, tx.Type)
201 if len(is) < 30 && len(was) < 30 {
202 fmt.Fprintf(&ans, "%s current:%s, default:%s\n", prefix, is, was)
204 fmt.Fprintf(&ans, "%s\n%scurrent:%s\n%sdefault:%s\n", prefix, indent, is, indent, was)
207 return strings.Split(ans.String(), "\n")
209 func strVal(val reflect.Value) string {
212 return fmt.Sprintf("%v", val.Interface())
213 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
214 return fmt.Sprintf("%v", val.Interface())
215 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
216 return fmt.Sprintf("%v", val.Interface())
217 case reflect.Uintptr, reflect.UnsafePointer:
218 return fmt.Sprintf("0x%x", val.Pointer())
219 case reflect.Complex64, reflect.Complex128:
220 return fmt.Sprintf("%v", val.Complex())
221 case reflect.Array, reflect.Slice:
223 for i := 0; i < val.Len(); i++ {
224 ans = append(ans, strVal(val.Index(i)))
227 return fmt.Sprintf("%v", ans)
228 case reflect.Chan, reflect.Func, reflect.Ptr:
229 return val.Kind().String()
231 var x source.Analyzer
232 if val.Type() != reflect.TypeOf(x) {
233 return val.Kind().String()
235 // this is sort of ugly, but usable
236 str := val.FieldByName("Analyzer").Elem().FieldByName("Doc").String()
237 ix := strings.Index(str, "\n")
243 return fmt.Sprintf("%q", val.Interface())
246 iter := val.MapRange()
250 ans = append(ans, fmt.Sprintf("%s:%s, ", strVal(k), strVal(v)))
253 return fmt.Sprintf("%v", ans)
255 return fmt.Sprintf("??%s??", val.Type())