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  }