提交 2cadddd7 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

proc: Update map reading code for Go 1.12 (#1532)

Go 1.12 introduced a change to the internal map representation where
empty map cells can be marked with a tophash value of 1 instead of just
0.

Fixes #1531
上级 0b3c7d80
package main
import (
"fmt"
"runtime"
)
type W struct {
x int
y int
}
func main() {
testMaps()
}
func testMaps() {
m := make(map[string]W)
m["t"] = W{}
m["s"] = W{}
m["r"] = W{}
m["v"] = W{}
mm := map[string]W{
"r": {},
"s": {},
"t": {},
"v": {},
}
delete(mm, "s")
delete(m, "t")
runtime.Breakpoint()
fmt.Println(m, mm)
}
......@@ -18,6 +18,7 @@ import (
"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/reader"
"github.com/go-delve/delve/pkg/goversion"
)
const (
......@@ -28,8 +29,10 @@ const (
chanRecv = "chan receive"
chanSend = "chan send"
hashTophashEmpty = 0 // used by map reading code, indicates an empty bucket
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
hashTophashEmptyZero = 0 // used by map reading code, indicates an empty cell
hashTophashEmptyOne = 1 // used by map reading code, indicates an empty cell in Go 1.12 and later
hashMinTopHashGo111 = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.11
hashMinTopHashGo112 = 5 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated, in Go1.12
maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
......@@ -1763,6 +1766,9 @@ type mapIterator struct {
maxNumBuckets uint64 // maximum number of buckets to scan
idx int64
hashTophashEmptyOne uint64 // Go 1.12 and later has two sentinel tophash values for an empty cell, this is the second one (the first one hashTophashEmptyZero, the same as Go 1.11 and earlier)
hashMinTopHash uint64 // minimum value of tophash for a cell that isn't either evacuated or empty
}
// Code derived from go/src/runtime/hashmap.go
......@@ -1814,6 +1820,13 @@ func (v *Variable) mapIterator() *mapIterator {
return nil
}
it.hashTophashEmptyOne = hashTophashEmptyZero
it.hashMinTopHash = hashMinTopHashGo111
if producer := v.bi.Producer(); producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 12) {
it.hashTophashEmptyOne = hashTophashEmptyOne
it.hashMinTopHash = hashMinTopHashGo112
}
return it
}
......@@ -1851,7 +1864,7 @@ func (it *mapIterator) nextBucket() bool {
oldb := it.oldbuckets.clone()
oldb.Addr += uintptr(uint64(it.oldbuckets.DwarfType.Size()) * oldbidx)
if mapEvacuated(oldb) {
if it.mapEvacuated(oldb) {
break
}
......@@ -1953,7 +1966,7 @@ func (it *mapIterator) next() bool {
return false
}
it.idx++
if h != hashTophashEmpty {
if h != hashTophashEmptyZero && h != it.hashTophashEmptyOne {
return true
}
}
......@@ -1969,7 +1982,7 @@ func (it *mapIterator) value() *Variable {
return v
}
func mapEvacuated(b *Variable) bool {
func (it *mapIterator) mapEvacuated(b *Variable) bool {
if b.Addr == 0 {
return true
}
......@@ -1983,7 +1996,8 @@ func mapEvacuated(b *Variable) bool {
if err != nil {
return true
}
return tophash0 > hashTophashEmpty && tophash0 < hashMinTopHash
//TODO: this needs to be > hashTophashEmptyOne for go >= 1.12
return tophash0 > it.hashTophashEmptyOne && tophash0 < it.hashMinTopHash
}
return true
}
......
......@@ -3,6 +3,7 @@ package service_test
import (
"errors"
"fmt"
"go/constant"
"runtime"
"sort"
"strings"
......@@ -1197,3 +1198,45 @@ func TestCallFunction(t *testing.T) {
}
})
}
func TestIssue1531(t *testing.T) {
// Go 1.12 introduced a change to the map representation where empty cells can be marked with 1 instead of just 0.
withTestProcess("issue1531", t, func(p proc.Process, fixture protest.Fixture) {
assertNoError(proc.Continue(p), t, "Continue()")
hasKeys := func(mv *proc.Variable, keys ...string) {
n := 0
for i := 0; i < len(mv.Children); i += 2 {
cv := &mv.Children[i]
s := constant.StringVal(cv.Value)
found := false
for j := range keys {
if keys[j] == s {
found = true
break
}
}
if !found {
t.Errorf("key %q not allowed", s)
return
}
n++
}
if n != len(keys) {
t.Fatalf("wrong number of keys found")
}
}
mv, err := evalVariable(p, "m", pnormalLoadConfig)
assertNoError(err, t, "EvalVariable(m)")
cmv := api.ConvertVar(mv)
t.Logf("m = %s", cmv.SinglelineString())
hasKeys(mv, "s", "r", "v")
mmv, err := evalVariable(p, "mm", pnormalLoadConfig)
assertNoError(err, t, "EvalVariable(mm)")
cmmv := api.ConvertVar(mmv)
t.Logf("mm = %s", cmmv.SinglelineString())
hasKeys(mmv, "r", "t", "v")
})
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册