From c509cf123c690af199c031ab537f2759a3e3d0d2 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 27 Feb 2014 14:13:52 -0500 Subject: [PATCH] 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 --- go/pointer/analysis.go | 25 +++++++++++++++++++++---- go/pointer/example_test.go | 5 ++++- go/pointer/pointer_test.go | 5 ++++- oracle/oracle.go | 6 +++++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go index 3b4469be..14649821 100644 --- a/go/pointer/analysis.go +++ b/go/pointer/analysis.go @@ -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. diff --git a/go/pointer/example_test.go b/go/pointer/example_test.go index dcf167c5..14b16e4f 100644 --- a/go/pointer/example_test.go +++ b/go/pointer/example_test.go @@ -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 diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go index 73fc4ac1..8ee42e71 100644 --- a/go/pointer/pointer_test.go +++ b/go/pointer/pointer_test.go @@ -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 { diff --git a/oracle/oracle.go b/oracle/oracle.go index 943b1ac5..4d08027d 100644 --- a/oracle/oracle.go +++ b/oracle/oracle.go @@ -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.