go.tools/go/pointer: recover from panic in Analyse and return an error.

This is to avoid an internal error in pointer analysis from
bringing down a long-lived application such as godoc.

LGTM=crawshaw
R=crawshaw
CC=golang-codereviews
https://golang.org/cl/68930046
This commit is contained in:
Alan Donovan 2014-02-27 14:13:52 -05:00
parent d15a452238
commit c509cf123c
4 changed files with 34 additions and 7 deletions

View File

@ -12,6 +12,7 @@ import (
"io"
"os"
"reflect"
"runtime/debug"
"code.google.com/p/go.tools/go/callgraph"
"code.google.com/p/go.tools/go/ssa"
@ -286,7 +287,19 @@ func (a *analysis) computeTrackBits() {
// Analyze runs the pointer analysis with the scope and options
// specified by config, and returns the (synthetic) root of the callgraph.
//
func Analyze(config *Config) *Result {
// Pointer analysis of a transitively closed well-typed program should
// always succeed. An error can occur only due to an internal bug.
//
func Analyze(config *Config) (result *Result, err error) {
stage := "setup"
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("internal error in pointer analysis %s: %v (please report this bug)", stage, p)
fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
debug.PrintStack()
}
}()
a := &analysis{
config: config,
log: config.Log,
@ -318,7 +331,7 @@ func Analyze(config *Config) *Result {
// (This only checks that the package scope is complete,
// not that func bodies exist, but it's a good signal.)
if !pkg.Object.Complete() {
panic(fmt.Sprintf(`pointer analysis requires a complete program yet package %q was incomplete (set loader.Config.SourceImports during loading)`, pkg.Object.Path()))
return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete (set loader.Config.SourceImports during loading)`, pkg.Object.Path())
}
}
@ -347,6 +360,7 @@ func Analyze(config *Config) *Result {
}
a.computeTrackBits()
stage = "constraint generation"
a.generate()
if a.log != nil {
@ -362,8 +376,10 @@ func Analyze(config *Config) *Result {
fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
}
//a.optimize()
// stage = "constraint optimization"
// a.optimize()
stage = "solver"
a.solve()
if a.log != nil {
@ -376,6 +392,7 @@ func Analyze(config *Config) *Result {
}
// Create callgraph.Nodes in deterministic order.
stage = "callgraph construction"
if cg := a.result.CallGraph; cg != nil {
for _, caller := range a.cgnodes {
cg.CreateNode(caller.fn)
@ -391,7 +408,7 @@ func Analyze(config *Config) *Result {
}
}
return a.result
return a.result, nil
}
// callEdge is called for each edge in the callgraph.

View File

@ -79,7 +79,10 @@ func main() {
config.AddQuery(Cfm)
// Run the pointer analysis.
result := pointer.Analyze(config)
result, err := pointer.Analyze(config)
if err != nil {
panic(err) // internal error in pointer analysis
}
// Find edges originating from the main package.
// By converting to strings, we de-duplicate nodes

View File

@ -307,7 +307,10 @@ func doOneInput(input, filename string) bool {
}
}()
result := pointer.Analyze(config)
result, err := pointer.Analyze(config)
if err != nil {
panic(err) // internal error in pointer analysis
}
// Check the expectations.
for _, e := range exps {

View File

@ -464,7 +464,11 @@ func buildSSA(o *Oracle) {
// ptrAnalysis runs the pointer analysis and returns its result.
func ptrAnalysis(o *Oracle) *pointer.Result {
return pointer.Analyze(&o.ptaConfig)
result, err := pointer.Analyze(&o.ptaConfig)
if err != nil {
panic(err) // pointer analysis internal error
}
return result
}
// unparen returns e with any enclosing parentheses stripped.