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  	o := runTestProg(t, "testprog", "RaiseException")
   632  	if strings.Contains(o, "RaiseException should not return") {
   633  		t.Fatalf("RaiseException did not crash program: %v", o)
   634  	}
   635  	if !strings.Contains(o, "Exception 0xbad") {
   636  		t.Fatalf("No stack trace: %v", o)
   637  	}
   638  }
   639  
   640  func TestZeroDivisionException(t *testing.T) {
   641  	o := runTestProg(t, "testprog", "ZeroDivisionException")
   642  	if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
   643  		t.Fatalf("No stack trace: %v", o)
   644  	}
   645  }
   646  
   647  func TestWERDialogue(t *testing.T) {
   648  	if os.Getenv("TEST_WER_DIALOGUE") == "1" {
   649  		const EXCEPTION_NONCONTINUABLE = 1
   650  		mod := syscall.MustLoadDLL("kernel32.dll")
   651  		proc := mod.MustFindProc("RaiseException")
   652  		proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   653  		t.Fatal("RaiseException should not return")
   654  	}
   655  	exe, err := os.Executable()
   656  	if err != nil {
   657  		t.Fatal(err)
   658  	}
   659  	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, "-test.run=^TestWERDialogue$"))
   660  	cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
   661  	// Child process should not open WER dialogue, but return immediately instead.
   662  	// The exit code can't be reliably tested here because Windows can change it.
   663  	_, err = cmd.CombinedOutput()
   664  	if err == nil {
   665  		t.Error("test program succeeded unexpectedly")
   666  	}
   667  }
   668  
   669  func TestWindowsStackMemory(t *testing.T) {
   670  	if race.Enabled {
   671  		t.Skip("skipping test: race mode uses more stack memory")
   672  	}
   673  	o := runTestProg(t, "testprog", "StackMemory")
   674  	stackUsage, err := strconv.Atoi(o)
   675  	if err != nil {
   676  		t.Fatalf("Failed to read stack usage: %v", err)
   677  	}
   678  	if expected, got := 128<<10, stackUsage; got > expected {
   679  		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
   680  	}
   681  }
   682  
   683  var used byte
   684  
   685  func use(buf []byte) {
   686  	for _, c := range buf {
   687  		used += c
   688  	}
   689  }
   690  
   691  func forceStackCopy() (r int) {
   692  	var f func(int) int
   693  	f = func(i int) int {
   694  		var buf [256]byte
   695  		use(buf[:])
   696  		if i == 0 {
   697  			return 0
   698  		}
   699  		return i + f(i-1)
   700  	}
   701  	r = f(128)
   702  	return
   703  }
   704  
   705  func TestReturnAfterStackGrowInCallback(t *testing.T) {
   706  	if _, err := exec.LookPath("gcc"); err != nil {
   707  		t.Skip("skipping test: gcc is missing")
   708  	}
   709  
   710  	const src = `
   711  #include <stdint.h>
   712  #include <windows.h>
   713  
   714  typedef uintptr_t __stdcall (*callback)(uintptr_t);
   715  
   716  uintptr_t cfunc(callback f, uintptr_t n) {
   717     uintptr_t r;
   718     r = f(n);
   719     SetLastError(333);
   720     return r;
   721  }
   722  `
   723  	tmpdir := t.TempDir()
   724  
   725  	srcname := "mydll.c"
   726  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   727  	if err != nil {
   728  		t.Fatal(err)
   729  	}
   730  	outname := "mydll.dll"
   731  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   732  	cmd.Dir = tmpdir
   733  	out, err := cmd.CombinedOutput()
   734  	if err != nil {
   735  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   736  	}
   737  	dllpath := filepath.Join(tmpdir, outname)
   738  
   739  	dll := syscall.MustLoadDLL(dllpath)
   740  	defer dll.Release()
   741  
   742  	proc := dll.MustFindProc("cfunc")
   743  
   744  	cb := syscall.NewCallback(func(n uintptr) uintptr {
   745  		forceStackCopy()
   746  		return n
   747  	})
   748  
   749  	// Use a new goroutine so that we get a small stack.
   750  	type result struct {
   751  		r   uintptr
   752  		err syscall.Errno
   753  	}
   754  	want := result{
   755  		// Make it large enough to test issue #29331.
   756  		r:   (^uintptr(0)) >> 24,
   757  		err: 333,
   758  	}
   759  	c := make(chan result)
   760  	go func() {
   761  		r, _, err := proc.Call(cb, want.r)
   762  		c <- result{r, err.(syscall.Errno)}
   763  	}()
   764  	if got := <-c; got != want {
   765  		t.Errorf("got %d want %d", got, want)
   766  	}
   767  }
   768  
   769  func TestSyscallN(t *testing.T) {
   770  	if _, err := exec.LookPath("gcc"); err != nil {
   771  		t.Skip("skipping test: gcc is missing")
   772  	}
   773  
   774  	var nargs = 64
   775  	if testing.Short() {
   776  		nargs = 16
   777  	}
   778  
   779  	for arglen := range nargs {
   780  		t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
   781  			t.Parallel()
   782  			args := make([]string, arglen)
   783  			rets := make([]string, arglen+1)
   784  			params := make([]uintptr, arglen)
   785  			for i := range args {
   786  				args[i] = fmt.Sprintf("int a%d", i)
   787  				rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
   788  				params[i] = uintptr(i)
   789  			}
   790  			rets[arglen] = "1" // for arglen == 0
   791  
   792  			src := fmt.Sprintf(`
   793  		#include <stdint.h>
   794  		#include <windows.h>
   795  		int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
   796  
   797  			tmpdir := t.TempDir()
   798  
   799  			srcname := "mydll.c"
   800  			err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   801  			if err != nil {
   802  				t.Fatal(err)
   803  			}
   804  			outname := "mydll.dll"
   805  			cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   806  			cmd.Dir = tmpdir
   807  			out, err := cmd.CombinedOutput()
   808  			if err != nil {
   809  				t.Fatalf("failed to build dll: %v\n%s", err, out)
   810  			}
   811  			dllpath := filepath.Join(tmpdir, outname)
   812  
   813  			dll := syscall.MustLoadDLL(dllpath)
   814  			defer dll.Release()
   815  
   816  			proc := dll.MustFindProc("cfunc")
   817  
   818  			// proc.Call() will call SyscallN() internally.
   819  			r, _, err := proc.Call(params...)
   820  			if r != 1 {
   821  				t.Errorf("got %d want 1 (err=%v)", r, err)
   822  			}
   823  		})
   824  	}
   825  }
   826  
   827  func TestFloatArgs(t *testing.T) {
   828  	if _, err := exec.LookPath("gcc"); err != nil {
   829  		t.Skip("skipping test: gcc is missing")
   830  	}
   831  	if runtime.GOARCH != "amd64" {
   832  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   833  	}
   834  
   835  	const src = `
   836  #include <stdint.h>
   837  #include <windows.h>
   838  
   839  uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
   840  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   841  		return 1;
   842  	}
   843  	return 0;
   844  }
   845  `
   846  	tmpdir := t.TempDir()
   847  
   848  	srcname := "mydll.c"
   849  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   850  	if err != nil {
   851  		t.Fatal(err)
   852  	}
   853  	outname := "mydll.dll"
   854  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   855  	cmd.Dir = tmpdir
   856  	out, err := cmd.CombinedOutput()
   857  	if err != nil {
   858  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   859  	}
   860  	dllpath := filepath.Join(tmpdir, outname)
   861  
   862  	dll := syscall.MustLoadDLL(dllpath)
   863  	defer dll.Release()
   864  
   865  	proc := dll.MustFindProc("cfunc")
   866  
   867  	r, _, err := proc.Call(
   868  		1,
   869  		uintptr(math.Float64bits(2.2)),
   870  		uintptr(math.Float32bits(3.3)),
   871  		uintptr(math.Float64bits(4.4e44)),
   872  	)
   873  	if r != 1 {
   874  		t.Errorf("got %d want 1 (err=%v)", r, err)
   875  	}
   876  }
   877  
   878  func TestFloatReturn(t *testing.T) {
   879  	if _, err := exec.LookPath("gcc"); err != nil {
   880  		t.Skip("skipping test: gcc is missing")
   881  	}
   882  	if runtime.GOARCH != "amd64" {
   883  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   884  	}
   885  
   886  	const src = `
   887  #include <stdint.h>
   888  #include <windows.h>
   889  
   890  float cfuncFloat(uintptr_t a, double b, float c, double d) {
   891  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   892  		return 1.5f;
   893  	}
   894  	return 0;
   895  }
   896  
   897  double cfuncDouble(uintptr_t a, double b, float c, double d) {
   898  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   899  		return 2.5;
   900  	}
   901  	return 0;
   902  }
   903  `
   904  	tmpdir := t.TempDir()
   905  
   906  	srcname := "mydll.c"
   907  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   908  	if err != nil {
   909  		t.Fatal(err)
   910  	}
   911  	outname := "mydll.dll"
   912  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   913  	cmd.Dir = tmpdir
   914  	out, err := cmd.CombinedOutput()
   915  	if err != nil {
   916  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   917  	}
   918  	dllpath := filepath.Join(tmpdir, outname)
   919  
   920  	dll := syscall.MustLoadDLL(dllpath)
   921  	defer dll.Release()
   922  
   923  	proc := dll.MustFindProc("cfuncFloat")
   924  
   925  	_, r, err := proc.Call(
   926  		1,
   927  		uintptr(math.Float64bits(2.2)),
   928  		uintptr(math.Float32bits(3.3)),
   929  		uintptr(math.Float64bits(4.4e44)),
   930  	)
   931  	fr := math.Float32frombits(uint32(r))
   932  	if fr != 1.5 {
   933  		t.Errorf("got %f want 1.5 (err=%v)", fr, err)
   934  	}
   935  
   936  	proc = dll.MustFindProc("cfuncDouble")
   937  
   938  	_, r, err = proc.Call(
   939  		1,
   940  		uintptr(math.Float64bits(2.2)),
   941  		uintptr(math.Float32bits(3.3)),
   942  		uintptr(math.Float64bits(4.4e44)),
   943  	)
   944  	dr := math.Float64frombits(uint64(r))
   945  	if dr != 2.5 {
   946  		t.Errorf("got %f want 2.5 (err=%v)", dr, err)
   947  	}
   948  }
   949  
   950  func TestTimeBeginPeriod(t *testing.T) {
   951  	const TIMERR_NOERROR = 0
   952  	if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
   953  		t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
   954  	}
   955  }
   956  
   957  // removeOneCPU removes one (any) cpu from affinity mask.
   958  // It returns new affinity mask.
   959  func removeOneCPU(mask uintptr) (uintptr, error) {
   960  	if mask == 0 {
   961  		return 0, fmt.Errorf("cpu affinity mask is empty")
   962  	}
   963  	maskbits := int(unsafe.Sizeof(mask) * 8)
   964  	for i := 0; i < maskbits; i++ {
   965  		newmask := mask & ^(1 << uint(i))
   966  		if newmask != mask {
   967  			return newmask, nil
   968  		}
   969  
   970  	}
   971  	panic("not reached")
   972  }
   973  
   974  func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
   975  	_OpenThread := kernel32.MustFindProc("OpenThread")
   976  	_ResumeThread := kernel32.MustFindProc("ResumeThread")
   977  	_Thread32First := kernel32.MustFindProc("Thread32First")
   978  	_Thread32Next := kernel32.MustFindProc("Thread32Next")
   979  
   980  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
   981  	if err != nil {
   982  		return err
   983  	}
   984  	defer syscall.CloseHandle(snapshot)
   985  
   986  	const _THREAD_SUSPEND_RESUME = 0x0002
   987  
   988  	type ThreadEntry32 struct {
   989  		Size           uint32
   990  		tUsage         uint32
   991  		ThreadID       uint32
   992  		OwnerProcessID uint32
   993  		BasePri        int32
   994  		DeltaPri       int32
   995  		Flags          uint32
   996  	}
   997  
   998  	var te ThreadEntry32
   999  	te.Size = uint32(unsafe.Sizeof(te))
  1000  	ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
  1001  	if ret == 0 {
  1002  		return err
  1003  	}
  1004  	for te.OwnerProcessID != uint32(childpid) {
  1005  		ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
  1006  		if ret == 0 {
  1007  			return err
  1008  		}
  1009  	}
  1010  	h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
  1011  	if h == 0 {
  1012  		return err
  1013  	}
  1014  	defer syscall.Close(syscall.Handle(h))
  1015  
  1016  	ret, _, err = _ResumeThread.Call(h)
  1017  	if ret == 0xffffffff {
  1018  		return err
  1019  	}
  1020  	return nil
  1021  }
  1022  
  1023  func TestNumCPU(t *testing.T) {
  1024  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
  1025  		// in child process
  1026  		fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
  1027  		os.Exit(0)
  1028  	}
  1029  
  1030  	switch n := runtime.NumberOfProcessors(); {
  1031  	case n < 1:
  1032  		t.Fatalf("system cannot have %d cpu(s)", n)
  1033  	case n == 1:
  1034  		if runtime.NumCPU() != 1 {
  1035  			t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
  1036  		}
  1037  		return
  1038  	}
  1039  
  1040  	const (
  1041  		_CREATE_SUSPENDED   = 0x00000004
  1042  		_PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
  1043  	)
  1044  
  1045  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
  1046  	_GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
  1047  	_SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
  1048  
  1049  	cmd := exec.Command(testenv.Executable(t), "-test.run=^TestNumCPU$")
  1050  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
  1051  	var buf strings.Builder
  1052  	cmd.Stdout = &buf
  1053  	cmd.Stderr = &buf
  1054  	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
  1055  	err := cmd.Start()
  1056  	if err != nil {
  1057  		t.Fatal(err)
  1058  	}
  1059  	defer func() {
  1060  		err = cmd.Wait()
  1061  		childOutput := buf.String()
  1062  		if err != nil {
  1063  			t.Fatalf("child failed: %v: %v", err, childOutput)
  1064  		}
  1065  		// removeOneCPU should have decreased child cpu count by 1
  1066  		want := fmt.Sprintf("%d", runtime.NumCPU()-1)
  1067  		if childOutput != want {
  1068  			t.Fatalf("child output: want %q, got %q", want, childOutput)
  1069  		}
  1070  	}()
  1071  
  1072  	defer func() {
  1073  		err = resumeChildThread(kernel32, cmd.Process.Pid)
  1074  		if err != nil {
  1075  			t.Fatal(err)
  1076  		}
  1077  	}()
  1078  
  1079  	ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
  1080  	if err != nil {
  1081  		t.Fatal(err)
  1082  	}
  1083  	defer syscall.CloseHandle(ph)
  1084  
  1085  	var mask, sysmask uintptr
  1086  	ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1087  	if ret == 0 {
  1088  		t.Fatal(err)
  1089  	}
  1090  
  1091  	newmask, err := removeOneCPU(mask)
  1092  	if err != nil {
  1093  		t.Fatal(err)
  1094  	}
  1095  
  1096  	ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
  1097  	if ret == 0 {
  1098  		t.Fatal(err)
  1099  	}
  1100  	ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1101  	if ret == 0 {
  1102  		t.Fatal(err)
  1103  	}
  1104  	if newmask != mask {
  1105  		t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
  1106  	}
  1107  }
  1108  
  1109  // See Issue 14959
  1110  func TestDLLPreloadMitigation(t *testing.T) {
  1111  	if _, err := exec.LookPath("gcc"); err != nil {
  1112  		t.Skip("skipping test: gcc is missing")
  1113  	}
  1114  
  1115  	tmpdir := t.TempDir()
  1116  
  1117  	const src = `
  1118  #include <stdint.h>
  1119  #include <windows.h>
  1120  
  1121  uintptr_t cfunc(void) {
  1122     SetLastError(123);
  1123     return 0;
  1124  }
  1125  `
  1126  	srcname := "nojack.c"
  1127  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
  1128  	if err != nil {
  1129  		t.Fatal(err)
  1130  	}
  1131  	name := "nojack.dll"
  1132  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
  1133  	cmd.Dir = tmpdir
  1134  	out, err := cmd.CombinedOutput()
  1135  	if err != nil {
  1136  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1137  	}
  1138  	dllpath := filepath.Join(tmpdir, name)
  1139  
  1140  	dll := syscall.MustLoadDLL(dllpath)
  1141  	dll.MustFindProc("cfunc")
  1142  	dll.Release()
  1143  
  1144  	// Get into the directory with the DLL we'll load by base name
  1145  	// ("nojack.dll") Think of this as the user double-clicking an
  1146  	// installer from their Downloads directory where a browser
  1147  	// silently downloaded some malicious DLLs.
  1148  	t.Chdir(tmpdir)
  1149  
  1150  	// First before we can load a DLL from the current directory,
  1151  	// loading it only as "nojack.dll", without an absolute path.
  1152  	delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
  1153  	dll, err = syscall.LoadDLL(name)
  1154  	if err != nil {
  1155  		t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
  1156  	}
  1157  	dll.Release()
  1158  
  1159  	// And now verify that if we register it as a system32-only
  1160  	// DLL, the implicit loading from the current directory no
  1161  	// longer works.
  1162  	sysdll.IsSystemDLL[name] = true
  1163  	dll, err = syscall.LoadDLL(name)
  1164  	if err == nil {
  1165  		dll.Release()
  1166  		t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
  1167  	}
  1168  }
  1169  
  1170  // Test that C code called via a DLL can use large Windows thread
  1171  // stacks and call back in to Go without crashing. See issue #20975.
  1172  //
  1173  // See also TestBigStackCallbackCgo.
  1174  func TestBigStackCallbackSyscall(t *testing.T) {
  1175  	if _, err := exec.LookPath("gcc"); err != nil {
  1176  		t.Skip("skipping test: gcc is missing")
  1177  	}
  1178  
  1179  	srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
  1180  	if err != nil {
  1181  		t.Fatal("Abs failed: ", err)
  1182  	}
  1183  
  1184  	tmpdir := t.TempDir()
  1185  
  1186  	outname := "mydll.dll"
  1187  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
  1188  	cmd.Dir = tmpdir
  1189  	out, err := cmd.CombinedOutput()
  1190  	if err != nil {
  1191  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1192  	}
  1193  	dllpath := filepath.Join(tmpdir, outname)
  1194  
  1195  	dll := syscall.MustLoadDLL(dllpath)
  1196  	defer dll.Release()
  1197  
  1198  	var ok bool
  1199  	proc := dll.MustFindProc("bigStack")
  1200  	cb := syscall.NewCallback(func() uintptr {
  1201  		// Do something interesting to force stack checks.
  1202  		forceStackCopy()
  1203  		ok = true
  1204  		return 0
  1205  	})
  1206  	proc.Call(cb)
  1207  	if !ok {
  1208  		t.Fatalf("callback not called")
  1209  	}
  1210  }
  1211  
  1212  func TestSyscallStackUsage(t *testing.T) {
  1213  	// Test that the stack usage of a syscall doesn't exceed the limit.
  1214  	// See https://go.dev/issue/69813.
  1215  	syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  1216  	syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  1217  }
  1218  
  1219  var (
  1220  	modwinmm    = syscall.NewLazyDLL("winmm.dll")
  1221  	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  1222  
  1223  	procCreateEvent = modkernel32.NewProc("CreateEventW")
  1224  	procSetEvent    = modkernel32.NewProc("SetEvent")
  1225  )
  1226  
  1227  func TestTrueVersion(t *testing.T) {
  1228  	ver, err := syscall.GetVersion()
  1229  	if err != nil {
  1230  		t.Fatalf("GetVersion failed: %v", err)
  1231  	}
  1232  	wantMajor, wantMinor, wantBuild := windows.Version()
  1233  	major := uint32(byte(ver))
  1234  	minor := uint32(uint8(ver >> 8))
  1235  	build := uint32(uint16(ver >> 16))
  1236  	if major != wantMajor || minor != wantMinor || build != wantBuild {
  1237  		t.Errorf("GetVersion = %d.%d (Build %d), want %d.%d (Build %d)", major, minor, build, wantMajor, wantMinor, wantBuild)
  1238  	}
  1239  }
  1240  
  1241  func createEvent() (syscall.Handle, error) {
  1242  	r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
  1243  	if r0 == 0 {
  1244  		return 0, syscall.Errno(e0)
  1245  	}
  1246  	return syscall.Handle(r0), nil
  1247  }
  1248  
  1249  func setEvent(h syscall.Handle) error {
  1250  	r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
  1251  	if r0 == 0 {
  1252  		return syscall.Errno(e0)
  1253  	}
  1254  	return nil
  1255  }
  1256  
  1257  func BenchmarkChanToSyscallPing(b *testing.B) {
  1258  	n := b.N
  1259  	ch := make(chan int)
  1260  	event, err := createEvent()
  1261  	if err != nil {
  1262  		b.Fatal(err)
  1263  	}
  1264  	go func() {
  1265  		for i := 0; i < n; i++ {
  1266  			syscall.WaitForSingleObject(event, syscall.INFINITE)
  1267  			ch <- 1
  1268  		}
  1269  	}()
  1270  	for i := 0; i < n; i++ {
  1271  		err := setEvent(event)
  1272  		if err != nil {
  1273  			b.Fatal(err)
  1274  		}
  1275  		<-ch
  1276  	}
  1277  }
  1278  
  1279  func BenchmarkSyscallToSyscallPing(b *testing.B) {
  1280  	n := b.N
  1281  	event1, err := createEvent()
  1282  	if err != nil {
  1283  		b.Fatal(err)
  1284  	}
  1285  	event2, err := createEvent()
  1286  	if err != nil {
  1287  		b.Fatal(err)
  1288  	}
  1289  	go func() {
  1290  		for i := 0; i < n; i++ {
  1291  			syscall.WaitForSingleObject(event1, syscall.INFINITE)
  1292  			if err := setEvent(event2); err != nil {
  1293  				b.Errorf("Set event failed: %v", err)
  1294  				return
  1295  			}
  1296  		}
  1297  	}()
  1298  	for i := 0; i < n; i++ {
  1299  		if err := setEvent(event1); err != nil {
  1300  			b.Fatal(err)
  1301  		}
  1302  		if b.Failed() {
  1303  			break
  1304  		}
  1305  		syscall.WaitForSingleObject(event2, syscall.INFINITE)
  1306  	}
  1307  }
  1308  
  1309  func BenchmarkChanToChanPing(b *testing.B) {
  1310  	n := b.N
  1311  	ch1 := make(chan int)
  1312  	ch2 := make(chan int)
  1313  	go func() {
  1314  		for i := 0; i < n; i++ {
  1315  			<-ch1
  1316  			ch2 <- 1
  1317  		}
  1318  	}()
  1319  	for i := 0; i < n; i++ {
  1320  		ch1 <- 1
  1321  		<-ch2
  1322  	}
  1323  }
  1324  
  1325  func BenchmarkOsYield(b *testing.B) {
  1326  	for i := 0; i < b.N; i++ {
  1327  		runtime.OsYield()
  1328  	}
  1329  }
  1330  
  1331  func BenchmarkRunningGoProgram(b *testing.B) {
  1332  	tmpdir := b.TempDir()
  1333  
  1334  	src := filepath.Join(tmpdir, "main.go")
  1335  	err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
  1336  	if err != nil {
  1337  		b.Fatal(err)
  1338  	}
  1339  
  1340  	exe := filepath.Join(tmpdir, "main.exe")
  1341  	cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
  1342  	cmd.Dir = tmpdir
  1343  	out, err := cmd.CombinedOutput()
  1344  	if err != nil {
  1345  		b.Fatalf("building main.exe failed: %v\n%s", err, out)
  1346  	}
  1347  
  1348  	b.ResetTimer()
  1349  	for i := 0; i < b.N; i++ {
  1350  		cmd := exec.Command(exe)
  1351  		out, err := cmd.CombinedOutput()
  1352  		if err != nil {
  1353  			b.Fatalf("running main.exe failed: %v\n%s", err, out)
  1354  		}
  1355  	}
  1356  }
  1357  
  1358  const benchmarkRunningGoProgram = `
  1359  package main
  1360  
  1361  import _ "os" // average Go program will use "os" package, do the same here
  1362  
  1363  func main() {
  1364  }
  1365  `
  1366  

View as plain text