Repository: go-delve/delve

Issue:
`unreadable could not resolve interface type` value for likely optimized out variables
Delve reports that it can't resolve empty interface typed variable starting with go1.25rc1 if it has been assigned with an empty struct. It seems that the compiler started optimizing them out recently. It has been working fine before, checked with go1.24. It still works as expected if the value's struct type has some fields though.

Delve version: 1.25.1

#### Steps to reproduce:

1. Create a new empty go module with following main.go file:
```go
package main

type T struct{}

func main() {
	var i interface{} = T{}
	println(i) //break
}
```
2. Start debugging: `$ dlv debug .`
2. Set a breakpoint at the line 7 (where the comment is) : `(dlv) break main:go:7`
3. Continue the program: `(dlv) continue`
4. Request list of local variables: `(dlv) locals`

#### Expected result: (reproducible with go1.24)
`i = interface {}(main.T) {}`

#### Actual result: (repoducible with go1.25rc1 and go1.25rc2)
`i = (unreadable could not resolve interface type)`

##### Example session:
```sh
user@MacBookPro ~/G/awesomeProject110> dlv version
Delve Debugger
Version: 1.25.1
Build: $Id: 4e95e55b6b38b12e8509c91ec55261df1f7ee38f $
nikita.mironov@MacBookPro ~/G/awesomeProject110> go version
go version go1.25rc2 darwin/arm64
nikita.mironov@MacBookPro ~/G/awesomeProject110> dlv debug .
Type 'help' for list of commands.
(dlv) break bp1 main.go:7
Breakpoint bp1 set at 0x104a2ddb0 for main.main() ./main.go:7
(dlv) continue
> [bp1] main.main() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x104a2ddb0)
     2: 
     3: type T struct{} //position p
     4: 
     5: func main() {
     6:         var i interface{} = T{}
=>   7:         println(i) //break
     8: }
(dlv) locals
i = (unreadable could not resolve interface type)
(dlv) 
```


Discussion:
@aarzilli: CL: https://go-review.googlesource.com/c/go/+/696955

Solution patch:
diff --git a/.github/workflows/test-windows-arm64.yml b/.github/workflows/test-windows-arm64.yml
new file mode 100644
index 00000000..73982215
--- /dev/null
+++ b/.github/workflows/test-windows-arm64.yml
@@ -0,0 +1,29 @@
+name: Test Windows ARM64
+
+on:
+  push:
+  pull_request:
+    branches: [master]
+
+# Cancel existing runs if user makes another push.
+concurrency:
+  group: "${{ github.ref }}-${{ github.workflow}}"
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+  test-windows-arm64:
+    runs-on: windows-11-arm
+    timeout-minutes: 90
+
+    strategy:
+      fail-fast: false
+      matrix:
+        go-version: ["1.24", "1.25", "tip"]
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v5
+
+      - name: Run tests
+        run: _scripts/test_windows.ps1 -version go${{ matrix.go-version }} -arch arm64 -binDir $env:RUNNER_TOOL_CACHE
+        continue-on-error: ${{ matrix.go-version == 'tip' }}
diff --git a/Documentation/backend_test_health.md b/Documentation/backend_test_health.md
index 19b468eb..9e9bd550 100644
--- a/Documentation/backend_test_health.md
+++ b/Documentation/backend_test_health.md
@@ -52,7 +52,6 @@ Tests skipped by each supported backend:
 	* 1 broken
 	* 2 not working on windows
 	* 6 see https://github.com/go-delve/delve/issues/2768
