mirror of https://github.com/golang/go.git
cmd/link,runtime: DWARF/gdb support for swiss maps
For #54766. Cq-Include-Trybots: luci.golang.try:gotip-linux-ppc64_power10,gotip-linux-amd64-longtest-swissmap Change-Id: I6695c0b143560d974b710e1d78e7a7d09278f7cc Reviewed-on: https://go-review.googlesource.com/c/go/+/620215 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Auto-Submit: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
24bc473173
commit
067c091564
|
|
@ -36,7 +36,7 @@ func SwissMapGroupType(t *types.Type) *types.Type {
|
|||
// }
|
||||
slotFields := []*types.Field{
|
||||
makefield("key", t.Key()),
|
||||
makefield("typ", t.Elem()),
|
||||
makefield("elem", t.Elem()),
|
||||
}
|
||||
slot := types.NewStruct(slotFields)
|
||||
slot.SetNoalg(true)
|
||||
|
|
|
|||
|
|
@ -159,6 +159,11 @@ func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) l
|
|||
return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
|
||||
}
|
||||
|
||||
func decodetypeMapSwissGroup(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
|
||||
relocs := ldr.Relocs(symIdx)
|
||||
return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+2*int32(arch.PtrSize)) // 0x24 / 0x40
|
||||
}
|
||||
|
||||
func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
|
||||
relocs := ldr.Relocs(symIdx)
|
||||
return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
|
||||
|
|
|
|||
|
|
@ -810,7 +810,7 @@ func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
|
|||
die = prototypedies[name]
|
||||
}
|
||||
if die == nil {
|
||||
log.Fatalf("internal error: DIE generation failed for %s\nprototypedies: %+v", name, prototypedies)
|
||||
log.Fatalf("internal error: DIE generation failed for %s\n", name)
|
||||
}
|
||||
return die
|
||||
}
|
||||
|
|
@ -873,101 +873,68 @@ func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
|
|||
}
|
||||
|
||||
func (d *dwctxt) synthesizemaptypesSwiss(ctxt *Link, die *dwarf.DWDie) {
|
||||
hash := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.table"))
|
||||
//bucket := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.Map"))
|
||||
|
||||
if hash == nil {
|
||||
return
|
||||
}
|
||||
mapType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.Map"))
|
||||
tableType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.table"))
|
||||
tableSliceType := walktypedef(d.findprotodie(ctxt, "type:[]*internal/runtime/maps.table"))
|
||||
groupsReferenceType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.groupsReference"))
|
||||
|
||||
for ; die != nil; die = die.Link {
|
||||
if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
|
||||
continue
|
||||
}
|
||||
gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
|
||||
keytype := decodetypeMapKey(d.ldr, d.arch, gotype)
|
||||
valtype := decodetypeMapValue(d.ldr, d.arch, gotype)
|
||||
//keydata := d.ldr.Data(keytype)
|
||||
//valdata := d.ldr.Data(valtype)
|
||||
//keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
|
||||
keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
|
||||
|
||||
// compute size info like hashmap.c does.
|
||||
//indirectKey, indirectVal := false, false
|
||||
//if keysize > abi.SwissMapMaxKeyBytes {
|
||||
// keysize = int64(d.arch.PtrSize)
|
||||
// indirectKey = true
|
||||
//}
|
||||
//if valsize > abi.SwissMapMaxElemBytes {
|
||||
// valsize = int64(d.arch.PtrSize)
|
||||
// indirectVal = true
|
||||
//}
|
||||
keyType := decodetypeMapKey(d.ldr, d.arch, gotype)
|
||||
valType := decodetypeMapValue(d.ldr, d.arch, gotype)
|
||||
groupType := decodetypeMapSwissGroup(d.ldr, d.arch, gotype)
|
||||
|
||||
// Construct type to represent an array of BucketSize keys
|
||||
// TODO
|
||||
keyname := d.nameFromDIESym(keytype)
|
||||
//dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
|
||||
// newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount*keysize, 0)
|
||||
// t := keytype
|
||||
// if indirectKey {
|
||||
// t = d.defptrto(keytype)
|
||||
// }
|
||||
// d.newrefattr(dwhk, dwarf.DW_AT_type, t)
|
||||
// fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size")
|
||||
// newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount, 0)
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
|
||||
//})
|
||||
keyType = d.walksymtypedef(d.defgotype(keyType))
|
||||
valType = d.walksymtypedef(d.defgotype(valType))
|
||||
groupType = d.walksymtypedef(d.defgotype(groupType))
|
||||
|
||||
// Construct type to represent an array of BucketSize values
|
||||
// TODO
|
||||
valname := d.nameFromDIESym(valtype)
|
||||
//dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
|
||||
// newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount*valsize, 0)
|
||||
// t := valtype
|
||||
// if indirectVal {
|
||||
// t = d.defptrto(valtype)
|
||||
// }
|
||||
// d.newrefattr(dwhv, dwarf.DW_AT_type, t)
|
||||
// fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size")
|
||||
// newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount, 0)
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
|
||||
//})
|
||||
keyName := d.nameFromDIESym(keyType)
|
||||
valName := d.nameFromDIESym(valType)
|
||||
|
||||
// Construct bucket<K,V>
|
||||
// TODO
|
||||
//dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
|
||||
// // Copy over all fields except the field "data" from the generic
|
||||
// // bucket. "data" will be replaced with keys/values below.
|
||||
// d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
|
||||
|
||||
// fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys")
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
|
||||
// newmemberoffsetattr(fld, abi.SwissMapBucketCount)
|
||||
// fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values")
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
|
||||
// newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*int32(keysize))
|
||||
// fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow")
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
|
||||
// newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*(int32(keysize)+int32(valsize)))
|
||||
// if d.arch.RegSize > d.arch.PtrSize {
|
||||
// fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad")
|
||||
// d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
|
||||
// newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
|
||||
// }
|
||||
|
||||
// newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount+abi.SwissMapBucketCount*keysize+abi.SwissMapBucketCount*valsize+int64(d.arch.RegSize), 0)
|
||||
//})
|
||||
|
||||
// Construct hash<K,V>
|
||||
dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
|
||||
d.copychildren(ctxt, dwh, hash)
|
||||
//d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
|
||||
//d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
|
||||
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
|
||||
// Construct groupsReference[K,V]
|
||||
dwGroupsReference := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "groupReference", keyName, valName, func(dwh *dwarf.DWDie) {
|
||||
d.copychildren(ctxt, dwh, groupsReferenceType)
|
||||
// data *group[K,V]
|
||||
//
|
||||
// This is actually a pointer to an array
|
||||
// *[lengthMask+1]group[K,V], but the length is
|
||||
// variable, so we can't statically record the length.
|
||||
d.substitutetype(dwh, "data", d.defptrto(groupType))
|
||||
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(groupsReferenceType, dwarf.DW_AT_byte_size).Value, nil)
|
||||
newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
|
||||
})
|
||||
|
||||
// make map type a pointer to hash<K,V>
|
||||
d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
|
||||
// Construct table[K,V]
|
||||
dwTable := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "table", keyName, valName, func(dwh *dwarf.DWDie) {
|
||||
d.copychildren(ctxt, dwh, tableType)
|
||||
d.substitutetype(dwh, "groups", dwGroupsReference)
|
||||
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(tableType, dwarf.DW_AT_byte_size).Value, nil)
|
||||
newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
|
||||
})
|
||||
|
||||
// Construct type to represent []*table[K,V].
|
||||
dwTableSlice := d.mkinternaltype(ctxt, dwarf.DW_ABRV_SLICETYPE, "[]*table", keyName, valName, func(dwh *dwarf.DWDie) {
|
||||
d.copychildren(ctxt, dwh, tableSliceType)
|
||||
d.substitutetype(dwh, "array", d.defptrto(d.defptrto(dwTable)))
|
||||
d.newrefattr(dwh, dwarf.DW_AT_go_elem, d.defptrto(dwTable))
|
||||
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(tableSliceType, dwarf.DW_AT_byte_size).Value, nil)
|
||||
newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Slice), 0)
|
||||
})
|
||||
|
||||
// Construct map[K,V]
|
||||
dwMap := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "map", keyName, valName, func(dwh *dwarf.DWDie) {
|
||||
d.copychildren(ctxt, dwh, mapType)
|
||||
d.substitutetype(dwh, "directory", dwTableSlice)
|
||||
newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(mapType, dwarf.DW_AT_byte_size).Value, nil)
|
||||
newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
|
||||
})
|
||||
|
||||
// make map type a pointer to map[K,V]
|
||||
d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwMap))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1882,7 +1849,10 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
|
|||
"type:runtime.hchan": nil,
|
||||
}
|
||||
if buildcfg.Experiment.SwissMap {
|
||||
prototypedies["type:internal/runtime/maps.Map"] = nil
|
||||
prototypedies["type:internal/runtime/maps.table"] = nil
|
||||
prototypedies["type:[]*internal/runtime/maps.table"] = nil
|
||||
prototypedies["type:internal/runtime/maps.groupsReference"] = nil
|
||||
} else {
|
||||
prototypedies["type:runtime.hmap"] = nil
|
||||
prototypedies["type:runtime.bmap"] = nil
|
||||
|
|
|
|||
|
|
@ -141,7 +141,6 @@ class SliceTypePrinter:
|
|||
yield ('[{0}]'.format(idx), item)
|
||||
|
||||
|
||||
# TODO(go.dev/issue/54766): Support swisstable maps.
|
||||
class MapTypePrinter:
|
||||
"""Pretty print map[K]V types.
|
||||
|
||||
|
|
@ -161,7 +160,56 @@ class MapTypePrinter:
|
|||
return str(self.val.type)
|
||||
|
||||
def children(self):
|
||||
MapBucketCount = 8 # see internal/abi.go:MapBucketCount
|
||||
fields = [f.name for f in self.val.type.strip_typedefs().target().fields()]
|
||||
if 'buckets' in fields:
|
||||
yield from self.old_map_children()
|
||||
else:
|
||||
yield from self.swiss_map_children()
|
||||
|
||||
def swiss_map_children(self):
|
||||
SwissMapGroupSlots = 8 # see internal/abi:SwissMapGroupSlots
|
||||
|
||||
cnt = 0
|
||||
directory = SliceValue(self.val['directory'])
|
||||
for table in directory:
|
||||
table = table.dereference()
|
||||
groups = table['groups']['data']
|
||||
length = table['groups']['lengthMask'] + 1
|
||||
|
||||
# The linker DWARF generation
|
||||
# (cmd/link/internal/ld.(*dwctxt).synthesizemaptypesSwiss) records
|
||||
# groups.data as a *group[K,V], but it is actually a pointer to
|
||||
# variable length array *[length]group[K,V].
|
||||
#
|
||||
# N.B. array() takes an _inclusive_ upper bound.
|
||||
|
||||
# group[K,V]
|
||||
group_type = groups.type.target()
|
||||
# [length]group[K,V]
|
||||
array_group_type = group_type.array(length-1)
|
||||
# *[length]group[K,V]
|
||||
ptr_array_group_type = array_group_type.pointer()
|
||||
# groups = (*[length]group[K,V])(groups.data)
|
||||
groups = groups.cast(ptr_array_group_type)
|
||||
groups = groups.dereference()
|
||||
|
||||
for i in xrange(length):
|
||||
group = groups[i]
|
||||
ctrl = group['ctrl']
|
||||
|
||||
for i in xrange(SwissMapGroupSlots):
|
||||
c = (ctrl >> (8*i)) & 0xff
|
||||
if (c & 0x80) != 0:
|
||||
# Empty or deleted
|
||||
continue
|
||||
|
||||
# Full
|
||||
yield str(cnt), group['slots'][i]['key']
|
||||
yield str(cnt+1), group['slots'][i]['elem']
|
||||
|
||||
|
||||
def old_map_children(self):
|
||||
MapBucketCount = 8 # see internal/abi:OldMapBucketCount
|
||||
B = self.val['B']
|
||||
buckets = self.val['buckets']
|
||||
oldbuckets = self.val['oldbuckets']
|
||||
|
|
@ -386,7 +434,7 @@ goobjfile.pretty_printers.append(ifacematcher)
|
|||
class GoLenFunc(gdb.Function):
|
||||
"Length of strings, slices, maps or channels"
|
||||
|
||||
how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount'))
|
||||
how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'used'), (ChanTypePrinter, 'qcount'))
|
||||
|
||||
def __init__(self):
|
||||
gdb.Function.__init__(self, "len")
|
||||
|
|
@ -395,6 +443,12 @@ class GoLenFunc(gdb.Function):
|
|||
typename = str(obj.type)
|
||||
for klass, fld in self.how:
|
||||
if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern):
|
||||
if klass == MapTypePrinter:
|
||||
fields = [f.name for f in self.val.type.strip_typedefs().target().fields()]
|
||||
if 'buckets' in fields:
|
||||
# Old maps.
|
||||
fld = 'count'
|
||||
|
||||
return obj[fld]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -186,9 +186,6 @@ func TestGdbPythonCgo(t *testing.T) {
|
|||
}
|
||||
|
||||
func testGdbPython(t *testing.T, cgo bool) {
|
||||
if goexperiment.SwissMap {
|
||||
t.Skip("TODO(prattmic): swissmap DWARF")
|
||||
}
|
||||
if cgo {
|
||||
testenv.MustHaveCGO(t)
|
||||
}
|
||||
|
|
@ -531,10 +528,6 @@ func main() {
|
|||
// TestGdbAutotmpTypes ensures that types of autotmp variables appear in .debug_info
|
||||
// See bug #17830.
|
||||
func TestGdbAutotmpTypes(t *testing.T) {
|
||||
if goexperiment.SwissMap {
|
||||
t.Skip("TODO(prattmic): swissmap DWARF")
|
||||
}
|
||||
|
||||
checkGdbEnvironment(t)
|
||||
t.Parallel()
|
||||
checkGdbVersion(t)
|
||||
|
|
@ -584,10 +577,21 @@ func TestGdbAutotmpTypes(t *testing.T) {
|
|||
// Check that the backtrace matches the source code.
|
||||
types := []string{
|
||||
"[]main.astruct",
|
||||
"bucket<string,main.astruct>",
|
||||
"hash<string,main.astruct>",
|
||||
"main.astruct",
|
||||
"hash<string,main.astruct> * map[string]main.astruct",
|
||||
}
|
||||
if goexperiment.SwissMap {
|
||||
types = append(types, []string{
|
||||
"groupReference<string,main.astruct>",
|
||||
"table<string,main.astruct>",
|
||||
"map<string,main.astruct>",
|
||||
"map<string,main.astruct> * map[string]main.astruct",
|
||||
}...)
|
||||
} else {
|
||||
types = append(types, []string{
|
||||
"bucket<string,main.astruct>",
|
||||
"hash<string,main.astruct>",
|
||||
"hash<string,main.astruct> * map[string]main.astruct",
|
||||
}...)
|
||||
}
|
||||
for _, name := range types {
|
||||
if !strings.Contains(sgot, name) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue