github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/runtime/syscall_windows_test.go (about)

     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  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"syscall"
    17  	"testing"
    18  	"unsafe"
    19  )
    20  
    21  type DLL struct {
    22  	*syscall.DLL
    23  	t *testing.T
    24  }
    25  
    26  func GetDLL(t *testing.T, name string) *DLL {
    27  	d, e := syscall.LoadDLL(name)
    28  	if e != nil {
    29  		t.Fatal(e)
    30  	}
    31  	return &DLL{DLL: d, t: t}
    32  }
    33  
    34  func (d *DLL) Proc(name string) *syscall.Proc {
    35  	p, e := d.FindProc(name)
    36  	if e != nil {
    37  		d.t.Fatal(e)
    38  	}
    39  	return p
    40  }
    41  
    42  func TestStdCall(t *testing.T) {
    43  	type Rect struct {
    44  		left, top, right, bottom int32
    45  	}
    46  	res := Rect{}
    47  	expected := Rect{1, 1, 40, 60}
    48  	a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
    49  		uintptr(unsafe.Pointer(&res)),
    50  		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
    51  		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
    52  	if a != 1 || res.left != expected.left ||
    53  		res.top != expected.top ||
    54  		res.right != expected.right ||
    55  		res.bottom != expected.bottom {
    56  		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
    57  	}
    58  }
    59  
    60  func Test64BitReturnStdCall(t *testing.T) {
    61  
    62  	const (
    63  		VER_BUILDNUMBER      = 0x0000004
    64  		VER_MAJORVERSION     = 0x0000002
    65  		VER_MINORVERSION     = 0x0000001
    66  		VER_PLATFORMID       = 0x0000008
    67  		VER_PRODUCT_TYPE     = 0x0000080
    68  		VER_SERVICEPACKMAJOR = 0x0000020
    69  		VER_SERVICEPACKMINOR = 0x0000010
    70  		VER_SUITENAME        = 0x0000040
    71  
    72  		VER_EQUAL         = 1
    73  		VER_GREATER       = 2
    74  		VER_GREATER_EQUAL = 3
    75  		VER_LESS          = 4
    76  		VER_LESS_EQUAL    = 5
    77  
    78  		ERROR_OLD_WIN_VERSION syscall.Errno = 1150
    79  	)
    80  
    81  	type OSVersionInfoEx struct {
    82  		OSVersionInfoSize uint32
    83  		MajorVersion      uint32
    84  		MinorVersion      uint32
    85  		BuildNumber       uint32
    86  		PlatformId        uint32
    87  		CSDVersion        [128]uint16
    88  		ServicePackMajor  uint16
    89  		ServicePackMinor  uint16
    90  		SuiteMask         uint16
    91  		ProductType       byte
    92  		Reserve           byte
    93  	}
    94  
    95  	d := GetDLL(t, "kernel32.dll")
    96  
    97  	var m1, m2 uintptr
    98  	VerSetConditionMask := d.Proc("VerSetConditionMask")
    99  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
   100  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
   101  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
   102  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)
   103  
   104  	vi := OSVersionInfoEx{
   105  		MajorVersion:     5,
   106  		MinorVersion:     1,
   107  		ServicePackMajor: 2,
   108  		ServicePackMinor: 0,
   109  	}
   110  	vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
   111  	r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
   112  		uintptr(unsafe.Pointer(&vi)),
   113  		VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
   114  		m1, m2)
   115  	if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
   116  		t.Errorf("VerifyVersionInfo failed: %s", e2)
   117  	}
   118  }
   119  
   120  func TestCDecl(t *testing.T) {
   121  	var buf [50]byte
   122  	fmtp, _ := syscall.BytePtrFromString("%d %d %d")
   123  	a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
   124  		uintptr(unsafe.Pointer(&buf[0])),
   125  		uintptr(unsafe.Pointer(fmtp)),
   126  		1000, 2000, 3000)
   127  	if string(buf[:a]) != "1000 2000 3000" {
   128  		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
   129  	}
   130  }
   131  
   132  func TestEnumWindows(t *testing.T) {
   133  	d := GetDLL(t, "user32.dll")
   134  	isWindows := d.Proc("IsWindow")
   135  	counter := 0
   136  	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
   137  		if lparam != 888 {
   138  			t.Error("lparam was not passed to callback")
   139  		}
   140  		b, _, _ := isWindows.Call(uintptr(hwnd))
   141  		if b == 0 {
   142  			t.Error("USER32.IsWindow returns FALSE")
   143  		}
   144  		counter++
   145  		return 1 // continue enumeration
   146  	})
   147  	a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
   148  	if a == 0 {
   149  		t.Error("USER32.EnumWindows returns FALSE")
   150  	}
   151  	if counter == 0 {
   152  		t.Error("Callback has been never called or your have no windows")
   153  	}
   154  }
   155  
   156  func callback(hwnd syscall.Handle, lparam uintptr) uintptr {
   157  	(*(*func())(unsafe.Pointer(&lparam)))()
   158  	return 0 // stop enumeration
   159  }
   160  
   161  // nestedCall calls into Windows, back into Go, and finally to f.
   162  func nestedCall(t *testing.T, f func()) {
   163  	c := syscall.NewCallback(callback)
   164  	d := GetDLL(t, "user32.dll")
   165  	defer d.Release()
   166  	d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
   167  }
   168  
   169  func TestCallback(t *testing.T) {
   170  	var x = false
   171  	nestedCall(t, func() { x = true })
   172  	if !x {
   173  		t.Fatal("nestedCall did not call func")
   174  	}
   175  }
   176  
   177  func TestCallbackGC(t *testing.T) {
   178  	nestedCall(t, runtime.GC)
   179  }
   180  
   181  func TestCallbackPanicLocked(t *testing.T) {
   182  	runtime.LockOSThread()
   183  	defer runtime.UnlockOSThread()
   184  
   185  	if !runtime.LockedOSThread() {
   186  		t.Fatal("runtime.LockOSThread didn't")
   187  	}
   188  	defer func() {
   189  		s := recover()
   190  		if s == nil {
   191  			t.Fatal("did not panic")
   192  		}
   193  		if s.(string) != "callback panic" {
   194  			t.Fatal("wrong panic:", s)
   195  		}
   196  		if !runtime.LockedOSThread() {
   197  			t.Fatal("lost lock on OS thread after panic")
   198  		}
   199  	}()
   200  	nestedCall(t, func() { panic("callback panic") })
   201  	panic("nestedCall returned")
   202  }
   203  
   204  func TestCallbackPanic(t *testing.T) {
   205  	// Make sure panic during callback unwinds properly.
   206  	if runtime.LockedOSThread() {
   207  		t.Fatal("locked OS thread on entry to TestCallbackPanic")
   208  	}
   209  	defer func() {
   210  		s := recover()
   211  		if s == nil {
   212  			t.Fatal("did not panic")
   213  		}
   214  		if s.(string) != "callback panic" {
   215  			t.Fatal("wrong panic:", s)
   216  		}
   217  		if runtime.LockedOSThread() {
   218  			t.Fatal("locked OS thread on exit from TestCallbackPanic")
   219  		}
   220  	}()
   221  	nestedCall(t, func() { panic("callback panic") })
   222  	panic("nestedCall returned")
   223  }
   224  
   225  func TestCallbackPanicLoop(t *testing.T) {
   226  	// Make sure we don't blow out m->g0 stack.
   227  	for i := 0; i < 100000; i++ {
   228  		TestCallbackPanic(t)
   229  	}
   230  }
   231  
   232  func TestBlockingCallback(t *testing.T) {
   233  	c := make(chan int)
   234  	go func() {
   235  		for i := 0; i < 10; i++ {
   236  			c <- <-c
   237  		}
   238  	}()
   239  	nestedCall(t, func() {
   240  		for i := 0; i < 10; i++ {
   241  			c <- i
   242  			if j := <-c; j != i {
   243  				t.Errorf("out of sync %d != %d", j, i)
   244  			}
   245  		}
   246  	})
   247  }
   248  
   249  func TestCallbackInAnotherThread(t *testing.T) {
   250  	// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
   251  }
   252  
   253  type cbDLLFunc int // int determines number of callback parameters
   254  
   255  func (f cbDLLFunc) stdcallName() string {
   256  	return fmt.Sprintf("stdcall%d", f)
   257  }
   258  
   259  func (f cbDLLFunc) cdeclName() string {
   260  	return fmt.Sprintf("cdecl%d", f)
   261  }
   262  
   263  func (f cbDLLFunc) buildOne(stdcall bool) string {
   264  	var funcname, attr string
   265  	if stdcall {
   266  		funcname = f.stdcallName()
   267  		attr = "__stdcall"
   268  	} else {
   269  		funcname = f.cdeclName()
   270  		attr = "__cdecl"
   271  	}
   272  	typename := "t" + funcname
   273  	p := make([]string, f)
   274  	for i := range p {
   275  		p[i] = "uintptr_t"
   276  	}
   277  	params := strings.Join(p, ",")
   278  	for i := range p {
   279  		p[i] = fmt.Sprintf("%d", i+1)
   280  	}
   281  	args := strings.Join(p, ",")
   282  	return fmt.Sprintf(`
   283  typedef void %s (*%s)(%s);
   284  void %s(%s f, uintptr_t n) {
   285  	uintptr_t i;
   286  	for(i=0;i<n;i++){
   287  		f(%s);
   288  	}
   289  }
   290  	`, attr, typename, params, funcname, typename, args)
   291  }
   292  
   293  func (f cbDLLFunc) build() string {
   294  	return "#include <stdint.h>\n\n" + f.buildOne(false) + f.buildOne(true)
   295  }
   296  
   297  var cbFuncs = [...]interface{}{
   298  	2: func(i1, i2 uintptr) uintptr {
   299  		if i1+i2 != 3 {
   300  			panic("bad input")
   301  		}
   302  		return 0
   303  	},
   304  	3: func(i1, i2, i3 uintptr) uintptr {
   305  		if i1+i2+i3 != 6 {
   306  			panic("bad input")
   307  		}
   308  		return 0
   309  	},
   310  	4: func(i1, i2, i3, i4 uintptr) uintptr {
   311  		if i1+i2+i3+i4 != 10 {
   312  			panic("bad input")
   313  		}
   314  		return 0
   315  	},
   316  	5: func(i1, i2, i3, i4, i5 uintptr) uintptr {
   317  		if i1+i2+i3+i4+i5 != 15 {
   318  			panic("bad input")
   319  		}
   320  		return 0
   321  	},
   322  	6: func(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
   323  		if i1+i2+i3+i4+i5+i6 != 21 {
   324  			panic("bad input")
   325  		}
   326  		return 0
   327  	},
   328  	7: func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
   329  		if i1+i2+i3+i4+i5+i6+i7 != 28 {
   330  			panic("bad input")
   331  		}
   332  		return 0
   333  	},
   334  	8: func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
   335  		if i1+i2+i3+i4+i5+i6+i7+i8 != 36 {
   336  			panic("bad input")
   337  		}
   338  		return 0
   339  	},
   340  	9: func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
   341  		if i1+i2+i3+i4+i5+i6+i7+i8+i9 != 45 {
   342  			panic("bad input")
   343  		}
   344  		return 0
   345  	},
   346  }
   347  
   348  type cbDLL struct {
   349  	name      string
   350  	buildArgs func(out, src string) []string
   351  }
   352  
   353  func (d *cbDLL) buildSrc(t *testing.T, path string) {
   354  	f, err := os.Create(path)
   355  	if err != nil {
   356  		t.Fatalf("failed to create source file: %v", err)
   357  	}
   358  	defer f.Close()
   359  
   360  	for i := 2; i < 10; i++ {
   361  		fmt.Fprint(f, cbDLLFunc(i).build())
   362  	}
   363  }
   364  
   365  func (d *cbDLL) build(t *testing.T, dir string) string {
   366  	srcname := d.name + ".c"
   367  	d.buildSrc(t, filepath.Join(dir, srcname))
   368  	outname := d.name + ".dll"
   369  	args := d.buildArgs(outname, srcname)
   370  	cmd := exec.Command(args[0], args[1:]...)
   371  	cmd.Dir = dir
   372  	out, err := cmd.CombinedOutput()
   373  	if err != nil {
   374  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   375  	}
   376  	return filepath.Join(dir, outname)
   377  }
   378  
   379  var cbDLLs = []cbDLL{
   380  	{
   381  		"test",
   382  		func(out, src string) []string {
   383  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
   384  		},
   385  	},
   386  	{
   387  		"testO2",
   388  		func(out, src string) []string {
   389  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
   390  		},
   391  	},
   392  }
   393  
   394  type cbTest struct {
   395  	n     int     // number of callback parameters
   396  	param uintptr // dll function parameter
   397  }
   398  
   399  func (test *cbTest) run(t *testing.T, dllpath string) {
   400  	dll := syscall.MustLoadDLL(dllpath)
   401  	defer dll.Release()
   402  	cb := cbFuncs[test.n]
   403  	stdcall := syscall.NewCallback(cb)
   404  	f := cbDLLFunc(test.n)
   405  	test.runOne(t, dll, f.stdcallName(), stdcall)
   406  	cdecl := syscall.NewCallbackCDecl(cb)
   407  	test.runOne(t, dll, f.cdeclName(), cdecl)
   408  }
   409  
   410  func (test *cbTest) runOne(t *testing.T, dll *syscall.DLL, proc string, cb uintptr) {
   411  	defer func() {
   412  		if r := recover(); r != nil {
   413  			t.Errorf("dll call %v(..., %d) failed: %v", proc, test.param, r)
   414  		}
   415  	}()
   416  	dll.MustFindProc(proc).Call(cb, test.param)
   417  }
   418  
   419  var cbTests = []cbTest{
   420  	{2, 1},
   421  	{2, 10000},
   422  	{3, 3},
   423  	{4, 5},
   424  	{4, 6},
   425  	{5, 2},
   426  	{6, 7},
   427  	{6, 8},
   428  	{7, 6},
   429  	{8, 1},
   430  	{9, 8},
   431  	{9, 10000},
   432  	{3, 4},
   433  	{5, 3},
   434  	{7, 7},
   435  	{8, 2},
   436  	{9, 9},
   437  }
   438  
   439  func TestStdcallAndCDeclCallbacks(t *testing.T) {
   440  	if _, err := exec.LookPath("gcc"); err != nil {
   441  		t.Skip("skipping test: gcc is missing")
   442  	}
   443  	tmp, err := ioutil.TempDir("", "TestCDeclCallback")
   444  	if err != nil {
   445  		t.Fatal("TempDir failed: ", err)
   446  	}
   447  	defer os.RemoveAll(tmp)
   448  
   449  	for _, dll := range cbDLLs {
   450  		dllPath := dll.build(t, tmp)
   451  		for _, test := range cbTests {
   452  			test.run(t, dllPath)
   453  		}
   454  	}
   455  }
   456  
   457  func TestRegisterClass(t *testing.T) {
   458  	kernel32 := GetDLL(t, "kernel32.dll")
   459  	user32 := GetDLL(t, "user32.dll")
   460  	mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
   461  	cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
   462  		t.Fatal("callback should never get called")
   463  		return 0
   464  	})
   465  	type Wndclassex struct {
   466  		Size       uint32
   467  		Style      uint32
   468  		WndProc    uintptr
   469  		ClsExtra   int32
   470  		WndExtra   int32
   471  		Instance   syscall.Handle
   472  		Icon       syscall.Handle
   473  		Cursor     syscall.Handle
   474  		Background syscall.Handle
   475  		MenuName   *uint16
   476  		ClassName  *uint16
   477  		IconSm     syscall.Handle
   478  	}
   479  	name := syscall.StringToUTF16Ptr("test_window")
   480  	wc := Wndclassex{
   481  		WndProc:   cb,
   482  		Instance:  syscall.Handle(mh),
   483  		ClassName: name,
   484  	}
   485  	wc.Size = uint32(unsafe.Sizeof(wc))
   486  	a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
   487  	if a == 0 {
   488  		t.Fatalf("RegisterClassEx failed: %v", err)
   489  	}
   490  	r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
   491  	if r == 0 {
   492  		t.Fatalf("UnregisterClass failed: %v", err)
   493  	}
   494  }
   495  
   496  func TestOutputDebugString(t *testing.T) {
   497  	d := GetDLL(t, "kernel32.dll")
   498  	p := syscall.StringToUTF16Ptr("testing OutputDebugString")
   499  	d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
   500  }
   501  
   502  func TestRaiseException(t *testing.T) {
   503  	o := executeTest(t, raiseExceptionSource, nil)
   504  	if strings.Contains(o, "RaiseException should not return") {
   505  		t.Fatalf("RaiseException did not crash program: %v", o)
   506  	}
   507  	if !strings.Contains(o, "Exception 0xbad") {
   508  		t.Fatalf("No stack trace: %v", o)
   509  	}
   510  }
   511  
   512  const raiseExceptionSource = `
   513  package main
   514  import "syscall"
   515  func main() {
   516  	const EXCEPTION_NONCONTINUABLE = 1
   517  	mod := syscall.MustLoadDLL("kernel32.dll")
   518  	proc := mod.MustFindProc("RaiseException")
   519  	proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   520  	println("RaiseException should not return")
   521  }
   522  `
   523  
   524  func TestZeroDivisionException(t *testing.T) {
   525  	o := executeTest(t, zeroDivisionExceptionSource, nil)
   526  	if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
   527  		t.Fatalf("No stack trace: %v", o)
   528  	}
   529  }
   530  
   531  const zeroDivisionExceptionSource = `
   532  package main
   533  func main() {
   534  	x := 1
   535  	y := 0
   536  	z := x / y
   537  	println(z)
   538  }
   539  `
   540  
   541  func TestWERDialogue(t *testing.T) {
   542  	if os.Getenv("TESTING_WER_DIALOGUE") == "1" {
   543  		defer os.Exit(0)
   544  
   545  		*runtime.TestingWER = true
   546  		const EXCEPTION_NONCONTINUABLE = 1
   547  		mod := syscall.MustLoadDLL("kernel32.dll")
   548  		proc := mod.MustFindProc("RaiseException")
   549  		proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   550  		println("RaiseException should not return")
   551  		return
   552  	}
   553  	cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue")
   554  	cmd.Env = []string{"TESTING_WER_DIALOGUE=1"}
   555  	// Child process should not open WER dialogue, but return immediately instead.
   556  	cmd.CombinedOutput()
   557  }
   558  
   559  var used byte
   560  
   561  func use(buf []byte) {
   562  	for _, c := range buf {
   563  		used += c
   564  	}
   565  }
   566  
   567  func forceStackCopy() (r int) {
   568  	var f func(int) int
   569  	f = func(i int) int {
   570  		var buf [256]byte
   571  		use(buf[:])
   572  		if i == 0 {
   573  			return 0
   574  		}
   575  		return i + f(i-1)
   576  	}
   577  	r = f(128)
   578  	return
   579  }
   580  
   581  func TestReturnAfterStackGrowInCallback(t *testing.T) {
   582  	if _, err := exec.LookPath("gcc"); err != nil {
   583  		t.Skip("skipping test: gcc is missing")
   584  	}
   585  
   586  	const src = `
   587  #include <stdint.h>
   588  #include <windows.h>
   589  
   590  typedef uintptr_t __stdcall (*callback)(uintptr_t);
   591  
   592  uintptr_t cfunc(callback f, uintptr_t n) {
   593     uintptr_t r;
   594     r = f(n);
   595     SetLastError(333);
   596     return r;
   597  }
   598  `
   599  	tmpdir, err := ioutil.TempDir("", "TestReturnAfterStackGrowInCallback")
   600  	if err != nil {
   601  		t.Fatal("TempDir failed: ", err)
   602  	}
   603  	defer os.RemoveAll(tmpdir)
   604  
   605  	srcname := "mydll.c"
   606  	err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  	outname := "mydll.dll"
   611  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   612  	cmd.Dir = tmpdir
   613  	out, err := cmd.CombinedOutput()
   614  	if err != nil {
   615  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   616  	}
   617  	dllpath := filepath.Join(tmpdir, outname)
   618  
   619  	dll := syscall.MustLoadDLL(dllpath)
   620  	defer dll.Release()
   621  
   622  	proc := dll.MustFindProc("cfunc")
   623  
   624  	cb := syscall.NewCallback(func(n uintptr) uintptr {
   625  		forceStackCopy()
   626  		return n
   627  	})
   628  
   629  	// Use a new goroutine so that we get a small stack.
   630  	type result struct {
   631  		r   uintptr
   632  		err syscall.Errno
   633  	}
   634  	c := make(chan result)
   635  	go func() {
   636  		r, _, err := proc.Call(cb, 100)
   637  		c <- result{r, err.(syscall.Errno)}
   638  	}()
   639  	want := result{r: 100, err: 333}
   640  	if got := <-c; got != want {
   641  		t.Errorf("got %d want %d", got, want)
   642  	}
   643  }
   644  
   645  // removeOneCPU removes one (any) cpu from affinity mask.
   646  // It returns new affinity mask.
   647  func removeOneCPU(mask uintptr) (uintptr, error) {
   648  	if mask == 0 {
   649  		return 0, fmt.Errorf("cpu affinity mask is empty")
   650  	}
   651  	maskbits := int(unsafe.Sizeof(mask) * 8)
   652  	for i := 0; i < maskbits; i++ {
   653  		newmask := mask & ^(1 << uint(i))
   654  		if newmask != mask {
   655  			return newmask, nil
   656  		}
   657  
   658  	}
   659  	panic("not reached")
   660  }
   661  
   662  func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
   663  	_OpenThread := kernel32.MustFindProc("OpenThread")
   664  	_ResumeThread := kernel32.MustFindProc("ResumeThread")
   665  	_Thread32First := kernel32.MustFindProc("Thread32First")
   666  	_Thread32Next := kernel32.MustFindProc("Thread32Next")
   667  
   668  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
   669  	if err != nil {
   670  		return err
   671  	}
   672  	defer syscall.CloseHandle(snapshot)
   673  
   674  	const _THREAD_SUSPEND_RESUME = 0x0002
   675  
   676  	type ThreadEntry32 struct {
   677  		Size           uint32
   678  		tUsage         uint32
   679  		ThreadID       uint32
   680  		OwnerProcessID uint32
   681  		BasePri        int32
   682  		DeltaPri       int32
   683  		Flags          uint32
   684  	}
   685  
   686  	var te ThreadEntry32
   687  	te.Size = uint32(unsafe.Sizeof(te))
   688  	ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
   689  	if ret == 0 {
   690  		return err
   691  	}
   692  	for te.OwnerProcessID != uint32(childpid) {
   693  		ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
   694  		if ret == 0 {
   695  			return err
   696  		}
   697  	}
   698  	h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
   699  	if h == 0 {
   700  		return err
   701  	}
   702  	defer syscall.Close(syscall.Handle(h))
   703  
   704  	ret, _, err = _ResumeThread.Call(h)
   705  	if ret == 0xffffffff {
   706  		return err
   707  	}
   708  	return nil
   709  }
   710  
   711  func TestNumCPU(t *testing.T) {
   712  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   713  		// in child process
   714  		fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
   715  		os.Exit(0)
   716  	}
   717  
   718  	switch n := runtime.NumberOfProcessors(); {
   719  	case n < 1:
   720  		t.Fatalf("system cannot have %d cpu(s)", n)
   721  	case n == 1:
   722  		if runtime.NumCPU() != 1 {
   723  			t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
   724  		}
   725  		return
   726  	}
   727  
   728  	const (
   729  		_CREATE_SUSPENDED   = 0x00000004
   730  		_PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
   731  	)
   732  
   733  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
   734  	_GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
   735  	_SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
   736  
   737  	cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU")
   738  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
   739  	var buf bytes.Buffer
   740  	cmd.Stdout = &buf
   741  	cmd.Stderr = &buf
   742  	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
   743  	err := cmd.Start()
   744  	if err != nil {
   745  		t.Fatal(err)
   746  	}
   747  	defer func() {
   748  		err = cmd.Wait()
   749  		childOutput := string(buf.Bytes())
   750  		if err != nil {
   751  			t.Fatalf("child failed: %v: %v", err, childOutput)
   752  		}
   753  		// removeOneCPU should have decreased child cpu count by 1
   754  		want := fmt.Sprintf("%d", runtime.NumCPU()-1)
   755  		if childOutput != want {
   756  			t.Fatalf("child output: want %q, got %q", want, childOutput)
   757  		}
   758  	}()
   759  
   760  	defer func() {
   761  		err = resumeChildThread(kernel32, cmd.Process.Pid)
   762  		if err != nil {
   763  			t.Fatal(err)
   764  		}
   765  	}()
   766  
   767  	ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
   768  	if err != nil {
   769  		t.Fatal(err)
   770  	}
   771  	defer syscall.CloseHandle(ph)
   772  
   773  	var mask, sysmask uintptr
   774  	ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
   775  	if ret == 0 {
   776  		t.Fatal(err)
   777  	}
   778  
   779  	newmask, err := removeOneCPU(mask)
   780  	if err != nil {
   781  		t.Fatal(err)
   782  	}
   783  
   784  	ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
   785  	if ret == 0 {
   786  		t.Fatal(err)
   787  	}
   788  	ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
   789  	if ret == 0 {
   790  		t.Fatal(err)
   791  	}
   792  	if newmask != mask {
   793  		t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
   794  	}
   795  }