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

View as plain text