Repository: go-delve/delve
Pull request #3144 (2022-09-21 10:16:29)

cmd/dlv: disable TestTraceMultipleGoroutines on FreeBSD
This test seems to fail frequently on FreeBSD.


derekparker: Do we know any kind of root cause?
aarzilli: I took a look at freebsd backend back in late july (hence go.dev/cl/419915) and there are multiple problems with the way trapWait and singleStep are implemented. It could be that, or it could be other problems in other parts of the backend. I intend to get back to it and fix it but I haven't had time to do it yet.

Patch:
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index 43055abf..284efbec 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -96,11 +96,6 @@ type BinaryInfo struct {
 
 	gStructOffset uint64
 
-	// nameOfRuntimeType maps an address of a runtime._type struct to its
-	// decoded name. Used with versions of Go <= 1.10 to figure out the DIE of
-	// the concrete type of interfaces.
-	nameOfRuntimeType map[uint64]nameOfRuntimeTypeEntry
-
 	// consts[off] lists all the constants with the type defined at offset off.
 	consts constantsMap
 
@@ -639,7 +634,7 @@ type ElfDynamicSection struct {
 
 // NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
 func NewBinaryInfo(goos, goarch string) *BinaryInfo {
-	r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uint64]nameOfRuntimeTypeEntry), logger: logflags.DebuggerLogger()}
+	r := &BinaryInfo{GOOS: goos, logger: logflags.DebuggerLogger()}
 
 	// TODO: find better way to determine proc arch (perhaps use executable file info).
 	switch goarch {
diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go
index 020ee83f..f01c4d2c 100644
--- a/pkg/proc/eval.go
+++ b/pkg/proc/eval.go
@@ -798,9 +798,9 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 	if err != nil {
 		switch targetTypeStr {
 		case "[]byte", "[]uint8":
-			styp = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "uint8"}, BitSize: 8, BitOffset: 0}})
+			styp = fakeSliceType(fakeBasicType("uint", 8))
 		case "[]int32", "[]rune":
-			styp = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "int32"}, BitSize: 32, BitOffset: 0}})
+			styp = fakeSliceType(fakeBasicType("int", 32))
 		default:
 			return nil, err
 		}
@@ -809,6 +809,13 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 
 	converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
 
+	// compatible underlying types
+	if typeCastCompatibleTypes(argv.RealType, typ) {
+		argv.RealType = typ
+		argv.DwarfType = styp
+		return argv, nil
+	}
+
 	v := newVariable("", 0, styp, scope.BinInfo, scope.Mem)
 	v.loaded = true
 
@@ -921,36 +928,39 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 		return nil, argv.Unreadable
 	}
 
