2016-02-11 15:57:17 -07:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// guru: a tool for answering questions about Go source code.
|
|
|
|
//
|
|
|
|
// http://golang.org/s/oracle-design
|
|
|
|
// http://golang.org/s/oracle-user-manual
|
|
|
|
//
|
|
|
|
// Run with -help flag or help subcommand for usage information.
|
|
|
|
//
|
|
|
|
package main // import "golang.org/x/tools/cmd/guru"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/xml"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"go/build"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"runtime/pprof"
|
2016-02-11 22:04:56 -07:00
|
|
|
"strings"
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
"golang.org/x/tools/go/buildutil"
|
|
|
|
)
|
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
// flags
|
|
|
|
var (
|
|
|
|
scopeFlag = flag.String("scope", "", "comma-separated list of `packages` to which the analysis should be limited (default=all)")
|
|
|
|
ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`")
|
|
|
|
formatFlag = flag.String("format", "plain", "output `format`; one of {plain,json,xml}")
|
|
|
|
reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
|
|
|
|
cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`")
|
|
|
|
)
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
|
|
|
|
}
|
|
|
|
|
|
|
|
const useHelp = "Run 'guru -help' for more information.\n"
|
|
|
|
|
|
|
|
const helpMessage = `Go source code guru.
|
2016-02-11 22:04:56 -07:00
|
|
|
Usage: guru [flags] <mode> <position>
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
The mode argument determines the query to perform:
|
|
|
|
|
|
|
|
callees show possible targets of selected function call
|
|
|
|
callers show possible callers of selected function
|
|
|
|
callstack show path from callgraph root to selected function
|
|
|
|
definition show declaration of selected identifier
|
|
|
|
describe describe selected syntax: definition, methods, etc
|
|
|
|
freevars show free variables of selection
|
|
|
|
implements show 'implements' relation for selected type or method
|
|
|
|
peers show send/receive corresponding to selected channel op
|
|
|
|
pointsto show variables to which the selected pointer may point
|
|
|
|
referrers show all refs to entity denoted by selected identifier
|
|
|
|
what show basic information about the selected syntax node
|
|
|
|
whicherrs show possible values of the selected error variable
|
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
The position argument specifies the filename and byte offset (or range)
|
|
|
|
of the syntax element to query. For example:
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
foo.go:#123,#128
|
|
|
|
bar.go:#123
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
The -format flag controls the output format:
|
|
|
|
plain an editor-friendly format in which every line of output
|
|
|
|
is of the form "pos: text", where pos is "-" if unknown.
|
|
|
|
json structured data in JSON syntax.
|
|
|
|
xml structured data in XML syntax.
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
User manual: http://golang.org/s/oracle-user-manual
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
Example: describe syntax at offset 530 in this file (an import spec):
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-02-11 22:04:56 -07:00
|
|
|
$ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530
|
|
|
|
`
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
func printHelp() {
|
|
|
|
fmt.Fprintln(os.Stderr, helpMessage)
|
|
|
|
fmt.Fprintln(os.Stderr, "Flags:")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2016-02-11 22:04:56 -07:00
|
|
|
log.SetPrefix("guru: ")
|
|
|
|
log.SetFlags(0)
|
|
|
|
|
2016-02-11 15:57:17 -07:00
|
|
|
// Don't print full help unless -help was requested.
|
|
|
|
// Just gently remind users that it's there.
|
|
|
|
flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) }
|
|
|
|
flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack
|
|
|
|
if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
|
|
|
|
// (err has already been printed)
|
|
|
|
if err == flag.ErrHelp {
|
|
|
|
printHelp()
|
|
|
|
}
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
args := flag.Args()
|
2016-02-11 22:04:56 -07:00
|
|
|
if len(args) != 2 {
|
|
|
|
flag.Usage()
|
2016-02-11 15:57:17 -07:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
2016-02-11 22:04:56 -07:00
|
|
|
mode, posn := args[0], args[1]
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
if mode == "help" {
|
|
|
|
printHelp()
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up points-to analysis log file.
|
|
|
|
var ptalog io.Writer
|
|
|
|
if *ptalogFlag != "" {
|
|
|
|
if f, err := os.Create(*ptalogFlag); err != nil {
|
|
|
|
log.Fatalf("Failed to create PTA log file: %s", err)
|
|
|
|
} else {
|
|
|
|
buf := bufio.NewWriter(f)
|
|
|
|
ptalog = buf
|
|
|
|
defer func() {
|
|
|
|
if err := buf.Flush(); err != nil {
|
|
|
|
log.Printf("flush: %s", err)
|
|
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
log.Printf("close: %s", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Profiling support.
|
2016-02-11 22:04:56 -07:00
|
|
|
if *cpuprofileFlag != "" {
|
|
|
|
f, err := os.Create(*cpuprofileFlag)
|
2016-02-11 15:57:17 -07:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
pprof.StartCPUProfile(f)
|
|
|
|
defer pprof.StopCPUProfile()
|
|
|
|
}
|
|
|
|
|
|
|
|
// -format flag
|
|
|
|
switch *formatFlag {
|
|
|
|
case "json", "plain", "xml":
|
|
|
|
// ok
|
|
|
|
default:
|
2016-02-11 22:04:56 -07:00
|
|
|
log.Fatalf("illegal -format value: %q.\n"+useHelp, *formatFlag)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ask the guru.
|
|
|
|
query := Query{
|
|
|
|
Mode: mode,
|
2016-02-11 22:04:56 -07:00
|
|
|
Pos: posn,
|
2016-02-11 15:57:17 -07:00
|
|
|
Build: &build.Default,
|
2016-02-11 22:04:56 -07:00
|
|
|
Scope: strings.Split(*scopeFlag, ","),
|
2016-02-11 15:57:17 -07:00
|
|
|
PTALog: ptalog,
|
|
|
|
Reflection: *reflectFlag,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := Run(&query); err != nil {
|
2016-02-11 22:04:56 -07:00
|
|
|
log.Fatal(err)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print the result.
|
|
|
|
switch *formatFlag {
|
|
|
|
case "json":
|
|
|
|
b, err := json.MarshalIndent(query.Serial(), "", "\t")
|
|
|
|
if err != nil {
|
2016-02-11 22:04:56 -07:00
|
|
|
log.Fatalf("JSON error: %s", err)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
os.Stdout.Write(b)
|
|
|
|
|
|
|
|
case "xml":
|
|
|
|
b, err := xml.MarshalIndent(query.Serial(), "", "\t")
|
|
|
|
if err != nil {
|
2016-02-11 22:04:56 -07:00
|
|
|
log.Fatalf("XML error: %s", err)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
os.Stdout.Write(b)
|
|
|
|
|
|
|
|
case "plain":
|
|
|
|
query.WriteTo(os.Stdout)
|
|
|
|
}
|
|
|
|
}
|