Commit eafe9a18 authored by Keith Randall's avatar Keith Randall Committed by Keith Randall

cmd/compile: hide init functions in tracebacks

Treat compiler-generated init functions as wrappers, so they will not
be shown in tracebacks.

The exception to this rule is that we'd like to show the line number
of initializers for global variables in tracebacks. In order to
preserve line numbers for those cases, separate out the code for those
initializers into a separate function (which is not marked as
autogenerated).

This CL makes the go binary 0.2% bigger.

Fixes #29919

Change-Id: I0f1fbfc03d10d764ce3a8ddb48fb387ca8453386
Reviewed-on: https://go-review.googlesource.com/c/159717
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent d585f04f
...@@ -57,6 +57,9 @@ func anyinit(n []*Node) bool { ...@@ -57,6 +57,9 @@ func anyinit(n []*Node) bool {
// fninit hand-crafts package initialization code. // fninit hand-crafts package initialization code.
// //
// func init.ializers() { (0)
// <init stmts>
// }
// var initdone· uint8 (1) // var initdone· uint8 (1)
// func init() { (2) // func init() { (2)
// if initdone· > 1 { (3) // if initdone· > 1 { (3)
...@@ -68,7 +71,7 @@ func anyinit(n []*Node) bool { ...@@ -68,7 +71,7 @@ func anyinit(n []*Node) bool {
// initdone· = 1 (5) // initdone· = 1 (5)
// // over all matching imported symbols // // over all matching imported symbols
// <pkg>.init() (6) // <pkg>.init() (6)
// { <init stmts> } (7) // init.ializers() (7)
// init.<n>() // if any (8) // init.<n>() // if any (8)
// initdone· = 2 (9) // initdone· = 2 (9)
// return (10) // return (10)
...@@ -80,6 +83,27 @@ func fninit(n []*Node) { ...@@ -80,6 +83,27 @@ func fninit(n []*Node) {
return return
} }
// (0)
// Make a function that contains all the initialization statements.
// This is a separate function because we want it to appear in
// stack traces, where the init function itself does not.
var initializers *types.Sym
if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
initializers = lookup("init.ializers")
disableExport(initializers)
fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
fn.Nbody.Set(nf)
funcbody()
fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
funccompile(fn)
lineno = autogeneratedPos
}
var r []*Node var r []*Node
// (1) // (1)
...@@ -130,7 +154,11 @@ func fninit(n []*Node) { ...@@ -130,7 +154,11 @@ func fninit(n []*Node) {
} }
// (7) // (7)
r = append(r, nf...) if initializers != nil {
n := newname(initializers)
addvar(n, functype(nil, nil, nil), PFUNC)
r = append(r, nod(OCALL, n, nil))
}
// (8) // (8)
......
...@@ -83,7 +83,7 @@ func GetFuncID(name, file string) FuncID { ...@@ -83,7 +83,7 @@ func GetFuncID(name, file string) FuncID {
case "runtime.panicwrap": case "runtime.panicwrap":
return FuncID_panicwrap return FuncID_panicwrap
} }
if file == "<autogenerated>" && !strings.HasSuffix(name, ".init") { if file == "<autogenerated>" {
return FuncID_wrapper return FuncID_wrapper
} }
if strings.HasPrefix(name, "runtime.call") { if strings.HasPrefix(name, "runtime.call") {
......
// Copyright 2019 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.
// Make sure tracebacks from initialization code are reported correctly.
package a
import (
"fmt"
"runtime"
"strings"
)
var x = f() // line 15
func f() int {
var b [4096]byte
n := runtime.Stack(b[:], false) // line 19
s := string(b[:n])
var pcs [10]uintptr
n = runtime.Callers(1, pcs[:]) // line 22
// Check the Stack results.
if debug {
println(s)
}
if strings.Contains(s, "autogenerated") {
panic("autogenerated code in traceback")
}
if !strings.Contains(s, "a.go:15") {
panic("missing a.go:15")
}
if !strings.Contains(s, "a.go:19") {
panic("missing a.go:19")
}
if !strings.Contains(s, "a.init.ializers") {
panic("missing a.init.ializers")
}
// Check the CallersFrames results.
if debug {
iter := runtime.CallersFrames(pcs[:n])
for {
f, more := iter.Next()
fmt.Printf("%s %s:%d\n", f.Function, f.File, f.Line)
if !more {
break
}
}
}
iter := runtime.CallersFrames(pcs[:n])
f, more := iter.Next()
if f.Function != "a.f" || !strings.HasSuffix(f.File, "a.go") || f.Line != 22 {
panic(fmt.Sprintf("bad f %v\n", f))
}
if !more {
panic("traceback truncated after f")
}
f, more = iter.Next()
if f.Function != "a.init.ializers" || !strings.HasSuffix(f.File, "a.go") || f.Line != 15 {
panic(fmt.Sprintf("bad init.ializers %v\n", f))
}
if !more {
panic("traceback truncated after init.ializers")
}
f, _ = iter.Next()
if f.Function != "runtime.main" {
panic("runtime.main missing")
}
return 0
}
const debug = false
// Copyright 2019 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.
package main
import _ "./a"
func main() {
}
// rundir
// Copyright 2019 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.
// Make sure tracebacks from initialization code are reported correctly.
package ignored
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment