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

View as plain text