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.
5 // guru: a tool for answering questions about Go source code.
7 // http://golang.org/s/using-guru
9 // Run with -help flag or help subcommand for usage information.
11 package main // import "golang.org/x/tools/cmd/guru"
28 "golang.org/x/tools/go/buildutil"
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`")
42 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
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"
53 const useHelp = "Run 'guru -help' for more information.\n"
55 const helpMessage = `Go source code guru.
56 Usage: guru [flags] <mode> <position>
58 The mode argument determines the query to perform:
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
73 The position argument specifies the filename and byte offset (or range)
74 of the syntax element to query. For example:
79 The -json flag causes guru to emit output in JSON format;
80 golang.org/x/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.
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.
91 The -scope flag restricts analysis to the specified packages.
92 Its value is a comma-separated list of patterns of these forms:
93 golang.org/x/tools/cmd/guru # a single package
94 golang.org/x/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.
100 User manual: http://golang.org/s/using-guru
102 Example: describe syntax at offset 530 in this file (an import spec):
104 $ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530
108 fmt.Fprintln(os.Stderr, helpMessage)
109 fmt.Fprintln(os.Stderr, "Flags:")
114 log.SetPrefix("guru: ")
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 {
134 mode, posn := args[0], args[1]
141 // Set up points-to analysis log file.
143 if *ptalogFlag != "" {
144 if f, err := os.Create(*ptalogFlag); err != nil {
145 log.Fatalf("Failed to create PTA log file: %s", err)
147 buf := bufio.NewWriter(f)
150 if err := buf.Flush(); err != nil {
151 log.Printf("flush: %s", err)
153 if err := f.Close(); err != nil {
154 log.Printf("close: %s", err)
160 // Profiling support.
161 if *cpuprofileFlag != "" {
162 f, err := os.Create(*cpuprofileFlag)
166 pprof.StartCPUProfile(f)
167 defer pprof.StopCPUProfile()
170 ctxt := &build.Default
172 // If there were modified files,
173 // read them from the standard input and
174 // overlay them on the build context.
176 modified, err := buildutil.ParseOverlayArchive(os.Stdin)
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.
185 if len(modified) > 0 {
186 ctxt = buildutil.OverlayContext(ctxt, modified)
190 var outputMu sync.Mutex
191 output := func(fset *token.FileSet, qr QueryResult) {
193 defer outputMu.Unlock()
196 fmt.Printf("%s\n", qr.JSON(fset))
199 printf := func(pos interface{}, format string, args ...interface{}) {
200 fprintf(os.Stdout, fset, pos, format, args...)
202 qr.PrintPlain(printf)
206 // Avoid corner case of split("").
208 if *scopeFlag != "" {
209 scope = strings.Split(*scopeFlag, ",")
218 Reflection: *reflectFlag,
222 if err := Run(mode, &query); err != nil {