Catch panics in build logic

Catch panics in the build logic in order to provide extra data on when
the panic occurred, for example which module had GenerateBuildActions
called on it.
This commit is contained in:
Colin Cross 2016-01-07 13:43:09 -08:00
parent ea5995439c
commit 0aa6a5f0ad
4 changed files with 223 additions and 28 deletions

View file

@ -157,6 +157,14 @@ type moduleInfo struct {
actionDefs localBuildActions actionDefs localBuildActions
} }
func (module *moduleInfo) String() string {
s := fmt.Sprintf("module %q", module.properties.Name)
if module.variantName != "" {
s += fmt.Sprintf(" variant %q", module.variantName)
}
return s
}
// A Variation is a way that a variant of a module differs from other variants of the same module. // A Variation is a way that a variant of a module differs from other variants of the same module.
// For example, two variants of the same module might have Variation{"arch","arm"} and // For example, two variants of the same module might have Variation{"arch","arm"} and
// Variation{"arch","arm64"} // Variation{"arch","arm64"}
@ -1122,13 +1130,20 @@ func blueprintDepsMutator(ctx BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), ctx.moduleInfo().properties.Deps...) ctx.AddDependency(ctx.Module(), ctx.moduleInfo().properties.Deps...)
if dynamicDepender, ok := ctx.Module().(DynamicDependerModule); ok { if dynamicDepender, ok := ctx.Module().(DynamicDependerModule); ok {
dynamicDeps := dynamicDepender.DynamicDependencies(ctx) func() {
defer func() {
if r := recover(); r != nil {
ctx.error(newPanicErrorf(r, "DynamicDependencies for %s", ctx.moduleInfo()))
}
}()
dynamicDeps := dynamicDepender.DynamicDependencies(ctx)
if ctx.Failed() { if ctx.Failed() {
return return
} }
ctx.AddDependency(ctx.Module(), dynamicDeps...) ctx.AddDependency(ctx.Module(), dynamicDeps...)
}()
} }
} }
@ -1516,7 +1531,20 @@ func (c *Context) runEarlyMutators(config interface{}) (errs []error) {
}, },
name: mutator.name, name: mutator.name,
} }
mutator.mutator(mctx) func() {
defer func() {
if r := recover(); r != nil {
in := fmt.Sprintf("early mutator %q for %s", mutator.name, module)
if err, ok := r.(panicError); ok {
err.addIn(in)
mctx.error(err)
} else {
mctx.error(newPanicErrorf(r, in))
}
}
}()
mutator.mutator(mctx)
}()
if len(mctx.errs) > 0 { if len(mctx.errs) > 0 {
errs = append(errs, mctx.errs...) errs = append(errs, mctx.errs...)
return errs return errs
@ -1576,8 +1604,21 @@ func (c *Context) runTopDownMutator(config interface{},
}, },
name: name, name: name,
} }
func() {
defer func() {
if r := recover(); r != nil {
in := fmt.Sprintf("top down mutator %q for %s", name, module)
if err, ok := r.(panicError); ok {
err.addIn(in)
mctx.error(err)
} else {
mctx.error(newPanicErrorf(r, in))
}
}
}()
mutator(mctx)
}()
mutator(mctx)
if len(mctx.errs) > 0 { if len(mctx.errs) > 0 {
errs = append(errs, mctx.errs...) errs = append(errs, mctx.errs...)
return errs return errs
@ -1609,7 +1650,20 @@ func (c *Context) runBottomUpMutator(config interface{},
reverseDeps: reverseDeps, reverseDeps: reverseDeps,
} }
mutator(mctx) func() {
defer func() {
if r := recover(); r != nil {
in := fmt.Sprintf("bottom up mutator %q for %s", name, module)
if err, ok := r.(panicError); ok {
err.addIn(in)
mctx.error(err)
} else {
mctx.error(newPanicErrorf(r, in))
}
}
}()
mutator(mctx)
}()
if len(mctx.errs) > 0 { if len(mctx.errs) > 0 {
errs = append(errs, mctx.errs...) errs = append(errs, mctx.errs...)
return errs return errs
@ -1726,7 +1780,20 @@ func (c *Context) generateModuleBuildActions(config interface{},
handledMissingDeps: module.missingDeps == nil, handledMissingDeps: module.missingDeps == nil,
} }
mctx.module.logicModule.GenerateBuildActions(mctx) func() {
defer func() {
if r := recover(); r != nil {
in := fmt.Sprintf("GenerateBuildActions for %s", module)
if err, ok := r.(panicError); ok {
err.addIn(in)
mctx.error(err)
} else {
mctx.error(newPanicErrorf(r, in))
}
}
}()
mctx.module.logicModule.GenerateBuildActions(mctx)
}()
if len(mctx.errs) > 0 { if len(mctx.errs) > 0 {
errsCh <- mctx.errs errsCh <- mctx.errs
@ -1781,7 +1848,20 @@ func (c *Context) generateSingletonBuildActions(config interface{},
scope: scope, scope: scope,
} }
info.singleton.GenerateBuildActions(sctx) func() {
defer func() {
if r := recover(); r != nil {
in := fmt.Sprintf("GenerateBuildActions for singleton %s", info.name)
if err, ok := r.(panicError); ok {
err.addIn(in)
sctx.error(err)
} else {
sctx.error(newPanicErrorf(r, in))
}
}
}()
info.singleton.GenerateBuildActions(sctx)
}()
if len(sctx.errs) > 0 { if len(sctx.errs) > 0 {
errs = append(errs, sctx.errs...) errs = append(errs, sctx.errs...)
@ -1849,6 +1929,14 @@ func (c *Context) walkDeps(topModule *moduleInfo,
visit func(Module, Module) bool) { visit func(Module, Module) bool) {
visited := make(map[*moduleInfo]bool) visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "WalkDeps(%s, %s) for dependency %s",
topModule, funcName(visit), visiting))
}
}()
var walk func(module *moduleInfo) var walk func(module *moduleInfo)
walk = func(module *moduleInfo) { walk = func(module *moduleInfo) {
@ -1856,6 +1944,7 @@ func (c *Context) walkDeps(topModule *moduleInfo,
for _, moduleDep := range module.directDeps { for _, moduleDep := range module.directDeps {
if !visited[moduleDep] { if !visited[moduleDep] {
visiting = moduleDep
if visit(moduleDep.logicModule, module.logicModule) { if visit(moduleDep.logicModule, module.logicModule) {
walk(moduleDep) walk(moduleDep)
} }
@ -1866,8 +1955,18 @@ func (c *Context) walkDeps(topModule *moduleInfo,
walk(topModule) walk(topModule)
} }
type innerPanicError error
func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) { func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module)) {
visited := make(map[*moduleInfo]bool) visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
topModule, funcName(visit), visiting))
}
}()
var walk func(module *moduleInfo) var walk func(module *moduleInfo)
walk = func(module *moduleInfo) { walk = func(module *moduleInfo) {
@ -1879,6 +1978,7 @@ func (c *Context) visitDepsDepthFirst(topModule *moduleInfo, visit func(Module))
} }
if module != topModule { if module != topModule {
visiting = module
visit(module.logicModule) visit(module.logicModule)
} }
} }
@ -1890,6 +1990,14 @@ func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module)
visit func(Module)) { visit func(Module)) {
visited := make(map[*moduleInfo]bool) visited := make(map[*moduleInfo]bool)
var visiting *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
topModule, funcName(pred), funcName(visit), visiting))
}
}()
var walk func(module *moduleInfo) var walk func(module *moduleInfo)
walk = func(module *moduleInfo) { walk = func(module *moduleInfo) {
@ -1902,6 +2010,7 @@ func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module)
if module != topModule { if module != topModule {
if pred(module.logicModule) { if pred(module.logicModule) {
visiting = module
visit(module.logicModule) visit(module.logicModule)
} }
} }
@ -1911,7 +2020,16 @@ func (c *Context) visitDepsDepthFirstIf(topModule *moduleInfo, pred func(Module)
} }
func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) { func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) {
for _, dep := range module.directDeps { var dep *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
module, funcName(visit), dep))
}
}()
for _, dep = range module.directDeps {
visit(dep.logicModule) visit(dep.logicModule)
} }
} }
@ -1919,7 +2037,16 @@ func (c *Context) visitDirectDeps(module *moduleInfo, visit func(Module)) {
func (c *Context) visitDirectDepsIf(module *moduleInfo, pred func(Module) bool, func (c *Context) visitDirectDepsIf(module *moduleInfo, pred func(Module) bool,
visit func(Module)) { visit func(Module)) {
for _, dep := range module.directDeps { var dep *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s",
module, funcName(pred), funcName(visit), dep))
}
}()
for _, dep = range module.directDeps {
if pred(dep.logicModule) { if pred(dep.logicModule) {
visit(dep.logicModule) visit(dep.logicModule)
} }
@ -1940,9 +2067,18 @@ func (c *Context) sortedModuleNames() []string {
} }
func (c *Context) visitAllModules(visit func(Module)) { func (c *Context) visitAllModules(visit func(Module)) {
var module *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitAllModules(%s) for %s",
funcName(visit), module))
}
}()
for _, moduleName := range c.sortedModuleNames() { for _, moduleName := range c.sortedModuleNames() {
group := c.moduleGroups[moduleName] group := c.moduleGroups[moduleName]
for _, module := range group.modules { for _, module = range group.modules {
visit(module.logicModule) visit(module.logicModule)
} }
} }
@ -1951,6 +2087,15 @@ func (c *Context) visitAllModules(visit func(Module)) {
func (c *Context) visitAllModulesIf(pred func(Module) bool, func (c *Context) visitAllModulesIf(pred func(Module) bool,
visit func(Module)) { visit func(Module)) {
var module *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitAllModulesIf(%s, %s) for %s",
funcName(pred), funcName(visit), module))
}
}()
for _, moduleName := range c.sortedModuleNames() { for _, moduleName := range c.sortedModuleNames() {
group := c.moduleGroups[moduleName] group := c.moduleGroups[moduleName]
for _, module := range group.modules { for _, module := range group.modules {
@ -1961,6 +2106,23 @@ func (c *Context) visitAllModulesIf(pred func(Module) bool,
} }
} }
func (c *Context) visitAllModuleVariants(module *moduleInfo,
visit func(Module)) {
var variant *moduleInfo
defer func() {
if r := recover(); r != nil {
panic(newPanicErrorf(r, "VisitAllModuleVariants(%s, %s) for %s",
module, funcName(visit), variant))
}
}()
for _, variant = range module.group.modules {
visit(variant.logicModule)
}
}
func (c *Context) requireNinjaVersion(major, minor, micro int) { func (c *Context) requireNinjaVersion(major, minor, micro int) {
if major != 1 { if major != 1 {
panic("ninja version with major version != 1 not supported") panic("ninja version with major version != 1 not supported")
@ -2236,9 +2398,7 @@ func (c *Context) FinalModule(module Module) Module {
func (c *Context) VisitAllModuleVariants(module Module, func (c *Context) VisitAllModuleVariants(module Module,
visit func(Module)) { visit func(Module)) {
for _, module := range c.moduleInfo[module].group.modules { c.visitAllModuleVariants(c.moduleInfo[module], visit)
visit(module.logicModule)
}
} }
// WriteBuildFile writes the Ninja manifeset text for the generated build // WriteBuildFile writes the Ninja manifeset text for the generated build
@ -2739,6 +2899,34 @@ func beforeInModuleList(a, b *moduleInfo, list []*moduleInfo) bool {
panic(fmt.Errorf("element %v not found in list %v", missing, list)) panic(fmt.Errorf("element %v not found in list %v", missing, list))
} }
type panicError struct {
panic interface{}
stack []byte
in string
}
func newPanicErrorf(panic interface{}, in string, a ...interface{}) error {
buf := make([]byte, 4096)
count := runtime.Stack(buf, false)
return panicError{
panic: panic,
in: fmt.Sprintf(in, a...),
stack: buf[:count],
}
}
func (p panicError) Error() string {
return fmt.Sprintf("panic in %s\n%s\n%s\n", p.in, p.panic, p.stack)
}
func (p *panicError) addIn(in string) {
p.in += " in " + in
}
func funcName(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}
var fileHeaderTemplate = `****************************************************************************** var fileHeaderTemplate = `******************************************************************************
*** This file is generated and should not be edited *** *** This file is generated and should not be edited ***
****************************************************************************** ******************************************************************************

View file

@ -119,6 +119,7 @@ type BaseModuleContext interface {
Failed() bool Failed() bool
moduleInfo() *moduleInfo moduleInfo() *moduleInfo
error(err error)
} }
type DynamicDependerModuleContext BottomUpMutatorContext type DynamicDependerModuleContext BottomUpMutatorContext
@ -180,10 +181,16 @@ func (d *baseModuleContext) Config() interface{} {
return d.config return d.config
} }
func (d *baseModuleContext) error(err error) {
if err != nil {
d.errs = append(d.errs, err)
}
}
func (d *baseModuleContext) Errorf(pos scanner.Position, func (d *baseModuleContext) Errorf(pos scanner.Position,
format string, args ...interface{}) { format string, args ...interface{}) {
d.errs = append(d.errs, &Error{ d.error(&Error{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: pos, Pos: pos,
}) })
@ -192,7 +199,7 @@ func (d *baseModuleContext) Errorf(pos scanner.Position,
func (d *baseModuleContext) ModuleErrorf(format string, func (d *baseModuleContext) ModuleErrorf(format string,
args ...interface{}) { args ...interface{}) {
d.errs = append(d.errs, &Error{ d.error(&Error{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: d.module.pos, Pos: d.module.pos,
}) })
@ -209,7 +216,7 @@ func (d *baseModuleContext) PropertyErrorf(property, format string,
format = property + ": " + format format = property + ": " + format
d.errs = append(d.errs, &Error{ d.error(&Error{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: pos, Pos: pos,
}) })
@ -320,9 +327,7 @@ func (m *moduleContext) FinalModule() Module {
} }
func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) { func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
for _, module := range m.module.group.modules { m.context.visitAllModuleVariants(m.module, visit)
visit(module.logicModule)
}
} }
func (m *moduleContext) GetMissingDependencies() []string { func (m *moduleContext) GetMissingDependencies() []string {

View file

@ -91,15 +91,21 @@ func (s *singletonContext) BlueprintFile(logicModule Module) string {
return s.context.BlueprintFile(logicModule) return s.context.BlueprintFile(logicModule)
} }
func (s *singletonContext) error(err error) {
if err != nil {
s.errs = append(s.errs, err)
}
}
func (s *singletonContext) ModuleErrorf(logicModule Module, format string, func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
args ...interface{}) { args ...interface{}) {
s.errs = append(s.errs, s.context.ModuleErrorf(logicModule, format, args...)) s.error(s.context.ModuleErrorf(logicModule, format, args...))
} }
func (s *singletonContext) Errorf(format string, args ...interface{}) { func (s *singletonContext) Errorf(format string, args ...interface{}) {
// TODO: Make this not result in the error being printed as "internal error" // TODO: Make this not result in the error being printed as "internal error"
s.errs = append(s.errs, fmt.Errorf(format, args...)) s.error(fmt.Errorf(format, args...))
} }
func (s *singletonContext) Failed() bool { func (s *singletonContext) Failed() bool {

View file

@ -28,10 +28,6 @@ var (
testModuleF = &moduleInfo{variantName: "testModuleF"} testModuleF = &moduleInfo{variantName: "testModuleF"}
) )
func (m *moduleInfo) String() string {
return m.variantName
}
var spliceModulesTestCases = []struct { var spliceModulesTestCases = []struct {
in []*moduleInfo in []*moduleInfo
replace *moduleInfo replace *moduleInfo