-	switch targetTypeStr {
-	case "[]byte", "[]uint8":
-		if argv.Kind != reflect.String {
-			return nil, converr
-		}
-		for i, ch := range []byte(constant.StringVal(argv.Value)) {
-			e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
-			e.loaded = true
-			e.Value = constant.MakeInt64(int64(ch))
-			v.Children = append(v.Children, *e)
-		}
-		v.Len = int64(len(v.Children))
-		v.Cap = v.Len
-		return v, nil
+	switch ttyp := typ.(type) {
+	case *godwarf.SliceType:
+		switch ttyp.ElemType.Common().ReflectKind {
+		case reflect.Uint8:
+			if argv.Kind != reflect.String {
+				return nil, converr
+			}
+			for i, ch := range []byte(constant.StringVal(argv.Value)) {
+				e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
+				e.loaded = true
+				e.Value = constant.MakeInt64(int64(ch))
+				v.Children = append(v.Children, *e)
+			}
+			v.Len = int64(len(v.Children))
+			v.Cap = v.Len
+			return v, nil
 
-	case "[]int32", "[]rune":
-		if argv.Kind != reflect.String {
-			return nil, converr
-		}
-		for i, ch := range constant.StringVal(argv.Value) {
-			e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
-			e.loaded = true
-			e.Value = constant.MakeInt64(int64(ch))
-			v.Children = append(v.Children, *e)
+		case reflect.Int32:
+			if argv.Kind != reflect.String {
+				return nil, converr
+			}
+			for i, ch := range constant.StringVal(argv.Value) {
+				e := newVariable("", argv.Addr+uint64(i), typ.(*godwarf.SliceType).ElemType, scope.BinInfo, argv.mem)
+				e.loaded = true
+				e.Value = constant.MakeInt64(int64(ch))
+				v.Children = append(v.Children, *e)
+			}
+			v.Len = int64(len(v.Children))
+			v.Cap = v.Len
+			return v, nil
 		}
-		v.Len = int64(len(v.Children))
-		v.Cap = v.Len
-		return v, nil
 
-	case "string":
+	case *godwarf.StringType:
 		switch argv.Kind {
 		case reflect.String:
 			s := constant.StringVal(argv.Value)
@@ -1004,6 +1014,70 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 	return nil, converr
 }
 
+// typeCastCompatibleTypes returns true if typ1 and typ2 are compatible for
+// a type cast where only the type of the variable is changed.
+func typeCastCompatibleTypes(typ1, typ2 godwarf.Type) bool {
+	if typ1 == nil || typ2 == nil || typ1.Common().Size() != typ2.Common().Size() || typ1.Common().Align() != typ2.Common().Align() {
+		return false
+	}
+
+	if typ1.String() == typ2.String() {
+		return true
+	}
+
+	switch ttyp1 := typ1.(type) {
+	case *godwarf.PtrType:
+		if ttyp2, ok := typ2.(*godwarf.PtrType); ok {
+			// pointer types are compatible if their element types are compatible
+			return typeCastCompatibleTypes(resolveTypedef(ttyp1.Type), resolveTypedef(ttyp2.Type))
+		}
+	case *godwarf.StringType:
+		if _, ok := typ2.(*godwarf.StringType); ok {
+			return true
+		}
+	case *godwarf.StructType:
+		if ttyp2, ok := typ2.(*godwarf.StructType); ok {
+			// struct types are compatible if they have the same fields
+			if len(ttyp1.Field) != len(ttyp2.Field) {
+				return false
+			}
+			for i := range ttyp1.Field {
+				if *ttyp1.Field[i] != *ttyp2.Field[i] {
+					return false
+				}
+			}
+			return true
+		}
+	case *godwarf.ComplexType:
+		if _, ok := typ2.(*godwarf.ComplexType); ok {
+			// size and alignment already checked above
+			return true
+		}
+	case *godwarf.FloatType:
+		if _, ok := typ2.(*godwarf.FloatType); ok {
+			// size and alignment already checked above
+			return true
+		}
+	case *godwarf.IntType:
+		if _, ok := typ2.(*godwarf.IntType); ok {
+			// size and alignment already checked above
+			return true
+		}
+	case *godwarf.UintType:
+		if _, ok := typ2.(*godwarf.UintType); ok {
+			// size and alignment already checked above
+			return true
+		}
+	case *godwarf.BoolType:
+		if _, ok := typ2.(*godwarf.BoolType); ok {
+			// size and alignment already checked above
+			return true
+		}
+	}
+
+	return false
+}
+
 func convertInt(n uint64, signed bool, size int64) uint64 {
 	bits := uint64(size) * 8
 	mask := uint64((1 << bits) - 1)
@@ -1170,7 +1244,7 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 		sz = 128
 	}
 
-	typ := &godwarf.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
+	typ := fakeBasicType("complex", int(sz))
 
 	r := realev.newVariable("", 0, typ, nil)
 	r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
@@ -1250,7 +1324,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
 
 				var typ godwarf.Type
 				if len(reg.Bytes) <= 8 {
-					typ = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8, Name: "uint64"}, BitSize: 64, BitOffset: 0}}
+					typ = fakeBasicType("uint", 64)
 				} else {
 					typ, err = scope.BinInfo.findType("string")
 					if err != nil {
@@ -2231,6 +2305,36 @@ func functionToVariable(fn *Function, bi *BinaryInfo, mem MemoryReadWriter) (*Va
 	return v, nil
 }
 
+func fakeBasicType(name string, bitSize int) godwarf.Type {
+	byteSize := bitSize / 8
+	szr := popcnt(uint64(byteSize^(byteSize-1))) - 1 // position of rightmost 1 bit, minus 1
+
+	basic := func(kind reflect.Kind) godwarf.BasicType {
+		return godwarf.BasicType{
+			CommonType: godwarf.CommonType{
+				ByteSize:    int64(byteSize),
+				Name:        fmt.Sprintf("%s%d", name, bitSize),
+				ReflectKind: kind,
+			},
+			BitSize:   int64(bitSize),
+			BitOffset: 0,
+		}
+	}
+
+	switch name {
+	case "int":
+		return &godwarf.IntType{BasicType: basic(reflect.Int8 + reflect.Kind(szr))}
+	case "uint":
+		return &godwarf.UintType{BasicType: basic(reflect.Uint8 + reflect.Kind(szr))}
+	case "float":
+		return &godwarf.FloatType{BasicType: basic(reflect.Float32 + reflect.Kind(szr-2))}
+	case "complex":
+		return &godwarf.ComplexType{BasicType: basic(reflect.Complex64 + reflect.Kind(szr-3))}
+	default:
+		panic("unsupported")
+	}
+}
+
 func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
 	return &godwarf.SliceType{
 		StructType: godwarf.StructType{
diff --git a/pkg/proc/moduledata.go b/pkg/proc/moduledata.go
index 65c02ed8..3dab0865 100644
--- a/pkg/proc/moduledata.go
+++ b/pkg/proc/moduledata.go
@@ -1,10 +1,5 @@
 package proc
 
-import (
-	"go/constant"
-	"unsafe"
-)
-
 // delve counterpart to runtime.moduledata
 type moduleData struct {
 	text, etext   uint64
@@ -83,127 +78,3 @@ func findModuleDataForType(bi *BinaryInfo, mds []moduleData, typeAddr uint64, me
 	}
 	return nil
 }
-
-func resolveTypeOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (*Variable, error) {
-	// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
-	md := findModuleDataForType(bi, mds, typeAddr, mem)
-
-	rtyp, err := bi.findType("runtime._type")
-	if err != nil {
-		return nil, err
-	}
-
-	if md == nil {
-		v, err := reflectOffsMapAccess(bi, off, mem)
-		if err != nil {
-			return nil, err
-		}
-		v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
-		addr, _ := constant.Int64Val(v.Value)
-		return v.newVariable(v.Name, uint64(addr), rtyp, mem), nil
-	}
-
-	if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
-		return t, nil
-	}
-
-	res := md.types + off
-
-	return newVariable("", uint64(res), rtyp, bi, mem), nil
-}
-
-func resolveNameOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
-	// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
-	for _, md := range mds {
-		if typeAddr >= md.types && typeAddr < md.etypes {
-			return loadName(bi, md.types+off, mem)
-		}
-	}
-
-	v, err := reflectOffsMapAccess(bi, off, mem)
-	if err != nil {
-		return "", "", 0, err
-	}
-
-	resv := v.maybeDereference()
-	if resv.Unreadable != nil {
-		return "", "", 0, resv.Unreadable
-	}
-
-	return loadName(bi, resv.Addr, mem)
-}
-
-func reflectOffsMapAccess(bi *BinaryInfo, off uint64, mem MemoryReadWriter) (*Variable, error) {
-	scope := globalScope(nil, bi, bi.Images[0], mem)
-	reflectOffs, err := scope.findGlobal("runtime", "reflectOffs")
-	if err != nil {
-		return nil, err
-	}
-
-	reflectOffsm, err := reflectOffs.structMember("m")
-	if err != nil {
-		return nil, err
-	}
-
-	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
-}
-
-const (
-	// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
-	nameflagExported = 1 << 0
-	nameflagHasTag   = 1 << 1
-	nameflagHasPkg   = 1 << 2
-)
-
-func loadName(bi *BinaryInfo, addr uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
-	off := addr
-	namedata := make([]byte, 3)
-	_, err = mem.ReadMemory(namedata, off)
-	off += 3
-	if err != nil {
-		return "", "", 0, err
-	}
-
-	namelen := uint16(namedata[1])<<8 | uint16(namedata[2])
-
-	rawstr := make([]byte, int(namelen))
-	_, err = mem.ReadMemory(rawstr, off)
-	off += uint64(namelen)
-	if err != nil {
-		return "", "", 0, err
-	}
-
-	name = string(rawstr)
-
-	if namedata[0]&nameflagHasTag != 0 {
-		taglendata := make([]byte, 2)
-		_, err = mem.ReadMemory(taglendata, off)
-		off += 2
-		if err != nil {
-			return "", "", 0, err
-		}
-		taglen := uint16(taglendata[0])<<8 | uint16(taglendata[1])
-
-		rawstr := make([]byte, int(taglen))
-		_, err = mem.ReadMemory(rawstr, off)
-		off += uint64(taglen)
-		if err != nil {
-			return "", "", 0, err
-		}
-
-		tag = string(rawstr)
-	}
-
-	if namedata[0]&nameflagHasPkg != 0 {
-		pkgdata := make([]byte, 4)
-		_, err = mem.ReadMemory(pkgdata, off)
-		if err != nil {
-			return "", "", 0, err
-		}
-
-		// see func pkgPath in $GOROOT/src/reflect/type.go
-		copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
-	}
-
-	return name, tag, pkgpathoff, nil
-}
diff --git a/pkg/proc/types.go b/pkg/proc/types.go
index 1cc1d6be..e9c72f0c 100644
--- a/pkg/proc/types.go
+++ b/pkg/proc/types.go
@@ -1,17 +1,14 @@
 package proc
 
 import (
-	"bytes"
 	"debug/dwarf"
 	"errors"
 	"fmt"
 	"go/constant"
 	"reflect"
-	"strings"
 
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 	"github.com/go-delve/delve/pkg/dwarf/reader"
-	"github.com/go-delve/delve/pkg/goversion"
 )
 
 // The kind field in runtime._type is a reflect.Kind value plus
@@ -24,24 +21,6 @@ const (
 	kindMask        = (1 << 5) - 1 // +rtype kindMask
 )
 
-// Value of tflag field in runtime._type.
-// See $GOROOT/reflect/type.go for a description of these flags.
-const (
-	tflagUncommon  = 1 << 0
-	tflagExtraStar = 1 << 1
-	tflagNamed     = 1 << 2
-)
-
-// These constants contain the names of the fields of runtime.interfacetype
-// and runtime.imethod.
-// runtime.interfacetype.mhdr is a slice of runtime.imethod describing the
-// methods of the interface.
-const (
-	imethodFieldName       = "name"
-	imethodFieldItyp       = "ityp"
-	interfacetypeFieldMhdr = "mhdr"
-)
-
 type runtimeTypeDIE struct {
 	offset dwarf.Offset
 	kind   int64
@@ -150,28 +129,7 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uint64) (typ godwarf.Type, kind
 		}
 	}
 
-	// go1.7 to go1.10 implementation: convert runtime._type structs to type names
-
-	if goversion.ProducerAfterOrEqual(_type.bi.Producer(), 1, 17) {
-		// Go 1.17 changed the encoding of names in runtime._type breaking the
-		// code below, but the codepath above, using runtimeTypeToDIE should be
-		// enough.
-		// The change happened in commit 287025925f66f90ad9b30aea2e533928026a8376
-		// reviewed in https://go-review.googlesource.com/c/go/+/318249
-		return nil, 0, fmt.Errorf("could not resolve interface type")
-	}
-
-	typename, kind, err := nameOfRuntimeType(mds, _type)
-	if err != nil {
-		return nil, 0, fmt.Errorf("invalid interface type: %v", err)
-	}
-
-	typ, err = bi.findType(typename)
-	if err != nil {
-		return nil, 0, fmt.Errorf("interface type %q not found for %#x: %v", typename, dataAddr, err)
-	}
-
-	return typ, kind, nil
+	return nil, 0, fmt.Errorf("could not resolve interface type")
 }
 
 // resolveParametricType returns the real type of t if t is a parametric
