1
0
mirror of https://github.com/golang/go synced 2024-11-19 02:04:42 -07:00
go/cmd/oracle/main.go
Alan Donovan d2cdbefbfc go.tools/oracle: add option to output results in JSON syntax.
See json.go for interface specification.

Example usage:
% oracle -format=json -mode=callgraph code.google.com/p/go.tools/cmd/oracle

+ Tests, based on (small) golden files.

Overview:
  Each <query>Result structure has been "lowered" so that all
  but the most trivial logic in each display() function has
  been moved to the main query.

  Each one now has a toJSON method that populates a json.Result
  struct.  Though the <query>Result structs are similar to the
  correponding JSON protocol, they're not close enough to be
  used directly; for example, the former contain richer
  semantic entities (token.Pos, ast.Expr, ssa.Value,
  pointer.Pointer, etc) whereas JSON contains only their
  printed forms using Go basic types.

  The choices of what levels of abstractions the two sets of
  structs should have is somewhat arbitrary.  We may want
  richer information in the JSON output in future.

Details:
- oracle.Main has been split into oracle.Query() and the
  printing of the oracle.Result.
- the display() method no longer needs an *oracle param, only
  a print function.
- callees: sort the result for determinism.
- callees: compute the union across all contexts.
- callers: sort the results for determinism.
- describe(package): fixed a bug in the predicate for method
  accessibility: an unexported method defined in pkg A may
  belong to a type defined in package B (via
  embedding/promotion) and may thus be accessible to A.  New
  accessibleMethods() utility fixes this.
- describe(type): filter methods by accessibility.
- added tests of 'callgraph'.
- pointer: eliminated the 'caller CallGraphNode' parameter from
  pointer.Context.Call callback since it was redundant w.r.t
  site.Caller().
- added warning if CGO_ENABLED is unset.

R=crawshaw
CC=golang-dev
https://golang.org/cl/13270045
2013-09-03 15:29:02 -04:00

148 lines
3.6 KiB
Go

// 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.
// oracle: a tool for answering questions about Go source code.
//
// Each query prints its results to the standard output in an
// editor-friendly format. Currently this is just text in a generic
// compiler diagnostic format, but in future we could provide
// sexpr/json/python formats for the raw data so that editors can
// provide more sophisticated UIs.
//
// Every line of output is of the form "pos: text", where pos = "-" if unknown.
//
package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/pprof"
"code.google.com/p/go.tools/oracle"
)
var posFlag = flag.String("pos", "",
"Filename and offset or extent of a syntax element about which to query, "+
"e.g. foo.go:123-456, bar.go:123.")
var modeFlag = flag.String("mode", "",
"Mode of query to perform: callers, callees, callstack, callgraph, describe.")
var ptalogFlag = flag.String("ptalog", "",
"Location of the points-to analysis log file, or empty to disable logging.")
var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")
const usage = `Go source code oracle.
Usage: oracle [<flag> ...] [<file.go> ...] [<arg> ...]
Use -help flag to display options.
Examples:
% oracle -pos 'hello.go 123' hello.go
% oracle -pos 'hello.go 123 456' hello.go
`
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
// TODO(adonovan): the caller must---before go/build.init
// runs---specify CGO_ENABLED=0, which entails the "!cgo" go/build
// tag, preferring (dummy) Go to native C implementations of
// cgoLookupHost et al.
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)
}
// For now, caller must---before go/build.init runs---specify
// CGO_ENABLED=0, which entails the "!cgo" go/build tag,
// preferring (dummy) Go to native C implementations of
// cgoLookupHost et al.
// TODO(adonovan): make the importer do this.
if os.Getenv("CGO_ENABLED") != "0" {
fmt.Fprint(os.Stderr, "Warning: CGO_ENABLED=0 not specified; "+
"analysis of cgo code may be less precise.\n")
}
}
func main() {
flag.Parse()
args := flag.Args()
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
// Set up points-to analysis log file.
var ptalog io.Writer
if *ptalogFlag != "" {
if f, err := os.Create(*ptalogFlag); err != nil {
log.Fatalf(err.Error())
} else {
buf := bufio.NewWriter(f)
ptalog = buf
defer func() {
buf.Flush()
f.Close()
}()
}
}
// Profiling support.
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
// -format flag
if *formatFlag != "json" && *formatFlag != "plain" {
fmt.Fprintf(os.Stderr, "illegal -format value: %q", *formatFlag)
os.Exit(1)
}
// Ask the oracle.
res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// Print the result.
switch *formatFlag {
case "json":
b, err := json.Marshal(res)
if err != nil {
fmt.Fprintf(os.Stderr, "JSON error: %s\n", err.Error())
os.Exit(1)
}
var buf bytes.Buffer
if err := json.Indent(&buf, b, "", "\t"); err != nil {
fmt.Fprintf(os.Stderr, "json.Indent failed: %s", err)
os.Exit(1)
}
os.Stdout.Write(buf.Bytes())
case "plain":
res.WriteTo(os.Stdout)
}
}