-* windows/arm64 skipped = 5
-	* 3 broken
-	* 1 broken - cgo stacktraces
-	* 1 broken - step concurrent
+* windows/arm64 skipped = 3
+	* 2 broken - cgo stacktraces
+	* 1 flaky
diff --git a/_scripts/capslock_windows_arm64-output.txt b/_scripts/capslock_windows_arm64-output.txt
new file mode 100644
index 00000000..5d6e6ae6
--- /dev/null
+++ b/_scripts/capslock_windows_arm64-output.txt
@@ -0,0 +1,40 @@
+Capslock is an experimental tool for static analysis of Go packages.
+Share feedback and file bugs at https://github.com/google/capslock.
+For additional debugging signals, use verbose mode with -output=verbose
+To get machine-readable full analysis output, use -output=json
+
+Analyzed packages:
+  github.com/cosiner/argv v0.1.0
+  github.com/cpuguy83/go-md2man/v2 v2.0.6
+  github.com/derekparker/trie/v3 v3.2.0
+  github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62
+  github.com/google/go-dap v0.12.0
+  github.com/hashicorp/golang-lru/v2 v2.0.7
+  github.com/inconshreveable/mousetrap v1.1.0
+  github.com/mattn/go-colorable v0.1.13
+  github.com/mattn/go-isatty v0.0.20
+  github.com/mattn/go-runewidth v0.0.13
+  github.com/rivo/uniseg v0.2.0
+  github.com/russross/blackfriday/v2 v2.1.0
+  github.com/spf13/cobra v1.9.1
+  github.com/spf13/pflag v1.0.6
+  go.starlark.net v0.0.0-20231101134539-556fd59b42f6
+  golang.org/x/arch v0.11.0
+  golang.org/x/sync v0.8.0
+  golang.org/x/sys v0.26.0
+  golang.org/x/telemetry v0.0.0-20241106142447-58a1122356f5
+  gopkg.in/yaml.v3 v3.0.1
+
+CAPABILITY_ARBITRARY_EXECUTION: 2 references
+CAPABILITY_EXEC: 2 references
+CAPABILITY_FILES: 2 references
+CAPABILITY_MODIFY_SYSTEM_STATE: 1 references
+CAPABILITY_NETWORK: 1 references
+CAPABILITY_OPERATING_SYSTEM: 1 references
+CAPABILITY_READ_SYSTEM_STATE: 2 references
+CAPABILITY_REFLECT: 2 references
+CAPABILITY_RUNTIME: 1 references
+CAPABILITY_SYSTEM_CALLS: 2 references
+CAPABILITY_UNANALYZED: 2 references
+CAPABILITY_UNSAFE_POINTER: 2 references
+
diff --git a/_scripts/gen-capslock-all.go b/_scripts/gen-capslock-all.go
index 8a170d6c..4e5d0688 100644
--- a/_scripts/gen-capslock-all.go
+++ b/_scripts/gen-capslock-all.go
@@ -26,6 +26,7 @@ var platforms = []Platform{
 	{GOOS: "darwin", GOARCH: "amd64"},
 	{GOOS: "darwin", GOARCH: "arm64"},
 	{GOOS: "windows", GOARCH: "amd64"},
+	{GOOS: "windows", GOARCH: "arm64", BuildTags: "exp.winarm64"},
 }
 
 const (
diff --git a/_scripts/rtype.go b/_scripts/rtype.go
index 04dc36d4..6f1aae7c 100644
--- a/_scripts/rtype.go
+++ b/_scripts/rtype.go
@@ -37,14 +37,14 @@
 // 	all calls to:
 //		v.loadFieldNamed
 //		v.fieldVariable
-//		v.structMember
+//		v.structField
 //	and check that type T has the specified fields.
 //
 // v.loadFieldNamed("F") // +rtype T
 // v.loadFieldNamed("F") // +rtype -opt T
 //
 // 	checks that field F of the struct type declared for v has type T. Can
-// 	also be used for fieldVariable, structMember and, inside parseG,
+// 	also be used for fieldVariable, structField and, inside parseG,
 // 	loadInt64Maybe.
 // 	The -opt flag specifies that the field can be missing (but if it exists
 // 	it must have type T).
@@ -439,7 +439,7 @@ func processProcVariableUses(decl ast.Node, tinfo *types.Info, procVarIdent *ast
 		}
 
 		switch methodName {
-		case "loadFieldNamed", "fieldVariable", "loadInt64Maybe", "structMember":
+		case "loadFieldNamed", "fieldVariable", "loadInt64Maybe", "structField":
 			rtcmntIdx := -1
 			if cmntgrps := cmntmap[lastStmt]; len(cmntgrps) > 0 && len(cmntgrps[0].List) > 0 {
 				rtcmntIdx = findComment(cmntgrps[0].List[0].Slash, rtcmnts)
diff --git a/_scripts/test_windows.ps1 b/_scripts/test_windows.ps1
index f015d187..25187edc 100644
--- a/_scripts/test_windows.ps1
+++ b/_scripts/test_windows.ps1
@@ -18,7 +18,7 @@ if ($arch -eq "amd64")
     #Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
     choco install -y mingw
 } elseif ($arch -eq "arm64") {
-    $llvmVersion = "20220906"
+    $llvmVersion = "20250924"
     $name = "llvm-mingw-$llvmVersion-ucrt-aarch64"
     if (-Not(Test-Path "$binDir\llvm-mingw\$name"))
     {
@@ -44,6 +44,7 @@ $env:PATH = "$binDir\procdump;$env:PATH"
 
 function GetGo($version) {
     $env:GOROOT = "$binDir\go\$version"
+    Write-Host "Installing Go $version to $env:GOROOT"
     if (-Not(Test-Path $env:GOROOT))
     {
         $file = "$version.windows-$arch.zip"
@@ -57,7 +58,8 @@ function GetGo($version) {
 
 if ($version -eq "gotip") {
     #Exit 0
-    $latest = (Invoke-WebRequest -Uri "https://golang.org/VERSION?m=text" -UseBasicParsing | Select-Object -ExpandProperty Content -ErrorAction Stop).Split([Environment]::NewLine) | select -first 1
+    $versions = Invoke-WebRequest -Uri "https://go.dev/dl/?mode=json" -UseBasicParsing | foreach {$_.Content} | ConvertFrom-Json -ErrorAction Stop
+    $latest = $versions[0].version
     GetGo $latest
     $env:GOROOT_BOOTSTRAP = $env:GOROOT
     $env:GOROOT = "$binDir\go\go-tip"
diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go
index a1e96326..609022c0 100644
--- a/pkg/proc/eval.go
+++ b/pkg/proc/eval.go
@@ -253,7 +253,7 @@ func (scope *EvalScope) ChanGoroutines(expr string, start, count int) ([]int64,
 	structMemberMulti := func(v *Variable, names ...string) *Variable {
 		for _, name := range names {
 			var err error
-			v, err = v.structMember(name)
+			v, err = v.structField(name)
 			if err != nil {
 				return nil
 			}
@@ -297,7 +297,7 @@ func (scope *EvalScope) ChanGoroutines(expr string, start, count int) ([]int64,
 				goids = append(goids, goid)
 			}
 
-			nextVar, err := qvar.structMember("next")
+			nextVar, err := qvar.structField("next")
 			if err != nil {
 				return err
 			}
diff --git a/pkg/proc/moduledata.go b/pkg/proc/moduledata.go
index e3a7c1eb..0d8c0c1b 100644
--- a/pkg/proc/moduledata.go
+++ b/pkg/proc/moduledata.go
@@ -34,7 +34,7 @@ func LoadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]ModuleData, error)
 
 		for _, fieldName := range []string{typesField, etypesField, textField, etextField, nextField, typemapField} {
 			var err error
-			vars[fieldName], err = md.structMember(fieldName)
+			vars[fieldName], err = md.structField(fieldName)
 			if err != nil {
 				return nil, err
 			}
diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go
index 635e8bc8..1792e48d 100644
--- a/pkg/proc/stack.go
+++ b/pkg/proc/stack.go
@@ -669,9 +669,9 @@ func (it *stackIterator) loadG0SchedSP() {
 	}
 	it.g0_sched_sp_loaded = true
 	if it.g != nil {
-		mvar, _ := it.g.variable.structMember("m")
+		mvar, _ := it.g.variable.structField("m")
 		if mvar != nil {
-			g0var, _ := mvar.structMember("g0")
+			g0var, _ := mvar.structField("g0")
 			if g0var != nil {
 				g0, _ := g0var.parseG()
 				if g0 != nil {
diff --git a/pkg/proc/target.go b/pkg/proc/target.go
index dab68027..de368b85 100644
--- a/pkg/proc/target.go
+++ b/pkg/proc/target.go
@@ -343,7 +343,7 @@ func setAsyncPreemptOff(p *Target, v int64) {
 		logger.Warnf("runtime/debug variable unreadable: %v", err, debugv.Unreadable)
 		return
 	}
-	asyncpreemptoffv, err := debugv.structMember("asyncpreemptoff") // +rtype int32
+	asyncpreemptoffv, err := debugv.structField("asyncpreemptoff") // +rtype int32
 	if err != nil {
 		logger.Warnf("could not find asyncpreemptoff field: %v", err)
 		return
diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go
index 635e4278..607e5c6f 100644
--- a/pkg/proc/target_exec.go
+++ b/pkg/proc/target_exec.go
@@ -1766,7 +1766,7 @@ func stepIntoCoroutineMaybe(curthread Thread, p *Target, text []AsmInstruction)
 	// runtime.coro (it is normally iter.coro, which is an internal
 	// placeholder).
 
-	cvar, err := clos.structMember("c")
+	cvar, err := clos.structField("c")
 	if err != nil {
 		logflags.DebuggerLogger().Errorf("iter.Pull problems accessing captured 'c' variable in closure: %v", err)
 		return false, nil
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index 1d558621..54594d74 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -281,11 +281,11 @@ func GetG(thread Thread) (*G, error) {
 		// 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.
 		// The real 'current goroutine' is stored in g0.m.curg
-		mvar, err := g.variable.structMember("m")
+		mvar, err := g.variable.structField("m")
 		if err != nil {
 			return nil, err
 		}
-		curgvar, err := mvar.structMember("curg")
+		curgvar, err := mvar.structField("curg")
 		if err != nil {
 			return nil, err
 		}
@@ -491,7 +491,7 @@ func (g *G) Defer() *Defer {
 	if g.variable.Unreadable != nil {
 		return nil
 	}
-	dvar, _ := g.variable.structMember("_defer")
+	dvar, _ := g.variable.structField("_defer")
 	if dvar == nil {
 		return nil
 	}
@@ -585,7 +585,10 @@ func (g *G) Labels() map[string]string {
 						}
 					}
 				case reflect.Struct:
-					labelMap, _ = labelMap.structMember("list")
+					labelMap, _ = labelMap.structField("LabelSet")
+					if labelMap != nil {
+						labelMap, _ = labelMap.structField("list")
+					}
 					if labelMap != nil {
 						// ensure the labelMap.Len is a valid value to prevent infinite loops
 						if labelMap.Len < 0 {
@@ -1005,7 +1008,7 @@ func (v *Variable) parseG() (*G, error) {
 }
 
 func (v *Variable) loadFieldNamed(name string) *Variable {
-	v, err := v.structMember(name)
+	v, err := v.structField(name)
 	if err != nil {
 		return nil
 	}
@@ -1041,7 +1044,7 @@ func Ancestors(p *Target, g *G, n int) ([]Ancestor, error) {
 		}
 	}
 
-	av, err := g.variable.structMember("ancestors")
+	av, err := g.variable.structField("ancestors")
 	if err != nil {
 		return nil, err
 	}
@@ -1118,7 +1121,9 @@ func (a *Ancestor) Stack(n int) ([]Stackframe, error) {
 	return r, nil
 }
 
-func (v *Variable) structMember(memberName string) (*Variable, error) {
+// structField returns the field named memberName in v.
+// This function is meant for internal use, it does not support interfaces, embeds or member methods. For those cases findStructMemberOrMethod should be used instead.
+func (v *Variable) structField(memberName string) (*Variable, error) {
 	if v.Unreadable != nil {
 		return v.clone(), nil
 	}
@@ -1139,11 +1144,6 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
 	case reflect.Chan:
 		v = v.clone()
 		v.RealType = godwarf.ResolveTypedef(&(v.RealType.(*godwarf.ChanType).TypedefType))
-	case reflect.Interface:
-		v.loadInterface(0, false, LoadConfig{})
-		if len(v.Children) > 0 {
-			v = &v.Children[0]
-		}
 	case reflect.Func:
 		fn := v.bi.PCToFunc(v.Base)
 		v.loadFunctionPtr(0, LoadConfig{MaxVariableRecurse: -1})
@@ -1168,64 +1168,28 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
 		}
 	}
 
-	queue := []*Variable{v}
-	seen := map[string]struct{}{} // prevent infinite loops
-	first := true
+	structVar := v.maybeDereference()
+	structVar.Name = v.Name
+	if structVar.Unreadable != nil {
+		return structVar, nil
+	}
 
-	for len(queue) > 0 {
-		v := queue[0]
-		queue = append(queue[:0], queue[1:]...)
-		if _, isseen := seen[v.RealType.String()]; isseen {
-			continue
-		}
-		seen[v.RealType.String()] = struct{}{}
+	t, isstruct := structVar.RealType.(*godwarf.StructType)
+	if !isstruct {
+		return nil, fmt.Errorf("%s (type %s) is not a struct", vname, structVar.TypeString())
+	}
 
-		structVar := v.maybeDereference()
-		structVar.Name = v.Name
-		if structVar.Unreadable != nil {
-			return structVar, nil
+	for _, field := range t.Field {
+		if field.Name == memberName {
+			return structVar.toField(field)
 		}
-
-		switch t := structVar.RealType.(type) {
-		case *godwarf.StructType:
-			for _, field := range t.Field {
-				if field.Name == memberName {
-					return structVar.toField(field)
-				}
-				if len(queue) == 0 && field.Name == "&"+memberName && closure {
-					f, err := structVar.toField(field)
-					if err != nil {
-						return nil, err
-					}
-					return f.maybeDereference(), nil
-				}
-				isEmbeddedStructMember :=
-					field.Embedded ||
-						(field.Type.Common().Name == field.Name) ||
-						(len(field.Name) > 1 &&
-							field.Name[0] == '*' &&
-							field.Type.Common().Name[1:] == field.Name[1:])
-				if !isEmbeddedStructMember {
-					continue
-				}
-				embeddedVar, err := structVar.toField(field)
-				if err != nil {
-					return nil, err
-				}
-				// Check for embedded field referenced by type name
-				parts := strings.Split(field.Name, ".")
-				if len(parts) > 1 && parts[1] == memberName {
-					return embeddedVar, nil
-				}
-				embeddedVar.Name = structVar.Name
-				queue = append(queue, embeddedVar)
-			}
-		default:
-			if first {
-				return nil, fmt.Errorf("%s (type %s) is not a struct", vname, structVar.TypeString())
+		if field.Name == "&"+memberName && closure {
+			f, err := structVar.toField(field)
+			if err != nil {
+				return nil, err
 			}
+			return f.maybeDereference(), nil
 		}
-		first = false
 	}
 
 	return nil, fmt.Errorf("%s has no member %s", vname, memberName)
@@ -2104,9 +2068,9 @@ func (v *Variable) readInterface() (_type, data *Variable, isnil bool) {
 			isnil = tab.Addr == 0
 			if !isnil {
 				var err error
-				_type, err = tab.structMember("Type") // +rtype *internal/abi.Type
+				_type, err = tab.structField("Type") // +rtype *internal/abi.Type
 				if err != nil {
-					_type, err = tab.structMember("_type") // +rtype *_type|*internal/abi.Type
+					_type, err = tab.structField("_type") // +rtype *_type|*internal/abi.Type
 					if err != nil {
 						v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
 						return



Test patch:
diff --git a/_fixtures/testvariables2.go b/_fixtures/testvariables2.go
index f8907166..d0d2d04a 100644
--- a/_fixtures/testvariables2.go
+++ b/_fixtures/testvariables2.go
@@ -169,6 +169,10 @@ type ThreeInts struct {
 	a, b, c int
 }
 
+type OnlyUsedInInterface struct {
+	s string
+}
+
 type Message []byte
 
 var _ I = (*W2)(nil)
@@ -427,6 +431,8 @@ func main() {
 
 	var messageVar Message = Message{1, 2, 3, 4, 5}
 
+	var iface7 interface{} = OnlyUsedInInterface{"test"}
+
 	var amb1 = 1
 	runtime.Breakpoint()
 	for amb1 := 0; amb1 < 10; amb1++ {
@@ -437,5 +443,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.NonPointerReceiverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl, tim1, tim2, typedstringvar, namedA1, namedA2, astructName1(namedA2), badslice, tim3, int3chan, longbyteslice, enum1, enum2, enum3, enum4, enum5, enum6, zeropoint4, mlarge, messageVar)
+	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.NonPointerReceiverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice, val, m6, m7, cl, tim1, tim2, typedstringvar, namedA1, namedA2, astructName1(namedA2), badslice, tim3, int3chan, longbyteslice, enum1, enum2, enum3, enum4, enum5, enum6, zeropoint4, mlarge, messageVar, iface7)
 }
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 6ce7615d..45acecf4 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -341,9 +341,6 @@ func TestGeneratedDoc(t *testing.T) {
 		//TODO(alexsaezm): finish CI integration
 		t.Skip("skipping test on Linux/PPC64LE in CI")
 	}
-	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 24) {
-		t.Skip("disabled due to export format changes")
-	}
 	// Checks gen-cli-docs.go
 	var generatedBuf bytes.Buffer
 	commands := terminal.DebugCommands(nil)
@@ -1467,6 +1464,9 @@ func TestCapsLock(t *testing.T) {
 	if goos == "linux" && goarch == "ppc64le" {
 		args = append([]string{"-buildtags", "exp.linuxppc64le"}, args...)
 	}
+	if goos == "windows" && goarch == "arm64" {
+		args = append([]string{"-buildtags", "exp.winarm64"}, args...)
+	}
 
 	cmd := exec.Command("capslock", args...)
 	cmd.Dir = protest.ProjectRoot()
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index 38ca5c19..5811a4fc 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -440,7 +440,6 @@ func countBreakpoints(p *proc.Target) int {
 }
 
 func TestNextConcurrent(t *testing.T) {
-	skipOn(t, "broken", "windows", "arm64")
 	testcases := []nextTest{
 		{8, 9},
 		{9, 10},
@@ -476,7 +475,6 @@ func TestNextConcurrent(t *testing.T) {
 }
 
 func TestNextConcurrentVariant2(t *testing.T) {
-	skipOn(t, "broken", "windows", "arm64")
 	// Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines
 	testcases := []nextTest{
 		{8, 9},
@@ -1260,7 +1258,7 @@ func TestBreakpointCounts(t *testing.T) {
 }
 
 func TestHardcodedBreakpointCounts(t *testing.T) {
-	skipOn(t, "broken", "windows", "arm64")
+	skipOn(t, "flaky", "windows", "arm64")
 	withTestProcess("hcbpcountstest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
 		counts := map[int64]int{}
 		for {
@@ -2108,7 +2106,6 @@ func TestStepOut(t *testing.T) {
 }
 
 func TestStepConcurrentDirect(t *testing.T) {
-	skipOn(t, "broken - step concurrent", "windows", "arm64")
 	protest.AllowRecording(t)
 	withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
 		bp := setFileBreakpoint(p, t, fixture.Source, 37)
@@ -3050,9 +3047,6 @@ func TestCgoStacktrace(t *testing.T) {
 	skipOn(t, "broken - cgo stacktraces", "windows", "arm64")
 	skipOn(t, "broken - cgo stacktraces", "linux", "ppc64le")
 	skipOn(t, "broken - cgo stacktraces", "linux", "riscv64")
-	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) {
-		skipOn(t, "broken - cgo stacktraces", "windows", "arm64")
-	}
 	protest.MustHaveCgo(t)
 
 	// Tests that:
@@ -4328,6 +4322,7 @@ func TestCgoStacktrace2(t *testing.T) {
 	}
 	skipOn(t, "broken", "386")
 	skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
+	skipOn(t, "broken - cgo stacktraces", "windows", "arm64")
 	skipOn(t, "broken", "ppc64le")
 	skipOn(t, "broken", "riscv64")
 	protest.MustHaveCgo(t)
diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go
index db7d548d..c1a2bc6a 100644
--- a/pkg/proc/variables_test.go
+++ b/pkg/proc/variables_test.go
@@ -958,6 +958,11 @@ func getEvalExpressionTestCases() []varTest {
 		testcases = append(testcases, varTest{`**(**runtime.hmap)(uintptr(&m1))`, false, `…`, `…`, "runtime.hmap", nil})
 	}
 
+	if goversion.VersionAfterOrEqualRev(runtime.Version(), 1, 25, 2) {
+		testcases = append(testcases, varTest{"iface7", true, "interface {}(main.OnlyUsedInInterface) {s: \"test\"}", "interface {}(main.OnlyUsedInInterface) {s: \"test\"}", "interface {}", nil})
+
+	}
+
 	return testcases
 }
 
diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go
index b0f311bf..cb64a162 100644
--- a/service/test/integration2_test.go
+++ b/service/test/integration2_test.go
@@ -3241,6 +3241,10 @@ func TestGuessSubstitutePath(t *testing.T) {
 			t.Setenv("GOFLAGS", "-tags=exp.linuxriscv64")
 		case "loong64":
 			t.Setenv("GOFLAGS", "-tags=exp.linuxloong64")
+		case "arm64":
+			if runtime.GOOS == "windows" {
+				t.Setenv("GOFLAGS", "-tags=exp.winarm64")
+			}
 		}
 
 		gsp, err := client.GuessSubstitutePath()