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:
parent
d15a452238
commit
c509cf123c
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
"code.google.com/p/go.tools/go/callgraph"
|
"code.google.com/p/go.tools/go/callgraph"
|
||||||
"code.google.com/p/go.tools/go/ssa"
|
"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
|
// Analyze runs the pointer analysis with the scope and options
|
||||||
// specified by config, and returns the (synthetic) root of the callgraph.
|
// 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{
|
a := &analysis{
|
||||||
config: config,
|
config: config,
|
||||||
log: config.Log,
|
log: config.Log,
|
||||||
|
|
@ -318,7 +331,7 @@ func Analyze(config *Config) *Result {
|
||||||
// (This only checks that the package scope is complete,
|
// (This only checks that the package scope is complete,
|
||||||
// not that func bodies exist, but it's a good signal.)
|
// not that func bodies exist, but it's a good signal.)
|
||||||
if !pkg.Object.Complete() {
|
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()
|
a.computeTrackBits()
|
||||||
|
|
||||||
|
stage = "constraint generation"
|
||||||
a.generate()
|
a.generate()
|
||||||
|
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
|
|
@ -362,8 +376,10 @@ func Analyze(config *Config) *Result {
|
||||||
fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
|
fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
//a.optimize()
|
// stage = "constraint optimization"
|
||||||
|
// a.optimize()
|
||||||
|
|
||||||
|
stage = "solver"
|
||||||
a.solve()
|
a.solve()
|
||||||
|
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
|
|
@ -376,6 +392,7 @@ func Analyze(config *Config) *Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create callgraph.Nodes in deterministic order.
|
// Create callgraph.Nodes in deterministic order.
|
||||||
|
stage = "callgraph construction"
|
||||||
if cg := a.result.CallGraph; cg != nil {
|
if cg := a.result.CallGraph; cg != nil {
|
||||||
for _, caller := range a.cgnodes {
|
for _, caller := range a.cgnodes {
|
||||||
cg.CreateNode(caller.fn)
|
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.
|
// callEdge is called for each edge in the callgraph.
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,10 @@ func main() {
|
||||||
config.AddQuery(Cfm)
|
config.AddQuery(Cfm)
|
||||||
|
|
||||||
// Run the pointer analysis.
|
// 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.
|
// Find edges originating from the main package.
|
||||||
// By converting to strings, we de-duplicate nodes
|
// By converting to strings, we de-duplicate nodes
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Check the expectations.
|
||||||
for _, e := range exps {
|
for _, e := range exps {
|
||||||
|
|
|
||||||
|
|
@ -464,7 +464,11 @@ func buildSSA(o *Oracle) {
|
||||||
|
|
||||||
// ptrAnalysis runs the pointer analysis and returns its result.
|
// ptrAnalysis runs the pointer analysis and returns its result.
|
||||||
func ptrAnalysis(o *Oracle) *pointer.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.
|
// unparen returns e with any enclosing parentheses stripped.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue