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