1
2
3
4
5
6
7 package errorstest
8
9 import (
10 "bytes"
11 "flag"
12 "fmt"
13 "internal/testenv"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "regexp"
18 "slices"
19 "strings"
20 "sync/atomic"
21 "testing"
22 )
23
24 var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
25
26
27 type ptrTest struct {
28 name string
29 c string
30 c1 string
31 imports []string
32 support string
33 body string
34 extra []extra
35 fail bool
36 expensive bool
37 errTextRegexp string
38 }
39
40 type extra struct {
41 name string
42 contents string
43 }
44
45 var ptrTests = []ptrTest{
46 {
47
48 name: "ptr1",
49 c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
50 body: `C.f1(&C.s1{new(C.int)})`,
51 fail: true,
52 },
53 {
54
55 name: "ptr2",
56 c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
57 body: `p := &C.s2{new(C.int)}; C.f2(p)`,
58 fail: true,
59 },
60 {
61
62
63 name: "ok1",
64 c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
65 body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
66 fail: false,
67 },
68 {
69
70 name: "ptrfield",
71 c: `struct s4 { int i; int *p; }; void f4(int **p) {}`,
72 body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
73 fail: true,
74 },
75 {
76
77
78
79 name: "ptrfieldok",
80 c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
81 body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
82 fail: false,
83 },
84 {
85
86 name: "sliceok1",
87 c: `void f6(void **p) {}`,
88 imports: []string{"unsafe"},
89 body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
90 fail: false,
91 },
92 {
93
94 name: "sliceptr1",
95 c: `void f7(void **p) {}`,
96 imports: []string{"unsafe"},
97 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
98 fail: true,
99 },
100 {
101
102
103
104 name: "sliceptr2",
105 c: `void f8(void **p) {}`,
106 imports: []string{"unsafe"},
107 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
108 fail: true,
109 },
110 {
111
112
113 name: "sliceok2",
114 c: `void f9(void **p) {}`,
115 imports: []string{"unsafe"},
116 support: `type S9 struct { p *int; s []unsafe.Pointer }`,
117 body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
118 fail: false,
119 },
120 {
121
122
123 name: "sliceok3",
124 c: `void f10(void* p) {}`,
125 imports: []string{"unsafe"},
126 support: `type S10 struct { p *int; a [4]byte }`,
127 body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
128 fail: false,
129 },
130 {
131
132
133 name: "sliceok4",
134 c: `typedef void* PV11; void f11(PV11 p) {}`,
135 imports: []string{"unsafe"},
136 support: `type S11 struct { p *int; a [4]byte }`,
137 body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
138 fail: false,
139 },
140 {
141
142
143 name: "varok",
144 c: `void f12(char** parg) {}`,
145 support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
146 body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
147 fail: false,
148 },
149 {
150
151
152 name: "var1",
153 c: `void f13(char*** parg) {}`,
154 support: `var hello13 = [...]*C.char{new(C.char)}`,
155 body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
156 fail: true,
157 },
158 {
159
160 name: "barrier",
161 c: `#include <stdlib.h>
162 char **f14a() { return malloc(sizeof(char*)); }
163 void f14b(char **p) {}`,
164 body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
165 fail: true,
166 expensive: true,
167 },
168 {
169
170 name: "barrierpinnedok",
171 c: `#include <stdlib.h>
172 char **f14a2() { return malloc(sizeof(char*)); }
173 void f14b2(char **p) {}`,
174 imports: []string{"runtime"},
175 body: `var pinr runtime.Pinner; p := C.f14a2(); x := new(C.char); pinr.Pin(x); *p = x; C.f14b2(p); pinr.Unpin()`,
176 fail: false,
177 expensive: true,
178 },
179 {
180
181
182 name: "barrierstruct",
183 c: `#include <stdlib.h>
184 struct s15 { char *a[10]; };
185 struct s15 *f15() { return malloc(sizeof(struct s15)); }
186 void f15b(struct s15 *p) {}`,
187 body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
188 fail: true,
189 expensive: true,
190 },
191 {
192
193
194 name: "barrierslice",
195 c: `#include <stdlib.h>
196 struct s16 { char *a[10]; };
197 struct s16 *f16() { return malloc(sizeof(struct s16)); }
198 void f16b(struct s16 *p) {}`,
199 body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
200 fail: true,
201 expensive: true,
202 },
203 {
204
205
206 name: "barriergcprogarray",
207 c: `#include <stdlib.h>
208 struct s17 { char *a[32769]; };
209 struct s17 *f17() { return malloc(sizeof(struct s17)); }
210 void f17b(struct s17 *p) {}`,
211 body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
212 fail: true,
213 expensive: true,
214 },
215 {
216
217 name: "barriergcprogarrayheap",
218 c: `#include <stdlib.h>
219 struct s18 { char *a[32769]; };
220 struct s18 *f18() { return malloc(sizeof(struct s18)); }
221 void f18b(struct s18 *p) {}
222 void f18c(void *p) {}`,
223 imports: []string{"unsafe"},
224 body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
225 fail: true,
226 expensive: true,
227 },
228 {
229
230 name: "barriergcprogstruct",
231 c: `#include <stdlib.h>
232 struct s19a { char *a[32769]; };
233 struct s19b { struct s19a f; };
234 struct s19b *f19() { return malloc(sizeof(struct s19b)); }
235 void f19b(struct s19b *p) {}`,
236 body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
237 fail: true,
238 expensive: true,
239 },
240 {
241
242 name: "barriergcprogstructheap",
243 c: `#include <stdlib.h>
244 struct s20a { char *a[32769]; };
245 struct s20b { struct s20a f; };
246 struct s20b *f20() { return malloc(sizeof(struct s20b)); }
247 void f20b(struct s20b *p) {}
248 void f20c(void *p) {}`,
249 imports: []string{"unsafe"},
250 body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
251 fail: true,
252 expensive: true,
253 },
254 {
255
256 name: "export1",
257 c: `#ifdef _WIN32
258 __declspec(dllexport)
259 #endif
260 extern unsigned char *GoFn21();`,
261 support: `//export GoFn21
262 func GoFn21() *byte { return new(byte) }`,
263 body: `C.GoFn21()`,
264 fail: true,
265 },
266 {
267
268 name: "exportok",
269 c: `#include <stdlib.h>
270 #ifdef _WIN32
271 __declspec(dllexport)
272 #endif
273 extern unsigned char *GoFn22();`,
274 support: `//export GoFn22
275 func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
276 body: `C.GoFn22()`,
277 },
278 {
279
280 name: "passstring",
281 c: `#include <stddef.h>
282 typedef struct { const char *p; ptrdiff_t n; } gostring23;
283 gostring23 f23(gostring23 s) { return s; }`,
284 imports: []string{"unsafe"},
285 body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
286 },
287 {
288
289 name: "passstringslice",
290 c: `void f24(void *p) {}`,
291 imports: []string{"strings", "unsafe"},
292 support: `type S24 struct { a [1]string }`,
293 body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
294 fail: true,
295 },
296 {
297
298 name: "retstring",
299 c: `extern void f25();`,
300 imports: []string{"strings"},
301 support: `//export GoStr25
302 func GoStr25() string { return strings.Repeat("a", 2) }`,
303 body: `C.f25()`,
304 c1: `#include <stddef.h>
305 typedef struct { const char *p; ptrdiff_t n; } gostring25;
306 extern gostring25 GoStr25();
307 void f25() { GoStr25(); }`,
308 fail: true,
309 },
310 {
311
312
313
314
315
316 name: "ptrdata1",
317 c: `#include <stdlib.h>
318 void f26(void* p) {}`,
319 imports: []string{"unsafe"},
320 support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
321 body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`,
322 fail: false,
323 },
324 {
325
326 name: "ptrdata2",
327 c: `#include <stdlib.h>
328 void f27(void* p) {}`,
329 imports: []string{"unsafe"},
330 support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
331 body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`,
332 fail: false,
333 },
334 {
335
336
337 name: "defer1",
338 c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
339 body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
340 fail: true,
341 },
342 {
343
344
345 name: "union1",
346 c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
347 imports: []string{"unsafe"},
348 body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
349 fail: true,
350 },
351 {
352
353
354
355
356
357 name: "union2",
358 c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
359 imports: []string{"unsafe"},
360 body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
361 fail: false,
362 },
363 {
364
365 name: "preemptduringcall",
366 c: `void f30() {}`,
367 imports: []string{"runtime", "sync"},
368 body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
369 fail: false,
370 },
371 {
372
373 name: "deadline",
374 c: `#define US31 10`,
375 imports: []string{"os", "time"},
376 body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
377 fail: false,
378 },
379 {
380
381 name: "chanrecv",
382 c: `void f32(char** p) {}`,
383 imports: []string{"time"},
384 body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`,
385 fail: false,
386 },
387 {
388
389
390
391 name: "structfield",
392 c: `void f33(void* p) {}`,
393 imports: []string{"unsafe"},
394 support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
395 body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
396 fail: false,
397 },
398 {
399
400
401
402 name: "structfield2",
403 c: `void f34(void* p, int r, void* s) {}`,
404 imports: []string{"unsafe"},
405 support: `type S34 struct { a [8]byte; p *int; b int64; }`,
406 body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
407 fail: false,
408 },
409 {
410
411
412
413 name: "defer2",
414 c: `void f35(char **pc) {}`,
415 support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
416 body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
417 fail: false,
418 },
419 {
420
421
422 name: "buffer",
423 c: `void f36(void *p) {}`,
424 imports: []string{"bytes", "unsafe"},
425 body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
426 fail: false,
427 },
428 {
429
430 name: "finalizer",
431 c: `// Nothing to declare.`,
432 imports: []string{"os"},
433 support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
434 body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
435 fail: false,
436 },
437 {
438
439 name: "structof",
440 c: `// Nothing to declare.`,
441 imports: []string{"reflect"},
442 support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
443 body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
444 fail: false,
445 },
446 {
447
448
449 name: "structfieldcast",
450 c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`,
451 support: `type S40 struct { p *int; a C.struct_S40i }`,
452 body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`,
453 fail: false,
454 },
455 {
456
457 name: "stringdata",
458 c: `void f41(void* p) {}`,
459 imports: []string{"unsafe"},
460 body: `s := struct { a [4]byte; p *int }{p: new(int)}; str := unsafe.String(&s.a[0], 4); C.f41(unsafe.Pointer(unsafe.StringData(str)))`,
461 fail: false,
462 },
463 {
464 name: "slicedata",
465 c: `void f42(void* p) {}`,
466 imports: []string{"unsafe"},
467 body: `s := []*byte{nil, new(byte)}; C.f42(unsafe.Pointer(unsafe.SliceData(s)))`,
468 fail: true,
469 },
470 {
471 name: "slicedata2",
472 c: `void f43(void* p) {}`,
473 imports: []string{"unsafe"},
474 body: `s := struct { a [4]byte; p *int }{p: new(int)}; C.f43(unsafe.Pointer(unsafe.SliceData(s.a[:])))`,
475 fail: false,
476 },
477 {
478
479 name: "arraypointer",
480 c: `void f44(void* p) {}`,
481 imports: []string{"unsafe"},
482 body: `a := new([10]byte); C.f44(unsafe.Pointer(&a[0]))`,
483 fail: false,
484 },
485 {
486
487
488 name: "arraypointer2",
489 c: `void f45(void** p) {}`,
490 imports: []string{"unsafe"},
491 body: `i := 0; a := &[2]unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f45(&a[0])`,
492 fail: true,
493 },
494 {
495
496 name: "argmap",
497 c: `void f46(void* p) {}`,
498 imports: []string{"unsafe"},
499 body: `m := map[int]int{0: 1,}; C.f46(unsafe.Pointer(&m))`,
500 fail: true,
501 errTextRegexp: `.*argument of cgo function has Go pointer to unpinned Go map`,
502 },
503 {
504
505 name: "retmap",
506 c: `extern void f47();`,
507 support: `//export GoMap47
508 func GoMap47() map[int]int { return map[int]int{0: 1,} }`,
509 body: `C.f47()`,
510 c1: `extern void* GoMap47();
511 void f47() { GoMap47(); }`,
512 fail: true,
513 errTextRegexp: `.*result of Go function GoMap47 called from cgo is unpinned Go map or points to unpinned Go map.*`,
514 },
515 }
516
517 func TestPointerChecks(t *testing.T) {
518 testenv.MustHaveGoBuild(t)
519 testenv.MustHaveCGO(t)
520
521 var gopath string
522 var dir string
523 if *tmp != "" {
524 gopath = *tmp
525 dir = ""
526 } else {
527 d, err := os.MkdirTemp("", filepath.Base(t.Name()))
528 if err != nil {
529 t.Fatal(err)
530 }
531 dir = d
532 gopath = d
533 }
534
535 exe := buildPtrTests(t, gopath, false)
536 exe2 := buildPtrTests(t, gopath, true)
537
538
539
540
541
542
543 var pending int32
544 for _, pt := range ptrTests {
545 t.Run(pt.name, func(t *testing.T) {
546 atomic.AddInt32(&pending, +1)
547 defer func() {
548 if atomic.AddInt32(&pending, -1) == 0 {
549 os.RemoveAll(dir)
550 }
551 }()
552 testOne(t, pt, exe, exe2)
553 })
554 }
555 }
556
557 func buildPtrTests(t *testing.T, gopath string, cgocheck2 bool) (exe string) {
558
559 src := filepath.Join(gopath, "src", "ptrtest")
560 if err := os.MkdirAll(src, 0777); err != nil {
561 t.Fatal(err)
562 }
563 if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest\ngo 1.20"), 0666); err != nil {
564 t.Fatal(err)
565 }
566
567
568
569 var cgo1, cgo2 bytes.Buffer
570 fmt.Fprintf(&cgo1, "package main\n\n/*\n")
571 fmt.Fprintf(&cgo2, "package main\n\n/*\n")
572
573
574 for _, pt := range ptrTests {
575 cgo := &cgo1
576 if strings.Contains(pt.support, "//export") {
577 cgo = &cgo2
578 }
579 fmt.Fprintf(cgo, "%s\n", pt.c)
580 fmt.Fprintf(&cgo1, "%s\n", pt.c1)
581 }
582 fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
583 fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
584
585
586 did1 := make(map[string]bool)
587 did2 := make(map[string]bool)
588 did1["os"] = true
589 fmt.Fprintf(&cgo1, "import \"os\"\n")
590
591 for _, pt := range ptrTests {
592 did := did1
593 cgo := &cgo1
594 if strings.Contains(pt.support, "//export") {
595 did = did2
596 cgo = &cgo2
597 }
598 for _, imp := range pt.imports {
599 if !did[imp] {
600 did[imp] = true
601 fmt.Fprintf(cgo, "import %q\n", imp)
602 }
603 }
604 }
605
606
607 for _, pt := range ptrTests {
608 cgo := &cgo1
609 if strings.Contains(pt.support, "//export") {
610 cgo = &cgo2
611 }
612 fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
613 }
614
615
616 fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
617 for _, pt := range ptrTests {
618 fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
619 }
620 fmt.Fprintf(&cgo1, "}\n\n")
621 fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
622
623 if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
624 t.Fatal(err)
625 }
626 if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
627 t.Fatal(err)
628 }
629
630 exeName := "ptrtest.exe"
631 if cgocheck2 {
632 exeName = "ptrtest2.exe"
633 }
634 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exeName)
635 cmd.Dir = src
636 cmd.Env = append(os.Environ(), "GOPATH="+gopath)
637
638
639 goexperiment := strings.Split(os.Getenv("GOEXPERIMENT"), ",")
640 if len(goexperiment) == 1 && goexperiment[0] == "" {
641 goexperiment = nil
642 }
643 i := slices.Index(goexperiment, "cgocheck2")
644 changed := false
645 if cgocheck2 && i < 0 {
646 goexperiment = append(goexperiment, "cgocheck2")
647 changed = true
648 } else if !cgocheck2 && i >= 0 {
649 goexperiment = slices.Delete(goexperiment, i, i+1)
650 changed = true
651 }
652 if changed {
653 cmd.Env = append(cmd.Env, "GOEXPERIMENT="+strings.Join(goexperiment, ","))
654 }
655
656 out, err := cmd.CombinedOutput()
657 if err != nil {
658 t.Fatalf("go build: %v\n%s", err, out)
659 }
660
661 return filepath.Join(src, exeName)
662 }
663
664 const ptrTestMain = `
665 func main() {
666 for _, arg := range os.Args[1:] {
667 f := funcs[arg]
668 if f == nil {
669 panic("missing func "+arg)
670 }
671 f()
672 }
673 }
674 `
675
676 var csem = make(chan bool, 16)
677
678 func testOne(t *testing.T, pt ptrTest, exe, exe2 string) {
679 t.Parallel()
680
681
682
683 runcmd := func(cgocheck string) ([]byte, error) {
684 csem <- true
685 defer func() { <-csem }()
686 x := exe
687 if cgocheck == "2" {
688 x = exe2
689 cgocheck = "1"
690 }
691 cmd := exec.Command(x, pt.name)
692 cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
693 return cmd.CombinedOutput()
694 }
695
696 if pt.expensive {
697 buf, err := runcmd("1")
698 if err != nil {
699 t.Logf("%s", buf)
700 if pt.fail {
701 t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
702 } else {
703 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
704 }
705 }
706
707 }
708
709 cgocheck := ""
710 if pt.expensive {
711 cgocheck = "2"
712 }
713
714 buf, err := runcmd(cgocheck)
715
716 var pattern string = pt.errTextRegexp
717 if pt.errTextRegexp == "" {
718 pattern = `.*unpinned Go.*`
719 }
720
721 if pt.fail {
722 if err == nil {
723 t.Logf("%s", buf)
724 t.Fatalf("did not fail as expected")
725 } else if ok, _ := regexp.Match(pattern, buf); !ok {
726 t.Logf("%s", buf)
727 t.Fatalf("did not print expected error (failed with %v)", err)
728 }
729 } else {
730 if err != nil {
731 t.Logf("%s", buf)
732 t.Fatalf("failed unexpectedly: %v", err)
733 }
734
735 if !pt.expensive {
736
737 buf, err := runcmd("2")
738 if err != nil {
739 t.Logf("%s", buf)
740 t.Fatalf("failed unexpectedly with expensive checks: %v", err)
741 }
742 }
743 }
744
745 if pt.fail {
746 buf, err := runcmd("0")
747 if err != nil {
748 t.Logf("%s", buf)
749 t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
750 }
751 }
752 }
753
View as plain text