github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/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 "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 TestSyscallN(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 for arglen := 0; arglen <= runtime.MaxArgs; arglen++ { 771 arglen := arglen 772 t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) { 773 args := make([]string, arglen) 774 rets := make([]string, arglen+1) 775 params := make([]uintptr, arglen) 776 for i := range args { 777 args[i] = fmt.Sprintf("int a%d", i) 778 rets[i] = fmt.Sprintf("(a%d == %d)", i, i) 779 params[i] = uintptr(i) 780 } 781 rets[arglen] = "1" // for arglen == 0 782 783 src := fmt.Sprintf(` 784 #include <stdint.h> 785 #include <windows.h> 786 int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && ")) 787 788 tmpdir := t.TempDir() 789 790 srcname := "mydll.c" 791 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 792 if err != nil { 793 t.Fatal(err) 794 } 795 outname := "mydll.dll" 796 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 797 cmd.Dir = tmpdir 798 out, err := cmd.CombinedOutput() 799 if err != nil { 800 t.Fatalf("failed to build dll: %v\n%s", err, out) 801 } 802 dllpath := filepath.Join(tmpdir, outname) 803 804 dll := syscall.MustLoadDLL(dllpath) 805 defer dll.Release() 806 807 proc := dll.MustFindProc("cfunc") 808 809 // proc.Call() will call SyscallN() internally. 810 r, _, err := proc.Call(params...) 811 if r != 1 { 812 t.Errorf("got %d want 1 (err=%v)", r, err) 813 } 814 }) 815 } 816 } 817 818 func TestFloatArgs(t *testing.T) { 819 if _, err := exec.LookPath("gcc"); err != nil { 820 t.Skip("skipping test: gcc is missing") 821 } 822 if runtime.GOARCH != "amd64" { 823 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 824 } 825 826 const src = ` 827 #include <stdint.h> 828 #include <windows.h> 829 830 uintptr_t cfunc(uintptr_t a, double b, float c, double d) { 831 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 832 return 1; 833 } 834 return 0; 835 } 836 ` 837 tmpdir := t.TempDir() 838 839 srcname := "mydll.c" 840 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 841 if err != nil { 842 t.Fatal(err) 843 } 844 outname := "mydll.dll" 845 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 846 cmd.Dir = tmpdir 847 out, err := cmd.CombinedOutput() 848 if err != nil { 849 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 850 } 851 dllpath := filepath.Join(tmpdir, outname) 852 853 dll := syscall.MustLoadDLL(dllpath) 854 defer dll.Release() 855 856 proc := dll.MustFindProc("cfunc") 857 858 r, _, err := proc.Call( 859 1, 860 uintptr(math.Float64bits(2.2)), 861 uintptr(math.Float32bits(3.3)), 862 uintptr(math.Float64bits(4.4e44)), 863 ) 864 if r != 1 { 865 t.Errorf("got %d want 1 (err=%v)", r, err) 866 } 867 } 868 869 func TestFloatReturn(t *testing.T) { 870 if _, err := exec.LookPath("gcc"); err != nil { 871 t.Skip("skipping test: gcc is missing") 872 } 873 if runtime.GOARCH != "amd64" { 874 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 875 } 876 877 const src = ` 878 #include <stdint.h> 879 #include <windows.h> 880 881 float cfuncFloat(uintptr_t a, double b, float c, double d) { 882 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 883 return 1.5f; 884 } 885 return 0; 886 } 887 888 double cfuncDouble(uintptr_t a, double b, float c, double d) { 889 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 890 return 2.5; 891 } 892 return 0; 893 } 894 ` 895 tmpdir := t.TempDir() 896 897 srcname := "mydll.c" 898 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 899 if err != nil { 900 t.Fatal(err) 901 } 902 outname := "mydll.dll" 903 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 904 cmd.Dir = tmpdir 905 out, err := cmd.CombinedOutput() 906 if err != nil { 907 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 908 } 909 dllpath := filepath.Join(tmpdir, outname) 910 911 dll := syscall.MustLoadDLL(dllpath) 912 defer dll.Release() 913 914 proc := dll.MustFindProc("cfuncFloat") 915 916 _, r, err := proc.Call( 917 1, 918 uintptr(math.Float64bits(2.2)), 919 uintptr(math.Float32bits(3.3)), 920 uintptr(math.Float64bits(4.4e44)), 921 ) 922 fr := math.Float32frombits(uint32(r)) 923 if fr != 1.5 { 924 t.Errorf("got %f want 1.5 (err=%v)", fr, err) 925 } 926 927 proc = dll.MustFindProc("cfuncDouble") 928 929 _, r, err = proc.Call( 930 1, 931 uintptr(math.Float64bits(2.2)), 932 uintptr(math.Float32bits(3.3)), 933 uintptr(math.Float64bits(4.4e44)), 934 ) 935 dr := math.Float64frombits(uint64(r)) 936 if dr != 2.5 { 937 t.Errorf("got %f want 2.5 (err=%v)", dr, err) 938 } 939 } 940 941 func TestTimeBeginPeriod(t *testing.T) { 942 const TIMERR_NOERROR = 0 943 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR { 944 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue) 945 } 946 } 947 948 // removeOneCPU removes one (any) cpu from affinity mask. 949 // It returns new affinity mask. 950 func removeOneCPU(mask uintptr) (uintptr, error) { 951 if mask == 0 { 952 return 0, fmt.Errorf("cpu affinity mask is empty") 953 } 954 maskbits := int(unsafe.Sizeof(mask) * 8) 955 for i := 0; i < maskbits; i++ { 956 newmask := mask & ^(1 << uint(i)) 957 if newmask != mask { 958 return newmask, nil 959 } 960 961 } 962 panic("not reached") 963 } 964 965 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error { 966 _OpenThread := kernel32.MustFindProc("OpenThread") 967 _ResumeThread := kernel32.MustFindProc("ResumeThread") 968 _Thread32First := kernel32.MustFindProc("Thread32First") 969 _Thread32Next := kernel32.MustFindProc("Thread32Next") 970 971 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0) 972 if err != nil { 973 return err 974 } 975 defer syscall.CloseHandle(snapshot) 976 977 const _THREAD_SUSPEND_RESUME = 0x0002 978 979 type ThreadEntry32 struct { 980 Size uint32 981 tUsage uint32 982 ThreadID uint32 983 OwnerProcessID uint32 984 BasePri int32 985 DeltaPri int32 986 Flags uint32 987 } 988 989 var te ThreadEntry32 990 te.Size = uint32(unsafe.Sizeof(te)) 991 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 992 if ret == 0 { 993 return err 994 } 995 for te.OwnerProcessID != uint32(childpid) { 996 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 997 if ret == 0 { 998 return err 999 } 1000 } 1001 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID)) 1002 if h == 0 { 1003 return err 1004 } 1005 defer syscall.Close(syscall.Handle(h)) 1006 1007 ret, _, err = _ResumeThread.Call(h) 1008 if ret == 0xffffffff { 1009 return err 1010 } 1011 return nil 1012 } 1013 1014 func TestNumCPU(t *testing.T) { 1015 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 1016 // in child process 1017 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU()) 1018 os.Exit(0) 1019 } 1020 1021 switch n := runtime.NumberOfProcessors(); { 1022 case n < 1: 1023 t.Fatalf("system cannot have %d cpu(s)", n) 1024 case n == 1: 1025 if runtime.NumCPU() != 1 { 1026 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU()) 1027 } 1028 return 1029 } 1030 1031 const ( 1032 _CREATE_SUSPENDED = 0x00000004 1033 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 1034 ) 1035 1036 kernel32 := syscall.MustLoadDLL("kernel32.dll") 1037 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask") 1038 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask") 1039 1040 cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU") 1041 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") 1042 var buf bytes.Buffer 1043 cmd.Stdout = &buf 1044 cmd.Stderr = &buf 1045 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED} 1046 err := cmd.Start() 1047 if err != nil { 1048 t.Fatal(err) 1049 } 1050 defer func() { 1051 err = cmd.Wait() 1052 childOutput := string(buf.Bytes()) 1053 if err != nil { 1054 t.Fatalf("child failed: %v: %v", err, childOutput) 1055 } 1056 // removeOneCPU should have decreased child cpu count by 1 1057 want := fmt.Sprintf("%d", runtime.NumCPU()-1) 1058 if childOutput != want { 1059 t.Fatalf("child output: want %q, got %q", want, childOutput) 1060 } 1061 }() 1062 1063 defer func() { 1064 err = resumeChildThread(kernel32, cmd.Process.Pid) 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 }() 1069 1070 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid)) 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 defer syscall.CloseHandle(ph) 1075 1076 var mask, sysmask uintptr 1077 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 1078 if ret == 0 { 1079 t.Fatal(err) 1080 } 1081 1082 newmask, err := removeOneCPU(mask) 1083 if err != nil { 1084 t.Fatal(err) 1085 } 1086 1087 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask) 1088 if ret == 0 { 1089 t.Fatal(err) 1090 } 1091 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 1092 if ret == 0 { 1093 t.Fatal(err) 1094 } 1095 if newmask != mask { 1096 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) 1097 } 1098 } 1099 1100 // See Issue 14959 1101 func TestDLLPreloadMitigation(t *testing.T) { 1102 if _, err := exec.LookPath("gcc"); err != nil { 1103 t.Skip("skipping test: gcc is missing") 1104 } 1105 1106 tmpdir := t.TempDir() 1107 1108 dir0, err := os.Getwd() 1109 if err != nil { 1110 t.Fatal(err) 1111 } 1112 defer os.Chdir(dir0) 1113 1114 const src = ` 1115 #include <stdint.h> 1116 #include <windows.h> 1117 1118 uintptr_t cfunc(void) { 1119 SetLastError(123); 1120 return 0; 1121 } 1122 ` 1123 srcname := "nojack.c" 1124 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 1125 if err != nil { 1126 t.Fatal(err) 1127 } 1128 name := "nojack.dll" 1129 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 1130 cmd.Dir = tmpdir 1131 out, err := cmd.CombinedOutput() 1132 if err != nil { 1133 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1134 } 1135 dllpath := filepath.Join(tmpdir, name) 1136 1137 dll := syscall.MustLoadDLL(dllpath) 1138 dll.MustFindProc("cfunc") 1139 dll.Release() 1140 1141 // Get into the directory with the DLL we'll load by base name 1142 // ("nojack.dll") Think of this as the user double-clicking an 1143 // installer from their Downloads directory where a browser 1144 // silently downloaded some malicious DLLs. 1145 os.Chdir(tmpdir) 1146 1147 // First before we can load a DLL from the current directory, 1148 // loading it only as "nojack.dll", without an absolute path. 1149 delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly 1150 dll, err = syscall.LoadDLL(name) 1151 if err != nil { 1152 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) 1153 } 1154 dll.Release() 1155 1156 // And now verify that if we register it as a system32-only 1157 // DLL, the implicit loading from the current directory no 1158 // longer works. 1159 sysdll.IsSystemDLL[name] = true 1160 dll, err = syscall.LoadDLL(name) 1161 if err == nil { 1162 dll.Release() 1163 if wantLoadLibraryEx() { 1164 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) 1165 } 1166 t.Skip("insecure load of DLL, but expected") 1167 } 1168 } 1169 1170 // Test that C code called via a DLL can use large Windows thread 1171 // stacks and call back in to Go without crashing. See issue #20975. 1172 // 1173 // See also TestBigStackCallbackCgo. 1174 func TestBigStackCallbackSyscall(t *testing.T) { 1175 if _, err := exec.LookPath("gcc"); err != nil { 1176 t.Skip("skipping test: gcc is missing") 1177 } 1178 1179 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c") 1180 if err != nil { 1181 t.Fatal("Abs failed: ", err) 1182 } 1183 1184 tmpdir := t.TempDir() 1185 1186 outname := "mydll.dll" 1187 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 1188 cmd.Dir = tmpdir 1189 out, err := cmd.CombinedOutput() 1190 if err != nil { 1191 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1192 } 1193 dllpath := filepath.Join(tmpdir, outname) 1194 1195 dll := syscall.MustLoadDLL(dllpath) 1196 defer dll.Release() 1197 1198 var ok bool 1199 proc := dll.MustFindProc("bigStack") 1200 cb := syscall.NewCallback(func() uintptr { 1201 // Do something interesting to force stack checks. 1202 forceStackCopy() 1203 ok = true 1204 return 0 1205 }) 1206 proc.Call(cb) 1207 if !ok { 1208 t.Fatalf("callback not called") 1209 } 1210 } 1211 1212 // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. 1213 func wantLoadLibraryEx() bool { 1214 return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" 1215 } 1216 1217 func TestLoadLibraryEx(t *testing.T) { 1218 use, have, flags := runtime.LoadLibraryExStatus() 1219 if use { 1220 return // success. 1221 } 1222 if wantLoadLibraryEx() { 1223 t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", 1224 have, flags) 1225 } 1226 t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", 1227 have, flags) 1228 } 1229 1230 var ( 1231 modwinmm = syscall.NewLazyDLL("winmm.dll") 1232 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 1233 1234 procCreateEvent = modkernel32.NewProc("CreateEventW") 1235 procSetEvent = modkernel32.NewProc("SetEvent") 1236 ) 1237 1238 func createEvent() (syscall.Handle, error) { 1239 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0) 1240 if r0 == 0 { 1241 return 0, syscall.Errno(e0) 1242 } 1243 return syscall.Handle(r0), nil 1244 } 1245 1246 func setEvent(h syscall.Handle) error { 1247 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0) 1248 if r0 == 0 { 1249 return syscall.Errno(e0) 1250 } 1251 return nil 1252 } 1253 1254 func BenchmarkChanToSyscallPing(b *testing.B) { 1255 n := b.N 1256 ch := make(chan int) 1257 event, err := createEvent() 1258 if err != nil { 1259 b.Fatal(err) 1260 } 1261 go func() { 1262 for i := 0; i < n; i++ { 1263 syscall.WaitForSingleObject(event, syscall.INFINITE) 1264 ch <- 1 1265 } 1266 }() 1267 for i := 0; i < n; i++ { 1268 err := setEvent(event) 1269 if err != nil { 1270 b.Fatal(err) 1271 } 1272 <-ch 1273 } 1274 } 1275 1276 func BenchmarkSyscallToSyscallPing(b *testing.B) { 1277 n := b.N 1278 event1, err := createEvent() 1279 if err != nil { 1280 b.Fatal(err) 1281 } 1282 event2, err := createEvent() 1283 if err != nil { 1284 b.Fatal(err) 1285 } 1286 go func() { 1287 for i := 0; i < n; i++ { 1288 syscall.WaitForSingleObject(event1, syscall.INFINITE) 1289 if err := setEvent(event2); err != nil { 1290 b.Errorf("Set event failed: %v", err) 1291 return 1292 } 1293 } 1294 }() 1295 for i := 0; i < n; i++ { 1296 if err := setEvent(event1); err != nil { 1297 b.Fatal(err) 1298 } 1299 if b.Failed() { 1300 break 1301 } 1302 syscall.WaitForSingleObject(event2, syscall.INFINITE) 1303 } 1304 } 1305 1306 func BenchmarkChanToChanPing(b *testing.B) { 1307 n := b.N 1308 ch1 := make(chan int) 1309 ch2 := make(chan int) 1310 go func() { 1311 for i := 0; i < n; i++ { 1312 <-ch1 1313 ch2 <- 1 1314 } 1315 }() 1316 for i := 0; i < n; i++ { 1317 ch1 <- 1 1318 <-ch2 1319 } 1320 } 1321 1322 func BenchmarkOsYield(b *testing.B) { 1323 for i := 0; i < b.N; i++ { 1324 runtime.OsYield() 1325 } 1326 } 1327 1328 func BenchmarkRunningGoProgram(b *testing.B) { 1329 tmpdir := b.TempDir() 1330 1331 src := filepath.Join(tmpdir, "main.go") 1332 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666) 1333 if err != nil { 1334 b.Fatal(err) 1335 } 1336 1337 exe := filepath.Join(tmpdir, "main.exe") 1338 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src) 1339 cmd.Dir = tmpdir 1340 out, err := cmd.CombinedOutput() 1341 if err != nil { 1342 b.Fatalf("building main.exe failed: %v\n%s", err, out) 1343 } 1344 1345 b.ResetTimer() 1346 for i := 0; i < b.N; i++ { 1347 cmd := exec.Command(exe) 1348 out, err := cmd.CombinedOutput() 1349 if err != nil { 1350 b.Fatalf("running main.exe failed: %v\n%s", err, out) 1351 } 1352 } 1353 } 1354 1355 const benchmarkRunningGoProgram = ` 1356 package main 1357 1358 import _ "os" // average Go program will use "os" package, do the same here 1359 1360 func main() { 1361 } 1362 `