@@ -202,449 +160,6 @@ func resolveParametricType(tgt *Target, bi *BinaryInfo, mem MemoryReadWriter, t
 	return typ, nil
 }
 
-type nameOfRuntimeTypeEntry struct {
-	typename string
-	kind     int64
-}
-
-// Returns the type name of the type described in _type.
-// _type is a non-loaded Variable pointing to runtime._type struct in the target.
-// The returned string is in the format that's used in DWARF data
-func nameOfRuntimeType(mds []moduleData, _type *Variable) (typename string, kind int64, err error) {
-	if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok {
-		return e.typename, e.kind, nil
-	}
-
-	var tflag int64
-
-	if tflagField := _type.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
-		tflag, _ = constant.Int64Val(tflagField.Value)
-	}
-	if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil {
-		kind, _ = constant.Int64Val(kindField.Value)
-	}
-
-	// Named types are defined by a 'type' expression, everything else
-	// (for example pointers to named types) are not considered named.
-	if tflag&tflagNamed != 0 {
-		typename, err = nameOfNamedRuntimeType(mds, _type, kind, tflag)
-		if err == nil {
-			_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
-		}
-		return typename, kind, err
-	}
-
-	typename, err = nameOfUnnamedRuntimeType(mds, _type, kind, tflag)
-	if err == nil {
-		_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
-	}
-	return typename, kind, err
-}
-
-// The layout of a runtime._type struct is as follows:
-//
-// <runtime._type><kind specific struct fields><runtime.uncommontype>
-//
-// with the 'uncommon type struct' being optional
-//
-// For named types first we extract the type name from the 'str'
-// field in the runtime._type struct.
-// Then we prepend the package path from the runtime.uncommontype
-// struct, when it exists.
-//
-// To find out the memory address of the runtime.uncommontype struct
-// we first cast the Variable pointing to the runtime._type struct
-// to a struct specific to the type's kind (for example, if the type
-// being described is a slice type the variable will be specialized
-// to a runtime.slicetype).
-func nameOfNamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (typename string, err error) {
-	var strOff int64
-	if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
-		strOff, _ = constant.Int64Val(strField.Value)
-	} else {
-		return "", errors.New("could not find str field")
-	}
-
-	// The following code is adapted from reflect.(*rtype).Name.
-	// For a description of how memory is organized for type names read
-	// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
-
-	typename, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uint64(strOff), _type.mem)
-	if err != nil {
-		return "", err
-	}
-
-	if tflag&tflagExtraStar != 0 {
-		typename = typename[1:]
-	}
-
-	if i := strings.Index(typename, "."); i >= 0 {
-		typename = typename[i+1:]
-	} else {
-		return typename, nil
-	}
-
-	// The following code is adapted from reflect.(*rtype).PkgPath in
-	// $GOROOT/src/reflect/type.go
-
-	_type, err = specificRuntimeType(_type, kind)
-	if err != nil {
-		return "", err
-	}
-
-	if ut := uncommon(_type, tflag); ut != nil {
-		if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
-			pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
-			pkgPath, _, _, err := resolveNameOff(_type.bi, mds, _type.Addr, uint64(pkgPathOff), _type.mem)
-			if err != nil {
-				return "", err
-			}
-			if slash := strings.LastIndex(pkgPath, "/"); slash >= 0 {
-				fixedName := strings.Replace(pkgPath[slash+1:], ".", "%2e", -1)
-				if fixedName != pkgPath[slash+1:] {
-					pkgPath = pkgPath[:slash+1] + fixedName
-				}
-			}
-			typename = pkgPath + "." + typename
-		}
-	}
-
-	return typename, nil
-}
-
-func nameOfUnnamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
-	_type, err := specificRuntimeType(_type, kind)
-	if err != nil {
-		return "", err
-	}
-
-	// The types referred to here are defined in $GOROOT/src/runtime/type.go
-	switch reflect.Kind(kind & kindMask) {
-	case reflect.Array:
-		var len int64
-		if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
-			len, _ = constant.Int64Val(lenField.Value)
-		}
-		elemname, err := fieldToType(mds, _type, "elem")
-		if err != nil {
-			return "", err
-		}
-		return fmt.Sprintf("[%d]%s", len, elemname), nil
-	case reflect.Chan:
-		elemname, err := fieldToType(mds, _type, "elem")
-		if err != nil {
-			return "", err
-		}
-		return "chan " + elemname, nil
-	case reflect.Func:
-		return nameOfFuncRuntimeType(mds, _type, tflag, true)
-	case reflect.Interface:
-		return nameOfInterfaceRuntimeType(mds, _type, kind, tflag)
-	case reflect.Map:
-		keyname, err := fieldToType(mds, _type, "key")
-		if err != nil {
-			return "", err
-		}
-		elemname, err := fieldToType(mds, _type, "elem")
-		if err != nil {
-			return "", err
-		}
-		return "map[" + keyname + "]" + elemname, nil
-	case reflect.Ptr:
-		elemname, err := fieldToType(mds, _type, "elem")
-		if err != nil {
-			return "", err
-		}
-		return "*" + elemname, nil
-	case reflect.Slice:
-		elemname, err := fieldToType(mds, _type, "elem")
-		if err != nil {
-			return "", err
-		}
-		return "[]" + elemname, nil
-	case reflect.Struct:
-		return nameOfStructRuntimeType(mds, _type, kind, tflag)
-	default:
-		return nameOfNamedRuntimeType(mds, _type, kind, tflag)
-	}
-}
-
-// Returns the expression describing an anonymous function type.
-// A runtime.functype is followed by a runtime.uncommontype
-// (optional) and then by an array of pointers to runtime._type,
-// one for each input and output argument.
-func nameOfFuncRuntimeType(mds []moduleData, _type *Variable, tflag int64, anonymous bool) (string, error) {
-	rtyp, err := _type.bi.findType("runtime._type")
-	if err != nil {
-		return "", err
-	}
-	prtyp := pointerTo(rtyp, _type.bi.Arch)
-
-	uadd := _type.RealType.Common().ByteSize
-	if ut := uncommon(_type, tflag); ut != nil {
-		uadd += ut.RealType.Common().ByteSize
-	}
-
-	var inCount, outCount int64
-	if inCountField := _type.loadFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
-		inCount, _ = constant.Int64Val(inCountField.Value)
-	}
-	if outCountField := _type.loadFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
-		outCount, _ = constant.Int64Val(outCountField.Value)
-		// only the lowest 15 bits of outCount are used, rest are flags
-		outCount = outCount & (1<<15 - 1)
-	}
-
-	cursortyp := _type.newVariable("", _type.Addr+uint64(uadd), prtyp, _type.mem)
-	var buf bytes.Buffer
-	if anonymous {
-		buf.WriteString("func(")
-	} else {
-		buf.WriteString("(")
-	}
-
-	for i := int64(0); i < inCount; i++ {
-		argtype := cursortyp.maybeDereference()
-		cursortyp.Addr += uint64(_type.bi.Arch.PtrSize())
-		argtypename, _, err := nameOfRuntimeType(mds, argtype)
-		if err != nil {
-			return "", err
-		}
-		buf.WriteString(argtypename)
-		if i != inCount-1 {
-			buf.WriteString(", ")
-		}
-	}
-	buf.WriteString(")")
-
-	switch outCount {
-	case 0:
-		// nothing to do
-	case 1:
-		buf.WriteString(" ")
-		argtype := cursortyp.maybeDereference()
-		argtypename, _, err := nameOfRuntimeType(mds, argtype)
-		if err != nil {
-			return "", err
-		}
-		buf.WriteString(argtypename)
-	default:
-		buf.WriteString(" (")
-		for i := int64(0); i < outCount; i++ {
-			argtype := cursortyp.maybeDereference()
-			cursortyp.Addr += uint64(_type.bi.Arch.PtrSize())
-			argtypename, _, err := nameOfRuntimeType(mds, argtype)
-			if err != nil {
-				return "", err
-			}
-			buf.WriteString(argtypename)
-			if i != inCount-1 {
-				buf.WriteString(", ")
-			}
-		}
-		buf.WriteString(")")
-	}
-	return buf.String(), nil
-}
-
-func nameOfInterfaceRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
-	var buf bytes.Buffer
-	buf.WriteString("interface {")
-
-	methods, _ := _type.structMember(interfacetypeFieldMhdr)
-	methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1, 0})
-	if methods.Unreadable != nil {
-		return "", nil
-	}
-
-	if len(methods.Children) == 0 {
-		buf.WriteString("}")
-		return buf.String(), nil
-	}
-	buf.WriteString(" ")
-
-	for i, im := range methods.Children {
-		var methodname, methodtype string
-		for i := range im.Children {
-			switch im.Children[i].Name {
-			case imethodFieldName:
-				nameoff, _ := constant.Int64Val(im.Children[i].Value)
-				var err error
-				methodname, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uint64(nameoff), _type.mem)
-				if err != nil {
-					return "", err
-				}
-
-			case imethodFieldItyp:
-				typeoff, _ := constant.Int64Val(im.Children[i].Value)
-				typ, err := resolveTypeOff(_type.bi, mds, _type.Addr, uint64(typeoff), _type.mem)
-				if err != nil {
-					return "", err
-				}
-				typ, err = specificRuntimeType(typ, int64(reflect.Func))
-				if err != nil {
-					return "", err
-				}
-				var tflag int64
-				if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
-					tflag, _ = constant.Int64Val(tflagField.Value)
-				}
-				methodtype, err = nameOfFuncRuntimeType(mds, typ, tflag, false)
-				if err != nil {
-					return "", err
-				}
-			}
-		}
-
-		buf.WriteString(methodname)
-		buf.WriteString(methodtype)
-
-		if i != len(methods.Children)-1 {
-			buf.WriteString("; ")
-		} else {
-			buf.WriteString(" }")
-		}
-	}
-	return buf.String(), nil
-}
-
-func nameOfStructRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
-	var buf bytes.Buffer
-	buf.WriteString("struct {")
-
-	fields, _ := _type.structMember("fields")
-	fields.loadArrayValues(0, LoadConfig{false, 2, 0, 4096, -1, 0})
-	if fields.Unreadable != nil {
-		return "", fields.Unreadable
-	}
-
-	if len(fields.Children) == 0 {
-		buf.WriteString("}")
-		return buf.String(), nil
-	}
-	buf.WriteString(" ")
-
-	for i, field := range fields.Children {
-		var fieldname, fieldtypename string
-		var typeField *Variable
-		isembed := false
-		for i := range field.Children {
-			switch field.Children[i].Name {
-			case "name":
-				var nameoff int64
-				switch field.Children[i].Kind {
-				case reflect.Struct:
-					nameoff = int64(field.Children[i].fieldVariable("bytes").Children[0].Addr)
-				default:
-					nameoff, _ = constant.Int64Val(field.Children[i].Value)
-				}
-
-				var err error
-				fieldname, _, _, err = loadName(_type.bi, uint64(nameoff), _type.mem)
-				if err != nil {
-					return "", err
-				}
-
-			case "typ":
-				typeField = field.Children[i].maybeDereference()
-				var err error
-				fieldtypename, _, err = nameOfRuntimeType(mds, typeField)
-				if err != nil {
-					return "", err
-				}
-
-			case "offsetAnon":
-				// The offsetAnon field of runtime.structfield combines the offset of
-				// the struct field from the base address of the struct with a flag
-				// determining whether the field is anonymous (i.e. an embedded struct).
-				//
-				//  offsetAnon = (offset<<1) | (anonFlag)
-				//
-				// Here we are only interested in the anonymous flag.
-				offsetAnon, _ := constant.Int64Val(field.Children[i].Value)
-				isembed = offsetAnon%2 != 0
-			}
-		}
-
-		// fieldname will be the empty string for anonymous fields
-		if fieldname != "" && !isembed {
-			buf.WriteString(fieldname)
-			buf.WriteString(" ")
-		}
-		buf.WriteString(fieldtypename)
-		if i != len(fields.Children)-1 {
-			buf.WriteString("; ")
-		} else {
-			buf.WriteString(" }")
-		}
-	}
-
-	return buf.String(), nil
-}
-
-func fieldToType(mds []moduleData, _type *Variable, fieldName string) (string, error) {
-	typeField, err := _type.structMember(fieldName)
-	if err != nil {
-		return "", err
-	}
-	typeField = typeField.maybeDereference()
-	typename, _, err := nameOfRuntimeType(mds, typeField)
-	return typename, err
-}
-
-func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
-	typ, err := typeForKind(kind, _type.bi)
-	if err != nil {
-		return nil, err
-	}
-	if typ == nil {
-		return _type, nil
-	}
-
-	return _type.newVariable(_type.Name, _type.Addr, typ, _type.mem), nil
-}
-
-// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
-func uncommon(_type *Variable, tflag int64) *Variable {
-	if tflag&tflagUncommon == 0 {
-		return nil
-	}
-
-	typ, err := _type.bi.findType("runtime.uncommontype")
-	if err != nil {
-		return nil
-	}
-
-	return _type.newVariable(_type.Name, _type.Addr+uint64(_type.RealType.Size()), typ, _type.mem)
-}
-
-var kindToRuntimeTypeName = map[reflect.Kind]string{
-	reflect.Array:     "runtime.arraytype",
-	reflect.Chan:      "runtime.chantype",
-	reflect.Func:      "runtime.functype",
-	reflect.Interface: "runtime.interfacetype",
-	reflect.Map:       "runtime.maptype",
-	reflect.Ptr:       "runtime.ptrtype",
-	reflect.Slice:     "runtime.slicetype",
-	reflect.Struct:    "runtime.structtype",
-}
-
-// typeForKind returns a *dwarf.StructType describing the specialization of
-// runtime._type for the specified type kind. For example if kind is
-// reflect.ArrayType it will return runtime.arraytype
-func typeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, error) {
-	typename, ok := kindToRuntimeTypeName[reflect.Kind(kind&kindMask)]
-	if !ok {
-		return nil, nil
-	}
-	typ, err := bi.findType(typename)
-	if err != nil {
-		return nil, err
-	}
-	typ = resolveTypedef(typ)
-	return typ.(*godwarf.StructType), nil
-}
-
 func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) (typeAddr uint64, typeKind uint64, found bool, err error) {
 	so := bi.typeToImage(typ)
 	rdr := so.DwarfReader()
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index 5c48a14e..9b6a70d1 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -666,7 +666,7 @@ func newVariable(name string, addr uint64, dwarfType godwarf.Type, bi *BinaryInf
 	case *godwarf.StringType:
 		v.Kind = reflect.String
 		v.stride = 1
-		v.fieldType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
+		v.fieldType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "byte", ReflectKind: reflect.Uint8}, BitSize: 8, BitOffset: 0}}
 		if v.Addr != 0 {
 			v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.Arch, v.Addr)
 		}
