2013-08-27 16:49:13 -06: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.
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// oracle: a tool for answering questions about Go source code.
|
2013-09-20 09:35:00 -06:00
|
|
|
// http://golang.org/s/oracle-design
|
|
|
|
// http://golang.org/s/oracle-user-manual
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
2013-09-25 12:34:39 -06:00
|
|
|
// Run with -help flag or help subcommand for usage information.
|
2013-08-27 15:58:26 -06:00
|
|
|
//
|
2014-12-08 21:00:58 -07:00
|
|
|
package main // import "golang.org/x/tools/cmd/oracle"
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2013-09-03 13:29:02 -06:00
|
|
|
"encoding/json"
|
2013-09-24 13:08:14 -06:00
|
|
|
"encoding/xml"
|
2013-08-27 15:58:26 -06:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2013-09-04 11:15:49 -06:00
|
|
|
"go/build"
|
2013-08-27 15:58:26 -06:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"runtime/pprof"
|
|
|
|
|
2014-11-09 14:50:40 -07:00
|
|
|
"golang.org/x/tools/go/loader"
|
|
|
|
"golang.org/x/tools/oracle"
|
2013-08-27 15:58:26 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
var posFlag = flag.String("pos", "",
|
2013-09-04 12:35:24 -06:00
|
|
|
"Filename and byte offset or extent of a syntax element about which to query, "+
|
2013-09-08 20:10:11 -06:00
|
|
|
"e.g. foo.go:#123,#456, bar.go:#123.")
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-08-29 19:32:49 -06:00
|
|
|
var ptalogFlag = flag.String("ptalog", "",
|
2013-08-27 15:58:26 -06:00
|
|
|
"Location of the points-to analysis log file, or empty to disable logging.")
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.")
|
2013-09-03 13:29:02 -06:00
|
|
|
|
2013-12-13 16:00:55 -07:00
|
|
|
var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).")
|
2013-09-23 14:13:01 -06:00
|
|
|
|
2013-09-20 09:35:00 -06:00
|
|
|
const useHelp = "Run 'oracle -help' for more information.\n"
|
|
|
|
|
|
|
|
const helpMessage = `Go source code oracle.
|
2013-09-25 12:34:39 -06:00
|
|
|
Usage: oracle [<flag> ...] <mode> <args> ...
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-20 09:35:00 -06: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.
|
2013-09-24 13:08:14 -06:00
|
|
|
xml structured data in XML syntax.
|
2013-09-20 09:35:00 -06:00
|
|
|
|
|
|
|
The -pos flag is required in all modes except 'callgraph'.
|
|
|
|
|
2013-09-25 12:34:39 -06:00
|
|
|
The mode argument determines the query to perform:
|
|
|
|
|
2013-09-20 09:35:00 -06:00
|
|
|
callees show possible targets of selected function call
|
|
|
|
callers show possible callers of selected function
|
|
|
|
callgraph show complete callgraph of program
|
|
|
|
callstack show path from callgraph root to selected function
|
|
|
|
describe describe selected syntax: definition, methods, etc
|
|
|
|
freevars show free variables of selection
|
2015-02-24 16:02:49 -07:00
|
|
|
implements show 'implements' relation for selected type or method
|
2013-09-20 09:35:00 -06:00
|
|
|
peers show send/receive corresponding to selected channel op
|
|
|
|
referrers show all refs to entity denoted by selected identifier
|
2014-11-17 11:50:23 -07:00
|
|
|
what show basic information about the selected syntax node
|
2013-09-20 09:35:00 -06:00
|
|
|
|
|
|
|
The user manual is available here: http://golang.org/s/oracle-user-manual
|
2013-09-08 20:10:11 -06:00
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
Examples:
|
2013-09-08 20:10:11 -06:00
|
|
|
|
2013-09-23 14:13:01 -06:00
|
|
|
Describe the syntax at offset 530 in this file (an import spec):
|
2014-11-09 14:50:40 -07:00
|
|
|
% oracle -pos=src/golang.org/x/tools/cmd/oracle/main.go:#530 describe \
|
|
|
|
golang.org/x/tools/cmd/oracle
|
2013-09-08 20:10:11 -06:00
|
|
|
|
|
|
|
Print the callgraph of the trivial web-server in JSON format:
|
2014-09-08 11:24:38 -06:00
|
|
|
% oracle -format=json $GOROOT/src/net/http/triv.go callgraph
|
2014-01-16 07:33:58 -07:00
|
|
|
` + loader.FromArgsUsage
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// If $GOMAXPROCS isn't set, use the full capacity of the machine.
|
|
|
|
// For small machines, use at least 4 threads.
|
|
|
|
if os.Getenv("GOMAXPROCS") == "" {
|
|
|
|
n := runtime.NumCPU()
|
|
|
|
if n < 4 {
|
|
|
|
n = 4
|
|
|
|
}
|
|
|
|
runtime.GOMAXPROCS(n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 12:34:39 -06:00
|
|
|
func printHelp() {
|
2013-12-03 15:37:59 -07:00
|
|
|
fmt.Fprintln(os.Stderr, helpMessage)
|
|
|
|
fmt.Fprintln(os.Stderr, "Flags:")
|
2013-09-25 12:34:39 -06:00
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
func main() {
|
2013-09-20 09:35:00 -06: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 {
|
2013-09-25 12:34:39 -06:00
|
|
|
printHelp()
|
2013-09-20 09:35:00 -06:00
|
|
|
}
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
2013-09-25 12:34:39 -06:00
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
args := flag.Args()
|
2013-09-25 12:34:39 -06:00
|
|
|
if len(args) == 0 || args[0] == "" {
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprint(os.Stderr, "oracle: a mode argument is required.\n"+useHelp)
|
2013-09-25 12:34:39 -06:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
mode := args[0]
|
|
|
|
args = args[1:]
|
|
|
|
if mode == "help" {
|
|
|
|
printHelp()
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
if len(args) == 0 && mode != "what" {
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprint(os.Stderr, "oracle: no package arguments.\n"+useHelp)
|
2013-09-20 09:35:00 -06:00
|
|
|
os.Exit(2)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set up points-to analysis log file.
|
|
|
|
var ptalog io.Writer
|
|
|
|
if *ptalogFlag != "" {
|
|
|
|
if f, err := os.Create(*ptalogFlag); err != nil {
|
2013-09-20 09:35:00 -06:00
|
|
|
log.Fatalf("Failed to create PTA log file: %s", err)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
buf := bufio.NewWriter(f)
|
|
|
|
ptalog = buf
|
|
|
|
defer func() {
|
2015-02-23 12:24:45 -07:00
|
|
|
if err := buf.Flush(); err != nil {
|
2015-02-23 15:19:41 -07:00
|
|
|
log.Printf("flush: %s", err)
|
2015-02-23 12:24:45 -07:00
|
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
2015-02-23 15:19:41 -07:00
|
|
|
log.Printf("close: %s", err)
|
2015-02-23 12:24:45 -07:00
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Profiling support.
|
|
|
|
if *cpuprofile != "" {
|
|
|
|
f, err := os.Create(*cpuprofile)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
pprof.StartCPUProfile(f)
|
|
|
|
defer pprof.StopCPUProfile()
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// -format flag
|
2013-09-24 13:08:14 -06:00
|
|
|
switch *formatFlag {
|
|
|
|
case "json", "plain", "xml":
|
|
|
|
// ok
|
|
|
|
default:
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "oracle: illegal -format value: %q.\n"+useHelp, *formatFlag)
|
2013-09-20 09:35:00 -06:00
|
|
|
os.Exit(2)
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ask the oracle.
|
2013-09-25 12:34:39 -06:00
|
|
|
res, err := oracle.Query(args, mode, *posFlag, ptalog, &build.Default, *reflectFlag)
|
2013-09-03 13:29:02 -06:00
|
|
|
if err != nil {
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "oracle: %s.\n", err)
|
2013-08-27 15:58:26 -06:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
|
|
|
|
// Print the result.
|
|
|
|
switch *formatFlag {
|
|
|
|
case "json":
|
2013-09-24 13:08:14 -06:00
|
|
|
b, err := json.MarshalIndent(res.Serial(), "", "\t")
|
2013-09-03 13:29:02 -06:00
|
|
|
if err != nil {
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "oracle: JSON error: %s.\n", err)
|
2013-09-03 13:29:02 -06:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
os.Stdout.Write(b)
|
|
|
|
|
|
|
|
case "xml":
|
|
|
|
b, err := xml.MarshalIndent(res.Serial(), "", "\t")
|
|
|
|
if err != nil {
|
2014-11-17 11:50:23 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "oracle: XML error: %s.\n", err)
|
2013-09-03 13:29:02 -06:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
os.Stdout.Write(b)
|
2013-09-03 13:29:02 -06:00
|
|
|
|
|
|
|
case "plain":
|
|
|
|
res.WriteTo(os.Stdout)
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|