...

Source file src/runtime/syscall_windows_test.go

Documentation: runtime

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

View as plain text