@@ -1665,7 +1665,7 @@ func (v *Variable) readComplex(size int64) {
 		return
 	}
 
-	ftyp := &godwarf.FloatType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: fs, Name: fmt.Sprintf("float%d", fs)}, BitSize: fs * 8, BitOffset: 0}}
+	ftyp := fakeBasicType("float", int(fs*8))
 
 	realvar := v.newVariable("real", v.Addr, ftyp, v.mem)
 	imagvar := v.newVariable("imaginary", v.Addr+uint64(fs), ftyp, v.mem)


Test patch:
diff --git a/_fixtures/testvariables2.go b/_fixtures/testvariables2.go
index a0faf81a..5ee588e2 100644
--- a/_fixtures/testvariables2.go
+++ b/_fixtures/testvariables2.go
@@ -10,12 +10,16 @@ import (
 )
 
 type Byte byte
+type String string
 
 type astruct struct {
 	A int
 	B int
 }
 
+type astructName1 astruct
+type astructName2 astruct
+
 type bstruct struct {
 	a astruct
 }
@@ -363,6 +367,9 @@ func main() {
 	tim1 := time.Unix(233431200, 0)
 	loc, _ := time.LoadLocation("Mexico/BajaSur")
 	tim2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2022-06-07 02:03:04", loc)
+	typedstringvar := String("blah")
+	namedA1 := astructName1{12, 45}
+	namedA2 := astructName2{13, 46}
 
 	var amb1 = 1
 	runtime.Breakpoint()
@@ -374,5 +381,5 @@ func main() {
 	longslice := make([]int, 100, 100)
 
 	runtime.Breakpoint()
-	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, rettm, errtypednil, emptyslice, emptymap, byteslice, bytestypeslice, runeslice, bytearray, bytetypearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl, tim1, tim2)
+	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, rettm, errtypednil, emptyslice, emptymap, byteslice, bytestypeslice, runeslice, bytearray, bytetypearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl, tim1, tim2, typedstringvar, namedA1, namedA2, astructName1(namedA2))
 }
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 9c4efd4d..562ade5e 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -1021,6 +1021,11 @@ func TestTrace(t *testing.T) {
 }
 
 func TestTraceMultipleGoroutines(t *testing.T) {
+	if runtime.GOOS == "freebsd" {
+		//TODO(aarzilli): investigate further when the FreeBSD backend is more stable.
+		t.Skip("temporarily disabled due to issues with FreeBSD in Delve and Go")
+	}
+
 	dlvbin, tmpdir := getDlvBin(t)
 	defer os.RemoveAll(tmpdir)
 
diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go
index ac76f625..22cc223e 100644
--- a/pkg/proc/variables_test.go
+++ b/pkg/proc/variables_test.go
@@ -805,6 +805,21 @@ func TestEvalExpression(t *testing.T) {
 		// issue #3138 - typecast to *interface{} breaking
 		{`*(*interface {})(uintptr(&iface1))`, false, `interface {}(*main.astruct) *{A: 1, B: 2}`, `interface {}(*main.astruct)…`, "interface {}", nil},
 		{`*(*struct {})(uintptr(&zsvar))`, false, `struct {} {}`, `struct {} {}`, "struct {}", nil},
+
+		// issue #3130 - typecasts that are only valid because of underlying type compatibility
+		{`main.Byte(bytearray[0])`, false, `116`, `116`, "main.Byte", nil},
+		{`uint8(bytetypearray[0])`, false, `116`, `116`, "uint8", nil},
+		{`main.Byte(2)`, false, `2`, `2`, "main.Byte", nil},
+		{`main.String(s1[0])`, false, `"one"`, `"one"`, "main.String", nil},
+		{`string(typedstringvar)`, false, `"blah"`, `"blah"`, "string", nil},
+		{`main.String("test")`, false, `"test"`, `"test"`, "main.String", nil},
+		{`main.astruct(namedA1)`, false, `main.astruct {A: 12, B: 45}`, `main.astruct {A: 12, B: 45}`, "main.astruct", nil},
+		{`main.astructName2(namedA1)`, false, `main.astructName2 {A: 12, B: 45}`, `main.astructName2 {A: 12, B: 45}`, "main.astructName2", nil},
+		{`(*main.Byte)(&bytearray[0])`, false, `(*main.Byte)(…`, `(*main.Byte)(…`, "*main.Byte", nil},
+		{`(*main.String)(&s1[0])`, false, `(*main.String)(…`, `(*main.String)(…`, "*main.String", nil},
+		{`(*string)(&typedstringvar)`, false, `(*string)(…`, `(*string)(…`, "*string", nil},
+		{`(*main.astruct)(&namedA1)`, false, `(*main.astruct)(…`, `(*main.astruct)(…`, "*main.astruct", nil},
+		{`(*main.astructName2)(&namedA1)`, false, `(*main.astructName2)(…`, `(*main.astructName2)(…`, "*main.astructName2", nil},
 	}
 
 	ver, _ := goversion.Parse(runtime.Version())
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index 8d67f8d0..5615d428 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -3903,7 +3903,7 @@ func TestEvaluateRequest(t *testing.T) {
 					// Type casts between string, []byte and []rune
 					client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored")
 					got = client.ExpectEvaluateResponse(t)
-					checkEvalIndexed(t, got, "[]uint8 len: 6, cap: 6, [65,66,67,226,130,172]", noChildren, 6, 0)
+					checkEvalIndexed(t, got, "[]uint8 len: 6, cap: 6, [65,66,67,226,130,172]", noChildren, 6, 1)
 
 					// Struct member access (i.e. somevar.memberfield)
 					client.EvaluateRequest("ms.Nest.Level", 1000, "this context will be ignored")