Source file src/runtime/syscall_windows_test.go

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/abi"
    10  	"internal/race"
    11  	"internal/syscall/windows/sysdll"
    12  	"internal/testenv"
    13  	"io"
    14  	"math"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"reflect"
    19  	"runtime"
    20  	"strconv"
    21  	"strings"
    22  	"syscall"
    23  	"testing"
    24  	"unsafe"
    25  )
    26  
    27  type DLL struct {
    28  	*syscall.DLL
    29  	t *testing.T
    30  }
    31  
    32  func GetDLL(t *testing.T, name string) *DLL {
    33  	d, e := syscall.LoadDLL(name)
    34  	if e != nil {
    35  		t.Fatal(e)
    36  	}
    37  	return &DLL{DLL: d, t: t}
    38  }
    39  
    40  func (d *DLL) Proc(name string) *syscall.Proc {
    41  	p, e := d.FindProc(name)
    42  	if e != nil {
    43  		d.t.Fatal(e)
    44  	}
    45  	return p
    46  }
    47  
    48  func TestStdCall(t *testing.T) {
    49  	type Rect struct {
    50  		left, top, right, bottom int32
    51  	}
    52  	res := Rect{}
    53  	expected := Rect{1, 1, 40, 60}
    54  	a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
    55  		uintptr(unsafe.Pointer(&res)),
    56  		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
    57  		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
    58  	if a != 1 || res.left != expected.left ||
    59  		res.top != expected.top ||
    60  		res.right != expected.right ||
    61  		res.bottom != expected.bottom {
    62  		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
    63  	}
    64  }
    65  
    66  func Test64BitReturnStdCall(t *testing.T) {
    67  
    68  	const (
    69  		VER_BUILDNUMBER      = 0x0000004
    70  		VER_MAJORVERSION     = 0x0000002
    71  		VER_MINORVERSION     = 0x0000001
    72  		VER_PLATFORMID       = 0x0000008
    73  		VER_PRODUCT_TYPE     = 0x0000080
    74  		VER_SERVICEPACKMAJOR = 0x0000020
    75  		VER_SERVICEPACKMINOR = 0x0000010
    76  		VER_SUITENAME        = 0x0000040
    77  
    78  		VER_EQUAL         = 1
    79  		VER_GREATER       = 2
    80  		VER_GREATER_EQUAL = 3
    81  		VER_LESS          = 4
    82  		VER_LESS_EQUAL    = 5
    83  
    84  		ERROR_OLD_WIN_VERSION syscall.Errno = 1150
    85  	)
    86  
    87  	type OSVersionInfoEx struct {
    88  		OSVersionInfoSize uint32
    89  		MajorVersion      uint32
    90  		MinorVersion      uint32
    91  		BuildNumber       uint32
    92  		PlatformId        uint32
    93  		CSDVersion        [128]uint16
    94  		ServicePackMajor  uint16
    95  		ServicePackMinor  uint16
    96  		SuiteMask         uint16
    97  		ProductType       byte
    98  		Reserve           byte
    99  	}
   100  
   101  	d := GetDLL(t, "kernel32.dll")
   102  
   103  	var m1, m2 uintptr
   104  	VerSetConditionMask := d.Proc("VerSetConditionMask")
   105  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
   106  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
   107  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
   108  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)
   109  
   110  	vi := OSVersionInfoEx{
   111  		MajorVersion:     5,
   112  		MinorVersion:     1,
   113  		ServicePackMajor: 2,
   114  		ServicePackMinor: 0,
   115  	}
   116  	vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
   117  	r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
   118  		uintptr(unsafe.Pointer(&vi)),
   119  		VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
   120  		m1, m2)
   121  	if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
   122  		t.Errorf("VerifyVersionInfo failed: %s", e2)
   123  	}
   124  }
   125  
   126  func TestCDecl(t *testing.T) {
   127  	var buf [50]byte
   128  	fmtp, _ := syscall.BytePtrFromString("%d %d %d")
   129  	a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
   130  		uintptr(unsafe.Pointer(&buf[0])),
   131  		uintptr(unsafe.Pointer(fmtp)),
   132  		1000, 2000, 3000)
   133  	if string(buf[:a]) != "1000 2000 3000" {
   134  		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
   135  	}
   136  }
   137  
   138  func TestEnumWindows(t *testing.T) {
   139  	d := GetDLL(t, "user32.dll")
   140  	isWindows := d.Proc("IsWindow")
   141  	counter := 0
   142  	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
   143  		if lparam != 888 {
   144  			t.Error("lparam was not passed to callback")
   145  		}
   146  		b, _, _ := isWindows.Call(uintptr(hwnd))
   147  		if b == 0 {
   148  			t.Error("USER32.IsWindow returns FALSE")
   149  		}
   150  		counter++
   151  		return 1 // continue enumeration
   152  	})
   153  	a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
   154  	if a == 0 {
   155  		t.Error("USER32.EnumWindows returns FALSE")
   156  	}
   157  	if counter == 0 {
   158  		t.Error("Callback has been never called or your have no windows")
   159  	}
   160  }
   161  
   162  func callback(timeFormatString unsafe.Pointer, lparam uintptr) uintptr {
   163  	(*(*func())(unsafe.Pointer(&lparam)))()
   164  	return 0 // stop enumeration
   165  }
   166  
   167  // nestedCall calls into Windows, back into Go, and finally to f.
   168  func nestedCall(t *testing.T, f func()) {
   169  	c := syscall.NewCallback(callback)
   170  	d := GetDLL(t, "kernel32.dll")
   171  	defer d.Release()
   172  	const LOCALE_NAME_USER_DEFAULT = 0
   173  	d.Proc("EnumTimeFormatsEx").Call(c, LOCALE_NAME_USER_DEFAULT, 0, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
   174  }
   175  
   176  func TestCallback(t *testing.T) {
   177  	var x = false
   178  	nestedCall(t, func() { x = true })
   179  	if !x {
   180  		t.Fatal("nestedCall did not call func")
   181  	}
   182  }
   183  
   184  func TestCallbackGC(t *testing.T) {
   185  	nestedCall(t, runtime.GC)
   186  }
   187  
   188  func TestCallbackPanicLocked(t *testing.T) {
   189  	runtime.LockOSThread()
   190  	defer runtime.UnlockOSThread()
   191  
   192  	if !runtime.LockedOSThread() {
   193  		t.Fatal("runtime.LockOSThread didn't")
   194  	}
   195  	defer func() {
   196  		s := recover()
   197  		if s == nil {
   198  			t.Fatal("did not panic")
   199  		}
   200  		if s.(string) != "callback panic" {
   201  			t.Fatal("wrong panic:", s)
   202  		}
   203  		if !runtime.LockedOSThread() {
   204  			t.Fatal("lost lock on OS thread after panic")
   205  		}
   206  	}()
   207  	nestedCall(t, func() { panic("callback panic") })
   208  	panic("nestedCall returned")
   209  }
   210  
   211  func TestCallbackPanic(t *testing.T) {
   212  	// Make sure panic during callback unwinds properly.
   213  	if runtime.LockedOSThread() {
   214  		t.Fatal("locked OS thread on entry to TestCallbackPanic")
   215  	}
   216  	defer func() {
   217  		s := recover()
   218  		if s == nil {
   219  			t.Fatal("did not panic")
   220  		}
   221  		if s.(string) != "callback panic" {
   222  			t.Fatal("wrong panic:", s)
   223  		}
   224  		if runtime.LockedOSThread() {
   225  			t.Fatal("locked OS thread on exit from TestCallbackPanic")
   226  		}
   227  	}()
   228  	nestedCall(t, func() { panic("callback panic") })
   229  	panic("nestedCall returned")
   230  }
   231  
   232  func TestCallbackPanicLoop(t *testing.T) {
   233  	// Make sure we don't blow out m->g0 stack.
   234  	for i := 0; i < 100000; i++ {
   235  		TestCallbackPanic(t)
   236  	}
   237  }
   238  
   239  func TestBlockingCallback(t *testing.T) {
   240  	c := make(chan int)
   241  	go func() {
   242  		for i := 0; i < 10; i++ {
   243  			c <- <-c
   244  		}
   245  	}()
   246  	nestedCall(t, func() {
   247  		for i := 0; i < 10; i++ {
   248  			c <- i
   249  			if j := <-c; j != i {
   250  				t.Errorf("out of sync %d != %d", j, i)
   251  			}
   252  		}
   253  	})
   254  }
   255  
   256  func TestCallbackInAnotherThread(t *testing.T) {
   257  	d := GetDLL(t, "kernel32.dll")
   258  
   259  	f := func(p uintptr) uintptr {
   260  		return p
   261  	}
   262  	r, _, err := d.Proc("CreateThread").Call(0, 0, syscall.NewCallback(f), 123, 0, 0)
   263  	if r == 0 {
   264  		t.Fatalf("CreateThread failed: %v", err)
   265  	}
   266  	h := syscall.Handle(r)
   267  	defer syscall.CloseHandle(h)
   268  
   269  	switch s, err := syscall.WaitForSingleObject(h, syscall.INFINITE); s {
   270  	case syscall.WAIT_OBJECT_0:
   271  		break
   272  	case syscall.WAIT_FAILED:
   273  		t.Fatalf("WaitForSingleObject failed: %v", err)
   274  	default:
   275  		t.Fatalf("WaitForSingleObject returns unexpected value %v", s)
   276  	}
   277  
   278  	var ec uint32
   279  	r, _, err = d.Proc("GetExitCodeThread").Call(uintptr(h), uintptr(unsafe.Pointer(&ec)))
   280  	if r == 0 {
   281  		t.Fatalf("GetExitCodeThread failed: %v", err)
   282  	}
   283  	if ec != 123 {
   284  		t.Fatalf("expected 123, but got %d", ec)
   285  	}
   286  }
   287  
   288  type cbFunc struct {
   289  	goFunc any
   290  }
   291  
   292  func (f cbFunc) cName(cdecl bool) string {
   293  	name := "stdcall"
   294  	if cdecl {
   295  		name = "cdecl"
   296  	}
   297  	t := reflect.TypeOf(f.goFunc)
   298  	for i := 0; i < t.NumIn(); i++ {
   299  		name += "_" + t.In(i).Name()
   300  	}
   301  	return name
   302  }
   303  
   304  func (f cbFunc) cSrc(w io.Writer, cdecl bool) {
   305  	// Construct a C function that takes a callback with
   306  	// f.goFunc's signature, and calls it with integers 1..N.
   307  	funcname := f.cName(cdecl)
   308  	attr := "__stdcall"
   309  	if cdecl {
   310  		attr = "__cdecl"
   311  	}
   312  	typename := "t" + funcname
   313  	t := reflect.TypeOf(f.goFunc)
   314  	cTypes := make([]string, t.NumIn())
   315  	cArgs := make([]string, t.NumIn())
   316  	for i := range cTypes {
   317  		// We included stdint.h, so this works for all sized
   318  		// integer types, and uint8Pair_t.
   319  		cTypes[i] = t.In(i).Name() + "_t"
   320  		if t.In(i).Name() == "uint8Pair" {
   321  			cArgs[i] = fmt.Sprintf("(uint8Pair_t){%d,1}", i)
   322  		} else {
   323  			cArgs[i] = fmt.Sprintf("%d", i+1)
   324  		}
   325  	}
   326  	fmt.Fprintf(w, `
   327  typedef uintptr_t %s (*%s)(%s);
   328  uintptr_t %s(%s f) {
   329  	return f(%s);
   330  }
   331  	`, attr, typename, strings.Join(cTypes, ","), funcname, typename, strings.Join(cArgs, ","))
   332  }
   333  
   334  func (f cbFunc) testOne(t *testing.T, dll *syscall.DLL, cdecl bool, cb uintptr) {
   335  	r1, _, _ := dll.MustFindProc(f.cName(cdecl)).Call(cb)
   336  
   337  	want := 0
   338  	for i := 0; i < reflect.TypeOf(f.goFunc).NumIn(); i++ {
   339  		want += i + 1
   340  	}
   341  	if int(r1) != want {
   342  		t.Errorf("wanted result %d; got %d", want, r1)
   343  	}
   344  }
   345  
   346  type uint8Pair struct{ x, y uint8 }
   347  
   348  var cbFuncs = []cbFunc{
   349  	{func(i1, i2 uintptr) uintptr {
   350  		return i1 + i2
   351  	}},
   352  	{func(i1, i2, i3 uintptr) uintptr {
   353  		return i1 + i2 + i3
   354  	}},
   355  	{func(i1, i2, i3, i4 uintptr) uintptr {
   356  		return i1 + i2 + i3 + i4
   357  	}},
   358  	{func(i1, i2, i3, i4, i5 uintptr) uintptr {
   359  		return i1 + i2 + i3 + i4 + i5
   360  	}},
   361  	{func(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
   362  		return i1 + i2 + i3 + i4 + i5 + i6
   363  	}},
   364  	{func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
   365  		return i1 + i2 + i3 + i4 + i5 + i6 + i7
   366  	}},
   367  	{func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
   368  		return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
   369  	}},
   370  	{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
   371  		return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
   372  	}},
   373  
   374  	// Non-uintptr parameters.
   375  	{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
   376  		return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   377  	}},
   378  	{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
   379  		return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   380  	}},
   381  	{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
   382  		return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   383  	}},
   384  	{func(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
   385  		return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
   386  	}},
   387  	{func(i1, i2, i3, i4, i5 uint8Pair) uintptr {
   388  		return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
   389  	}},
   390  	{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
   391  		runtime.GC()
   392  		return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   393  	}},
   394  }
   395  
   396  //go:registerparams
   397  func sum2(i1, i2 uintptr) uintptr {
   398  	return i1 + i2
   399  }
   400  
   401  //go:registerparams
   402  func sum3(i1, i2, i3 uintptr) uintptr {
   403  	return i1 + i2 + i3
   404  }
   405  
   406  //go:registerparams
   407  func sum4(i1, i2, i3, i4 uintptr) uintptr {
   408  	return i1 + i2 + i3 + i4
   409  }
   410  
   411  //go:registerparams
   412  func sum5(i1, i2, i3, i4, i5 uintptr) uintptr {
   413  	return i1 + i2 + i3 + i4 + i5
   414  }
   415  
   416  //go:registerparams
   417  func sum6(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
   418  	return i1 + i2 + i3 + i4 + i5 + i6
   419  }
   420  
   421  //go:registerparams
   422  func sum7(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
   423  	return i1 + i2 + i3 + i4 + i5 + i6 + i7
   424  }
   425  
   426  //go:registerparams
   427  func sum8(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
   428  	return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
   429  }
   430  
   431  //go:registerparams
   432  func sum9(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
   433  	return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
   434  }
   435  
   436  //go:registerparams
   437  func sum10(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10 uintptr) uintptr {
   438  	return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10
   439  }
   440  
   441  //go:registerparams
   442  func sum9uint8(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
   443  	return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   444  }
   445  
   446  //go:registerparams
   447  func sum9uint16(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
   448  	return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   449  }
   450  
   451  //go:registerparams
   452  func sum9int8(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
   453  	return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   454  }
   455  
   456  //go:registerparams
   457  func sum5mix(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
   458  	return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
   459  }
   460  
   461  //go:registerparams
   462  func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr {
   463  	return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
   464  }
   465  
   466  // This test forces a GC. The idea is to have enough arguments
   467  // that insufficient spill slots allocated (according to the ABI)
   468  // may cause compiler-generated spills to clobber the return PC.
   469  // Then, the GC stack scanning will catch that.
   470  //
   471  //go:registerparams
   472  func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
   473  	runtime.GC()
   474  	return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   475  }
   476  
   477  // TODO(register args): Remove this once we switch to using the register
   478  // calling convention by default, since this is redundant with the existing
   479  // tests.
   480  var cbFuncsRegABI = []cbFunc{
   481  	{sum2},
   482  	{sum3},
   483  	{sum4},
   484  	{sum5},
   485  	{sum6},
   486  	{sum7},
   487  	{sum8},
   488  	{sum9},
   489  	{sum10},
   490  	{sum9uint8},
   491  	{sum9uint16},
   492  	{sum9int8},
   493  	{sum5mix},
   494  	{sum5andPair},
   495  	{sum9andGC},
   496  }
   497  
   498  func getCallbackTestFuncs() []cbFunc {
   499  	if regs := runtime.SetIntArgRegs(-1); regs > 0 {
   500  		return cbFuncsRegABI
   501  	}
   502  	return cbFuncs
   503  }
   504  
   505  type cbDLL struct {
   506  	name      string
   507  	buildArgs func(out, src string) []string
   508  }
   509  
   510  func (d *cbDLL) makeSrc(t *testing.T, path string) {
   511  	f, err := os.Create(path)
   512  	if err != nil {
   513  		t.Fatalf("failed to create source file: %v", err)
   514  	}
   515  	defer f.Close()
   516  
   517  	fmt.Fprint(f, `
   518  #include <stdint.h>
   519  typedef struct { uint8_t x, y; } uint8Pair_t;
   520  `)
   521  	for _, cbf := range getCallbackTestFuncs() {
   522  		cbf.cSrc(f, false)
   523  		cbf.cSrc(f, true)
   524  	}
   525  }
   526  
   527  func (d *cbDLL) build(t *testing.T, dir string) string {
   528  	srcname := d.name + ".c"
   529  	d.makeSrc(t, filepath.Join(dir, srcname))
   530  	outname := d.name + ".dll"
   531  	args := d.buildArgs(outname, srcname)
   532  	cmd := exec.Command(args[0], args[1:]...)
   533  	cmd.Dir = dir
   534  	out, err := cmd.CombinedOutput()
   535  	if err != nil {
   536  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   537  	}
   538  	return filepath.Join(dir, outname)
   539  }
   540  
   541  var cbDLLs = []cbDLL{
   542  	{
   543  		"test",
   544  		func(out, src string) []string {
   545  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
   546  		},
   547  	},
   548  	{
   549  		"testO2",
   550  		func(out, src string) []string {
   551  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
   552  		},
   553  	},
   554  }
   555  
   556  func TestStdcallAndCDeclCallbacks(t *testing.T) {
   557  	if _, err := exec.LookPath("gcc"); err != nil {
   558  		t.Skip("skipping test: gcc is missing")
   559  	}
   560  	tmp := t.TempDir()
   561  
   562  	oldRegs := runtime.SetIntArgRegs(abi.IntArgRegs)
   563  	defer runtime.SetIntArgRegs(oldRegs)
   564  
   565  	for _, dll := range cbDLLs {
   566  		t.Run(dll.name, func(t *testing.T) {
   567  			dllPath := dll.build(t, tmp)
   568  			dll := syscall.MustLoadDLL(dllPath)
   569  			defer dll.Release()
   570  			for _, cbf := range getCallbackTestFuncs() {
   571  				t.Run(cbf.cName(false), func(t *testing.T) {
   572  					stdcall := syscall.NewCallback(cbf.goFunc)
   573  					cbf.testOne(t, dll, false, stdcall)
   574  				})
   575  				t.Run(cbf.cName(true), func(t *testing.T) {
   576  					cdecl := syscall.NewCallbackCDecl(cbf.goFunc)
   577  					cbf.testOne(t, dll, true, cdecl)
   578  				})
   579  			}
   580  		})
   581  	}
   582  }
   583  
   584  func TestRegisterClass(t *testing.T) {
   585  	kernel32 := GetDLL(t, "kernel32.dll")
   586  	user32 := GetDLL(t, "user32.dll")
   587  	mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
   588  	cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
   589  		t.Fatal("callback should never get called")
   590  		return 0
   591  	})
   592  	type Wndclassex struct {
   593  		Size       uint32
   594  		Style      uint32
   595  		WndProc    uintptr
   596  		ClsExtra   int32
   597  		WndExtra   int32
   598  		Instance   syscall.Handle
   599  		Icon       syscall.Handle
   600  		Cursor     syscall.Handle
   601  		Background syscall.Handle
   602  		MenuName   *uint16
   603  		ClassName  *uint16
   604  		IconSm     syscall.Handle
   605  	}
   606  	name := syscall.StringToUTF16Ptr("test_window")
   607  	wc := Wndclassex{
   608  		WndProc:   cb,
   609  		Instance:  syscall.Handle(mh),
   610  		ClassName: name,
   611  	}
   612  	wc.Size = uint32(unsafe.Sizeof(wc))
   613  	a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
   614  	if a == 0 {
   615  		t.Fatalf("RegisterClassEx failed: %v", err)
   616  	}
   617  	r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
   618  	if r == 0 {
   619  		t.Fatalf("UnregisterClass failed: %v", err)
   620  	}
   621  }
   622  
   623  func TestOutputDebugString(t *testing.T) {
   624  	d := GetDLL(t, "kernel32.dll")
   625  	p := syscall.StringToUTF16Ptr("testing OutputDebugString")
   626  	d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
   627  }
   628  
   629  func TestRaiseException(t *testing.T) {
   630  	if strings.HasPrefix(testenv.Builder(), "windows-amd64-2012") {
   631  		testenv.SkipFlaky(t, 49681)
   632  	}
   633  	o := runTestProg(t, "testprog", "RaiseException")
   634  	if strings.Contains(o, "RaiseException should not return") {
   635  		t.Fatalf("RaiseException did not crash program: %v", o)
   636  	}
   637  	if !strings.Contains(o, "Exception 0xbad") {
   638  		t.Fatalf("No stack trace: %v", o)
   639  	}
   640  }
   641  
   642  func TestZeroDivisionException(t *testing.T) {
   643  	o := runTestProg(t, "testprog", "ZeroDivisionException")
   644  	if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
   645  		t.Fatalf("No stack trace: %v", o)
   646  	}
   647  }
   648  
   649  func TestWERDialogue(t *testing.T) {
   650  	if os.Getenv("TEST_WER_DIALOGUE") == "1" {
   651  		const EXCEPTION_NONCONTINUABLE = 1
   652  		mod := syscall.MustLoadDLL("kernel32.dll")
   653  		proc := mod.MustFindProc("RaiseException")
   654  		proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   655  		t.Fatal("RaiseException should not return")
   656  	}
   657  	exe, err := os.Executable()
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, "-test.run=^TestWERDialogue$"))
   662  	cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
   663  	// Child process should not open WER dialogue, but return immediately instead.
   664  	// The exit code can't be reliably tested here because Windows can change it.
   665  	_, err = cmd.CombinedOutput()
   666  	if err == nil {
   667  		t.Error("test program succeeded unexpectedly")
   668  	}
   669  }
   670  
   671  func TestWindowsStackMemory(t *testing.T) {
   672  	if race.Enabled {
   673  		t.Skip("skipping test: race mode uses more stack memory")
   674  	}
   675  	o := runTestProg(t, "testprog", "StackMemory")
   676  	stackUsage, err := strconv.Atoi(o)
   677  	if err != nil {
   678  		t.Fatalf("Failed to read stack usage: %v", err)
   679  	}
   680  	if expected, got := 128<<10, stackUsage; got > expected {
   681  		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
   682  	}
   683  }
   684  
   685  var used byte
   686  
   687  func use(buf []byte) {
   688  	for _, c := range buf {
   689  		used += c
   690  	}
   691  }
   692  
   693  func forceStackCopy() (r int) {
   694  	var f func(int) int
   695  	f = func(i int) int {
   696  		var buf [256]byte
   697  		use(buf[:])
   698  		if i == 0 {
   699  			return 0
   700  		}
   701  		return i + f(i-1)
   702  	}
   703  	r = f(128)
   704  	return
   705  }
   706  
   707  func TestReturnAfterStackGrowInCallback(t *testing.T) {
   708  	if _, err := exec.LookPath("gcc"); err != nil {
   709  		t.Skip("skipping test: gcc is missing")
   710  	}
   711  
   712  	const src = `
   713  #include <stdint.h>
   714  #include <windows.h>
   715  
   716  typedef uintptr_t __stdcall (*callback)(uintptr_t);
   717  
   718  uintptr_t cfunc(callback f, uintptr_t n) {
   719     uintptr_t r;
   720     r = f(n);
   721     SetLastError(333);
   722     return r;
   723  }
   724  `
   725  	tmpdir := t.TempDir()
   726  
   727  	srcname := "mydll.c"
   728  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   729  	if err != nil {
   730  		t.Fatal(err)
   731  	}
   732  	outname := "mydll.dll"
   733  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   734  	cmd.Dir = tmpdir
   735  	out, err := cmd.CombinedOutput()
   736  	if err != nil {
   737  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   738  	}
   739  	dllpath := filepath.Join(tmpdir, outname)
   740  
   741  	dll := syscall.MustLoadDLL(dllpath)
   742  	defer dll.Release()
   743  
   744  	proc := dll.MustFindProc("cfunc")
   745  
   746  	cb := syscall.NewCallback(func(n uintptr) uintptr {
   747  		forceStackCopy()
   748  		return n
   749  	})
   750  
   751  	// Use a new goroutine so that we get a small stack.
   752  	type result struct {
   753  		r   uintptr
   754  		err syscall.Errno
   755  	}
   756  	want := result{
   757  		// Make it large enough to test issue #29331.
   758  		r:   (^uintptr(0)) >> 24,
   759  		err: 333,
   760  	}
   761  	c := make(chan result)
   762  	go func() {
   763  		r, _, err := proc.Call(cb, want.r)
   764  		c <- result{r, err.(syscall.Errno)}
   765  	}()
   766  	if got := <-c; got != want {
   767  		t.Errorf("got %d want %d", got, want)
   768  	}
   769  }
   770  
   771  func TestSyscallN(t *testing.T) {
   772  	if _, err := exec.LookPath("gcc"); err != nil {
   773  		t.Skip("skipping test: gcc is missing")
   774  	}
   775  	if runtime.GOARCH != "amd64" {
   776  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   777  	}
   778  
   779  	for arglen := 0; arglen <= runtime.MaxArgs; arglen++ {
   780  		arglen := arglen
   781  		t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
   782  			t.Parallel()
   783  			args := make([]string, arglen)
   784  			rets := make([]string, arglen+1)
   785  			params := make([]uintptr, arglen)
   786  			for i := range args {
   787  				args[i] = fmt.Sprintf("int a%d", i)
   788  				rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
   789  				params[i] = uintptr(i)
   790  			}
   791  			rets[arglen] = "1" // for arglen == 0
   792  
   793  			src := fmt.Sprintf(`
   794  		#include <stdint.h>
   795  		#include <windows.h>
   796  		int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
   797  
   798  			tmpdir := t.TempDir()
   799  
   800  			srcname := "mydll.c"
   801  			err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   802  			if err != nil {
   803  				t.Fatal(err)
   804  			}
   805  			outname := "mydll.dll"
   806  			cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   807  			cmd.Dir = tmpdir
   808  			out, err := cmd.CombinedOutput()
   809  			if err != nil {
   810  				t.Fatalf("failed to build dll: %v\n%s", err, out)
   811  			}
   812  			dllpath := filepath.Join(tmpdir, outname)
   813  
   814  			dll := syscall.MustLoadDLL(dllpath)
   815  			defer dll.Release()
   816  
   817  			proc := dll.MustFindProc("cfunc")
   818  
   819  			// proc.Call() will call SyscallN() internally.
   820  			r, _, err := proc.Call(params...)
   821  			if r != 1 {
   822  				t.Errorf("got %d want 1 (err=%v)", r, err)
   823  			}
   824  		})
   825  	}
   826  }
   827  
   828  func TestFloatArgs(t *testing.T) {
   829  	if _, err := exec.LookPath("gcc"); err != nil {
   830  		t.Skip("skipping test: gcc is missing")
   831  	}
   832  	if runtime.GOARCH != "amd64" {
   833  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   834  	}
   835  
   836  	const src = `
   837  #include <stdint.h>
   838  #include <windows.h>
   839  
   840  uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
   841  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   842  		return 1;
   843  	}
   844  	return 0;
   845  }
   846  `
   847  	tmpdir := t.TempDir()
   848  
   849  	srcname := "mydll.c"
   850  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   851  	if err != nil {
   852  		t.Fatal(err)
   853  	}
   854  	outname := "mydll.dll"
   855  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   856  	cmd.Dir = tmpdir
   857  	out, err := cmd.CombinedOutput()
   858  	if err != nil {
   859  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   860  	}
   861  	dllpath := filepath.Join(tmpdir, outname)
   862  
   863  	dll := syscall.MustLoadDLL(dllpath)
   864  	defer dll.Release()
   865  
   866  	proc := dll.MustFindProc("cfunc")
   867  
   868  	r, _, err := proc.Call(
   869  		1,
   870  		uintptr(math.Float64bits(2.2)),
   871  		uintptr(math.Float32bits(3.3)),
   872  		uintptr(math.Float64bits(4.4e44)),
   873  	)
   874  	if r != 1 {
   875  		t.Errorf("got %d want 1 (err=%v)", r, err)
   876  	}
   877  }
   878  
   879  func TestFloatReturn(t *testing.T) {
   880  	if _, err := exec.LookPath("gcc"); err != nil {
   881  		t.Skip("skipping test: gcc is missing")
   882  	}
   883  	if runtime.GOARCH != "amd64" {
   884  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   885  	}
   886  
   887  	const src = `
   888  #include <stdint.h>
   889  #include <windows.h>
   890  
   891  float cfuncFloat(uintptr_t a, double b, float c, double d) {
   892  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   893  		return 1.5f;
   894  	}
   895  	return 0;
   896  }
   897  
   898  double cfuncDouble(uintptr_t a, double b, float c, double d) {
   899  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   900  		return 2.5;
   901  	}
   902  	return 0;
   903  }
   904  `
   905  	tmpdir := t.TempDir()
   906  
   907  	srcname := "mydll.c"
   908  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   909  	if err != nil {
   910  		t.Fatal(err)
   911  	}
   912  	outname := "mydll.dll"
   913  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   914  	cmd.Dir = tmpdir
   915  	out, err := cmd.CombinedOutput()
   916  	if err != nil {
   917  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   918  	}
   919  	dllpath := filepath.Join(tmpdir, outname)
   920  
   921  	dll := syscall.MustLoadDLL(dllpath)
   922  	defer dll.Release()
   923  
   924  	proc := dll.MustFindProc("cfuncFloat")
   925  
   926  	_, r, err := proc.Call(
   927  		1,
   928  		uintptr(math.Float64bits(2.2)),
   929  		uintptr(math.Float32bits(3.3)),
   930  		uintptr(math.Float64bits(4.4e44)),
   931  	)
   932  	fr := math.Float32frombits(uint32(r))
   933  	if fr != 1.5 {
   934  		t.Errorf("got %f want 1.5 (err=%v)", fr, err)
   935  	}
   936  
   937  	proc = dll.MustFindProc("cfuncDouble")
   938  
   939  	_, r, err = proc.Call(
   940  		1,
   941  		uintptr(math.Float64bits(2.2)),
   942  		uintptr(math.Float32bits(3.3)),
   943  		uintptr(math.Float64bits(4.4e44)),
   944  	)
   945  	dr := math.Float64frombits(uint64(r))
   946  	if dr != 2.5 {
   947  		t.Errorf("got %f want 2.5 (err=%v)", dr, err)
   948  	}
   949  }
   950  
   951  func TestTimeBeginPeriod(t *testing.T) {
   952  	const TIMERR_NOERROR = 0
   953  	if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
   954  		t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
   955  	}
   956  }
   957  
   958  // removeOneCPU removes one (any) cpu from affinity mask.
   959  // It returns new affinity mask.
   960  func removeOneCPU(mask uintptr) (uintptr, error) {
   961  	if mask == 0 {
   962  		return 0, fmt.Errorf("cpu affinity mask is empty")
   963  	}
   964  	maskbits := int(unsafe.Sizeof(mask) * 8)
   965  	for i := 0; i < maskbits; i++ {
   966  		newmask := mask & ^(1 << uint(i))
   967  		if newmask != mask {
   968  			return newmask, nil
   969  		}
   970  
   971  	}
   972  	panic("not reached")
   973  }
   974  
   975  func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
   976  	_OpenThread := kernel32.MustFindProc("OpenThread")
   977  	_ResumeThread := kernel32.MustFindProc("ResumeThread")
   978  	_Thread32First := kernel32.MustFindProc("Thread32First")
   979  	_Thread32Next := kernel32.MustFindProc("Thread32Next")
   980  
   981  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
   982  	if err != nil {
   983  		return err
   984  	}
   985  	defer syscall.CloseHandle(snapshot)
   986  
   987  	const _THREAD_SUSPEND_RESUME = 0x0002
   988  
   989  	type ThreadEntry32 struct {
   990  		Size           uint32
   991  		tUsage         uint32
   992  		ThreadID       uint32
   993  		OwnerProcessID uint32
   994  		BasePri        int32
   995  		DeltaPri       int32
   996  		Flags          uint32
   997  	}
   998  
   999  	var te ThreadEntry32
  1000  	te.Size = uint32(unsafe.Sizeof(te))
  1001  	ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
  1002  	if ret == 0 {
  1003  		return err
  1004  	}
  1005  	for te.OwnerProcessID != uint32(childpid) {
  1006  		ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
  1007  		if ret == 0 {
  1008  			return err
  1009  		}
  1010  	}
  1011  	h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
  1012  	if h == 0 {
  1013  		return err
  1014  	}
  1015  	defer syscall.Close(syscall.Handle(h))
  1016  
  1017  	ret, _, err = _ResumeThread.Call(h)
  1018  	if ret == 0xffffffff {
  1019  		return err
  1020  	}
  1021  	return nil
  1022  }
  1023  
  1024  func TestNumCPU(t *testing.T) {
  1025  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
  1026  		// in child process
  1027  		fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
  1028  		os.Exit(0)
  1029  	}
  1030  
  1031  	switch n := runtime.NumberOfProcessors(); {
  1032  	case n < 1:
  1033  		t.Fatalf("system cannot have %d cpu(s)", n)
  1034  	case n == 1:
  1035  		if runtime.NumCPU() != 1 {
  1036  			t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
  1037  		}
  1038  		return
  1039  	}
  1040  
  1041  	const (
  1042  		_CREATE_SUSPENDED   = 0x00000004
  1043  		_PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
  1044  	)
  1045  
  1046  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
  1047  	_GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
  1048  	_SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
  1049  
  1050  	cmd := exec.Command(testenv.Executable(t), "-test.run=^TestNumCPU$")
  1051  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
  1052  	var buf strings.Builder
  1053  	cmd.Stdout = &buf
  1054  	cmd.Stderr = &buf
  1055  	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
  1056  	err := cmd.Start()
  1057  	if err != nil {
  1058  		t.Fatal(err)
  1059  	}
  1060  	defer func() {
  1061  		err = cmd.Wait()
  1062  		childOutput := buf.String()
  1063  		if err != nil {
  1064  			t.Fatalf("child failed: %v: %v", err, childOutput)
  1065  		}
  1066  		// removeOneCPU should have decreased child cpu count by 1
  1067  		want := fmt.Sprintf("%d", runtime.NumCPU()-1)
  1068  		if childOutput != want {
  1069  			t.Fatalf("child output: want %q, got %q", want, childOutput)
  1070  		}
  1071  	}()
  1072  
  1073  	defer func() {
  1074  		err = resumeChildThread(kernel32, cmd.Process.Pid)
  1075  		if err != nil {
  1076  			t.Fatal(err)
  1077  		}
  1078  	}()
  1079  
  1080  	ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
  1081  	if err != nil {
  1082  		t.Fatal(err)
  1083  	}
  1084  	defer syscall.CloseHandle(ph)
  1085  
  1086  	var mask, sysmask uintptr
  1087  	ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1088  	if ret == 0 {
  1089  		t.Fatal(err)
  1090  	}
  1091  
  1092  	newmask, err := removeOneCPU(mask)
  1093  	if err != nil {
  1094  		t.Fatal(err)
  1095  	}
  1096  
  1097  	ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
  1098  	if ret == 0 {
  1099  		t.Fatal(err)
  1100  	}
  1101  	ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1102  	if ret == 0 {
  1103  		t.Fatal(err)
  1104  	}
  1105  	if newmask != mask {
  1106  		t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
  1107  	}
  1108  }
  1109  
  1110  // See Issue 14959
  1111  func TestDLLPreloadMitigation(t *testing.T) {
  1112  	if _, err := exec.LookPath("gcc"); err != nil {
  1113  		t.Skip("skipping test: gcc is missing")
  1114  	}
  1115  
  1116  	tmpdir := t.TempDir()
  1117  
  1118  	const src = `
  1119  #include <stdint.h>
  1120  #include <windows.h>
  1121  
  1122  uintptr_t cfunc(void) {
  1123     SetLastError(123);
  1124     return 0;
  1125  }
  1126  `
  1127  	srcname := "nojack.c"
  1128  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
  1129  	if err != nil {
  1130  		t.Fatal(err)
  1131  	}
  1132  	name := "nojack.dll"
  1133  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
  1134  	cmd.Dir = tmpdir
  1135  	out, err := cmd.CombinedOutput()
  1136  	if err != nil {
  1137  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1138  	}
  1139  	dllpath := filepath.Join(tmpdir, name)
  1140  
  1141  	dll := syscall.MustLoadDLL(dllpath)
  1142  	dll.MustFindProc("cfunc")
  1143  	dll.Release()
  1144  
  1145  	// Get into the directory with the DLL we'll load by base name
  1146  	// ("nojack.dll") Think of this as the user double-clicking an
  1147  	// installer from their Downloads directory where a browser
  1148  	// silently downloaded some malicious DLLs.
  1149  	t.Chdir(tmpdir)
  1150  
  1151  	// First before we can load a DLL from the current directory,
  1152  	// loading it only as "nojack.dll", without an absolute path.
  1153  	delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
  1154  	dll, err = syscall.LoadDLL(name)
  1155  	if err != nil {
  1156  		t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
  1157  	}
  1158  	dll.Release()
  1159  
  1160  	// And now verify that if we register it as a system32-only
  1161  	// DLL, the implicit loading from the current directory no
  1162  	// longer works.
  1163  	sysdll.IsSystemDLL[name] = true
  1164  	dll, err = syscall.LoadDLL(name)
  1165  	if err == nil {
  1166  		dll.Release()
  1167  		t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
  1168  	}
  1169  }
  1170  
  1171  // Test that C code called via a DLL can use large Windows thread
  1172  // stacks and call back in to Go without crashing. See issue #20975.
  1173  //
  1174  // See also TestBigStackCallbackCgo.
  1175  func TestBigStackCallbackSyscall(t *testing.T) {
  1176  	if _, err := exec.LookPath("gcc"); err != nil {
  1177  		t.Skip("skipping test: gcc is missing")
  1178  	}
  1179  
  1180  	srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
  1181  	if err != nil {
  1182  		t.Fatal("Abs failed: ", err)
  1183  	}
  1184  
  1185  	tmpdir := t.TempDir()
  1186  
  1187  	outname := "mydll.dll"
  1188  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
  1189  	cmd.Dir = tmpdir
  1190  	out, err := cmd.CombinedOutput()
  1191  	if err != nil {
  1192  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1193  	}
  1194  	dllpath := filepath.Join(tmpdir, outname)
  1195  
  1196  	dll := syscall.MustLoadDLL(dllpath)
  1197  	defer dll.Release()
  1198  
  1199  	var ok bool
  1200  	proc := dll.MustFindProc("bigStack")
  1201  	cb := syscall.NewCallback(func() uintptr {
  1202  		// Do something interesting to force stack checks.
  1203  		forceStackCopy()
  1204  		ok = true
  1205  		return 0
  1206  	})
  1207  	proc.Call(cb)
  1208  	if !ok {
  1209  		t.Fatalf("callback not called")
  1210  	}
  1211  }
  1212  
  1213  func TestSyscallStackUsage(t *testing.T) {
  1214  	// Test that the stack usage of a syscall doesn't exceed the limit.
  1215  	// See https://go.dev/issue/69813.
  1216  	syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  1217  	syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  1218  }
  1219  
  1220  var (
  1221  	modwinmm    = syscall.NewLazyDLL("winmm.dll")
  1222  	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  1223  
  1224  	procCreateEvent = modkernel32.NewProc("CreateEventW")
  1225  	procSetEvent    = modkernel32.NewProc("SetEvent")
  1226  )
  1227  
  1228  func createEvent() (syscall.Handle, error) {
  1229  	r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
  1230  	if r0 == 0 {
  1231  		return 0, syscall.Errno(e0)
  1232  	}
  1233  	return syscall.Handle(r0), nil
  1234  }
  1235  
  1236  func setEvent(h syscall.Handle) error {
  1237  	r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
  1238  	if r0 == 0 {
  1239  		return syscall.Errno(e0)
  1240  	}
  1241  	return nil
  1242  }
  1243  
  1244  func BenchmarkChanToSyscallPing(b *testing.B) {
  1245  	n := b.N
  1246  	ch := make(chan int)
  1247  	event, err := createEvent()
  1248  	if err != nil {
  1249  		b.Fatal(err)
  1250  	}
  1251  	go func() {
  1252  		for i := 0; i < n; i++ {
  1253  			syscall.WaitForSingleObject(event, syscall.INFINITE)
  1254  			ch <- 1
  1255  		}
  1256  	}()
  1257  	for i := 0; i < n; i++ {
  1258  		err := setEvent(event)
  1259  		if err != nil {
  1260  			b.Fatal(err)
  1261  		}
  1262  		<-ch
  1263  	}
  1264  }
  1265  
  1266  func BenchmarkSyscallToSyscallPing(b *testing.B) {
  1267  	n := b.N
  1268  	event1, err := createEvent()
  1269  	if err != nil {
  1270  		b.Fatal(err)
  1271  	}
  1272  	event2, err := createEvent()
  1273  	if err != nil {
  1274  		b.Fatal(err)
  1275  	}
  1276  	go func() {
  1277  		for i := 0; i < n; i++ {
  1278  			syscall.WaitForSingleObject(event1, syscall.INFINITE)
  1279  			if err := setEvent(event2); err != nil {
  1280  				b.Errorf("Set event failed: %v", err)
  1281  				return
  1282  			}
  1283  		}
  1284  	}()
  1285  	for i := 0; i < n; i++ {
  1286  		if err := setEvent(event1); err != nil {
  1287  			b.Fatal(err)
  1288  		}
  1289  		if b.Failed() {
  1290  			break
  1291  		}
  1292  		syscall.WaitForSingleObject(event2, syscall.INFINITE)
  1293  	}
  1294  }
  1295  
  1296  func BenchmarkChanToChanPing(b *testing.B) {
  1297  	n := b.N
  1298  	ch1 := make(chan int)
  1299  	ch2 := make(chan int)
  1300  	go func() {
  1301  		for i := 0; i < n; i++ {
  1302  			<-ch1
  1303  			ch2 <- 1
  1304  		}
  1305  	}()
  1306  	for i := 0; i < n; i++ {
  1307  		ch1 <- 1
  1308  		<-ch2
  1309  	}
  1310  }
  1311  
  1312  func BenchmarkOsYield(b *testing.B) {
  1313  	for i := 0; i < b.N; i++ {
  1314  		runtime.OsYield()
  1315  	}
  1316  }
  1317  
  1318  func BenchmarkRunningGoProgram(b *testing.B) {
  1319  	tmpdir := b.TempDir()
  1320  
  1321  	src := filepath.Join(tmpdir, "main.go")
  1322  	err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
  1323  	if err != nil {
  1324  		b.Fatal(err)
  1325  	}
  1326  
  1327  	exe := filepath.Join(tmpdir, "main.exe")
  1328  	cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
  1329  	cmd.Dir = tmpdir
  1330  	out, err := cmd.CombinedOutput()
  1331  	if err != nil {
  1332  		b.Fatalf("building main.exe failed: %v\n%s", err, out)
  1333  	}
  1334  
  1335  	b.ResetTimer()
  1336  	for i := 0; i < b.N; i++ {
  1337  		cmd := exec.Command(exe)
  1338  		out, err := cmd.CombinedOutput()
  1339  		if err != nil {
  1340  			b.Fatalf("running main.exe failed: %v\n%s", err, out)
  1341  		}
  1342  	}
  1343  }
  1344  
  1345  const benchmarkRunningGoProgram = `
  1346  package main
  1347  
  1348  import _ "os" // average Go program will use "os" package, do the same here
  1349  
  1350  func main() {
  1351  }
  1352  `
  1353  

View as plain text