golang.org/x/tools/gopls@v0.15.3/internal/debug/info.go (about)

     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.
     4  
     5  // Package debug exports debug information for gopls.
     6  package debug
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"runtime"
    15  	"runtime/debug"
    16  	"strings"
    17  
    18  	"golang.org/x/tools/gopls/internal/version"
    19  )
    20  
    21  type PrintMode int
    22  
    23  const (
    24  	PlainText = PrintMode(iota)
    25  	Markdown
    26  	HTML
    27  	JSON
    28  )
    29  
    30  // ServerVersion is the format used by gopls to report its version to the
    31  // client. This format is structured so that the client can parse it easily.
    32  type ServerVersion struct {
    33  	*debug.BuildInfo
    34  	Version string
    35  }
    36  
    37  // VersionInfo returns the build info for the gopls process. If it was not
    38  // built in module mode, we return a GOPATH-specific message with the
    39  // hardcoded version.
    40  func VersionInfo() *ServerVersion {
    41  	if info, ok := debug.ReadBuildInfo(); ok {
    42  		return &ServerVersion{
    43  			Version:   version.Version(),
    44  			BuildInfo: info,
    45  		}
    46  	}
    47  	return &ServerVersion{
    48  		Version: version.Version(),
    49  		BuildInfo: &debug.BuildInfo{
    50  			Path:      "gopls, built in GOPATH mode",
    51  			GoVersion: runtime.Version(),
    52  		},
    53  	}
    54  }
    55  
    56  // PrintServerInfo writes HTML debug info to w for the Instance.
    57  func (i *Instance) PrintServerInfo(ctx context.Context, w io.Writer) {
    58  	workDir, _ := os.Getwd()
    59  	section(w, HTML, "Server Instance", func() {
    60  		fmt.Fprintf(w, "Start time: %v\n", i.StartTime)
    61  		fmt.Fprintf(w, "LogFile: %s\n", i.Logfile)
    62  		fmt.Fprintf(w, "pid: %d\n", os.Getpid())
    63  		fmt.Fprintf(w, "Working directory: %s\n", workDir)
    64  		fmt.Fprintf(w, "Address: %s\n", i.ServerAddress)
    65  		fmt.Fprintf(w, "Debug address: %s\n", i.DebugAddress())
    66  	})
    67  	PrintVersionInfo(ctx, w, true, HTML)
    68  	section(w, HTML, "Command Line", func() {
    69  		fmt.Fprintf(w, "<a href=/debug/pprof/cmdline>cmdline</a>")
    70  	})
    71  }
    72  
    73  // PrintVersionInfo writes version information to w, using the output format
    74  // specified by mode. verbose controls whether additional information is
    75  // written, including section headers.
    76  func PrintVersionInfo(_ context.Context, w io.Writer, verbose bool, mode PrintMode) error {
    77  	info := VersionInfo()
    78  	if mode == JSON {
    79  		return printVersionInfoJSON(w, info)
    80  	}
    81  
    82  	if !verbose {
    83  		printBuildInfo(w, info, false, mode)
    84  		return nil
    85  	}
    86  	section(w, mode, "Build info", func() {
    87  		printBuildInfo(w, info, true, mode)
    88  	})
    89  	return nil
    90  }
    91  
    92  func printVersionInfoJSON(w io.Writer, info *ServerVersion) error {
    93  	js, err := json.MarshalIndent(info, "", "\t")
    94  	if err != nil {
    95  		return err
    96  	}
    97  	_, err = fmt.Fprint(w, string(js))
    98  	return err
    99  }
   100  
   101  func section(w io.Writer, mode PrintMode, title string, body func()) {
   102  	switch mode {
   103  	case PlainText:
   104  		fmt.Fprintln(w, title)
   105  		fmt.Fprintln(w, strings.Repeat("-", len(title)))
   106  		body()
   107  	case Markdown:
   108  		fmt.Fprintf(w, "#### %s\n\n```\n", title)
   109  		body()
   110  		fmt.Fprintf(w, "```\n")
   111  	case HTML:
   112  		fmt.Fprintf(w, "<h3>%s</h3>\n<pre>\n", title)
   113  		body()
   114  		fmt.Fprint(w, "</pre>\n")
   115  	}
   116  }
   117  
   118  func printBuildInfo(w io.Writer, info *ServerVersion, verbose bool, mode PrintMode) {
   119  	fmt.Fprintf(w, "%v %v\n", info.Path, version.Version())
   120  	if !verbose {
   121  		return
   122  	}
   123  	printModuleInfo(w, info.Main, mode)
   124  	for _, dep := range info.Deps {
   125  		printModuleInfo(w, *dep, mode)
   126  	}
   127  	fmt.Fprintf(w, "go: %v\n", info.GoVersion)
   128  }
   129  
   130  func printModuleInfo(w io.Writer, m debug.Module, _ PrintMode) {
   131  	fmt.Fprintf(w, "    %s@%s", m.Path, m.Version)
   132  	if m.Sum != "" {
   133  		fmt.Fprintf(w, " %s", m.Sum)
   134  	}
   135  	if m.Replace != nil {
   136  		fmt.Fprintf(w, " => %v", m.Replace.Path)
   137  	}
   138  	fmt.Fprintf(w, "\n")
   139  }