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

View as plain text