summaryrefslogtreecommitdiffstats
path: root/src/internal/trace/v2/testdata/testprog
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
commitf6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch)
tree7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/internal/trace/v2/testdata/testprog
parentInitial commit. (diff)
downloadgolang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz
golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/internal/trace/v2/testdata/testprog')
-rw-r--r--src/internal/trace/v2/testdata/testprog/annotations-stress.go84
-rw-r--r--src/internal/trace/v2/testdata/testprog/annotations.go60
-rw-r--r--src/internal/trace/v2/testdata/testprog/cgo-callback.go80
-rw-r--r--src/internal/trace/v2/testdata/testprog/cpu-profile.go137
-rw-r--r--src/internal/trace/v2/testdata/testprog/futile-wakeup.go84
-rw-r--r--src/internal/trace/v2/testdata/testprog/gc-stress.go76
-rw-r--r--src/internal/trace/v2/testdata/testprog/gomaxprocs.go46
-rw-r--r--src/internal/trace/v2/testdata/testprog/many-start-stop.go38
-rw-r--r--src/internal/trace/v2/testdata/testprog/stacks.go129
-rw-r--r--src/internal/trace/v2/testdata/testprog/stress-start-stop.go166
-rw-r--r--src/internal/trace/v2/testdata/testprog/stress.go146
-rw-r--r--src/internal/trace/v2/testdata/testprog/wait-on-pipe.go66
12 files changed, 1112 insertions, 0 deletions
diff --git a/src/internal/trace/v2/testdata/testprog/annotations-stress.go b/src/internal/trace/v2/testdata/testprog/annotations-stress.go
new file mode 100644
index 0000000..511d6ed
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/annotations-stress.go
@@ -0,0 +1,84 @@
+// Copyright 2023 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.
+
+// Tests user tasks, regions, and logging.
+
+//go:build ignore
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "runtime/trace"
+ "time"
+)
+
+func main() {
+ baseCtx := context.Background()
+
+ // Create a task that starts and ends entirely outside of the trace.
+ ctx0, t0 := trace.NewTask(baseCtx, "parent")
+
+ // Create a task that starts before the trace and ends during the trace.
+ ctx1, t1 := trace.NewTask(ctx0, "type1")
+
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ t1.End()
+
+ // Create a task that starts during the trace and ends after.
+ ctx2, t2 := trace.NewTask(ctx0, "type2")
+
+ // Create a task that starts and ends during the trace.
+ ctx3, t3 := trace.NewTask(baseCtx, "type3")
+
+ // Generate some events.
+ for i := 0; i < 2; i++ {
+ do(baseCtx, 4)
+ do(ctx0, 2)
+ do(ctx1, 3)
+ do(ctx2, 6)
+ do(ctx3, 5)
+ }
+
+ // Finish up tasks according to their lifetime relative to the trace.
+ t3.End()
+ trace.Stop()
+ t2.End()
+ t0.End()
+}
+
+func do(ctx context.Context, k int) {
+ trace.Log(ctx, "log", "before do")
+
+ var t *trace.Task
+ ctx, t = trace.NewTask(ctx, "do")
+ defer t.End()
+
+ trace.Log(ctx, "log2", "do")
+
+ // Create a region and spawn more tasks and more workers.
+ trace.WithRegion(ctx, "fanout", func() {
+ for i := 0; i < k; i++ {
+ go func(i int) {
+ trace.WithRegion(ctx, fmt.Sprintf("region%d", i), func() {
+ trace.Logf(ctx, "log", "fanout region%d", i)
+ if i == 2 {
+ do(ctx, 0)
+ return
+ }
+ })
+ }(i)
+ }
+ })
+
+ // Sleep to let things happen, but also increase the chance that we
+ // advance a generation.
+ time.Sleep(10 * time.Millisecond)
+}
diff --git a/src/internal/trace/v2/testdata/testprog/annotations.go b/src/internal/trace/v2/testdata/testprog/annotations.go
new file mode 100644
index 0000000..2507bc4
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/annotations.go
@@ -0,0 +1,60 @@
+// Copyright 2023 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.
+
+// Tests user tasks, regions, and logging.
+
+//go:build ignore
+
+package main
+
+import (
+ "context"
+ "log"
+ "os"
+ "runtime/trace"
+ "sync"
+)
+
+func main() {
+ bgctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ // Create a pre-existing region. This won't end up in the trace.
+ preExistingRegion := trace.StartRegion(bgctx, "pre-existing region")
+
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+
+ // Beginning of traced execution.
+ var wg sync.WaitGroup
+ ctx, task := trace.NewTask(bgctx, "task0") // EvUserTaskCreate("task0")
+ trace.StartRegion(ctx, "task0 region")
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer task.End() // EvUserTaskEnd("task0")
+
+ trace.StartRegion(ctx, "unended region")
+
+ trace.WithRegion(ctx, "region0", func() {
+ // EvUserRegionBegin("region0", start)
+ trace.WithRegion(ctx, "region1", func() {
+ trace.Log(ctx, "key0", "0123456789abcdef") // EvUserLog("task0", "key0", "0....f")
+ })
+ // EvUserRegionEnd("region0", end)
+ })
+ }()
+ wg.Wait()
+
+ preExistingRegion.End()
+ postExistingRegion := trace.StartRegion(bgctx, "post-existing region")
+
+ // End of traced execution.
+ trace.Stop()
+
+ postExistingRegion.End()
+}
diff --git a/src/internal/trace/v2/testdata/testprog/cgo-callback.go b/src/internal/trace/v2/testdata/testprog/cgo-callback.go
new file mode 100644
index 0000000..d636500
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/cgo-callback.go
@@ -0,0 +1,80 @@
+// Copyright 2023 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.
+
+// Tests CPU profiling.
+
+//go:build ignore
+
+package main
+
+/*
+#include <pthread.h>
+
+void go_callback();
+void go_callback2();
+
+static void *thr(void *arg) {
+ go_callback();
+ return 0;
+}
+
+static void foo() {
+ pthread_t th;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 256 << 10);
+ pthread_create(&th, &attr, thr, 0);
+ pthread_join(th, 0);
+}
+
+static void bar() {
+ go_callback2();
+}
+*/
+import "C"
+
+import (
+ "log"
+ "os"
+ "runtime"
+ "runtime/trace"
+)
+
+//export go_callback
+func go_callback() {
+ // Do another call into C, just to test that path too.
+ C.bar()
+}
+
+//export go_callback2
+func go_callback2() {
+ runtime.GC()
+}
+
+func main() {
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+
+ // Do a whole bunch of cgocallbacks.
+ const n = 10
+ done := make(chan bool)
+ for i := 0; i < n; i++ {
+ go func() {
+ C.foo()
+ done <- true
+ }()
+ }
+ for i := 0; i < n; i++ {
+ <-done
+ }
+
+ // Do something to steal back any Ps from the Ms, just
+ // for coverage.
+ runtime.GC()
+
+ // End of traced execution.
+ trace.Stop()
+}
diff --git a/src/internal/trace/v2/testdata/testprog/cpu-profile.go b/src/internal/trace/v2/testdata/testprog/cpu-profile.go
new file mode 100644
index 0000000..293a2ac
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/cpu-profile.go
@@ -0,0 +1,137 @@
+// Copyright 2023 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.
+
+// Tests CPU profiling.
+
+//go:build ignore
+
+package main
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "internal/profile"
+ "log"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "runtime/trace"
+ "strings"
+ "time"
+)
+
+func main() {
+ cpuBuf := new(bytes.Buffer)
+ if err := pprof.StartCPUProfile(cpuBuf); err != nil {
+ log.Fatalf("failed to start CPU profile: %v", err)
+ }
+
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+
+ dur := 100 * time.Millisecond
+ func() {
+ // Create a region in the execution trace. Set and clear goroutine
+ // labels fully within that region, so we know that any CPU profile
+ // sample with the label must also be eligible for inclusion in the
+ // execution trace.
+ ctx := context.Background()
+ defer trace.StartRegion(ctx, "cpuHogger").End()
+ pprof.Do(ctx, pprof.Labels("tracing", "on"), func(ctx context.Context) {
+ cpuHogger(cpuHog1, &salt1, dur)
+ })
+ // Be sure the execution trace's view, when filtered to this goroutine
+ // via the explicit goroutine ID in each event, gets many more samples
+ // than the CPU profiler when filtered to this goroutine via labels.
+ cpuHogger(cpuHog1, &salt1, dur)
+ }()
+
+ trace.Stop()
+ pprof.StopCPUProfile()
+
+ // Summarize the CPU profile to stderr so the test can check against it.
+
+ prof, err := profile.Parse(cpuBuf)
+ if err != nil {
+ log.Fatalf("failed to parse CPU profile: %v", err)
+ }
+ // Examine the CPU profiler's view. Filter it to only include samples from
+ // the single test goroutine. Use labels to execute that filter: they should
+ // apply to all work done while that goroutine is getg().m.curg, and they
+ // should apply to no other goroutines.
+ pprofStacks := make(map[string]int)
+ for _, s := range prof.Sample {
+ if s.Label["tracing"] != nil {
+ var fns []string
+ var leaf string
+ for _, loc := range s.Location {
+ for _, line := range loc.Line {
+ fns = append(fns, fmt.Sprintf("%s:%d", line.Function.Name, line.Line))
+ leaf = line.Function.Name
+ }
+ }
+ // runtime.sigprof synthesizes call stacks when "normal traceback is
+ // impossible or has failed", using particular placeholder functions
+ // to represent common failure cases. Look for those functions in
+ // the leaf position as a sign that the call stack and its
+ // symbolization are more complex than this test can handle.
+ //
+ // TODO: Make the symbolization done by the execution tracer and CPU
+ // profiler match up even in these harder cases. See #53378.
+ switch leaf {
+ case "runtime._System", "runtime._GC", "runtime._ExternalCode", "runtime._VDSO":
+ continue
+ }
+ stack := strings.Join(fns, "|")
+ samples := int(s.Value[0])
+ pprofStacks[stack] += samples
+ }
+ }
+ for stack, samples := range pprofStacks {
+ fmt.Fprintf(os.Stderr, "%s\t%d\n", stack, samples)
+ }
+}
+
+func cpuHogger(f func(x int) int, y *int, dur time.Duration) {
+ // We only need to get one 100 Hz clock tick, so we've got
+ // a large safety buffer.
+ // But do at least 500 iterations (which should take about 100ms),
+ // otherwise TestCPUProfileMultithreaded can fail if only one
+ // thread is scheduled during the testing period.
+ t0 := time.Now()
+ accum := *y
+ for i := 0; i < 500 || time.Since(t0) < dur; i++ {
+ accum = f(accum)
+ }
+ *y = accum
+}
+
+var (
+ salt1 = 0
+)
+
+// The actual CPU hogging function.
+// Must not call other functions nor access heap/globals in the loop,
+// otherwise under race detector the samples will be in the race runtime.
+func cpuHog1(x int) int {
+ return cpuHog0(x, 1e5)
+}
+
+func cpuHog0(x, n int) int {
+ foo := x
+ for i := 0; i < n; i++ {
+ if i%1000 == 0 {
+ // Spend time in mcall, stored as gp.m.curg, with g0 running
+ runtime.Gosched()
+ }
+ if foo > 0 {
+ foo *= foo
+ } else {
+ foo *= foo + 1
+ }
+ }
+ return foo
+}
diff --git a/src/internal/trace/v2/testdata/testprog/futile-wakeup.go b/src/internal/trace/v2/testdata/testprog/futile-wakeup.go
new file mode 100644
index 0000000..cc48981
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/futile-wakeup.go
@@ -0,0 +1,84 @@
+// Copyright 2023 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.
+
+// Tests to make sure the runtime doesn't generate futile wakeups. For example,
+// it makes sure that a block on a channel send that unblocks briefly only to
+// immediately go back to sleep (in such a way that doesn't reveal any useful
+// information, and is purely an artifact of the runtime implementation) doesn't
+// make it into the trace.
+
+//go:build ignore
+
+package main
+
+import (
+ "context"
+ "log"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "sync"
+)
+
+func main() {
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
+ c0 := make(chan int, 1)
+ c1 := make(chan int, 1)
+ c2 := make(chan int, 1)
+ const procs = 2
+ var done sync.WaitGroup
+ done.Add(4 * procs)
+ for p := 0; p < procs; p++ {
+ const iters = 1e3
+ go func() {
+ trace.WithRegion(context.Background(), "special", func() {
+ for i := 0; i < iters; i++ {
+ runtime.Gosched()
+ c0 <- 0
+ }
+ done.Done()
+ })
+ }()
+ go func() {
+ trace.WithRegion(context.Background(), "special", func() {
+ for i := 0; i < iters; i++ {
+ runtime.Gosched()
+ <-c0
+ }
+ done.Done()
+ })
+ }()
+ go func() {
+ trace.WithRegion(context.Background(), "special", func() {
+ for i := 0; i < iters; i++ {
+ runtime.Gosched()
+ select {
+ case c1 <- 0:
+ case c2 <- 0:
+ }
+ }
+ done.Done()
+ })
+ }()
+ go func() {
+ trace.WithRegion(context.Background(), "special", func() {
+ for i := 0; i < iters; i++ {
+ runtime.Gosched()
+ select {
+ case <-c1:
+ case <-c2:
+ }
+ }
+ done.Done()
+ })
+ }()
+ }
+ done.Wait()
+
+ trace.Stop()
+}
diff --git a/src/internal/trace/v2/testdata/testprog/gc-stress.go b/src/internal/trace/v2/testdata/testprog/gc-stress.go
new file mode 100644
index 0000000..70d3a24
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/gc-stress.go
@@ -0,0 +1,76 @@
+// Copyright 2023 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.
+
+// Tests a GC-heavy program. This is useful for shaking out
+// all sorts of corner cases about GC-related ranges.
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "math/rand"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "time"
+)
+
+type node struct {
+ children [4]*node
+ data [128]byte
+}
+
+func makeTree(depth int) *node {
+ if depth == 0 {
+ return new(node)
+ }
+ return &node{
+ children: [4]*node{
+ makeTree(depth - 1),
+ makeTree(depth - 1),
+ makeTree(depth - 1),
+ makeTree(depth - 1),
+ },
+ }
+}
+
+var trees [16]*node
+var ballast *[16]*[8192]*node
+var sink []byte
+
+func main() {
+ for i := range trees {
+ trees[i] = makeTree(6)
+ }
+ ballast = new([16]*[8192]*node)
+ for i := range ballast {
+ ballast[i] = new([8192]*node)
+ for j := range ballast[i] {
+ ballast[i][j] = &node{
+ data: [128]byte{1, 2, 3, 4},
+ }
+ }
+ }
+ for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
+ go func() {
+ for {
+ sink = make([]byte, rand.Intn(32<<10))
+ }
+ }()
+ }
+ // Increase the chance that we end up starting and stopping
+ // mid-GC by only starting to trace after a few milliseconds.
+ time.Sleep(5 * time.Millisecond)
+
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ defer trace.Stop()
+
+ // Let the tracing happen for a bit.
+ time.Sleep(400 * time.Millisecond)
+}
diff --git a/src/internal/trace/v2/testdata/testprog/gomaxprocs.go b/src/internal/trace/v2/testdata/testprog/gomaxprocs.go
new file mode 100644
index 0000000..2651207
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/gomaxprocs.go
@@ -0,0 +1,46 @@
+// Copyright 2023 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.
+
+// Tests increasing and decreasing GOMAXPROCS to try and
+// catch issues with stale proc state.
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "time"
+)
+
+func main() {
+ // Start a goroutine that calls runtime.GC to try and
+ // introduce some interesting events in between the
+ // GOMAXPROCS calls.
+ go func() {
+ for {
+ runtime.GC()
+ time.Sleep(1 * time.Millisecond)
+ }
+ }()
+
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ // Run GOMAXPROCS a bunch of times, up and down.
+ for i := 1; i <= 16; i *= 2 {
+ runtime.GOMAXPROCS(i)
+ time.Sleep(1 * time.Millisecond)
+ }
+ for i := 16; i >= 1; i /= 2 {
+ runtime.GOMAXPROCS(i)
+ time.Sleep(1 * time.Millisecond)
+ }
+ // Stop tracing.
+ trace.Stop()
+}
diff --git a/src/internal/trace/v2/testdata/testprog/many-start-stop.go b/src/internal/trace/v2/testdata/testprog/many-start-stop.go
new file mode 100644
index 0000000..2d5d063
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/many-start-stop.go
@@ -0,0 +1,38 @@
+// Copyright 2023 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.
+
+// Tests simply starting and stopping tracing multiple times.
+//
+// This is useful for finding bugs in trace state reset.
+
+//go:build ignore
+
+package main
+
+import (
+ "bytes"
+ "log"
+ "os"
+ "runtime"
+ "runtime/trace"
+)
+
+func main() {
+ // Trace a few times.
+ for i := 0; i < 10; i++ {
+ var buf bytes.Buffer
+ if err := trace.Start(&buf); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ runtime.GC()
+ trace.Stop()
+ }
+
+ // Start tracing again, this time writing out the result.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ runtime.GC()
+ trace.Stop()
+}
diff --git a/src/internal/trace/v2/testdata/testprog/stacks.go b/src/internal/trace/v2/testdata/testprog/stacks.go
new file mode 100644
index 0000000..e64bc86
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/stacks.go
@@ -0,0 +1,129 @@
+// Copyright 2023 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.
+
+// Tests stack symbolization.
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "net"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "sync"
+ "time"
+)
+
+func main() {
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ defer trace.Stop() // in case of early return
+
+ // Now we will do a bunch of things for which we verify stacks later.
+ // It is impossible to ensure that a goroutine has actually blocked
+ // on a channel, in a select or otherwise. So we kick off goroutines
+ // that need to block first in the hope that while we are executing
+ // the rest of the test, they will block.
+ go func() { // func1
+ select {}
+ }()
+ go func() { // func2
+ var c chan int
+ c <- 0
+ }()
+ go func() { // func3
+ var c chan int
+ <-c
+ }()
+ done1 := make(chan bool)
+ go func() { // func4
+ <-done1
+ }()
+ done2 := make(chan bool)
+ go func() { // func5
+ done2 <- true
+ }()
+ c1 := make(chan int)
+ c2 := make(chan int)
+ go func() { // func6
+ select {
+ case <-c1:
+ case <-c2:
+ }
+ }()
+ var mu sync.Mutex
+ mu.Lock()
+ go func() { // func7
+ mu.Lock()
+ mu.Unlock()
+ }()
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() { // func8
+ wg.Wait()
+ }()
+ cv := sync.NewCond(&sync.Mutex{})
+ go func() { // func9
+ cv.L.Lock()
+ cv.Wait()
+ cv.L.Unlock()
+ }()
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatalf("failed to listen: %v", err)
+ }
+ go func() { // func10
+ c, err := ln.Accept()
+ if err != nil {
+ log.Printf("failed to accept: %v", err)
+ return
+ }
+ c.Close()
+ }()
+ rp, wp, err := os.Pipe()
+ if err != nil {
+ log.Fatalf("failed to create a pipe: %v", err)
+ }
+ defer rp.Close()
+ defer wp.Close()
+ pipeReadDone := make(chan bool)
+ go func() { // func11
+ var data [1]byte
+ rp.Read(data[:])
+ pipeReadDone <- true
+ }()
+
+ time.Sleep(100 * time.Millisecond)
+ runtime.GC()
+ runtime.Gosched()
+ time.Sleep(100 * time.Millisecond) // the last chance for the goroutines above to block
+ done1 <- true
+ <-done2
+ select {
+ case c1 <- 0:
+ case c2 <- 0:
+ }
+ mu.Unlock()
+ wg.Done()
+ cv.Signal()
+ c, err := net.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ log.Fatalf("failed to dial: %v", err)
+ }
+ c.Close()
+ var data [1]byte
+ wp.Write(data[:])
+ <-pipeReadDone
+
+ oldGoMaxProcs := runtime.GOMAXPROCS(0)
+ runtime.GOMAXPROCS(oldGoMaxProcs + 1)
+
+ trace.Stop()
+
+ runtime.GOMAXPROCS(oldGoMaxProcs)
+}
diff --git a/src/internal/trace/v2/testdata/testprog/stress-start-stop.go b/src/internal/trace/v2/testdata/testprog/stress-start-stop.go
new file mode 100644
index 0000000..72c1c59
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/stress-start-stop.go
@@ -0,0 +1,166 @@
+// Copyright 2023 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.
+
+// Tests a many interesting cases (network, syscalls, a little GC, busy goroutines,
+// blocked goroutines, LockOSThread, pipes, and GOMAXPROCS).
+
+//go:build ignore
+
+package main
+
+import (
+ "bytes"
+ "io"
+ "log"
+ "net"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "sync"
+ "time"
+)
+
+func main() {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
+ outerDone := make(chan bool)
+
+ go func() {
+ defer func() {
+ outerDone <- true
+ }()
+
+ var wg sync.WaitGroup
+ done := make(chan bool)
+
+ wg.Add(1)
+ go func() {
+ <-done
+ wg.Done()
+ }()
+
+ rp, wp, err := os.Pipe()
+ if err != nil {
+ log.Fatalf("failed to create pipe: %v", err)
+ return
+ }
+ defer func() {
+ rp.Close()
+ wp.Close()
+ }()
+ wg.Add(1)
+ go func() {
+ var tmp [1]byte
+ rp.Read(tmp[:])
+ <-done
+ wg.Done()
+ }()
+ time.Sleep(time.Millisecond)
+
+ go func() {
+ runtime.LockOSThread()
+ for {
+ select {
+ case <-done:
+ return
+ default:
+ runtime.Gosched()
+ }
+ }
+ }()
+
+ runtime.GC()
+ // Trigger GC from malloc.
+ n := 512
+ for i := 0; i < n; i++ {
+ _ = make([]byte, 1<<20)
+ }
+
+ // Create a bunch of busy goroutines to load all Ps.
+ for p := 0; p < 10; p++ {
+ wg.Add(1)
+ go func() {
+ // Do something useful.
+ tmp := make([]byte, 1<<16)
+ for i := range tmp {
+ tmp[i]++
+ }
+ _ = tmp
+ <-done
+ wg.Done()
+ }()
+ }
+
+ // Block in syscall.
+ wg.Add(1)
+ go func() {
+ var tmp [1]byte
+ rp.Read(tmp[:])
+ <-done
+ wg.Done()
+ }()
+
+ runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+
+ // Test timers.
+ timerDone := make(chan bool)
+ go func() {
+ time.Sleep(time.Millisecond)
+ timerDone <- true
+ }()
+ <-timerDone
+
+ // A bit of network.
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatalf("listen failed: %v", err)
+ return
+ }
+ defer ln.Close()
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ return
+ }
+ time.Sleep(time.Millisecond)
+ var buf [1]byte
+ c.Write(buf[:])
+ c.Close()
+ }()
+ c, err := net.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ log.Fatalf("dial failed: %v", err)
+ return
+ }
+ var tmp [1]byte
+ c.Read(tmp[:])
+ c.Close()
+
+ go func() {
+ runtime.Gosched()
+ select {}
+ }()
+
+ // Unblock helper goroutines and wait them to finish.
+ wp.Write(tmp[:])
+ wp.Write(tmp[:])
+ close(done)
+ wg.Wait()
+ }()
+
+ const iters = 5
+ for i := 0; i < iters; i++ {
+ var w io.Writer
+ if i == iters-1 {
+ w = os.Stdout
+ } else {
+ w = new(bytes.Buffer)
+ }
+ if err := trace.Start(w); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ time.Sleep(time.Millisecond)
+ trace.Stop()
+ }
+ <-outerDone
+}
diff --git a/src/internal/trace/v2/testdata/testprog/stress.go b/src/internal/trace/v2/testdata/testprog/stress.go
new file mode 100644
index 0000000..99696d1
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/stress.go
@@ -0,0 +1,146 @@
+// Copyright 2023 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.
+
+// Tests a many interesting cases (network, syscalls, a little GC, busy goroutines,
+// blocked goroutines, LockOSThread, pipes, and GOMAXPROCS).
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "net"
+ "os"
+ "runtime"
+ "runtime/trace"
+ "sync"
+ "time"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ done := make(chan bool)
+
+ // Create a goroutine blocked before tracing.
+ wg.Add(1)
+ go func() {
+ <-done
+ wg.Done()
+ }()
+
+ // Create a goroutine blocked in syscall before tracing.
+ rp, wp, err := os.Pipe()
+ if err != nil {
+ log.Fatalf("failed to create pipe: %v", err)
+ }
+ defer func() {
+ rp.Close()
+ wp.Close()
+ }()
+ wg.Add(1)
+ go func() {
+ var tmp [1]byte
+ rp.Read(tmp[:])
+ <-done
+ wg.Done()
+ }()
+ time.Sleep(time.Millisecond) // give the goroutine above time to block
+
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+ defer trace.Stop()
+
+ procs := runtime.GOMAXPROCS(10)
+ time.Sleep(50 * time.Millisecond) // test proc stop/start events
+
+ go func() {
+ runtime.LockOSThread()
+ for {
+ select {
+ case <-done:
+ return
+ default:
+ runtime.Gosched()
+ }
+ }
+ }()
+
+ runtime.GC()
+ // Trigger GC from malloc.
+ n := 512
+ for i := 0; i < n; i++ {
+ _ = make([]byte, 1<<20)
+ }
+
+ // Create a bunch of busy goroutines to load all Ps.
+ for p := 0; p < 10; p++ {
+ wg.Add(1)
+ go func() {
+ // Do something useful.
+ tmp := make([]byte, 1<<16)
+ for i := range tmp {
+ tmp[i]++
+ }
+ _ = tmp
+ <-done
+ wg.Done()
+ }()
+ }
+
+ // Block in syscall.
+ wg.Add(1)
+ go func() {
+ var tmp [1]byte
+ rp.Read(tmp[:])
+ <-done
+ wg.Done()
+ }()
+
+ // Test timers.
+ timerDone := make(chan bool)
+ go func() {
+ time.Sleep(time.Millisecond)
+ timerDone <- true
+ }()
+ <-timerDone
+
+ // A bit of network.
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatalf("listen failed: %v", err)
+ }
+ defer ln.Close()
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ return
+ }
+ time.Sleep(time.Millisecond)
+ var buf [1]byte
+ c.Write(buf[:])
+ c.Close()
+ }()
+ c, err := net.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ log.Fatalf("dial failed: %v", err)
+ }
+ var tmp [1]byte
+ c.Read(tmp[:])
+ c.Close()
+
+ go func() {
+ runtime.Gosched()
+ select {}
+ }()
+
+ // Unblock helper goroutines and wait them to finish.
+ wp.Write(tmp[:])
+ wp.Write(tmp[:])
+ close(done)
+ wg.Wait()
+
+ runtime.GOMAXPROCS(procs)
+}
diff --git a/src/internal/trace/v2/testdata/testprog/wait-on-pipe.go b/src/internal/trace/v2/testdata/testprog/wait-on-pipe.go
new file mode 100644
index 0000000..912f5dd
--- /dev/null
+++ b/src/internal/trace/v2/testdata/testprog/wait-on-pipe.go
@@ -0,0 +1,66 @@
+// Copyright 2023 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.
+
+// Tests a goroutine sitting blocked in a syscall for
+// an entire generation. This is a regression test for
+// #65196.
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "os"
+ "runtime/trace"
+ "syscall"
+ "time"
+)
+
+func main() {
+ // Create a pipe to block on.
+ var p [2]int
+ if err := syscall.Pipe(p[:]); err != nil {
+ log.Fatalf("failed to create pipe: %v", err)
+ }
+ rfd, wfd := p[0], p[1]
+
+ // Create a goroutine that blocks on the pipe.
+ done := make(chan struct{})
+ go func() {
+ var data [1]byte
+ _, err := syscall.Read(rfd, data[:])
+ if err != nil {
+ log.Fatalf("failed to read from pipe: %v", err)
+ }
+ done <- struct{}{}
+ }()
+
+ // Give the goroutine ample chance to block on the pipe.
+ time.Sleep(10 * time.Millisecond)
+
+ // Start tracing.
+ if err := trace.Start(os.Stdout); err != nil {
+ log.Fatalf("failed to start tracing: %v", err)
+ }
+
+ // This isn't enough to have a full generation pass by default,
+ // but it is generally enough in stress mode.
+ time.Sleep(100 * time.Millisecond)
+
+ // Write to the pipe to unblock it.
+ if _, err := syscall.Write(wfd, []byte{10}); err != nil {
+ log.Fatalf("failed to write to pipe: %v", err)
+ }
+
+ // Wait for the goroutine to unblock and start running.
+ // This is helpful to catch incorrect information written
+ // down for the syscall-blocked goroutine, since it'll start
+ // executing, and that execution information will be
+ // inconsistent.
+ <-done
+
+ // Stop tracing.
+ trace.Stop()
+}