proc: optimize parseG (#1866)
runtime.g is a large and growing struct, we only need a few fields. Instead of using loadValue to load the full contents of g, cache its memory and then only load the fields we care about. Benchmark before: BenchmarkConditionalBreakpoints-4 1 14586710018 ns/op Benchmark after: BenchmarkConditionalBreakpoints-4 1 12476166303 ns/op Conditional breakpoint evaluation: 1.45ms -> 1.24ms Updates #1549
This commit is contained in:
parent
c272212baa
commit
ecea2e1814
@ -534,11 +534,14 @@ func (it *stackIterator) loadG0SchedSP() {
|
|||||||
}
|
}
|
||||||
it.g0_sched_sp_loaded = true
|
it.g0_sched_sp_loaded = true
|
||||||
if it.g != nil {
|
if it.g != nil {
|
||||||
g0var, _ := it.g.variable.fieldVariable("m").structMember("g0")
|
mvar, _ := it.g.variable.structMember("m")
|
||||||
if g0var != nil {
|
if mvar != nil {
|
||||||
g0, _ := g0var.parseG()
|
g0var, _ := mvar.structMember("g0")
|
||||||
if g0 != nil {
|
if g0var != nil {
|
||||||
it.g0_sched_sp = g0.SP
|
g0, _ := g0var.parseG()
|
||||||
|
if g0 != nil {
|
||||||
|
it.g0_sched_sp = g0.SP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -499,7 +499,11 @@ func GetG(thread Thread) (*G, error) {
|
|||||||
// For our purposes it's better if we always return the real goroutine
|
// For our purposes it's better if we always return the real goroutine
|
||||||
// since the rest of the code assumes the goroutine ID is univocal.
|
// since the rest of the code assumes the goroutine ID is univocal.
|
||||||
// The real 'current goroutine' is stored in g0.m.curg
|
// The real 'current goroutine' is stored in g0.m.curg
|
||||||
curgvar, err := g.variable.fieldVariable("m").structMember("curg")
|
mvar, err := g.variable.structMember("m")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
curgvar, err := mvar.structMember("curg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -186,19 +186,18 @@ const (
|
|||||||
// G represents a runtime G (goroutine) structure (at least the
|
// G represents a runtime G (goroutine) structure (at least the
|
||||||
// fields that Delve is interested in).
|
// fields that Delve is interested in).
|
||||||
type G struct {
|
type G struct {
|
||||||
ID int // Goroutine ID
|
ID int // Goroutine ID
|
||||||
PC uint64 // PC of goroutine when it was parked.
|
PC uint64 // PC of goroutine when it was parked.
|
||||||
SP uint64 // SP of goroutine when it was parked.
|
SP uint64 // SP of goroutine when it was parked.
|
||||||
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
||||||
LR uint64 // LR of goroutine when it was parked.
|
LR uint64 // LR of goroutine when it was parked.
|
||||||
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
||||||
StartPC uint64 // PC of the first function run on this goroutine.
|
StartPC uint64 // PC of the first function run on this goroutine.
|
||||||
WaitReason string // Reason for goroutine being parked.
|
Status uint64
|
||||||
Status uint64
|
stkbarVar *Variable // stkbar field of g struct
|
||||||
stkbarVar *Variable // stkbar field of g struct
|
stkbarPos int // stkbarPos field of g struct
|
||||||
stkbarPos int // stkbarPos field of g struct
|
stackhi uint64 // value of stack.hi
|
||||||
stackhi uint64 // value of stack.hi
|
stacklo uint64 // value of stack.lo
|
||||||
stacklo uint64 // value of stack.lo
|
|
||||||
|
|
||||||
SystemStack bool // SystemStack is true if this goroutine is currently executing on a system stack.
|
SystemStack bool // SystemStack is true if this goroutine is currently executing on a system stack.
|
||||||
|
|
||||||
@ -220,7 +219,11 @@ func (g *G) Defer() *Defer {
|
|||||||
if g.variable.Unreadable != nil {
|
if g.variable.Unreadable != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
dvar := g.variable.fieldVariable("_defer").maybeDereference()
|
dvar, _ := g.variable.structMember("_defer")
|
||||||
|
if dvar == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dvar = dvar.maybeDereference()
|
||||||
if dvar.Addr == 0 {
|
if dvar.Addr == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -518,6 +521,8 @@ func (ng ErrNoGoroutine) Error() string {
|
|||||||
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ErrUnreadableG = errors.New("could not read G struct")
|
||||||
|
|
||||||
func (v *Variable) parseG() (*G, error) {
|
func (v *Variable) parseG() (*G, error) {
|
||||||
mem := v.mem
|
mem := v.mem
|
||||||
gaddr := uint64(v.Addr)
|
gaddr := uint64(v.Addr)
|
||||||
@ -543,11 +548,13 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
}
|
}
|
||||||
v = v.maybeDereference()
|
v = v.maybeDereference()
|
||||||
}
|
}
|
||||||
v.loadValue(LoadConfig{false, 2, 64, 0, -1, 0})
|
|
||||||
if v.Unreadable != nil {
|
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
||||||
return nil, v.Unreadable
|
|
||||||
|
schedVar := v.loadFieldNamed("sched")
|
||||||
|
if schedVar == nil {
|
||||||
|
return nil, ErrUnreadableG
|
||||||
}
|
}
|
||||||
schedVar := v.fieldVariable("sched")
|
|
||||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
||||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
||||||
var bp, lr int64
|
var bp, lr int64
|
||||||
@ -557,21 +564,24 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
if bpvar := schedVar.fieldVariable("lr"); bpvar != nil && bpvar.Value != nil {
|
if bpvar := schedVar.fieldVariable("lr"); bpvar != nil && bpvar.Value != nil {
|
||||||
lr, _ = constant.Int64Val(bpvar.Value)
|
lr, _ = constant.Int64Val(bpvar.Value)
|
||||||
}
|
}
|
||||||
id, _ := constant.Int64Val(v.fieldVariable("goid").Value)
|
|
||||||
gopc, _ := constant.Int64Val(v.fieldVariable("gopc").Value)
|
|
||||||
startpc, _ := constant.Int64Val(v.fieldVariable("startpc").Value)
|
|
||||||
waitReason := ""
|
|
||||||
if wrvar := v.fieldVariable("waitreason"); wrvar.Value != nil {
|
|
||||||
switch wrvar.Kind {
|
|
||||||
case reflect.String:
|
|
||||||
waitReason = constant.StringVal(wrvar.Value)
|
|
||||||
case reflect.Uint:
|
|
||||||
waitReason = wrvar.ConstDescr()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
unreadable := false
|
||||||
|
|
||||||
|
loadInt64Maybe := func(name string) int64 {
|
||||||
|
vv := v.loadFieldNamed(name)
|
||||||
|
if vv == nil {
|
||||||
|
unreadable = true
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
n, _ := constant.Int64Val(vv.Value)
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := loadInt64Maybe("goid")
|
||||||
|
gopc := loadInt64Maybe("gopc")
|
||||||
|
startpc := loadInt64Maybe("startpc")
|
||||||
var stackhi, stacklo uint64
|
var stackhi, stacklo uint64
|
||||||
if stackVar := v.fieldVariable("stack"); stackVar != nil {
|
if stackVar := v.loadFieldNamed("stack"); stackVar != nil {
|
||||||
if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil {
|
if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil {
|
||||||
stackhi, _ = constant.Uint64Val(stackhiVar.Value)
|
stackhi, _ = constant.Uint64Val(stackhiVar.Value)
|
||||||
}
|
}
|
||||||
@ -580,14 +590,19 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stkbarVar, _ := v.structMember("stkbar")
|
stkbarVar := v.loadFieldNamed("stkbar")
|
||||||
stkbarVarPosFld := v.fieldVariable("stkbarPos")
|
stkbarVarPosFld := v.loadFieldNamed("stkbarPos")
|
||||||
var stkbarPos int64
|
var stkbarPos int64
|
||||||
if stkbarVarPosFld != nil { // stack barriers were removed in Go 1.9
|
if stkbarVarPosFld != nil { // stack barriers were removed in Go 1.9
|
||||||
stkbarPos, _ = constant.Int64Val(stkbarVarPosFld.Value)
|
stkbarPos, _ = constant.Int64Val(stkbarVarPosFld.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
status, _ := constant.Int64Val(v.fieldVariable("atomicstatus").Value)
|
status := loadInt64Maybe("atomicstatus")
|
||||||
|
|
||||||
|
if unreadable {
|
||||||
|
return nil, ErrUnreadableG
|
||||||
|
}
|
||||||
|
|
||||||
f, l, fn := v.bi.PCToLine(uint64(pc))
|
f, l, fn := v.bi.PCToLine(uint64(pc))
|
||||||
|
|
||||||
g := &G{
|
g := &G{
|
||||||
@ -598,7 +613,6 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
SP: uint64(sp),
|
SP: uint64(sp),
|
||||||
BP: uint64(bp),
|
BP: uint64(bp),
|
||||||
LR: uint64(lr),
|
LR: uint64(lr),
|
||||||
WaitReason: waitReason,
|
|
||||||
Status: uint64(status),
|
Status: uint64(status),
|
||||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||||
variable: v,
|
variable: v,
|
||||||
@ -623,6 +637,9 @@ func (v *Variable) loadFieldNamed(name string) *Variable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) fieldVariable(name string) *Variable {
|
func (v *Variable) fieldVariable(name string) *Variable {
|
||||||
|
if !v.loaded {
|
||||||
|
panic("fieldVariable called on a variable that wasn't loaded")
|
||||||
|
}
|
||||||
for i := range v.Children {
|
for i := range v.Children {
|
||||||
if child := &v.Children[i]; child.Name == name {
|
if child := &v.Children[i]; child.Name == name {
|
||||||
return child
|
return child
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user