diff options
Diffstat (limited to 'libgo/go/runtime/testdata')
34 files changed, 294 insertions, 133 deletions
diff --git a/libgo/go/runtime/testdata/testprog/badtraceback.go b/libgo/go/runtime/testdata/testprog/badtraceback.go index d558adc..09aa2b8 100644 --- a/libgo/go/runtime/testdata/testprog/badtraceback.go +++ b/libgo/go/runtime/testdata/testprog/badtraceback.go @@ -17,6 +17,9 @@ func init() { func BadTraceback() { // Disable GC to prevent traceback at unexpected time. debug.SetGCPercent(-1) + // Out of an abundance of caution, also make sure that there are + // no GCs actively in progress. + runtime.GC() // Run badLR1 on its own stack to minimize the stack size and // exercise the stack bounds logic in the hex dump. diff --git a/libgo/go/runtime/testdata/testprog/checkptr.go b/libgo/go/runtime/testdata/testprog/checkptr.go index 9c55613..b27e5f7 100644 --- a/libgo/go/runtime/testdata/testprog/checkptr.go +++ b/libgo/go/runtime/testdata/testprog/checkptr.go @@ -20,6 +20,7 @@ func init() { register("CheckPtrSmall", CheckPtrSmall) register("CheckPtrSliceOK", CheckPtrSliceOK) register("CheckPtrSliceFail", CheckPtrSliceFail) + register("CheckPtrAlignmentNested", CheckPtrAlignmentNested) } func CheckPtrAlignmentNoPtr() { @@ -96,3 +97,10 @@ func CheckPtrSliceFail() { sink2 = p sink2 = unsafe.Slice(p, 100) } + +func CheckPtrAlignmentNested() { + s := make([]int8, 100) + p := unsafe.Pointer(&s[0]) + n := 9 + _ = ((*[10]int8)(unsafe.Pointer((*[10]int64)(unsafe.Pointer(&p)))))[:n:n] +} diff --git a/libgo/go/runtime/testdata/testprog/gc.go b/libgo/go/runtime/testdata/testprog/gc.go index 74732cd..215228e 100644 --- a/libgo/go/runtime/testdata/testprog/gc.go +++ b/libgo/go/runtime/testdata/testprog/gc.go @@ -90,7 +90,7 @@ func GCFairness2() { runtime.GOMAXPROCS(1) debug.SetGCPercent(1) var count [3]int64 - var sink [3]interface{} + var sink [3]any for i := range count { go func(i int) { for { @@ -132,81 +132,88 @@ func GCFairness2() { func GCPhys() { // This test ensures that heap-growth scavenging is working as intended. // - // It sets up a specific scenario: it allocates two pairs of objects whose - // sizes sum to size. One object in each pair is "small" (though must be - // large enough to be considered a large object by the runtime) and one is - // large. The small objects are kept while the large objects are freed, - // creating two large unscavenged holes in the heap. The heap goal should - // also be small as a result (so size must be at least as large as the - // minimum heap size). We then allocate one large object, bigger than both - // pairs of objects combined. This allocation, because it will tip - // HeapSys-HeapReleased well above the heap goal, should trigger heap-growth - // scavenging and scavenge most, if not all, of the large holes we created - // earlier. + // It attempts to construct a sizeable "swiss cheese" heap, with many + // allocChunk-sized holes. Then, it triggers a heap growth by trying to + // allocate as much memory as would fit in those holes. + // + // The heap growth should cause a large number of those holes to be + // returned to the OS. + const ( - // Size must be also large enough to be considered a large - // object (not in any size-segregated span). - size = 4 << 20 - split = 64 << 10 - objects = 2 + // The total amount of memory we're willing to allocate. + allocTotal = 32 << 20 // The page cache could hide 64 8-KiB pages from the scavenger today. maxPageCache = (8 << 10) * 64 - - // Reduce GOMAXPROCS down to 4 if it's greater. We need to bound the amount - // of memory held in the page cache because the scavenger can't reach it. - // The page cache will hold at most maxPageCache of memory per-P, so this - // bounds the amount of memory hidden from the scavenger to 4*maxPageCache - // at most. - maxProcs = 4 ) - // Set GOGC so that this test operates under consistent assumptions. - debug.SetGCPercent(100) - procs := runtime.GOMAXPROCS(-1) - if procs > maxProcs { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) - procs = runtime.GOMAXPROCS(-1) + + // How big the allocations are needs to depend on the page size. + // If the page size is too big and the allocations are too small, + // they might not be aligned to the physical page size, so the scavenger + // will gloss over them. + pageSize := os.Getpagesize() + var allocChunk int + if pageSize <= 8<<10 { + allocChunk = 64 << 10 + } else { + allocChunk = 512 << 10 } - // Save objects which we want to survive, and condemn objects which we don't. - // Note that we condemn objects in this way and release them all at once in - // order to avoid having the GC start freeing up these objects while the loop - // is still running and filling in the holes we intend to make. - saved := make([][]byte, 0, objects+1) - condemned := make([][]byte, 0, objects) - for i := 0; i < 2*objects; i++ { + allocs := allocTotal / allocChunk + + // Set GC percent just so this test is a little more consistent in the + // face of varying environments. + debug.SetGCPercent(100) + + // Set GOMAXPROCS to 1 to minimize the amount of memory held in the page cache, + // and to reduce the chance that the background scavenger gets scheduled. + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + // Allocate allocTotal bytes of memory in allocChunk byte chunks. + // Alternate between whether the chunk will be held live or will be + // condemned to GC to create holes in the heap. + saved := make([][]byte, allocs/2+1) + condemned := make([][]byte, allocs/2) + for i := 0; i < allocs; i++ { + b := make([]byte, allocChunk) if i%2 == 0 { - saved = append(saved, make([]byte, split)) + saved = append(saved, b) } else { - condemned = append(condemned, make([]byte, size-split)) + condemned = append(condemned, b) } } - condemned = nil - // Clean up the heap. This will free up every other object created above - // (i.e. everything in condemned) creating holes in the heap. - // Also, if the condemned objects are still being swept, its possible that - // the scavenging that happens as a result of the next allocation won't see - // the holes at all. We call runtime.GC() twice here so that when we allocate - // our large object there's no race with sweeping. - runtime.GC() + + // Run a GC cycle just so we're at a consistent state. runtime.GC() - // Perform one big allocation which should also scavenge any holes. - // - // The heap goal will rise after this object is allocated, so it's very - // important that we try to do all the scavenging in a single allocation - // that exceeds the heap goal. Otherwise the rising heap goal could foil our - // test. - saved = append(saved, make([]byte, objects*size)) - // Clean up the heap again just to put it in a known state. + + // Drop the only reference to all the condemned memory. + condemned = nil + + // Clear the condemned memory. runtime.GC() + + // At this point, the background scavenger is likely running + // and could pick up the work, so the next line of code doesn't + // end up doing anything. That's fine. What's important is that + // this test fails somewhat regularly if the runtime doesn't + // scavenge on heap growth, and doesn't fail at all otherwise. + + // Make a large allocation that in theory could fit, but won't + // because we turned the heap into swiss cheese. + saved = append(saved, make([]byte, allocTotal/2)) + // heapBacked is an estimate of the amount of physical memory used by // this test. HeapSys is an estimate of the size of the mapped virtual // address space (which may or may not be backed by physical pages) // whereas HeapReleased is an estimate of the amount of bytes returned // to the OS. Their difference then roughly corresponds to the amount // of virtual address space that is backed by physical pages. + // + // heapBacked also subtracts out maxPageCache bytes of memory because + // this is memory that may be hidden from the scavenger per-P. Since + // GOMAXPROCS=1 here, subtracting it out once is fine. var stats runtime.MemStats runtime.ReadMemStats(&stats) - heapBacked := stats.HeapSys - stats.HeapReleased + heapBacked := stats.HeapSys - stats.HeapReleased - maxPageCache // If heapBacked does not exceed the heap goal by more than retainExtraPercent // then the scavenger is working as expected; the newly-created holes have been // scavenged immediately as part of the allocations which cannot fit in the holes. @@ -216,19 +223,14 @@ func GCPhys() { // to other allocations that happen during this test we may still see some physical // memory over-use. overuse := (float64(heapBacked) - float64(stats.HeapAlloc)) / float64(stats.HeapAlloc) - // Compute the threshold. + // Check against our overuse threshold, which is what the scavenger always reserves + // to encourage allocation of memory that doesn't need to be faulted in. // - // In theory, this threshold should just be zero, but that's not possible in practice. - // Firstly, the runtime's page cache can hide up to maxPageCache of free memory from the - // scavenger per P. To account for this, we increase the threshold by the ratio between the - // total amount the runtime could hide from the scavenger to the amount of memory we expect - // to be able to scavenge here, which is (size-split)*objects. This computation is the crux - // GOMAXPROCS above; if GOMAXPROCS is too high the threshold just becomes 100%+ since the - // amount of memory being allocated is fixed. Then we add 5% to account for noise, such as - // other allocations this test may have performed that we don't explicitly account for The - // baseline threshold here is around 11% for GOMAXPROCS=1, capping out at around 30% for - // GOMAXPROCS=4. - threshold := 0.05 + float64(procs)*maxPageCache/float64((size-split)*objects) + // Add additional slack in case the page size is large and the scavenger + // can't reach that memory because it doesn't constitute a complete aligned + // physical page. Assume the worst case: a full physical page out of each + // allocation. + threshold := 0.1 + float64(pageSize)/float64(allocChunk) if overuse <= threshold { fmt.Println("OK") return @@ -243,6 +245,7 @@ func GCPhys() { "(alloc: %d, goal: %d, sys: %d, rel: %d, objs: %d)\n", threshold*100, overuse*100, stats.HeapAlloc, stats.NextGC, stats.HeapSys, stats.HeapReleased, len(saved)) runtime.KeepAlive(saved) + runtime.KeepAlive(condemned) } // Test that defer closure is correctly scanned when the stack is scanned. @@ -263,9 +266,9 @@ func DeferLiveness() { } //go:noinline -func escape(x interface{}) { sink2 = x; sink2 = nil } +func escape(x any) { sink2 = x; sink2 = nil } -var sink2 interface{} +var sink2 any // Test zombie object detection and reporting. func GCZombie() { diff --git a/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go b/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go index aff36ec..7209f67 100644 --- a/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go +++ b/libgo/go/runtime/testdata/testprog/numcpu_freebsd.go @@ -85,19 +85,18 @@ func getList() ([]string, error) { if err != nil { return nil, fmt.Errorf("fail to execute '%s': %s", cmdline, err) } - pos := bytes.IndexRune(output, '\n') - if pos == -1 { + output, _, ok := bytes.Cut(output, []byte("\n")) + if !ok { return nil, fmt.Errorf("invalid output from '%s', '\\n' not found: %s", cmdline, output) } - output = output[0:pos] - pos = bytes.IndexRune(output, ':') - if pos == -1 { + _, cpus, ok := bytes.Cut(output, []byte(":")) + if !ok { return nil, fmt.Errorf("invalid output from '%s', ':' not found: %s", cmdline, output) } var list []string - for _, val := range bytes.Split(output[pos+1:], []byte(",")) { + for _, val := range bytes.Split(cpus, []byte(",")) { index := string(bytes.TrimSpace(val)) if len(index) == 0 { continue diff --git a/libgo/go/runtime/testdata/testprog/preempt.go b/libgo/go/runtime/testdata/testprog/preempt.go index 1c74d0e..fb6755a 100644 --- a/libgo/go/runtime/testdata/testprog/preempt.go +++ b/libgo/go/runtime/testdata/testprog/preempt.go @@ -20,6 +20,10 @@ func AsyncPreempt() { runtime.GOMAXPROCS(1) // Disable GC so we have complete control of what we're testing. debug.SetGCPercent(-1) + // Out of an abundance of caution, also make sure that there are + // no GCs actively in progress. The sweep phase of a GC cycle + // for instance tries to preempt Ps at the very beginning. + runtime.GC() // Start a goroutine with no sync safe-points. var ready, ready2 uint32 diff --git a/libgo/go/runtime/testdata/testprog/signal.go b/libgo/go/runtime/testdata/testprog/signal.go index 417e105..cc5ac8a 100644 --- a/libgo/go/runtime/testdata/testprog/signal.go +++ b/libgo/go/runtime/testdata/testprog/signal.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !plan9 // +build !windows,!plan9 package main diff --git a/libgo/go/runtime/testdata/testprog/sleep.go b/libgo/go/runtime/testdata/testprog/sleep.go index 86e2f6c..b230e60 100644 --- a/libgo/go/runtime/testdata/testprog/sleep.go +++ b/libgo/go/runtime/testdata/testprog/sleep.go @@ -4,7 +4,10 @@ package main -import "time" +import ( + "os" + "time" +) // for golang.org/issue/27250 @@ -13,5 +16,7 @@ func init() { } func After1() { + os.Stdout.WriteString("ready\n") + os.Stdout.Close() <-time.After(1 * time.Second) } diff --git a/libgo/go/runtime/testdata/testprog/syscalls_none.go b/libgo/go/runtime/testdata/testprog/syscalls_none.go index 7f8ded3..068bb59 100644 --- a/libgo/go/runtime/testdata/testprog/syscalls_none.go +++ b/libgo/go/runtime/testdata/testprog/syscalls_none.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !linux // +build !linux package main diff --git a/libgo/go/runtime/testdata/testprog/traceback_ancestors.go b/libgo/go/runtime/testdata/testprog/traceback_ancestors.go index 0ee402c..1d0d00b 100644 --- a/libgo/go/runtime/testdata/testprog/traceback_ancestors.go +++ b/libgo/go/runtime/testdata/testprog/traceback_ancestors.go @@ -33,30 +33,27 @@ func printStack() { for { n := runtime.Stack(buf, true) if n < len(buf) { - tb := string(buf[:n]) + all := string(buf[:n]) + var saved string // Delete any ignored goroutines, if present. - pos := 0 - for pos < len(tb) { - next := pos + strings.Index(tb[pos:], "\n\n") - if next < pos { - next = len(tb) - } else { - next += len("\n\n") - } + for all != "" { + var g string + g, all, _ = strings.Cut(all, "\n\n") - if strings.HasPrefix(tb[pos:], "goroutine ") { - id := tb[pos+len("goroutine "):] - id = id[:strings.IndexByte(id, ' ')] + if strings.HasPrefix(g, "goroutine ") { + id, _, _ := strings.Cut(strings.TrimPrefix(g, "goroutine "), " ") if ignoreGoroutines[id] { - tb = tb[:pos] + tb[next:] - next = pos + continue } } - pos = next + if saved != "" { + saved += "\n\n" + } + saved += g } - fmt.Print(tb) + fmt.Print(saved) return } buf = make([]byte, 2*len(buf)) @@ -89,11 +86,10 @@ func recurseThenCallGo(w chan struct{}, frames int, goroutines int, main bool) { func goroutineID() string { buf := make([]byte, 128) runtime.Stack(buf, false) - const prefix = "goroutine " - if !bytes.HasPrefix(buf, []byte(prefix)) { + prefix := []byte("goroutine ") + if !bytes.HasPrefix(buf, prefix) { panic(fmt.Sprintf("expected %q at beginning of traceback:\n%s", prefix, buf)) } - buf = buf[len(prefix):] - n := bytes.IndexByte(buf, ' ') - return string(buf[:n]) + id, _, _ := bytes.Cut(bytes.TrimPrefix(buf, prefix), []byte(" ")) + return string(id) } diff --git a/libgo/go/runtime/testdata/testprogcgo/callback.go b/libgo/go/runtime/testdata/testprogcgo/callback.go index 2f7568c..45baeb1 100644 --- a/libgo/go/runtime/testdata/testprogcgo/callback.go +++ b/libgo/go/runtime/testdata/testprogcgo/callback.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main @@ -70,7 +71,7 @@ func grow1(x, sum *int) int { func CgoCallbackGC() { P := 100 - if os.Getenv("RUNTIME_TESTING_SHORT") != "" { + if os.Getenv("RUNTIME_TEST_SHORT") != "" { P = 10 } done := make(chan bool) diff --git a/libgo/go/runtime/testdata/testprogcgo/catchpanic.go b/libgo/go/runtime/testdata/testprogcgo/catchpanic.go index 55a606d..c722d40 100644 --- a/libgo/go/runtime/testdata/testprogcgo/catchpanic.go +++ b/libgo/go/runtime/testdata/testprogcgo/catchpanic.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/dropm.go b/libgo/go/runtime/testdata/testprogcgo/dropm.go index 9e782f5..700b7fa 100644 --- a/libgo/go/runtime/testdata/testprogcgo/dropm.go +++ b/libgo/go/runtime/testdata/testprogcgo/dropm.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows // Test that a sequence of callbacks from C to Go get the same m. diff --git a/libgo/go/runtime/testdata/testprogcgo/eintr.go b/libgo/go/runtime/testdata/testprogcgo/eintr.go index 1722a75..b35b280 100644 --- a/libgo/go/runtime/testdata/testprogcgo/eintr.go +++ b/libgo/go/runtime/testdata/testprogcgo/eintr.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/exec.go b/libgo/go/runtime/testdata/testprogcgo/exec.go index 15723c7..c268bcd 100644 --- a/libgo/go/runtime/testdata/testprogcgo/exec.go +++ b/libgo/go/runtime/testdata/testprogcgo/exec.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/gprof.go b/libgo/go/runtime/testdata/testprogcgo/gprof.go new file mode 100644 index 0000000..85986cc --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/gprof.go @@ -0,0 +1,49 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !gccgo +// +build !gccgo + +package main + +// Test taking a goroutine profile with C traceback. + +/* +// Defined in gprof_c.c. +void CallGoSleep(void); +void gprofCgoTraceback(void* parg); +void gprofCgoContext(void* parg); +*/ +import "C" + +import ( + "fmt" + "io" + "runtime" + "runtime/pprof" + "time" + "unsafe" +) + +func init() { + register("GoroutineProfile", GoroutineProfile) +} + +func GoroutineProfile() { + runtime.SetCgoTraceback(0, unsafe.Pointer(C.gprofCgoTraceback), unsafe.Pointer(C.gprofCgoContext), nil) + + go C.CallGoSleep() + go C.CallGoSleep() + go C.CallGoSleep() + time.Sleep(1 * time.Second) + + prof := pprof.Lookup("goroutine") + prof.WriteTo(io.Discard, 1) + fmt.Println("OK") +} + +//export GoSleep +func GoSleep() { + time.Sleep(time.Hour) +} diff --git a/libgo/go/runtime/testdata/testprogcgo/gprof_c.c b/libgo/go/runtime/testdata/testprogcgo/gprof_c.c new file mode 100644 index 0000000..11b0649 --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/gprof_c.c @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !gccgo +// +build !gccgo + +// The C definitions for gprof.go. That file uses //export so +// it can't put function definitions in the "C" import comment. + +#include <stdint.h> +#include <stdlib.h> + +// Functions exported from Go. +extern void GoSleep(); + +struct cgoContextArg { + uintptr_t context; +}; + +void gprofCgoContext(void *arg) { + ((struct cgoContextArg*)arg)->context = 1; +} + +void gprofCgoTraceback(void *arg) { + // spend some time here so the P is more likely to be retaken. + volatile int i; + for (i = 0; i < 123456789; i++); +} + +void CallGoSleep() { + GoSleep(); +} diff --git a/libgo/go/runtime/testdata/testprogcgo/lockosthread.go b/libgo/go/runtime/testdata/testprogcgo/lockosthread.go index 36423d9..8fcea35 100644 --- a/libgo/go/runtime/testdata/testprogcgo/lockosthread.go +++ b/libgo/go/runtime/testdata/testprogcgo/lockosthread.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/needmdeadlock.go b/libgo/go/runtime/testdata/testprogcgo/needmdeadlock.go index 5a9c359..b95ec77 100644 --- a/libgo/go/runtime/testdata/testprogcgo/needmdeadlock.go +++ b/libgo/go/runtime/testdata/testprogcgo/needmdeadlock.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/numgoroutine.go b/libgo/go/runtime/testdata/testprogcgo/numgoroutine.go index b7134a4..e09706d 100644 --- a/libgo/go/runtime/testdata/testprogcgo/numgoroutine.go +++ b/libgo/go/runtime/testdata/testprogcgo/numgoroutine.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/panic.c b/libgo/go/runtime/testdata/testprogcgo/panic.c new file mode 100644 index 0000000..deb5ed5 --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/panic.c @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern void panic_callback(); + +void call_callback(void) { + panic_callback(); +} diff --git a/libgo/go/runtime/testdata/testprogcgo/panic.go b/libgo/go/runtime/testdata/testprogcgo/panic.go new file mode 100644 index 0000000..57ac895 --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/panic.go @@ -0,0 +1,23 @@ +package main + +// This program will crash. +// We want to test unwinding from a cgo callback. + +/* +void call_callback(void); +*/ +import "C" + +func init() { + register("PanicCallback", PanicCallback) +} + +//export panic_callback +func panic_callback() { + var i *int + *i = 42 +} + +func PanicCallback() { + C.call_callback() +} diff --git a/libgo/go/runtime/testdata/testprogcgo/pprof.go b/libgo/go/runtime/testdata/testprogcgo/pprof.go index 668d4e0..bbf93c4 100644 --- a/libgo/go/runtime/testdata/testprogcgo/pprof.go +++ b/libgo/go/runtime/testdata/testprogcgo/pprof.go @@ -32,8 +32,6 @@ void cpuHog() { void cpuHog2() { } -static int cpuHogCount; - struct cgoTracebackArg { uintptr_t context; uintptr_t sigContext; @@ -50,13 +48,6 @@ void pprofCgoTraceback(void* parg) { arg->buf[0] = (uintptr_t)(cpuHog) + 0x10; arg->buf[1] = (uintptr_t)(cpuHog2) + 0x4; arg->buf[2] = 0; - ++cpuHogCount; -} - -// getCpuHogCount fetches the number of times we've seen cpuHog in the -// traceback. -int getCpuHogCount() { - return cpuHogCount; } */ import "C" @@ -89,7 +80,7 @@ func CgoPprof() { } t0 := time.Now() - for C.getCpuHogCount() < 2 && time.Since(t0) < time.Second { + for time.Since(t0) < time.Second { C.cpuHog() } diff --git a/libgo/go/runtime/testdata/testprogcgo/raceprof.go b/libgo/go/runtime/testdata/testprogcgo/raceprof.go index c17bb39..3329139 100644 --- a/libgo/go/runtime/testdata/testprogcgo/raceprof.go +++ b/libgo/go/runtime/testdata/testprogcgo/raceprof.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build ((linux && amd64) || (freebsd && amd64)) && !gccgo -// +build linux,amd64 freebsd,amd64 -// +build !gccgo package main diff --git a/libgo/go/runtime/testdata/testprogcgo/racesig.go b/libgo/go/runtime/testdata/testprogcgo/racesig.go index a079b3f..9352679 100644 --- a/libgo/go/runtime/testdata/testprogcgo/racesig.go +++ b/libgo/go/runtime/testdata/testprogcgo/racesig.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build (linux && amd64) || (freebsd && amd64) // +build linux,amd64 freebsd,amd64 package main diff --git a/libgo/go/runtime/testdata/testprogcgo/segv.go b/libgo/go/runtime/testdata/testprogcgo/segv.go index 3237a8c..0632475 100644 --- a/libgo/go/runtime/testdata/testprogcgo/segv.go +++ b/libgo/go/runtime/testdata/testprogcgo/segv.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/libgo/go/runtime/testdata/testprogcgo/sigstack.go b/libgo/go/runtime/testdata/testprogcgo/sigstack.go index 21b668d..12ca661 100644 --- a/libgo/go/runtime/testdata/testprogcgo/sigstack.go +++ b/libgo/go/runtime/testdata/testprogcgo/sigstack.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows // Test handling of Go-allocated signal stacks when calling from diff --git a/libgo/go/runtime/testdata/testprogcgo/sigthrow.go b/libgo/go/runtime/testdata/testprogcgo/sigthrow.go new file mode 100644 index 0000000..665e3b0 --- /dev/null +++ b/libgo/go/runtime/testdata/testprogcgo/sigthrow.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program will abort. + +/* +#include <stdlib.h> +*/ +import "C" + +func init() { + register("Abort", Abort) +} + +func Abort() { + C.abort() +} diff --git a/libgo/go/runtime/testdata/testprogcgo/threadpanic.go b/libgo/go/runtime/testdata/testprogcgo/threadpanic.go index f9b48a9..2d24fe6 100644 --- a/libgo/go/runtime/testdata/testprogcgo/threadpanic.go +++ b/libgo/go/runtime/testdata/testprogcgo/threadpanic.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 // +build !plan9 package main diff --git a/libgo/go/runtime/testdata/testprogcgo/threadpprof.go b/libgo/go/runtime/testdata/testprogcgo/threadpprof.go index ed3faed..ec83d2d 100644 --- a/libgo/go/runtime/testdata/testprogcgo/threadpprof.go +++ b/libgo/go/runtime/testdata/testprogcgo/threadpprof.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 && !windows && !gccgo -// +build !plan9,!windows,!gccgo package main @@ -34,8 +33,6 @@ void cpuHogThread() { void cpuHogThread2() { } -static int cpuHogThreadCount; - struct cgoTracebackArg { uintptr_t context; uintptr_t sigContext; @@ -50,13 +47,6 @@ void pprofCgoThreadTraceback(void* parg) { arg->buf[0] = (uintptr_t)(cpuHogThread) + 0x10; arg->buf[1] = (uintptr_t)(cpuHogThread2) + 0x4; arg->buf[2] = 0; - __sync_add_and_fetch(&cpuHogThreadCount, 1); -} - -// getCPUHogThreadCount fetches the number of times we've seen cpuHogThread -// in the traceback. -int getCPUHogThreadCount() { - return __sync_add_and_fetch(&cpuHogThreadCount, 0); } static void* cpuHogDriver(void* arg __attribute__ ((unused))) { @@ -74,6 +64,7 @@ void runCPUHogThread(void) { import "C" import ( + "context" "fmt" "os" "runtime" @@ -108,12 +99,16 @@ func pprofThread() { os.Exit(2) } - C.runCPUHogThread() + // This goroutine may receive a profiling signal while creating the C-owned + // thread. If it does, the SetCgoTraceback handler will make the leaf end of + // the stack look almost (but not exactly) like the stacks the test case is + // trying to find. Attach a profiler label so the test can filter out those + // confusing samples. + pprof.Do(context.Background(), pprof.Labels("ignore", "ignore"), func(ctx context.Context) { + C.runCPUHogThread() + }) - t0 := time.Now() - for C.getCPUHogThreadCount() < 2 && time.Since(t0) < time.Second { - time.Sleep(100 * time.Millisecond) - } + time.Sleep(1 * time.Second) pprof.StopCPUProfile() diff --git a/libgo/go/runtime/testdata/testprogcgo/threadprof.go b/libgo/go/runtime/testdata/testprogcgo/threadprof.go index 2d4c103..d62d4b4 100644 --- a/libgo/go/runtime/testdata/testprogcgo/threadprof.go +++ b/libgo/go/runtime/testdata/testprogcgo/threadprof.go @@ -2,21 +2,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// We only build this file with the tag "threadprof", since it starts -// a thread running a busy loop at constructor time. - +//go:build !plan9 && !windows // +build !plan9,!windows -// +build threadprof package main /* #include <stdint.h> +#include <stdlib.h> #include <signal.h> #include <pthread.h> volatile int32_t spinlock; +// Note that this thread is only started if GO_START_SIGPROF_THREAD +// is set in the environment, which is only done when running the +// CgoExternalThreadSIGPROF test. static void *thread1(void *p) { (void)p; while (spinlock == 0) @@ -26,9 +27,13 @@ static void *thread1(void *p) { return NULL; } +// This constructor function is run when the program starts. +// It is used for the CgoExternalThreadSIGPROF test. __attribute__((constructor)) void issue9456() { - pthread_t tid; - pthread_create(&tid, 0, thread1, NULL); + if (getenv("GO_START_SIGPROF_THREAD") != NULL) { + pthread_t tid; + pthread_create(&tid, 0, thread1, NULL); + } } void **nullptr; diff --git a/libgo/go/runtime/testdata/testprognet/signal.go b/libgo/go/runtime/testdata/testprognet/signal.go index 4d2de79..dfa2e10 100644 --- a/libgo/go/runtime/testdata/testprognet/signal.go +++ b/libgo/go/runtime/testdata/testprognet/signal.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !plan9 // +build !windows,!plan9 // This is in testprognet instead of testprog because testprog diff --git a/libgo/go/runtime/testdata/testprognet/signalexec.go b/libgo/go/runtime/testdata/testprognet/signalexec.go index 4a988ef..62ebce7 100644 --- a/libgo/go/runtime/testdata/testprognet/signalexec.go +++ b/libgo/go/runtime/testdata/testprognet/signalexec.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd // +build darwin dragonfly freebsd linux netbsd openbsd // This is in testprognet instead of testprog because testprog diff --git a/libgo/go/runtime/testdata/testwinlib/main.c b/libgo/go/runtime/testdata/testwinlib/main.c index e84a32f..c3fe3cb 100644 --- a/libgo/go/runtime/testdata/testwinlib/main.c +++ b/libgo/go/runtime/testdata/testwinlib/main.c @@ -41,17 +41,20 @@ int main() if (NULL == exceptionHandlerHandle) { printf("cannot add vectored exception handler\n"); + fflush(stdout); return 2; } void *continueHandlerHandle = AddVectoredContinueHandler(0, customContinueHandlder); if (NULL == continueHandlerHandle) { printf("cannot add vectored continue handler\n"); + fflush(stdout); return 2; } CallMeBack(throwFromC); RemoveVectoredContinueHandler(continueHandlerHandle); RemoveVectoredExceptionHandler(exceptionHandlerHandle); printf("exceptionCount: %d\ncontinueCount: %d\n", exceptionCount, continueCount); + fflush(stdout); return 0; -}
\ No newline at end of file +} diff --git a/libgo/go/runtime/testdata/testwinlib/main.go b/libgo/go/runtime/testdata/testwinlib/main.go index 400eaa1..025ef91 100644 --- a/libgo/go/runtime/testdata/testwinlib/main.go +++ b/libgo/go/runtime/testdata/testwinlib/main.go @@ -1,3 +1,4 @@ +//go:build windows && cgo // +build windows,cgo package main |