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

View as plain text