github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/guru/main.go (about) 1 // Copyright 2013 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 // guru: a tool for answering questions about Go source code. 6 // 7 // http://golang.org/s/using-guru 8 // 9 // Run with -help flag or help subcommand for usage information. 10 package main // import "golang.org/x/tools/cmd/guru" 11 12 import ( 13 "bufio" 14 "flag" 15 "fmt" 16 "go/build" 17 "go/token" 18 "io" 19 "log" 20 "os" 21 "path/filepath" 22 "runtime" 23 "runtime/pprof" 24 "strings" 25 "sync" 26 27 "golang.org/x/tools/go/buildutil" 28 ) 29 30 // flags 31 var ( 32 modifiedFlag = flag.Bool("modified", false, "read archive of modified files from standard input") 33 scopeFlag = flag.String("scope", "", "comma-separated list of `packages` the analysis should be limited to") 34 ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`") 35 jsonFlag = flag.Bool("json", false, "emit output in JSON format") 36 reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)") 37 cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`") 38 ) 39 40 func init() { 41 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) 42 43 // gccgo does not provide a GOROOT with standard library sources. 44 // If we have one in the environment, force gc mode. 45 if build.Default.Compiler == "gccgo" { 46 if _, err := os.Stat(filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime.go")); err == nil { 47 build.Default.Compiler = "gc" 48 } 49 } 50 } 51 52 const useHelp = "Run 'guru -help' for more information.\n" 53 54 const helpMessage = `Go source code guru. 55 Usage: guru [flags] <mode> <position> 56 57 The mode argument determines the query to perform: 58 59 callees show possible targets of selected function call 60 callers show possible callers of selected function 61 callstack show path from callgraph root to selected function 62 definition show declaration of selected identifier 63 describe describe selected syntax: definition, methods, etc 64 freevars show free variables of selection 65 implements show 'implements' relation for selected type or method 66 peers show send/receive corresponding to selected channel op 67 pointsto show variables the selected pointer may point to 68 referrers show all refs to entity denoted by selected identifier 69 what show basic information about the selected syntax node 70 whicherrs show possible values of the selected error variable 71 72 The position argument specifies the filename and byte offset (or range) 73 of the syntax element to query. For example: 74 75 foo.go:#123,#128 76 bar.go:#123 77 78 The -json flag causes guru to emit output in JSON format; 79 golang.org/x/tools/cmd/guru/serial defines its schema. 80 Otherwise, the output is in an editor-friendly format in which 81 every line has the form "pos: text", where pos is "-" if unknown. 82 83 The -modified flag causes guru to read an archive from standard input. 84 Files in this archive will be used in preference to those in 85 the file system. In this way, a text editor may supply guru 86 with the contents of its unsaved buffers. Each archive entry 87 consists of the file name, a newline, the decimal file size, 88 another newline, and the contents of the file. 89 90 The -scope flag restricts analysis to the specified packages. 91 Its value is a comma-separated list of patterns of these forms: 92 golang.org/x/tools/cmd/guru # a single package 93 golang.org/x/tools/... # all packages beneath dir 94 ... # the entire workspace. 95 A pattern preceded by '-' is negative, so the scope 96 encoding/...,-encoding/xml 97 matches all encoding packages except encoding/xml. 98 99 User manual: http://golang.org/s/using-guru 100 101 Example: describe syntax at offset 530 in this file (an import spec): 102 103 $ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530 104 ` 105 106 func printHelp() { 107 fmt.Fprint(os.Stderr, helpMessage) 108 fmt.Fprintln(os.Stderr, "\nFlags:") 109 flag.PrintDefaults() 110 } 111 112 func main() { 113 log.SetPrefix("guru: ") 114 log.SetFlags(0) 115 116 // Don't print full help unless -help was requested. 117 // Just gently remind users that it's there. 118 flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) } 119 flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack 120 if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { 121 // (err has already been printed) 122 if err == flag.ErrHelp { 123 printHelp() 124 } 125 os.Exit(2) 126 } 127 128 args := flag.Args() 129 if len(args) != 2 { 130 flag.Usage() 131 os.Exit(2) 132 } 133 mode, posn := args[0], args[1] 134 135 if mode == "help" { 136 printHelp() 137 os.Exit(2) 138 } 139 140 // Set up points-to analysis log file. 141 var ptalog io.Writer 142 if *ptalogFlag != "" { 143 if f, err := os.Create(*ptalogFlag); err != nil { 144 log.Fatalf("Failed to create PTA log file: %s", err) 145 } else { 146 buf := bufio.NewWriter(f) 147 ptalog = buf 148 defer func() { 149 if err := buf.Flush(); err != nil { 150 log.Printf("flush: %s", err) 151 } 152 if err := f.Close(); err != nil { 153 log.Printf("close: %s", err) 154 } 155 }() 156 } 157 } 158 159 // Profiling support. 160 if *cpuprofileFlag != "" { 161 f, err := os.Create(*cpuprofileFlag) 162 if err != nil { 163 log.Fatal(err) 164 } 165 pprof.StartCPUProfile(f) 166 defer pprof.StopCPUProfile() 167 } 168 169 ctxt := &build.Default 170 171 // If there were modified files, 172 // read them from the standard input and 173 // overlay them on the build context. 174 if *modifiedFlag { 175 modified, err := buildutil.ParseOverlayArchive(os.Stdin) 176 if err != nil { 177 log.Fatal(err) 178 } 179 180 // All I/O done by guru needs to consult the modified map. 181 // The ReadFile done by referrers does, 182 // but the loader's cgo preprocessing currently does not. 183 184 if len(modified) > 0 { 185 ctxt = buildutil.OverlayContext(ctxt, modified) 186 } 187 } 188 189 var outputMu sync.Mutex 190 output := func(fset *token.FileSet, qr QueryResult) { 191 outputMu.Lock() 192 defer outputMu.Unlock() 193 if *jsonFlag { 194 // JSON output 195 fmt.Printf("%s\n", qr.JSON(fset)) 196 } else { 197 // plain output 198 printf := func(pos interface{}, format string, args ...interface{}) { 199 fprintf(os.Stdout, fset, pos, format, args...) 200 } 201 qr.PrintPlain(printf) 202 } 203 } 204 205 // Avoid corner case of split(""). 206 var scope []string 207 if *scopeFlag != "" { 208 scope = strings.Split(*scopeFlag, ",") 209 } 210 211 // Ask the guru. 212 query := Query{ 213 Pos: posn, 214 Build: ctxt, 215 Scope: scope, 216 PTALog: ptalog, 217 Reflection: *reflectFlag, 218 Output: output, 219 } 220 221 if err := Run(mode, &query); err != nil { 222 log.Fatal(err) 223 } 224 }