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  //
   473  //go:registerparams
   474  func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
   475  	runtime.GC()
   476  	return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
   477  }
   478  
   479  // TODO(register args): Remove this once we switch to using the register
   480  // calling convention by default, since this is redundant with the existing
   481  // tests.
   482  var cbFuncsRegABI = []cbFunc{
   483  	{sum2},
   484  	{sum3},
   485  	{sum4},
   486  	{sum5},
   487  	{sum6},
   488  	{sum7},
   489  	{sum8},
   490  	{sum9},
   491  	{sum10},
   492  	{sum9uint8},
   493  	{sum9uint16},
   494  	{sum9int8},
   495  	{sum5mix},
   496  	{sum5andPair},
   497  	{sum9andGC},
   498  }
   499  
   500  func getCallbackTestFuncs() []cbFunc {
   501  	if regs := runtime.SetIntArgRegs(-1); regs > 0 {
   502  		return cbFuncsRegABI
   503  	}
   504  	return cbFuncs
   505  }
   506  
   507  type cbDLL struct {
   508  	name      string
   509  	buildArgs func(out, src string) []string
   510  }
   511  
   512  func (d *cbDLL) makeSrc(t *testing.T, path string) {
   513  	f, err := os.Create(path)
   514  	if err != nil {
   515  		t.Fatalf("failed to create source file: %v", err)
   516  	}
   517  	defer f.Close()
   518  
   519  	fmt.Fprint(f, `
   520  #include <stdint.h>
   521  typedef struct { uint8_t x, y; } uint8Pair_t;
   522  `)
   523  	for _, cbf := range getCallbackTestFuncs() {
   524  		cbf.cSrc(f, false)
   525  		cbf.cSrc(f, true)
   526  	}
   527  }
   528  
   529  func (d *cbDLL) build(t *testing.T, dir string) string {
   530  	srcname := d.name + ".c"
   531  	d.makeSrc(t, filepath.Join(dir, srcname))
   532  	outname := d.name + ".dll"
   533  	args := d.buildArgs(outname, srcname)
   534  	cmd := exec.Command(args[0], args[1:]...)
   535  	cmd.Dir = dir
   536  	out, err := cmd.CombinedOutput()
   537  	if err != nil {
   538  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   539  	}
   540  	return filepath.Join(dir, outname)
   541  }
   542  
   543  var cbDLLs = []cbDLL{
   544  	{
   545  		"test",
   546  		func(out, src string) []string {
   547  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
   548  		},
   549  	},
   550  	{
   551  		"testO2",
   552  		func(out, src string) []string {
   553  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
   554  		},
   555  	},
   556  }
   557  
   558  func TestStdcallAndCDeclCallbacks(t *testing.T) {
   559  	if _, err := exec.LookPath("gcc"); err != nil {
   560  		t.Skip("skipping test: gcc is missing")
   561  	}
   562  	tmp := t.TempDir()
   563  
   564  	oldRegs := runtime.SetIntArgRegs(abi.IntArgRegs)
   565  	defer runtime.SetIntArgRegs(oldRegs)
   566  
   567  	for _, dll := range cbDLLs {
   568  		t.Run(dll.name, func(t *testing.T) {
   569  			dllPath := dll.build(t, tmp)
   570  			dll := syscall.MustLoadDLL(dllPath)
   571  			defer dll.Release()
   572  			for _, cbf := range getCallbackTestFuncs() {
   573  				t.Run(cbf.cName(false), func(t *testing.T) {
   574  					stdcall := syscall.NewCallback(cbf.goFunc)
   575  					cbf.testOne(t, dll, false, stdcall)
   576  				})
   577  				t.Run(cbf.cName(true), func(t *testing.T) {
   578  					cdecl := syscall.NewCallbackCDecl(cbf.goFunc)
   579  					cbf.testOne(t, dll, true, cdecl)
   580  				})
   581  			}
   582  		})
   583  	}
   584  }
   585  
   586  func TestRegisterClass(t *testing.T) {
   587  	kernel32 := GetDLL(t, "kernel32.dll")
   588  	user32 := GetDLL(t, "user32.dll")
   589  	mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
   590  	cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
   591  		t.Fatal("callback should never get called")
   592  		return 0
   593  	})
   594  	type Wndclassex struct {
   595  		Size       uint32
   596  		Style      uint32
   597  		WndProc    uintptr
   598  		ClsExtra   int32
   599  		WndExtra   int32
   600  		Instance   syscall.Handle
   601  		Icon       syscall.Handle
   602  		Cursor     syscall.Handle
   603  		Background syscall.Handle
   604  		MenuName   *uint16
   605  		ClassName  *uint16
   606  		IconSm     syscall.Handle
   607  	}
   608  	name := syscall.StringToUTF16Ptr("test_window")
   609  	wc := Wndclassex{
   610  		WndProc:   cb,
   611  		Instance:  syscall.Handle(mh),
   612  		ClassName: name,
   613  	}
   614  	wc.Size = uint32(unsafe.Sizeof(wc))
   615  	a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
   616  	if a == 0 {
   617  		t.Fatalf("RegisterClassEx failed: %v", err)
   618  	}
   619  	r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
   620  	if r == 0 {
   621  		t.Fatalf("UnregisterClass failed: %v", err)
   622  	}
   623  }
   624  
   625  func TestOutputDebugString(t *testing.T) {
   626  	d := GetDLL(t, "kernel32.dll")
   627  	p := syscall.StringToUTF16Ptr("testing OutputDebugString")
   628  	d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
   629  }
   630  
   631  func TestRaiseException(t *testing.T) {
   632  	if testenv.Builder() == "windows-amd64-2012" {
   633  		testenv.SkipFlaky(t, 49681)
   634  	}
   635  	o := runTestProg(t, "testprog", "RaiseException")
   636  	if strings.Contains(o, "RaiseException should not return") {
   637  		t.Fatalf("RaiseException did not crash program: %v", o)
   638  	}
   639  	if !strings.Contains(o, "Exception 0xbad") {
   640  		t.Fatalf("No stack trace: %v", o)
   641  	}
   642  }
   643  
   644  func TestZeroDivisionException(t *testing.T) {
   645  	o := runTestProg(t, "testprog", "ZeroDivisionException")
   646  	if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
   647  		t.Fatalf("No stack trace: %v", o)
   648  	}
   649  }
   650  
   651  func TestWERDialogue(t *testing.T) {
   652  	if os.Getenv("TESTING_WER_DIALOGUE") == "1" {
   653  		defer os.Exit(0)
   654  
   655  		*runtime.TestingWER = true
   656  		const EXCEPTION_NONCONTINUABLE = 1
   657  		mod := syscall.MustLoadDLL("kernel32.dll")
   658  		proc := mod.MustFindProc("RaiseException")
   659  		proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   660  		println("RaiseException should not return")
   661  		return
   662  	}
   663  	cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue")
   664  	cmd.Env = []string{"TESTING_WER_DIALOGUE=1"}
   665  	// Child process should not open WER dialogue, but return immediately instead.
   666  	cmd.CombinedOutput()
   667  }
   668  
   669  func TestWindowsStackMemory(t *testing.T) {
   670  	o := runTestProg(t, "testprog", "StackMemory")
   671  	stackUsage, err := strconv.Atoi(o)
   672  	if err != nil {
   673  		t.Fatalf("Failed to read stack usage: %v", err)
   674  	}
   675  	if expected, got := 100<<10, stackUsage; got > expected {
   676  		t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
   677  	}
   678  }
   679  
   680  var used byte
   681  
   682  func use(buf []byte) {
   683  	for _, c := range buf {
   684  		used += c
   685  	}
   686  }
   687  
   688  func forceStackCopy() (r int) {
   689  	var f func(int) int
   690  	f = func(i int) int {
   691  		var buf [256]byte
   692  		use(buf[:])
   693  		if i == 0 {
   694  			return 0
   695  		}
   696  		return i + f(i-1)
   697  	}
   698  	r = f(128)
   699  	return
   700  }
   701  
   702  func TestReturnAfterStackGrowInCallback(t *testing.T) {
   703  	if _, err := exec.LookPath("gcc"); err != nil {
   704  		t.Skip("skipping test: gcc is missing")
   705  	}
   706  
   707  	const src = `
   708  #include <stdint.h>
   709  #include <windows.h>
   710  
   711  typedef uintptr_t __stdcall (*callback)(uintptr_t);
   712  
   713  uintptr_t cfunc(callback f, uintptr_t n) {
   714     uintptr_t r;
   715     r = f(n);
   716     SetLastError(333);
   717     return r;
   718  }
   719  `
   720  	tmpdir := t.TempDir()
   721  
   722  	srcname := "mydll.c"
   723  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   724  	if err != nil {
   725  		t.Fatal(err)
   726  	}
   727  	outname := "mydll.dll"
   728  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   729  	cmd.Dir = tmpdir
   730  	out, err := cmd.CombinedOutput()
   731  	if err != nil {
   732  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   733  	}
   734  	dllpath := filepath.Join(tmpdir, outname)
   735  
   736  	dll := syscall.MustLoadDLL(dllpath)
   737  	defer dll.Release()
   738  
   739  	proc := dll.MustFindProc("cfunc")
   740  
   741  	cb := syscall.NewCallback(func(n uintptr) uintptr {
   742  		forceStackCopy()
   743  		return n
   744  	})
   745  
   746  	// Use a new goroutine so that we get a small stack.
   747  	type result struct {
   748  		r   uintptr
   749  		err syscall.Errno
   750  	}
   751  	want := result{
   752  		// Make it large enough to test issue #29331.
   753  		r:   (^uintptr(0)) >> 24,
   754  		err: 333,
   755  	}
   756  	c := make(chan result)
   757  	go func() {
   758  		r, _, err := proc.Call(cb, want.r)
   759  		c <- result{r, err.(syscall.Errno)}
   760  	}()
   761  	if got := <-c; got != want {
   762  		t.Errorf("got %d want %d", got, want)
   763  	}
   764  }
   765  
   766  func TestSyscallN(t *testing.T) {
   767  	if _, err := exec.LookPath("gcc"); err != nil {
   768  		t.Skip("skipping test: gcc is missing")
   769  	}
   770  	if runtime.GOARCH != "amd64" {
   771  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   772  	}
   773  
   774  	for arglen := 0; arglen <= runtime.MaxArgs; arglen++ {
   775  		arglen := arglen
   776  		t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
   777  			t.Parallel()
   778  			args := make([]string, arglen)
   779  			rets := make([]string, arglen+1)
   780  			params := make([]uintptr, arglen)
   781  			for i := range args {
   782  				args[i] = fmt.Sprintf("int a%d", i)
   783  				rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
   784  				params[i] = uintptr(i)
   785  			}
   786  			rets[arglen] = "1" // for arglen == 0
   787  
   788  			src := fmt.Sprintf(`
   789  		#include <stdint.h>
   790  		#include <windows.h>
   791  		int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
   792  
   793  			tmpdir := t.TempDir()
   794  
   795  			srcname := "mydll.c"
   796  			err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   797  			if err != nil {
   798  				t.Fatal(err)
   799  			}
   800  			outname := "mydll.dll"
   801  			cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   802  			cmd.Dir = tmpdir
   803  			out, err := cmd.CombinedOutput()
   804  			if err != nil {
   805  				t.Fatalf("failed to build dll: %v\n%s", err, out)
   806  			}
   807  			dllpath := filepath.Join(tmpdir, outname)
   808  
   809  			dll := syscall.MustLoadDLL(dllpath)
   810  			defer dll.Release()
   811  
   812  			proc := dll.MustFindProc("cfunc")
   813  
   814  			// proc.Call() will call SyscallN() internally.
   815  			r, _, err := proc.Call(params...)
   816  			if r != 1 {
   817  				t.Errorf("got %d want 1 (err=%v)", r, err)
   818  			}
   819  		})
   820  	}
   821  }
   822  
   823  func TestFloatArgs(t *testing.T) {
   824  	if _, err := exec.LookPath("gcc"); err != nil {
   825  		t.Skip("skipping test: gcc is missing")
   826  	}
   827  	if runtime.GOARCH != "amd64" {
   828  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   829  	}
   830  
   831  	const src = `
   832  #include <stdint.h>
   833  #include <windows.h>
   834  
   835  uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
   836  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   837  		return 1;
   838  	}
   839  	return 0;
   840  }
   841  `
   842  	tmpdir := t.TempDir()
   843  
   844  	srcname := "mydll.c"
   845  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   846  	if err != nil {
   847  		t.Fatal(err)
   848  	}
   849  	outname := "mydll.dll"
   850  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   851  	cmd.Dir = tmpdir
   852  	out, err := cmd.CombinedOutput()
   853  	if err != nil {
   854  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   855  	}
   856  	dllpath := filepath.Join(tmpdir, outname)
   857  
   858  	dll := syscall.MustLoadDLL(dllpath)
   859  	defer dll.Release()
   860  
   861  	proc := dll.MustFindProc("cfunc")
   862  
   863  	r, _, err := proc.Call(
   864  		1,
   865  		uintptr(math.Float64bits(2.2)),
   866  		uintptr(math.Float32bits(3.3)),
   867  		uintptr(math.Float64bits(4.4e44)),
   868  	)
   869  	if r != 1 {
   870  		t.Errorf("got %d want 1 (err=%v)", r, err)
   871  	}
   872  }
   873  
   874  func TestFloatReturn(t *testing.T) {
   875  	if _, err := exec.LookPath("gcc"); err != nil {
   876  		t.Skip("skipping test: gcc is missing")
   877  	}
   878  	if runtime.GOARCH != "amd64" {
   879  		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
   880  	}
   881  
   882  	const src = `
   883  #include <stdint.h>
   884  #include <windows.h>
   885  
   886  float cfuncFloat(uintptr_t a, double b, float c, double d) {
   887  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   888  		return 1.5f;
   889  	}
   890  	return 0;
   891  }
   892  
   893  double cfuncDouble(uintptr_t a, double b, float c, double d) {
   894  	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
   895  		return 2.5;
   896  	}
   897  	return 0;
   898  }
   899  `
   900  	tmpdir := t.TempDir()
   901  
   902  	srcname := "mydll.c"
   903  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   904  	if err != nil {
   905  		t.Fatal(err)
   906  	}
   907  	outname := "mydll.dll"
   908  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   909  	cmd.Dir = tmpdir
   910  	out, err := cmd.CombinedOutput()
   911  	if err != nil {
   912  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   913  	}
   914  	dllpath := filepath.Join(tmpdir, outname)
   915  
   916  	dll := syscall.MustLoadDLL(dllpath)
   917  	defer dll.Release()
   918  
   919  	proc := dll.MustFindProc("cfuncFloat")
   920  
   921  	_, r, err := proc.Call(
   922  		1,
   923  		uintptr(math.Float64bits(2.2)),
   924  		uintptr(math.Float32bits(3.3)),
   925  		uintptr(math.Float64bits(4.4e44)),
   926  	)
   927  	fr := math.Float32frombits(uint32(r))
   928  	if fr != 1.5 {
   929  		t.Errorf("got %f want 1.5 (err=%v)", fr, err)
   930  	}
   931  
   932  	proc = dll.MustFindProc("cfuncDouble")
   933  
   934  	_, r, err = proc.Call(
   935  		1,
   936  		uintptr(math.Float64bits(2.2)),
   937  		uintptr(math.Float32bits(3.3)),
   938  		uintptr(math.Float64bits(4.4e44)),
   939  	)
   940  	dr := math.Float64frombits(uint64(r))
   941  	if dr != 2.5 {
   942  		t.Errorf("got %f want 2.5 (err=%v)", dr, err)
   943  	}
   944  }
   945  
   946  func TestTimeBeginPeriod(t *testing.T) {
   947  	const TIMERR_NOERROR = 0
   948  	if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
   949  		t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
   950  	}
   951  }
   952  
   953  // removeOneCPU removes one (any) cpu from affinity mask.
   954  // It returns new affinity mask.
   955  func removeOneCPU(mask uintptr) (uintptr, error) {
   956  	if mask == 0 {
   957  		return 0, fmt.Errorf("cpu affinity mask is empty")
   958  	}
   959  	maskbits := int(unsafe.Sizeof(mask) * 8)
   960  	for i := 0; i < maskbits; i++ {
   961  		newmask := mask & ^(1 << uint(i))
   962  		if newmask != mask {
   963  			return newmask, nil
   964  		}
   965  
   966  	}
   967  	panic("not reached")
   968  }
   969  
   970  func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
   971  	_OpenThread := kernel32.MustFindProc("OpenThread")
   972  	_ResumeThread := kernel32.MustFindProc("ResumeThread")
   973  	_Thread32First := kernel32.MustFindProc("Thread32First")
   974  	_Thread32Next := kernel32.MustFindProc("Thread32Next")
   975  
   976  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
   977  	if err != nil {
   978  		return err
   979  	}
   980  	defer syscall.CloseHandle(snapshot)
   981  
   982  	const _THREAD_SUSPEND_RESUME = 0x0002
   983  
   984  	type ThreadEntry32 struct {
   985  		Size           uint32
   986  		tUsage         uint32
   987  		ThreadID       uint32
   988  		OwnerProcessID uint32
   989  		BasePri        int32
   990  		DeltaPri       int32
   991  		Flags          uint32
   992  	}
   993  
   994  	var te ThreadEntry32
   995  	te.Size = uint32(unsafe.Sizeof(te))
   996  	ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
   997  	if ret == 0 {
   998  		return err
   999  	}
  1000  	for te.OwnerProcessID != uint32(childpid) {
  1001  		ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
  1002  		if ret == 0 {
  1003  			return err
  1004  		}
  1005  	}
  1006  	h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
  1007  	if h == 0 {
  1008  		return err
  1009  	}
  1010  	defer syscall.Close(syscall.Handle(h))
  1011  
  1012  	ret, _, err = _ResumeThread.Call(h)
  1013  	if ret == 0xffffffff {
  1014  		return err
  1015  	}
  1016  	return nil
  1017  }
  1018  
  1019  func TestNumCPU(t *testing.T) {
  1020  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
  1021  		// in child process
  1022  		fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
  1023  		os.Exit(0)
  1024  	}
  1025  
  1026  	switch n := runtime.NumberOfProcessors(); {
  1027  	case n < 1:
  1028  		t.Fatalf("system cannot have %d cpu(s)", n)
  1029  	case n == 1:
  1030  		if runtime.NumCPU() != 1 {
  1031  			t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
  1032  		}
  1033  		return
  1034  	}
  1035  
  1036  	const (
  1037  		_CREATE_SUSPENDED   = 0x00000004
  1038  		_PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
  1039  	)
  1040  
  1041  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
  1042  	_GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
  1043  	_SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
  1044  
  1045  	cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU")
  1046  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
  1047  	var buf bytes.Buffer
  1048  	cmd.Stdout = &buf
  1049  	cmd.Stderr = &buf
  1050  	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
  1051  	err := cmd.Start()
  1052  	if err != nil {
  1053  		t.Fatal(err)
  1054  	}
  1055  	defer func() {
  1056  		err = cmd.Wait()
  1057  		childOutput := string(buf.Bytes())
  1058  		if err != nil {
  1059  			t.Fatalf("child failed: %v: %v", err, childOutput)
  1060  		}
  1061  		// removeOneCPU should have decreased child cpu count by 1
  1062  		want := fmt.Sprintf("%d", runtime.NumCPU()-1)
  1063  		if childOutput != want {
  1064  			t.Fatalf("child output: want %q, got %q", want, childOutput)
  1065  		}
  1066  	}()
  1067  
  1068  	defer func() {
  1069  		err = resumeChildThread(kernel32, cmd.Process.Pid)
  1070  		if err != nil {
  1071  			t.Fatal(err)
  1072  		}
  1073  	}()
  1074  
  1075  	ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
  1076  	if err != nil {
  1077  		t.Fatal(err)
  1078  	}
  1079  	defer syscall.CloseHandle(ph)
  1080  
  1081  	var mask, sysmask uintptr
  1082  	ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1083  	if ret == 0 {
  1084  		t.Fatal(err)
  1085  	}
  1086  
  1087  	newmask, err := removeOneCPU(mask)
  1088  	if err != nil {
  1089  		t.Fatal(err)
  1090  	}
  1091  
  1092  	ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
  1093  	if ret == 0 {
  1094  		t.Fatal(err)
  1095  	}
  1096  	ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
  1097  	if ret == 0 {
  1098  		t.Fatal(err)
  1099  	}
  1100  	if newmask != mask {
  1101  		t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
  1102  	}
  1103  }
  1104  
  1105  // See Issue 14959
  1106  func TestDLLPreloadMitigation(t *testing.T) {
  1107  	if _, err := exec.LookPath("gcc"); err != nil {
  1108  		t.Skip("skipping test: gcc is missing")
  1109  	}
  1110  
  1111  	tmpdir := t.TempDir()
  1112  
  1113  	dir0, err := os.Getwd()
  1114  	if err != nil {
  1115  		t.Fatal(err)
  1116  	}
  1117  	defer os.Chdir(dir0)
  1118  
  1119  	const src = `
  1120  #include <stdint.h>
  1121  #include <windows.h>
  1122  
  1123  uintptr_t cfunc(void) {
  1124     SetLastError(123);
  1125     return 0;
  1126  }
  1127  `
  1128  	srcname := "nojack.c"
  1129  	err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
  1130  	if err != nil {
  1131  		t.Fatal(err)
  1132  	}
  1133  	name := "nojack.dll"
  1134  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
  1135  	cmd.Dir = tmpdir
  1136  	out, err := cmd.CombinedOutput()
  1137  	if err != nil {
  1138  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1139  	}
  1140  	dllpath := filepath.Join(tmpdir, name)
  1141  
  1142  	dll := syscall.MustLoadDLL(dllpath)
  1143  	dll.MustFindProc("cfunc")
  1144  	dll.Release()
  1145  
  1146  	// Get into the directory with the DLL we'll load by base name
  1147  	// ("nojack.dll") Think of this as the user double-clicking an
  1148  	// installer from their Downloads directory where a browser
  1149  	// silently downloaded some malicious DLLs.
  1150  	os.Chdir(tmpdir)
  1151  
  1152  	// First before we can load a DLL from the current directory,
  1153  	// loading it only as "nojack.dll", without an absolute path.
  1154  	delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
  1155  	dll, err = syscall.LoadDLL(name)
  1156  	if err != nil {
  1157  		t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
  1158  	}
  1159  	dll.Release()
  1160  
  1161  	// And now verify that if we register it as a system32-only
  1162  	// DLL, the implicit loading from the current directory no
  1163  	// longer works.
  1164  	sysdll.IsSystemDLL[name] = true
  1165  	dll, err = syscall.LoadDLL(name)
  1166  	if err == nil {
  1167  		dll.Release()
  1168  		if wantLoadLibraryEx() {
  1169  			t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
  1170  		}
  1171  		t.Skip("insecure load of DLL, but expected")
  1172  	}
  1173  }
  1174  
  1175  // Test that C code called via a DLL can use large Windows thread
  1176  // stacks and call back in to Go without crashing. See issue #20975.
  1177  //
  1178  // See also TestBigStackCallbackCgo.
  1179  func TestBigStackCallbackSyscall(t *testing.T) {
  1180  	if _, err := exec.LookPath("gcc"); err != nil {
  1181  		t.Skip("skipping test: gcc is missing")
  1182  	}
  1183  
  1184  	srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
  1185  	if err != nil {
  1186  		t.Fatal("Abs failed: ", err)
  1187  	}
  1188  
  1189  	tmpdir := t.TempDir()
  1190  
  1191  	outname := "mydll.dll"
  1192  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
  1193  	cmd.Dir = tmpdir
  1194  	out, err := cmd.CombinedOutput()
  1195  	if err != nil {
  1196  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1197  	}
  1198  	dllpath := filepath.Join(tmpdir, outname)
  1199  
  1200  	dll := syscall.MustLoadDLL(dllpath)
  1201  	defer dll.Release()
  1202  
  1203  	var ok bool
  1204  	proc := dll.MustFindProc("bigStack")
  1205  	cb := syscall.NewCallback(func() uintptr {
  1206  		// Do something interesting to force stack checks.
  1207  		forceStackCopy()
  1208  		ok = true
  1209  		return 0
  1210  	})
  1211  	proc.Call(cb)
  1212  	if !ok {
  1213  		t.Fatalf("callback not called")
  1214  	}
  1215  }
  1216  
  1217  // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
  1218  func wantLoadLibraryEx() bool {
  1219  	return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce"
  1220  }
  1221  
  1222  func TestLoadLibraryEx(t *testing.T) {
  1223  	use, have, flags := runtime.LoadLibraryExStatus()
  1224  	if use {
  1225  		return // success.
  1226  	}
  1227  	if wantLoadLibraryEx() {
  1228  		t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)",
  1229  			have, flags)
  1230  	}
  1231  	t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
  1232  		have, flags)
  1233  }
  1234  
  1235  var (
  1236  	modwinmm    = syscall.NewLazyDLL("winmm.dll")
  1237  	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
  1238  
  1239  	procCreateEvent = modkernel32.NewProc("CreateEventW")
  1240  	procSetEvent    = modkernel32.NewProc("SetEvent")
  1241  )
  1242  
  1243  func createEvent() (syscall.Handle, error) {
  1244  	r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
  1245  	if r0 == 0 {
  1246  		return 0, syscall.Errno(e0)
  1247  	}
  1248  	return syscall.Handle(r0), nil
  1249  }
  1250  
  1251  func setEvent(h syscall.Handle) error {
  1252  	r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
  1253  	if r0 == 0 {
  1254  		return syscall.Errno(e0)
  1255  	}
  1256  	return nil
  1257  }
  1258  
  1259  func BenchmarkChanToSyscallPing(b *testing.B) {
  1260  	n := b.N
  1261  	ch := make(chan int)
  1262  	event, err := createEvent()
  1263  	if err != nil {
  1264  		b.Fatal(err)
  1265  	}
  1266  	go func() {
  1267  		for i := 0; i < n; i++ {
  1268  			syscall.WaitForSingleObject(event, syscall.INFINITE)
  1269  			ch <- 1
  1270  		}
  1271  	}()
  1272  	for i := 0; i < n; i++ {
  1273  		err := setEvent(event)
  1274  		if err != nil {
  1275  			b.Fatal(err)
  1276  		}
  1277  		<-ch
  1278  	}
  1279  }
  1280  
  1281  func BenchmarkSyscallToSyscallPing(b *testing.B) {
  1282  	n := b.N
  1283  	event1, err := createEvent()
  1284  	if err != nil {
  1285  		b.Fatal(err)
  1286  	}
  1287  	event2, err := createEvent()
  1288  	if err != nil {
  1289  		b.Fatal(err)
  1290  	}
  1291  	go func() {
  1292  		for i := 0; i < n; i++ {
  1293  			syscall.WaitForSingleObject(event1, syscall.INFINITE)
  1294  			if err := setEvent(event2); err != nil {
  1295  				b.Errorf("Set event failed: %v", err)
  1296  				return
  1297  			}
  1298  		}
  1299  	}()
  1300  	for i := 0; i < n; i++ {
  1301  		if err := setEvent(event1); err != nil {
  1302  			b.Fatal(err)
  1303  		}
  1304  		if b.Failed() {
  1305  			break
  1306  		}
  1307  		syscall.WaitForSingleObject(event2, syscall.INFINITE)
  1308  	}
  1309  }
  1310  
  1311  func BenchmarkChanToChanPing(b *testing.B) {
  1312  	n := b.N
  1313  	ch1 := make(chan int)
  1314  	ch2 := make(chan int)
  1315  	go func() {
  1316  		for i := 0; i < n; i++ {
  1317  			<-ch1
  1318  			ch2 <- 1
  1319  		}
  1320  	}()
  1321  	for i := 0; i < n; i++ {
  1322  		ch1 <- 1
  1323  		<-ch2
  1324  	}
  1325  }
  1326  
  1327  func BenchmarkOsYield(b *testing.B) {
  1328  	for i := 0; i < b.N; i++ {
  1329  		runtime.OsYield()
  1330  	}
  1331  }
  1332  
  1333  func BenchmarkRunningGoProgram(b *testing.B) {
  1334  	tmpdir := b.TempDir()
  1335  
  1336  	src := filepath.Join(tmpdir, "main.go")
  1337  	err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
  1338  	if err != nil {
  1339  		b.Fatal(err)
  1340  	}
  1341  
  1342  	exe := filepath.Join(tmpdir, "main.exe")
  1343  	cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
  1344  	cmd.Dir = tmpdir
  1345  	out, err := cmd.CombinedOutput()
  1346  	if err != nil {
  1347  		b.Fatalf("building main.exe failed: %v\n%s", err, out)
  1348  	}
  1349  
  1350  	b.ResetTimer()
  1351  	for i := 0; i < b.N; i++ {
  1352  		cmd := exec.Command(exe)
  1353  		out, err := cmd.CombinedOutput()
  1354  		if err != nil {
  1355  			b.Fatalf("running main.exe failed: %v\n%s", err, out)
  1356  		}
  1357  	}
  1358  }
  1359  
  1360  const benchmarkRunningGoProgram = `
  1361  package main
  1362  
  1363  import _ "os" // average Go program will use "os" package, do the same here
  1364  
  1365  func main() {
  1366  }
  1367  `
  1368  

View as plain text