mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	 1f644b0018
			
		
	
	1f644b0018
	
	
	
		
			
			This bug was reported previously as PRs to fix it but the reporters always declined to provide more informations as to how it happens, which remains a mystery. Fixes #4063
		
			
				
	
	
		
			609 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			609 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | |
| 	"github.com/go-delve/delve/pkg/goversion"
 | |
| )
 | |
| 
 | |
| type mapIterator interface {
 | |
| 	next() bool
 | |
| 	key() *Variable
 | |
| 	value() *Variable
 | |
| }
 | |
| 
 | |
| func (v *Variable) mapIterator(maxNumBuckets uint64) mapIterator {
 | |
| 	mt := v.RealType.(*godwarf.MapType)
 | |
| 	sv := v.clone()
 | |
| 	sv.RealType = godwarf.ResolveTypedef(&(sv.RealType.(*godwarf.MapType).TypedefType))
 | |
| 	sv = sv.maybeDereference()
 | |
| 	v.Base = sv.Addr
 | |
| 
 | |
| 	maptype, ok := sv.RealType.(*godwarf.StructType)
 | |
| 	if !ok {
 | |
| 		v.Unreadable = errors.New("wrong real type for map")
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	isptr := func(typ godwarf.Type) bool {
 | |
| 		_, isptr := typ.(*godwarf.PtrType)
 | |
| 		return isptr
 | |
| 	}
 | |
| 
 | |
| 	it := &mapIteratorClassic{v: v, bidx: 0, b: nil, idx: 0, maxNumBuckets: maxNumBuckets, keyTypeIsPtr: isptr(mt.KeyType), elemTypeIsPtr: isptr(mt.ElemType)}
 | |
| 	itswiss := &mapIteratorSwiss{v: v, maxNumGroups: maxNumBuckets, keyTypeIsPtr: isptr(mt.KeyType), elemTypeIsPtr: isptr(mt.ElemType)}
 | |
| 
 | |
| 	if sv.Addr == 0 {
 | |
| 		it.numbuckets = 0
 | |
| 		return it
 | |
| 	}
 | |
| 
 | |
| 	v.mem = cacheMemory(v.mem, v.Base, int(v.RealType.Size()))
 | |
| 
 | |
| 	for _, f := range maptype.Field {
 | |
| 		var err error
 | |
| 		field, _ := sv.toField(f)
 | |
| 		switch f.Name {
 | |
| 		// Classic map fields
 | |
| 		case "count": // +rtype -fieldof hmap int
 | |
| 			v.Len, err = field.asInt()
 | |
| 		case "B": // +rtype -fieldof hmap uint8
 | |
| 			var b uint64
 | |
| 			b, err = field.asUint()
 | |
| 			it.numbuckets = 1 << b
 | |
| 			it.oldmask = (1 << (b - 1)) - 1
 | |
| 		case "buckets": // +rtype -fieldof hmap unsafe.Pointer
 | |
| 			it.buckets = field.maybeDereference()
 | |
| 		case "oldbuckets": // +rtype -fieldof hmap unsafe.Pointer
 | |
| 			it.oldbuckets = field.maybeDereference()
 | |
| 
 | |
| 		// Swisstable map fields
 | |
| 		case "used":
 | |
| 			var n uint64
 | |
| 			n, err = field.asUint()
 | |
| 			v.Len = int64(n)
 | |
| 		case "dirPtr":
 | |
| 			itswiss.dirPtr = field
 | |
| 		case "dirLen":
 | |
| 			itswiss.dirLen, err = field.asInt()
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			v.Unreadable = err
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if it.buckets == nil && itswiss.dirPtr != nil {
 | |
| 		itswiss.loadTypes()
 | |
| 		return itswiss
 | |
| 	}
 | |
| 
 | |
| 	if it.buckets == nil || it.oldbuckets == nil || it.buckets.Kind != reflect.Struct || it.oldbuckets.Kind != reflect.Struct {
 | |
| 		v.Unreadable = errMapBucketsNotStruct
 | |
| 		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
 | |
| }
 | |
| 
 | |
| // Classic Maps ///////////////////////////////////////////////////////////////
 | |
| 
 | |
| type mapIteratorClassic struct {
 | |
| 	v          *Variable
 | |
| 	numbuckets uint64
 | |
| 	oldmask    uint64
 | |
| 	buckets    *Variable
 | |
| 	oldbuckets *Variable
 | |
| 	b          *Variable
 | |
| 	bidx       uint64
 | |
| 
 | |
| 	keyTypeIsPtr, elemTypeIsPtr bool
 | |
| 
 | |
| 	tophashes *Variable
 | |
| 	keys      *Variable
 | |
| 	values    *Variable
 | |
| 	overflow  *Variable
 | |
| 
 | |
| 	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
 | |
| }
 | |
| 
 | |
| var errMapBucketContentsNotArray = errors.New("malformed map type: keys, values or tophash of a bucket is not an array")
 | |
| var errMapBucketContentsInconsistentLen = errors.New("malformed map type: inconsistent array length in bucket")
 | |
| var errMapBucketsNotStruct = errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct")
 | |
| 
 | |
| func (it *mapIteratorClassic) nextBucket() bool {
 | |
| 	if it.overflow != nil && it.overflow.Addr > 0 {
 | |
| 		it.b = it.overflow
 | |
| 	} else {
 | |
| 		it.b = nil
 | |
| 
 | |
| 		if it.maxNumBuckets > 0 && it.bidx >= it.maxNumBuckets {
 | |
| 			return false
 | |
| 		}
 | |
| 
 | |
| 		for it.bidx < it.numbuckets {
 | |
| 			it.b = it.buckets.clone()
 | |
| 			it.b.Addr += uint64(it.buckets.DwarfType.Size()) * it.bidx
 | |
| 
 | |
| 			if it.oldbuckets.Addr <= 0 {
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			// if oldbuckets is not nil we are iterating through a map that is in
 | |
| 			// the middle of a grow.
 | |
| 			// if the bucket we are looking at hasn't been filled in we iterate
 | |
| 			// instead through its corresponding "oldbucket" (i.e. the bucket the
 | |
| 			// elements of this bucket are coming from) but only if this is the first
 | |
| 			// of the two buckets being created from the same oldbucket (otherwise we
 | |
| 			// would print some keys twice)
 | |
| 
 | |
| 			oldbidx := it.bidx & it.oldmask
 | |
| 			oldb := it.oldbuckets.clone()
 | |
| 			oldb.Addr += uint64(it.oldbuckets.DwarfType.Size()) * oldbidx
 | |
| 
 | |
| 			if it.mapEvacuated(oldb) {
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			if oldbidx == it.bidx {
 | |
| 				it.b = oldb
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			// oldbucket origin for current bucket has not been evacuated but we have already
 | |
| 			// iterated over it so we should just skip it
 | |
| 			it.b = nil
 | |
| 			it.bidx++
 | |
| 		}
 | |
| 
 | |
| 		if it.b == nil {
 | |
| 			return false
 | |
| 		}
 | |
| 		it.bidx++
 | |
| 	}
 | |
| 
 | |
| 	if it.b.Addr <= 0 {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	it.b.mem = cacheMemory(it.b.mem, it.b.Addr, int(it.b.RealType.Size()))
 | |
| 
 | |
| 	it.tophashes = nil
 | |
| 	it.keys = nil
 | |
| 	it.values = nil
 | |
| 	it.overflow = nil
 | |
| 
 | |
| 	for _, f := range it.b.DwarfType.(*godwarf.StructType).Field {
 | |
| 		field, err := it.b.toField(f)
 | |
| 		if err != nil {
 | |
| 			it.v.Unreadable = err
 | |
| 			return false
 | |
| 		}
 | |
| 		if field.Unreadable != nil {
 | |
| 			it.v.Unreadable = field.Unreadable
 | |
| 			return false
 | |
| 		}
 | |
| 
 | |
| 		switch f.Name {
 | |
| 		case "tophash": // +rtype -fieldof bmap [8]uint8
 | |
| 			it.tophashes = field
 | |
| 		case "keys":
 | |
| 			it.keys = field
 | |
| 		case "values":
 | |
| 			it.values = field
 | |
| 		case "overflow":
 | |
| 			it.overflow = field.maybeDereference()
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// sanity checks
 | |
| 	if it.tophashes == nil || it.keys == nil || it.values == nil || it.overflow == nil {
 | |
| 		it.v.Unreadable = errors.New("malformed map type")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if it.tophashes.Kind != reflect.Array || it.keys.Kind != reflect.Array || it.values.Kind != reflect.Array {
 | |
| 		it.v.Unreadable = errMapBucketContentsNotArray
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if it.tophashes.Len != it.keys.Len {
 | |
| 		it.v.Unreadable = errMapBucketContentsInconsistentLen
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if it.values.fieldType.Size() > 0 && it.tophashes.Len != it.values.Len {
 | |
| 		// if the type of the value is zero-sized (i.e. struct{}) then the values
 | |
| 		// array's length is zero.
 | |
| 		it.v.Unreadable = errMapBucketContentsInconsistentLen
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if it.overflow.Kind != reflect.Struct {
 | |
| 		it.v.Unreadable = errMapBucketsNotStruct
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorClassic) next() bool {
 | |
| 	for {
 | |
| 		if it.b == nil || it.idx >= it.tophashes.Len {
 | |
| 			r := it.nextBucket()
 | |
| 			if !r {
 | |
| 				return false
 | |
| 			}
 | |
| 			it.idx = 0
 | |
| 		}
 | |
| 		tophash, _ := it.tophashes.sliceAccess(int(it.idx))
 | |
| 		h, err := tophash.asUint()
 | |
| 		if err != nil {
 | |
| 			it.v.Unreadable = fmt.Errorf("unreadable tophash: %v", err)
 | |
| 			return false
 | |
| 		}
 | |
| 		it.idx++
 | |
| 		if h != hashTophashEmptyZero && h != it.hashTophashEmptyOne {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorClassic) key() *Variable {
 | |
| 	k, _ := it.keys.sliceAccess(int(it.idx - 1))
 | |
| 	if k.Kind == reflect.Ptr && !it.keyTypeIsPtr {
 | |
| 		k = k.maybeDereference()
 | |
| 	}
 | |
| 	return k
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorClassic) value() *Variable {
 | |
| 	if it.values.fieldType.Size() <= 0 {
 | |
| 		return it.v.newVariable("", it.values.Addr, it.values.fieldType, DereferenceMemory(it.v.mem))
 | |
| 	}
 | |
| 
 | |
| 	v, _ := it.values.sliceAccess(int(it.idx - 1))
 | |
| 	if v.Kind == reflect.Ptr && !it.elemTypeIsPtr {
 | |
| 		v = v.maybeDereference()
 | |
| 	}
 | |
| 
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorClassic) mapEvacuated(b *Variable) bool {
 | |
| 	if b.Addr == 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	for _, f := range b.DwarfType.(*godwarf.StructType).Field {
 | |
| 		if f.Name != "tophash" {
 | |
| 			continue
 | |
| 		}
 | |
| 		tophashes, _ := b.toField(f)
 | |
| 		tophash0var, _ := tophashes.sliceAccess(0)
 | |
| 		tophash0, err := tophash0var.asUint()
 | |
| 		if err != nil {
 | |
| 			return true
 | |
| 		}
 | |
| 		//TODO: this needs to be > hashTophashEmptyOne for go >= 1.12
 | |
| 		return tophash0 > it.hashTophashEmptyOne && tophash0 < it.hashMinTopHash
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Swisstable Maps ///////////////////////////////////////////////////////////////
 | |
| 
 | |
| const (
 | |
| 	swissTableCtrlEmpty = 0b10000000 // +rtype go1.24 internal/runtime/maps.ctrlEmpty
 | |
| )
 | |
| 
 | |
| type mapIteratorSwiss struct {
 | |
| 	v            *Variable
 | |
| 	dirPtr       *Variable
 | |
| 	dirLen       int64
 | |
| 	maxNumGroups uint64 // Maximum number of groups we will visit
 | |
| 
 | |
| 	keyTypeIsPtr, elemTypeIsPtr bool
 | |
| 	tableType, groupType        *godwarf.StructType
 | |
| 
 | |
| 	tableFieldIndex, tableFieldGroups, groupsFieldLengthMask, groupsFieldData, groupFieldCtrl, groupFieldSlots, slotFieldKey, slotFieldElem *godwarf.StructField
 | |
| 
 | |
| 	dirIdx int64
 | |
| 	tab    *swissTable
 | |
| 
 | |
| 	groupIdx uint64
 | |
| 	group    *swissGroup
 | |
| 
 | |
| 	slotIdx uint32
 | |
| 
 | |
| 	groupCount uint64 // Total count of visited groups except for current table
 | |
| 
 | |
| 	curKey, curValue *Variable
 | |
| }
 | |
| 
 | |
| type swissTable struct {
 | |
| 	index  int64
 | |
| 	groups *Variable
 | |
| }
 | |
| 
 | |
| type swissGroup struct {
 | |
| 	slots *Variable
 | |
| 	ctrls []byte
 | |
| }
 | |
| 
 | |
| var errSwissTableCouldNotLoad = errors.New("could not load one of the tables")
 | |
| var errSwissMapBadType = errors.New("swiss table type does not have some required fields")
 | |
| var errSwissMapBadTableField = errors.New("swiss table bad table field")
 | |
| var errSwissMapBadGroupTypeErr = errors.New("bad swiss map type, group type lacks some required fields")
 | |
| var errSwissTableNilGroups = errors.New("bad swiss map, groups pointer is nil")
 | |
| 
 | |
| // loadTypes determines the correct type for it.dirPtr:  the linker records
 | |
| // this type as **table but in reality it is either *[dirLen]*table for
 | |
| // large maps or *group for small maps, when it.dirLen == 0.
 | |
| func (it *mapIteratorSwiss) loadTypes() {
 | |
| 	tableptrptrtyp, ok := it.dirPtr.DwarfType.(*godwarf.PtrType)
 | |
| 	if !ok {
 | |
| 		it.v.Unreadable = errSwissMapBadTableField
 | |
| 		return
 | |
| 	}
 | |
| 	tableptrtyp, ok := tableptrptrtyp.Type.(*godwarf.PtrType)
 | |
| 	if !ok {
 | |
| 		it.v.Unreadable = errSwissMapBadTableField
 | |
| 		return
 | |
| 	}
 | |
| 	it.tableType, ok = tableptrtyp.Type.(*godwarf.StructType)
 | |
| 	if !ok {
 | |
| 		it.v.Unreadable = errSwissMapBadTableField
 | |
| 		return
 | |
| 	}
 | |
| 	for _, field := range it.tableType.Field {
 | |
| 		switch field.Name {
 | |
| 		case "index":
 | |
| 			it.tableFieldIndex = field
 | |
| 		case "groups":
 | |
| 			it.tableFieldGroups = field
 | |
| 			groupstyp, ok := field.Type.(*godwarf.StructType)
 | |
| 			if ok {
 | |
| 				for _, field := range groupstyp.Field {
 | |
| 					switch field.Name {
 | |
| 					case "data":
 | |
| 						it.groupsFieldData = field
 | |
| 						typ, ok := field.Type.(*godwarf.PtrType)
 | |
| 						if ok {
 | |
| 							it.groupType, _ = godwarf.ResolveTypedef(typ.Type).(*godwarf.StructType)
 | |
| 						}
 | |
| 					case "lengthMask":
 | |
| 						it.groupsFieldLengthMask = field
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if it.groupType == nil || it.tableFieldIndex == nil || it.tableFieldGroups == nil || it.groupsFieldLengthMask == nil {
 | |
| 		it.v.Unreadable = errSwissMapBadType
 | |
| 		return
 | |
| 	}
 | |
| 	for _, field := range it.groupType.Field {
 | |
| 		switch field.Name {
 | |
| 		case "ctrl":
 | |
| 			it.groupFieldCtrl = field
 | |
| 		case "slots":
 | |
| 			it.groupFieldSlots = field
 | |
| 		}
 | |
| 	}
 | |
| 	if it.groupFieldCtrl == nil || it.groupFieldSlots == nil {
 | |
| 		it.v.Unreadable = errSwissMapBadGroupTypeErr
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	slotsType, ok := godwarf.ResolveTypedef(it.groupFieldSlots.Type).(*godwarf.ArrayType)
 | |
| 	if !ok {
 | |
| 		it.v.Unreadable = errSwissMapBadGroupTypeErr
 | |
| 		return
 | |
| 	}
 | |
| 	slotType, ok := slotsType.Type.(*godwarf.StructType)
 | |
| 	if !ok {
 | |
| 		it.v.Unreadable = errSwissMapBadGroupTypeErr
 | |
| 		return
 | |
| 	}
 | |
| 	for _, field := range slotType.Field {
 | |
| 		switch field.Name {
 | |
| 		case "key":
 | |
| 			it.slotFieldKey = field
 | |
| 		case "elem":
 | |
| 			it.slotFieldElem = field
 | |
| 		}
 | |
| 	}
 | |
| 	if it.slotFieldKey == nil || it.slotFieldElem == nil {
 | |
| 		it.v.Unreadable = errSwissMapBadGroupTypeErr
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if it.dirLen <= 0 {
 | |
| 		// small maps, convert it.dirPtr to be of type *group, then dereference it
 | |
| 		it.dirPtr.DwarfType = pointerTo(fakeArrayType(1, it.groupType), it.v.bi.Arch)
 | |
| 		it.dirPtr.RealType = it.dirPtr.DwarfType
 | |
| 		it.dirPtr = it.dirPtr.maybeDereference()
 | |
| 		it.dirLen = 1
 | |
| 		it.tab = &swissTable{groups: it.dirPtr} // so that we don't try to load this later on
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// normal map, convert it.dirPtr to be of type *[dirLen]*table, then dereference it
 | |
| 
 | |
| 	it.dirPtr.DwarfType = pointerTo(fakeArrayType(uint64(it.dirLen), tableptrtyp), it.v.bi.Arch)
 | |
| 	it.dirPtr.RealType = it.dirPtr.DwarfType
 | |
| 	it.dirPtr = it.dirPtr.maybeDereference()
 | |
| }
 | |
| 
 | |
| // derived from $GOROOT/src/internal/runtime/maps/table.go and $GOROOT/src/runtime/runtime-gdb.py
 | |
| func (it *mapIteratorSwiss) next() bool {
 | |
| 	if it.v.Unreadable != nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	for it.dirIdx < it.dirLen {
 | |
| 		if it.tab == nil {
 | |
| 			it.loadCurrentTable()
 | |
| 			if it.tab == nil {
 | |
| 				return false
 | |
| 			}
 | |
| 			if it.tab.index != it.dirIdx {
 | |
| 				it.nextTable()
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for ; it.groupIdx < uint64(it.tab.groups.Len); it.nextGroup() {
 | |
| 			if it.maxNumGroups > 0 && it.groupIdx+it.groupCount >= it.maxNumGroups {
 | |
| 				return false
 | |
| 			}
 | |
| 			if it.group == nil {
 | |
| 				it.loadCurrentGroup()
 | |
| 				if it.group == nil {
 | |
| 					return false
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			for ; it.slotIdx < uint32(it.group.slots.Len); it.slotIdx++ {
 | |
| 				if it.slotIsEmptyOrDeleted(it.slotIdx) {
 | |
| 					continue
 | |
| 				}
 | |
| 
 | |
| 				cur, err := it.group.slots.sliceAccess(int(it.slotIdx))
 | |
| 				if err != nil {
 | |
| 					it.v.Unreadable = fmt.Errorf("error accessing swiss map in table %d, group %d, slot %d", it.dirIdx, it.groupIdx, it.slotIdx)
 | |
| 					return false
 | |
| 				}
 | |
| 
 | |
| 				var err1, err2 error
 | |
| 				it.curKey, err1 = cur.toField(it.slotFieldKey)
 | |
| 				it.curValue, err2 = cur.toField(it.slotFieldElem)
 | |
| 				if err1 != nil || err2 != nil {
 | |
| 					it.v.Unreadable = fmt.Errorf("error accessing swiss map slot: %v %v", err1, err2)
 | |
| 					return false
 | |
| 				}
 | |
| 
 | |
| 				// If the type we expect is non-pointer but we read a pointer type it
 | |
| 				// means that the key (or the value) is stored indirectly into the map
 | |
| 				// because it is too big. We dereference it here so that the type of the
 | |
| 				// key (or value) matches the type on the map definition.
 | |
| 				if it.curKey.Kind == reflect.Ptr && !it.keyTypeIsPtr {
 | |
| 					it.curKey = it.curKey.maybeDereference()
 | |
| 				}
 | |
| 				if it.curValue.Kind == reflect.Ptr && !it.elemTypeIsPtr {
 | |
| 					it.curValue = it.curValue.maybeDereference()
 | |
| 				}
 | |
| 
 | |
| 				it.slotIdx++
 | |
| 				return true
 | |
| 			}
 | |
| 
 | |
| 			it.slotIdx = 0
 | |
| 		}
 | |
| 
 | |
| 		it.groupCount += it.groupIdx
 | |
| 		it.groupIdx = 0
 | |
| 		it.group = nil
 | |
| 		it.nextTable()
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorSwiss) nextTable() {
 | |
| 	it.dirIdx++
 | |
| 	it.tab = nil
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorSwiss) nextGroup() {
 | |
| 	it.groupIdx++
 | |
| 	it.group = nil
 | |
| }
 | |
| 
 | |
| // loadCurrentTable loads the table at index it.dirIdx into it.tab
 | |
| func (it *mapIteratorSwiss) loadCurrentTable() {
 | |
| 	tab, err := it.dirPtr.sliceAccess(int(it.dirIdx))
 | |
| 	if err != nil || tab == nil || tab.Unreadable != nil {
 | |
| 		it.v.Unreadable = errSwissTableCouldNotLoad
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	tab = tab.maybeDereference()
 | |
| 
 | |
| 	r := &swissTable{}
 | |
| 
 | |
| 	field, _ := tab.toField(it.tableFieldIndex)
 | |
| 	r.index, err = field.asInt()
 | |
| 	if err != nil {
 | |
| 		it.v.Unreadable = fmt.Errorf("could not load swiss table index: %v", err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	groups, _ := tab.toField(it.tableFieldGroups)
 | |
| 	r.groups, _ = groups.toField(it.groupsFieldData)
 | |
| 
 | |
| 	field, _ = groups.toField(it.groupsFieldLengthMask)
 | |
| 	groupsLengthMask, err := field.asUint()
 | |
| 	if err != nil {
 | |
| 		it.v.Unreadable = fmt.Errorf("could not load swiss table group lengthMask: %v", err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// convert the type of groups from *group to *[len]group so that it's easier to use
 | |
| 	r.groups.DwarfType = pointerTo(fakeArrayType(groupsLengthMask+1, it.groupType), it.v.bi.Arch)
 | |
| 	r.groups.RealType = r.groups.DwarfType
 | |
| 	r.groups = r.groups.maybeDereference()
 | |
| 
 | |
| 	if r.groups.Addr == 0 {
 | |
| 		it.v.Unreadable = errSwissTableNilGroups
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	it.tab = r
 | |
| }
 | |
| 
 | |
| // loadCurrentGroup loads the group at index it.groupIdx of it.tab into it.group
 | |
| func (it *mapIteratorSwiss) loadCurrentGroup() {
 | |
| 	group, err := it.tab.groups.sliceAccess(int(it.groupIdx))
 | |
| 	if err != nil {
 | |
| 		it.v.Unreadable = fmt.Errorf("could not load swiss map group: %v", err)
 | |
| 		return
 | |
| 	}
 | |
| 	g := &swissGroup{}
 | |
| 	g.slots, _ = group.toField(it.groupFieldSlots)
 | |
| 	ctrl, _ := group.toField(it.groupFieldCtrl)
 | |
| 	g.ctrls = make([]byte, ctrl.DwarfType.Size())
 | |
| 	_, err = ctrl.mem.ReadMemory(g.ctrls, ctrl.Addr)
 | |
| 	if err != nil {
 | |
| 		it.v.Unreadable = err
 | |
| 		return
 | |
| 	}
 | |
| 	it.group = g
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorSwiss) key() *Variable {
 | |
| 	return it.curKey
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorSwiss) value() *Variable {
 | |
| 	return it.curValue
 | |
| }
 | |
| 
 | |
| func (it *mapIteratorSwiss) slotIsEmptyOrDeleted(k uint32) bool {
 | |
| 	//TODO: check that this hasn't changed after it's merged and the TODO is deleted
 | |
| 	return it.group.ctrls[k]&swissTableCtrlEmpty == swissTableCtrlEmpty
 | |
| }
 |