Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/syscall_windows_test.go

Documentation: runtime

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

View as plain text